/* Common functions used by both the client and server. Many of the functions defined here work with IPv4 and v6. */ #include /* standard C i/o facilities */ #include /* Unix System Calls */ #include /* system data type definitions */ #include /* socket specific definitions */ #include /* INET constants and stuff */ #include /* IP address conversion stuff */ #include /* malloc */ #include /* gethostname */ #include /* This function will print out information about a connected socket, including the server hostname and service (port). Uses getaddrinfo (so it takes care of IP version details). Note that we need to used a sockaddr_in6 here, since a sockaddr_in is too small to hold an IPv6 address... */ void printremoteinfo(int s) { struct sockaddr_in6 peer; int len; char hostname[NI_MAXHOST]; char servname[NI_MAXSERV]; len = sizeof(peer); getpeername(s,(struct sockaddr *)&peer,&len); /* call getnameinfo and request that the address returned is specified as a numeric value instead of a hostname */ if (getnameinfo((struct sockaddr *)&peer,len,hostname,sizeof(hostname),servname,sizeof(servname),NI_NUMERICHOST) != 0) { printf("Error getting name info\n"); return; } // figure out what kind of address this is if (peer.sin6_family==PF_INET6) { if (IN6_IS_ADDR_V4MAPPED( &peer.sin6_addr )) { printf("Host: %s (IPv4 Mapped ) Service: %s\n",hostname,servname); } else if (IN6_IS_ADDR_V4COMPAT(&peer.sin6_addr)) { printf("Host: %s (IPv4 Compatible ) Service: %s\n",hostname,servname); } else { printf("Host: %s (IPv6) Service: %s\n",hostname,servname); } } else { printf("Host: %s (IPv4) Service: %s\n",hostname,servname); } } /* print local endpoint IPv6 address uses getsockname and getaddrinfo to come up with a printable version of the address. */ void printlocalinfo(int s) { struct sockaddr_in6 local; struct in6_addr x; int len; char hostname[NI_MAXHOST]; char servname[NI_MAXSERV]; len = sizeof(local); getsockname(s,(struct sockaddr *)&local,&len); if (getnameinfo((struct sockaddr *)&local,len,hostname,sizeof(hostname),servname,sizeof(servname),NULL) != 0) { printf("Error getting name info\n"); return; } /* figure out what kind of address this is */ if (local.sin6_family==PF_INET6) { x = local.sin6_addr; printf("Host: %s (IPv6) Service: %s\n",hostname,servname); if (IN6_IS_ADDR_V4MAPPED( &x )) { printf("v4 Mapped\n"); } else if (IN6_IS_ADDR_V4COMPAT(&x)) { printf("v4 Compat\n"); } } else { printf("Host: %s (IPv4) Service: %s\n",hostname,servname); } } /* Create a TCP passive mode socket bound to a port (service). The proto_family argument determines whether the socket created is an IPv4 of IPv6 socket (PF_INET or PF_INET6). When run on a machine with both IPv4 and IPv6 support, this socket can be connected to by either and IPv4 or IPv6 client. IPv4 clients appear to this server with IPv4-mapped addresses automatically. */ int tcp_serversock(int proto_family, char * port) { int s; int rcode; struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); hints.ai_family = proto_family; hints.ai_socktype = SOCK_STREAM; rcode = getaddrinfo("0::0", port, &hints, &res); /* Make sure we got something */ if (rcode != 0) { /* use gai_strerror to print out the error (and then quit). */ printf("Fatal Error calling getaddrinfo: %s\n",gai_strerror(rcode)); exit(1); } /* create a socket using the information returned by getaddrinfo. */ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s<0) { perror("Error creating socket"); exit(1); } /* bind to the specified port (service) */ if (bind(s, res->ai_addr, res->ai_addrlen) < 0) { perror("ERROR Can't bind: "); exit(1); } if (listen(s,5)<0) { perror("ERROR can't listen: "); exit(1); } /* we need to free up the linked list built by getaddrinfo! */ freeaddrinfo(res); return(s); } /* Function that creates a stream socket and connects to a named stream (TCP) server. Uses getaddrinfo (can support IPv6 and IPv4). */ int tcp_connect(char *server, char * port) { int s; int rcode; struct addrinfo hints, *res; memset(&hints, 0, sizeof(hints)); /*=================================================== you can force getaddrinfo to return only an IPv6 address by setting the ai_family in hints to PF_INET6. Note that if you expect this to work with a hostname (like eggbeater.cs.rpi.edu, you need support from DNS, the DNS server must be willing to serve up IPv6 addresses (the CS dns server does not seem to do this). Asking getaddrinfo for only IPv6 addresses will work fine on the CS machines as long as you give it an actual IPv6 address string (something like: 2001:468:903:d50c:a00:20ff:fec6:7a1f) Use the "ifconfig -a" command to find out the IPv6 address of the machine you are logged in to. ======================================================*/ hints.ai_family = PF_UNSPEC; /* hints.ai_family = PF_INET6; */ hints.ai_socktype = SOCK_STREAM; rcode = getaddrinfo(server, port, &hints, &res); /* Make sure we got something */ if (rcode != 0) { /* use gai_strerror to print out the error (and then quit). */ printf("Fatal Error calling getaddrinfo: %s\n",gai_strerror(rcode)); exit(1); } if (res->ai_family==AF_INET6) printf("Using an INET 6 address for server\n"); else if (res->ai_family==AF_INET) printf("Using an INET 4 address for server\n"); else printf("Don't know what kind of address is being used!\n"); /* create a socket using the information returned by getaddrinfo. */ s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); /* attempt to connect to the server */ if (connect(s, res->ai_addr, res->ai_addrlen) < 0) { perror("ERROR CONNECTING: "); exit(1); } /* we need to free up the linked list built by getaddrinfo! */ freeaddrinfo(res); return(s); } /* multiplexing function reads from stdin and a socket, forwarding whatever is received from one source to the other. This code doesn't care whether the socket is IPv4 or v6. */ #define MAXMSG 1000 void tcp_talk(int tcpsock) { fd_set inputfds; char buff[MAXMSG]; int i; int max; int watch_stdin = 1; /* Flag keeps track of status of stdin */ /* determine the highest number file descriptor we will use */ max = (tcpsock>STDIN_FILENO ? tcpsock : STDIN_FILENO); /* do this forever */ while (1) { FD_ZERO(&inputfds); /* clear out entire set */ FD_SET(tcpsock,&inputfds); /* add the socket to the set */ if (watch_stdin) FD_SET(STDIN_FILENO,&inputfds); /* add stdin to the set */ /* call select - this blocks until there is something to read on one of the file descriptors */ if (select(max+1,&inputfds,NULL,NULL,NULL)<0) { perror("select problem"); exit(1); } /* If STDIN has something - handle it */ if (FD_ISSET(STDIN_FILENO,&inputfds)) { /* Read up to MAXMSG bytes from STDIN */ i = read(STDIN_FILENO,buff,MAXMSG); if (i<0) { perror("Error reading from STDIN"); exit(1); } else if (i==0) { /* STDIN closed (EOF) - don't quit! (we want to hang around and get stuff from socket)*/ watch_stdin=0; } else { /* Write whatever we got to the socket */ if (write(tcpsock,buff,i)<0) { perror("Error writing to socket"); exit(1); } } } /* If tcpsock has something - handle it */ if (FD_ISSET(tcpsock,&inputfds)) { /* Read up to MAXMSG bytes from socket */ i = read(tcpsock,buff,MAXMSG); if (i<0) { perror("Error reading from socket"); exit(1); } else if (i==0) { /* socket closed (EOF) - quit */ break; } else { /* Write whatever we got to STDOUT */ if (write(STDOUT_FILENO,buff,i)<0) { perror("Error writing to STDOUT!?!"); exit(1); } } } } }