To get started on internet programing, let's try to bounce a packet of a distant machine. True, this is not as hopelessly techno-romantic as moon bouncing, but I would think there would be a thrill of accomplishment as you take your first trip on the information highway.
And as the picture shows, we will be riding minimal — a protocol called UDP. UDP is the slightest sliver of a protocol about IP, so it will give you a good feel for packet communication on the Internet. The major software abstraction that we use as programmers is the socket, more properly the Berkeley Socket. This abstraction summarizes all of the machinery into a file handle, very similar to reading and writing a simple file, except with a few outside API entry points, to set the destination addresses, or to sense the source address.
The level 4 UDP protocol add to the level 3 IP procotol the concept of ports. A port is a a 16-bit number the identifies in the context of the IP-endpoint which application-endpoint the data comes from or goes to. In the language of client-server communication, the client must know the destination port on the server of where the service is bound, but must choose as a formality a port for it's own socket so that return-trip data can be returned to it. The server must bind to a known port number and listen for incoming packets on that port, and if a response is required, read the source port of the sender and use that as the destination port for a return trip packet.
This divides port numbers into two classes: those that are fixed and known (on the server side), and those that are floating and will be assigned at some point during sending. The first are called well-known and registered and the second are called ephemeral. This distinction is obvious in the sidebar code in that the well-known port is a pound-define, while the ephemeral port is discovered by a call to getsockname in the client and recvfrom in the server.
/*
** netbounce-server.c
** from Beej's Guide to Network Programming: Using Unix Sockets,
** by Brian "Beej" Hall.
**
** modified by Burt Rosenberg,
** Created: Feb 8, 2009
** Last modified: Jan 30, 2013
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define MYPORT 3333
#define MAXBUFLEN 100
int main(void) {
int sockfd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
unsigned int addr_len, numbytes;
char buf[MAXBUFLEN];
if ((sockfd=socket(AF_INET,SOCK_DGRAM,0))==-1) {
perror("socket") ;
exit(1) ;
}
my_addr.sin_family = AF_INET ;
my_addr.sin_port = htons(MYPORT) ;
my_addr.sin_addr.s_addr = INADDR_ANY ;
memset(&(my_addr.sin_zero),'\0',8) ;
if (bind(sockfd, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr)) == -1 ) {
perror("bind") ;
exit(1) ;
}
addr_len = sizeof(struct sockaddr) ;
if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN-1,0,
(struct sockaddr *)&their_addr, &addr_len)) == -1 ) {
perror("recvfrom") ;
exit(1) ;
}
/* added to get source port number */
printf("got packet from %s, port %d\n", inet_ntoa(their_addr.sin_addr),
ntohs(their_addr.sin_port)) ;
printf("packet is %d bytes long\n", numbytes ) ;
buf[numbytes] = '\0' ;
printf("packet contains \"%s\"\n", buf ) ;
{
/* return the packet to sender,
*/
int bytes_sent ;
if ((bytes_sent = sendto( sockfd, buf, strlen(buf), 0,
(struct sockaddr *)&their_addr,
(struct sockaddr)))==-1 ) {
perror("send") ;
exit(1) ;
}
printf("packet sent, %d bytes\n", bytes_sent) ;
}
close(sockfd) ;
return 0 ;
}
/*
** netbounce-client.c
** from Beej's Guide to Network Programming: Using Unix Sockets,
** by Brian "Beej" Hall.
**
** modified by Burt Rosenberg,
** Created: Feb 8, 2009
** Last modified: Jan 30, 2013
*/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<netdb.h>
#define MYPORT 3333
int main( int argc, char * argv[] ) {
int sockfd ;
struct sockaddr_in their_addr ;
struct hostent *he ;
int numbytes ;
if ( argc!=3 ) {
fprintf(stderr,"usage: netbounce-client hostname message\n") ;
exit(1) ;
}
if ((he=gethostbyname(argv[1]))==NULL) {
perror("gethostbyname") ;
exit(1) ;
}
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1 ) {
perror("socket") ;
exit(1) ;
}
their_addr.sin_family = AF_INET ;
their_addr.sin_port = htons(MYPORT) ;
their_addr.sin_addr = *((struct in_addr *)he->h_addr) ;
memset(&(their_addr.sin_zero), '\0', 8 ) ;
if ((numbytes=sendto(sockfd, argv[2], strlen(argv[2]),0,
(struct sockaddr *)&their_addr,
sizeof(struct sockaddr)) ) == -1 ) {
perror("sendto") ;
exit(1) ;
}
printf("send %d bytes to %s\n", numbytes,
inet_ntoa(their_addr.sin_addr)) ;
/* added by burt;
sendto bound socket, let's get the port number that was selected
*/
{
#define MAXBUFLEN 100
struct sockaddr_in my_addr ;
unsigned int addr_len ;
char buf[MAXBUFLEN];
addr_len = sizeof(struct sockaddr_in) ;
getsockname( sockfd, (struct sockaddr *)&my_addr, &addr_len ) ;
printf("sent from port %d\n", ntohs(my_addr.sin_port)) ;
/* on OSX 10.5.4, this prints out ascending port numbers
each time run, starting around 49486
*/
/* now receive the response ..
*/
/*** put a wait in here to show how buffering worls ***/
printf("sleeping for 5 seconds\n") ;
sleep(5) ;
printf("calling for return packet\n") ;
addr_len = sizeof(struct sockaddr_in) ;
if ((numbytes=recvfrom(sockfd, buf, MAXBUFLEN-1,0,
(struct sockaddr *)&their_addr, &addr_len)) == -1 ) {
perror("recvfrom") ;
exit(1) ;
}
/* added to get source port number */
printf("got packet from %s, port %d\n",
inet_ntoa(their_addr.sin_addr), ntohs(their_addr.sin_port)) ;
printf("packet is %d bytes long\n", numbytes ) ;
buf[numbytes] = '\0' ;
printf("packet contains \"%s\"\n", buf ) ;
}
close(sockfd) ;
return 0 ;
}
#
# Feb 8, 2009
#
CC = gcc
LOCALHOST = localhost
REMOTEHOST = ec2-50-19-171-60.compute-1.amazonaws.com
all:
${CC} -o netbounce-server netbounce-server.c ${LIBS}
${CC} -o netbounce-client netbounce-client.c ${LIBS}
test:
@echo For a local test, for run test-local-s in one window,
@echo then test-local-c in another.
@echo For a netbounce, run nb-s in the far computer,
@echo then nb-c in the near computer.
test-local-c:
./netbounce-server
test-local-s:
./netbounce-client ${LOCALHOST} hello_world
nb-s:
@echo Ready to bounce!
./netbounce-server
nb-c:
./netbounce-client ${REMOTEHOST} "The amazing net bounce!"
submit:
svn commit -m "submitted for grade"
clean:
-rm netbounce-client netbounce-server
UDP sockets can be bound or unbound, and connected or unconnected. First they get bound, after which packets to the port from any host are put on the socket's receive queue. If the socket becomes connected, it is associated with a specific source host and port and the queue will be restricted to packets from that port on that host. Bind binds a socket, and connect connects it.
Recvfrom must be called on a bound socket, and sendto, if called on an unbound socket will bind it to an default interface at an ephemeral port. Neither ever connects the socket. Binding a socket sets the port for both sento and recvfrom. A socket sends and receives on the same port.
Once connected using the connect call, the usage of sendto and recvfrom is restricted — at best the address structure will be ignored. I'm a bit confused about the use of connect, as there seems to be a critical race between arriving packets during the interval between bind and connect — unless connect auto-flushes the receive queue.
As a final note about firewall issues: to bounce a packet, the server code must be on a server, i.e. a machine whose firewall ruleset lets pass unsolicited UDP packets destined for the bounce port. Generally this will not be true of your home machine which is behind a ADSL router, or a typical end-user machine at a school or business. While we can bounce off the moon, martians are not allowed to bounce off of us. We are concerned that the bounce packet will contain a virus that will take over the world.
Seriously. Although not martians, but hackers, are the concern.
You can arrange to have the server run behind a home firewall, but you will have to enable port forwarding, so that the incoming packet is let pass. You can also arrange for this at your university. You'd have to ask IT. And fill out a form. And go before a committee. And take an on-line ethics exam.
However, it will work if the client is behind a firewall, because most firewalls
allow outgoing UDP packets and are generally configured to allow an incoming
UDP packet if it somehow matches a recent outgoing packet.
The firewall, noting a packet crossing its protection barrier from port A of
machine M to port B of machine N, will often drop its guard for
packets coming across its barrier from port B of machine N if it is addressed
to port A of machine M.
Unsolicited packets are dropped as potential attacks.
Copyright 2013 burton rosenberg
Last modified: 30 jan 2013