The examples in ths appendix demonstrate a basic IPv4 client and server and a variety of IPv6 ports of the client and the server. Each example is known to compile and run. Each client can be used with each server in any combination. They are provided for informational purposes only and Solaris Software assumes no liability from their use.
The first example is the original unmodified IPv4 client: myconnect.
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <stdio.h> #include <uinstd.h> int myconnect(char *hostname, int port) { struct sockaddr_in sin; struct hostent *hp; int sock; /* Open socket. */ sock = socket(AF_INET, SOCK_STREAM,0); if (sock == -1) { perror("socket"); return (-1); } /* Get host address. */ hp = gethostbyname(hostname); if(hp == NULL || hp->h_addrtype != AF_INET || hp->h_length != 4) { (void) fprintf(stderr, "Unknown host \"%s\"\n", hostname); (void) close(sock); return (-1); } sin.sin_family = AF_INET; sin.sin_port = htons(port); (void) memcpy((void *)&sin.sin_addr, (void *)hp->h_addr, hp->h_length); /* Connect to the host. */ if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) == -1) { perror("connect"); (void) close(sock); return (-1); } return (sock); } main(int argc, char *argv[]) { int sock; char buf[BUFSIZ]; int cc; switch (argc) { case 2: sock = myconnect(argv[1], IPPORT_ECHO); break; case 3: sock = myconnect(argv[1], atoi(argv[2])); break; default: (void) fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]); exit(1); } if (sock == -1) exit(1); if (write(sock, "hello world", strlen("hello world") + 1) == -1) { perror("write"); exit(1); } cc = read(sock, buf, sizeof (buf)); if (cc == -1) { perror("read"); exit(1); } (void) printf("Read <%s>\n", buf); return (0); }
The second example is a minimal port of myconnect that can connect to both IPv4 and IPv6 servers: myconnect2.
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <stdio.h> #include <unistd.h> int myconnect2(char*hostname, int port) { struct sockaddr_in6 sin; struct hostent *hp; int sock, errnum; /* Open socket. */ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); return (-1); } /* Get host address. An IPv4-mapped IPv6 address is okay. */ hp = getipnodebyname(hostname, AF_INET6, AI_DEFAULT, &errnum); if (hp == NULL) { (void) fprintf(stderr, "Unknown host \"%s\"\n", hostname); (void) close(sock); return (-1); } /* Make sure all sockaddr_in6 fields are zero */ bzero(&sin, sizeof (sin)); sin.sin6_family = hp->h_addrtype; sin.sin6_port = htons(port); (void) memcpy((void *)&sin.sin6_addr, (void *)hp->h_addr, hp->h_length); freehostent(hp); /* Connect to the host. */ if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) == -1) { perror("connect"); (void) close(sock); return (-1); } return (sock); } main(int argc, char *argv[]) { int sock; char buf[BUFSIZ]; int cc; switch (argc) { case 2: sock = myconnect2(argv[1], IPPORT_ECHO); break; case 3: sock = myconnect2(argv[1], atoi(argv[2])); break; default: (void) fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]); exit(1); } if (sock == -1) exit(1); if (write(sock, "hello world", strlen("hello world") + 1) == -1) { perror("write"); exit(1); } cc = read(sock, buf, sizeof (buf)); if (cc == -1) { perror("read"); exit(1); } (void) printf("Read <%s>\n", buf); return (0); }
The third example is an expansion of the second example which tries all addresses until one works: myconnect2all. Note that it is more complex than the fourth example, which has the same capability.
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <stdio.h> #include <unistd.h> #include <errno.h> int myconnect2(char *hostname, int port) { struct sockaddr_in6 sin; struct hostent *hp; int sock, errnum; int h_addr_index; /* Open socket. */ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); return (-1); } /* Get host address. An IPv4-mapped IPv6 address is okay. */ hp = getipnodebyname(hostname, AF_INET6, AI_DEFAULT|AI_ALL, &errnum); if (hp == NULL) { (void) fprintf(stderr, "Unknown host \"%s\"\n", hostname); (void) close(sock); return (-1); } /* Make sure all sockaddr_in6 fields are zero */ bzero(&sin, sizeof (sin)); sin.sin6_family = hp->h_addrtype; sin.sin6_port = htons(port); /* Try all returned addresses until one works */ h_addr_index = 0; while (hp->h_addr_list[h_addr_index] != NULL) { bcopy(hp->h_addr_list[h_addr_index], &sin.sin6_addr, hp->h_length); /* Connect to the host. */ if (connect(sock, (struct sockaddr *)&sin, sizeof (sin)) == -1) { if (hp->h_addr_list[++h_addr_index] != NULL) { /* Try next address */ continue; } perror("connect"); freehostent(hp); (void) close(sock); return (-1); } break; } freehostent(hp); return (sock); } main(int argc, char *argv[]) { int sock; char buf[BUFSIZ]; int cc; switch (argc) { case 2: sock = myconnect2(argv[1], IPPORT_ECHO); break; case 3: sock = myconnect2(argv[1], atoi(argv[2])); break; default: (void) fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]); exit(1); } if (sock == -1) exit(1); if (write(sock, "hello world", strlen("hello world") + 1) == -1) { perror("write"); exit(1); } cc = read(sock, buf, sizeof (buf)); if (cc == -1) { perror("read"); exit(1); } (void) printf("Read <%s>\n", buf); return (0); }
The fourth example is a port of myconnect that uses the new getaddrinfo(3SOCKET) interface. The routine tries all addresses until one works. It is myconnect3. Note that this program is much simpler than is myconnect2all and is the recommended way to do this port from IPv4 to IPv6.
#include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <string.h> #include <stdio.h> #include <unistd.h> int myconnect3(char *hostname, char *servicename) { struct addrinfo *res, *aip; struct addrinfo hints; int sock = -1; int error; /* Get host address. Any type of address will do. */ bzero(&hints, sizeof (hints)); hints.ai_flags = AI_ALL|AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(hostname, servicename, &hints, &res); if (error != 0) { (void) fprintf(stderr, "getaddrinfo: %s for host %s service %s\n", gai_strerror(error), hostname, servicename); return (-1); } /* Try all returned addresses until one works */ for (aip = res; aip != NULL; aip = aip->ai_next) { /* * Open socket. The address type depends on what * getaddrinfo() gave us. */ sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); if (sock == -1) { perror("socket"); freeaddrinfo(res); return (-1); } /* Connect to the host. */ if (connect(sock, aip->ai_addr, aip->ai_addrlen) == -1) { perror("connect"); (void) close(sock); sock = -1; continue; } break; } freeaddrinfo(res); return (sock); } main(int argc, char *argv[]) { int sock; char buf[BUFSIZ]; int cc; switch (argc) { case 1: sock = myconnect3(NULL, NULL); break; case 2: sock = myconnect3(argv[1], "echo"); break; case 3: sock = myconnect3(argv[1], argv[2]); break; default: (void) fprintf(stderr, "Usage: %s <hostname> <port>\n", argv[0]); exit(1); } if (sock == -1) exit(1); if (write(sock, "hello world", strlen("hello world") + 1) == -1) { perror("write"); exit(1); } cc = read(sock, buf, sizeof (buf)); if (cc == -1) { perror("read"); exit(1); } (void) printf("Read <%s>\n", buf); return (0); }
The fifth example is an original, unmodified IPv4 server: myserver.
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #define BACKLOG 1024 /* max # pending connections */ void do_work(int sock); int myserver(int port) { struct sockaddr_in laddr, faddr; int sock, new_sock, sock_opt; socklen_t faddrlen; /* Set up a socket to listen on for connections. */ laddr.sin_family = AF_INET; laddr.sin_port = htons(port); laddr.sin_addr.s_addr = INADDR_ANY; sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); return (-1); } /* Tell the system to allow local addresses to be reused. */ sock_opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof (sock_opt)) == -1) { perror("setsockopt(SO_REUSEADDR)"); (void) close(sock); return (-1); } if (bind(sock, (struct sockaddr *)&laddr, sizeof (laddr)) == -1) { perror("bind"); (void) close(sock); return (-1); } if (listen(sock, BACKLOG) == -1) { perror("listen"); (void) close(sock); return (-1); } /* Wait for a connection request. */ for (;;) { faddrlen = sizeof (faddr); new_sock = accept(sock, (struct sockaddr *)&faddr, &faddrlen); if (new_sock == -1) { if (errno != EINTR && errno != ECONNABORTED) { perror("accept"); } continue; } (void) printf("Connection from %s/%d\n", inet_ntoa(faddr.sin_addr), ntohs(faddr.sin_port)); do_work(new_sock); /* do some work */ } /*NOTREACHED*/ } void do_work(int sock) { char buf[BUFSIZ]; int cc; while (1) { cc = read(sock, buf, sizeof (buf)); if (cc == -1) { perror("read"); exit(1); } if (cc == 0) { /* EOF */ (void) close(sock); (void) printf("Connection closed\n"); return; } buf[cc + 1] = '\0'; (void) printf("Read <%s>\n", buf); if (write(sock, buf, cc) == -1) { perror("write"); exit(1); } } } main(int argc, char *argv[]) { if (argc != 2) { (void) fprintf(stderr, "Usage: %s <port>\n", argv[0]); exit(1); } (void) myserver(htons(atoi(argv[1]))); return (0); }
The sixth example is a minimal port of myserver to handle connection to both IPv4 and IPv6 clients. It uses a single socket, so, IPv4 connections are represented as IPv4-mapped addresses.
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #define BACKLOG 1024 /* max # pending connections */ void do_work(int sock); int myserver2(int port) { struct sockaddr_in6 laddr, faddr; int sock, new_sock, sock_opt; socklen_t faddrlen; char addrbuf[INET6_ADDRSTRLEN]; /* * Set up a socket to listen on for connections. * Make sure all sockaddr_in6 fields are zero. */ bzero(&laddr, sizeof (laddr)); laddr.sin6_family = AF_INET6; laddr.sin6_port = htons(port); laddr.sin6_addr = in6addr_any; /* structure assignment */ sock = socket(AF_INET6, SOCK_STREAM, 0); if (sock == -1) { perror("socket"); return (-1); } /* Tell the system to allow local addresses to be reused. */ sock_opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof (sock_opt)) == -1) { perror("setsockopt(SO_REUSEADDR)"); (void) close(sock); return (-1); } if (bind(sock, (struct sockaddr *)&laddr, sizeof (laddr)) == -1) { perror("bind"); (void) close(sock); return (-1); } if (listen(sock, BACKLOG) == -1) { perror("listen"); (void) close(sock); return (-1); } /* Wait for a connection request. */ for (;;) { faddrlen = sizeof (faddr); new_sock = accept(sock, (struct sockaddr *)&faddr, &faddrlen); if (new_sock == -1) { if (errno != EINTR && errno != ECONNABORTED) { perror("accept"); } continue; } if (IN6_IS_ADDR_V4MAPPED(&faddr.sin6_addr)) { struct in_addr ina; IN6_V4MAPPED_TO_INADDR(&faddr.sin6_addr, &ina); (void) printf("Connection from %s/%d\n", inet_ntop(AF_INET, (void *)&ina, addrbuf, sizeof (addrbuf)), ntohs(faddr.sin6_port)); } else { (void) printf("Connection from %s/%d\n", inet_ntop(AF_INET6, (void *)&faddr.sin6_addr, addrbuf, sizeof (addrbuf)), ntohs(faddr.sin6_port)); } do_work(new_sock); /* do some work */ } /*NOTREACHED*/ } void do_work(intsock) { char buf[BUFSIZ]; int cc; while (1) { cc = read(sock, buf, sizeof (buf)); if (cc == -1) { perror("read"); exit(1); } if (cc == 0) { /* EOF */ (void) close(sock); (void) printf("Connection closed\n"); return; } buf[cc + 1] = '\0'; (void) printf("Read <%s>\n", buf); if (write(sock, buf, cc) == -1) { perror("write"); exit(1); } } } main(int argc, char *argv[]) { if (argc != 2) { (void) fprintf(stderr, "Usage: %s <port>\n", argv[0]); exit(1); } (void) myserver2(htons(atoi(argv[1]))); return (0); }
The seventh example is a port of myserver to IPv6 that uses getnameinfo(3SOCKET) to simplify the logging of the peer's address and name. This is the recommended way to do this port from IPv4 to IPv6.
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #define BACKLOG 1024 /* max # pending connections */ void do_work(int sock); int myserver3(char *servicename) { struct addrinfo *aip; struct addrinfo hints; struct sockaddr_storage faddr; int sock, new_sock, sock_opt; socklen_t faddrlen; int error; char hname[NI_MAXHOST]; char sname[NI_MAXSERV]; /* Set up a socket to listen on for connections. */ bzero(&hints, sizeof (hints)); hints.ai_flags = AI_ALL|AI_ADDRCONFIG|AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(NULL, servicename, &hints, &aip); if (error != 0) { (void) fprintf(stderr, "getaddrinfo: %s for service %s\n", gai_strerror(error), servicename); return (-1); } sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol); if (sock == -1) { perror("socket"); return (-1); } /* Tell the system to allow local addresses to be reused. */ sock_opt = 1; if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof (sock_opt)) == -1) { perror("setsockopt(SO_REUSEADDR)"); (void) close(sock); return (-1); } if (bind(sock, aip->ai_addr, aip->ai_addrlen) == -1) { perror("bind"); (void) close(sock); return (-1); } if (listen(sock, BACKLOG) == -1) { perror("listen"); (void) close(sock); return (-1); } /* Wait for a connection request. */ for (;;) { faddrlen = sizeof (faddr); new_sock = accept(sock, (struct sockaddr *)&faddr, &faddrlen); if (new_sock == -1) { if (errno != EINTR && errno != ECONNABORTED) { perror("accept"); } continue; } error = getnameinfo((struct sockaddr *)&faddr, faddrlen, hname, sizeof (hname), sname, sizeof (sname), NI_NUMERICHOST|NI_NUMERICSERV); if (error) { (void) fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); } else { (void) printf("Connection from (addr) %s/%s\n", hname, sname); } error = getnameinfo((struct sockaddr *)&faddr, faddrlen, hname, sizeof (hname), sname, sizeof (sname), 0); if (error) { (void) fprintf(stderr, "getnameinfo: %s\n", gai_strerror(error)); } else { (void) printf("Connection from (name) %s/%s\n", hname, sname); } do_work(new_sock); /* do some work */ } /*NOTREACHED*/ } void do_work(int sock) { char buf[BUFSIZ]; int cc; while (1) { cc = read(sock, buf, sizeof (buf)); if (cc == -1) { perror("read"); exit(1); } if (cc == 0) { /* EOF */ (void) close(sock); (void) printf("Connection closed\n"); return; } buf[cc + 1] = '\0'; (void) printf("Read <%s>\n", buf); if (write(sock, buf, cc) == -1) { perror("write"); exit(1); } } } main(int argc, char *argv[]) { if (argc != 2) { (void) fprintf(stderr, "Usage: %s <servicename>\n", argv[0]); exit(1); } (void) myserver3(argv[1]); return (0); }
The eighth example is a port of myserver that uses seperate sockets for IPv4 and IPv6 instead of using mapped addresses. This approach is more complex because the use of poll(2) is required.
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdio.h> #include <errno.h> #include <unistd.h> #define BACKLOG 1024 /* max # pending connections */ #define NUMSOCKS 2 /* # sockets to poll */ void do_work(int sock); int myserver4(int port) { struct sockaddr_in laddr4, faddr4; struct sockaddr_in6 laddr6, faddr6; int sock4, sock6, new_sock, sock_opt; socklen_t faddrlen; char addrbuf[INET6_ADDRSTRLEN]; struct pollfd pfd[NUMSOCKS]; /* * Set up the sockets to listen on for connections. One for IPv4; the * other for IPv6. */ laddr4.sin_family = AF_INET; laddr4.sin_port = htons(port); laddr4.sin_addr.s_addr = INADDR_ANY; sock4 = socket(AF_INET, SOCK_STREAM, 0); if (sock4 == -1) { perror("socket"); return (-1); } /* Make sure all sockaddr_in6 fields are zero */ bzero(&laddr6, sizeof (laddr6)); laddr6.sin6_family = AF_INET6; laddr6.sin6_port = htons(port); laddr6.sin6_addr = in6addr_any; /* structure assignment */ sock6 = socket(AF_INET6, SOCK_STREAM, 0); if (sock6 == -1) { perror("socket"); (void) close(sock4); return (-1); } /* Tell the system to allow local addresses to be reused. */ sock_opt = 1; if (setsockopt(sock4, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof (sock_opt)) == -1 || setsockopt(sock6, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, sizeof (sock_opt))) { perror("setsockopt(SO_REUSEADDR)"); (void) close(sock4); (void) close(sock6); return (-1); } if (bind(sock4, (struct sockaddr *)&laddr4, sizeof (laddr4)) == -1 || bind(sock6, (struct sockaddr *)&laddr6, sizeof (laddr6)) == -1) { perror("bind"); (void) close(sock4); (void) close(sock6); return (-1); } if (listen(sock4, BACKLOG) == -1 || listen(sock6, BACKLOG) == -1) { perror("listen"); (void) close(sock4); (void) close(sock6); return (-1); } /* Wait for a connection request. */ pfd[0].fd = sock4; pfd[1].fd = sock6; pfd[0].events = pfd[1].events = POLLIN; for (;;) { if (poll(pfd, NUMSOCKS, -1) == -1) { if (errno != EINTR) { perror("poll"); (void) close(sock4); (void) close(sock6); return (-1); } continue; } if (pfd[0].revents & POLLIN) { /* IPv4 */ faddrlen = sizeof (faddr4); new_sock = accept(sock4, (struct sockaddr *)&faddr4, &faddrlen); if (new_sock == -1) { if (errno != EINTR && errno != ECONNABORTED) { perror("accept"); } continue; } (void) printf("Connection from %s/%d\n", inet_ntop(AF_INET, (void *)&faddr4.sin_addr, addrbuf, sizeof (addrbuf)), ntohs(faddr4.sin_port)); do_work(new_sock); /* do some work */ } if (pfd[1].revents & POLLIN) { /* IPv6 */ faddrlen = sizeof (faddr6); new_sock = accept(sock6, (struct sockaddr *)&faddr6, &faddrlen); if (new_sock == -1) { if (errno != EINTR && errno != ECONNABORTED) { perror("accept"); } continue; } (void) printf("Connection from %s/%d\n", inet_ntop(AF_INET6, (void*)&faddr6.sin6_addr, addrbuf, sizeof (addrbuf)), ntohs(faddr6.sin6_port)); do_work(new_sock); /* do some work */ } } /*NOTREACHED*/ } void do_work(int sock) { char buf[BUFSIZ]; int cc; while (1) { cc = read(sock, buf, sizeof (buf)); if (cc == -1) { perror("read"); exit(1); } if (cc == 0) { /* EOF */ (void) close(sock); (void) printf("Connection closed\n"); return; } buf[cc + 1] = '\0'; (void) printf("Read <%s>\n", buf); if (write(sock, buf, cc) == -1) { perror("write"); exit(1); } } } main(int argc, char *argv[]) { if (argc != 2) { (void) fprintf(stderr, "Usage: %s <port>\n", argv[0]); exit(1); } (void) myserver4(htons(atoi(argv[1]))); return (0); }
This make file compiles and links or lints any or all of the exmples in this appendix:
SRC = myconnect.c myconnect2.c myconnect3.c myconnect2all.c \ myserver.c myserver2.c myserver3.c myserver4.c EXES = $(SRC:%.c=%) LINTOUT = $(SRC:%.c=%.lint) #TOOLSDIR = /ws/on28-tools/SUNWspro/SC5.0/bin #CC = $(TOOLSDIR)/cc #LINT.c = $(TOOLSDIR)/lint CFLAGS = -g LDLIBS = -lsocket -lnsl LFLAGS = $(CFLAGS) LINTOPTS = $(LFLAGS) $(LIBS) all: $(EXES) lint: $(LINTOUT) myconnect.o: myconnect.c $(CC) -c -o $@ $(CFLAGS) myconnect.c myconnect2.o: myconnect2.c $(CC) -c -o $@ $(CFLAGS) myconnect2.c myconnect3.o: myconnect3.c $(CC) -c -o $@ $(CFLAGS) myconnect3.c myserver.o: myserver.c $(CC) -c -o $@ $(CFLAGS) myserver.c myserver2.o: myserver2.c $(CC) -c -o $@ $(CFLAGS) myserver2.c myserver3.o: myserver3.c $(CC) -c -o $@ $(CFLAGS) myserver3.c myconnect.lint: $(LINT.c) $(LINTOPTS) myconnect.c myconnect2.lint: $(LINT.c) $(LINTOPTS) myconnect2.c myconnect3.lint: $(LINT.c) $(LINTOPTS) myconnect3.c myserver.lint: $(LINT.c) $(LINTOPTS) myserver.c myserver2.lint: $(LINT.c) $(LINTOPTS) myserver2.c myserver3.lint: $(LINT.c) $(LINTOPTS) myserver3.c myserver4.lint: $(LINT.c) $(LINTOPTS) myserver4.c clean: rm -f *.o core