Network Interface Guide

Appendix B Live Code Example

Live Code Examples

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.


Example B-1 IPv4 Client

#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.


Example B-2 Minimal Port to IPv4 and IPv6 Client

#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.


Example B-3 Expanded Minimal Port to IPv4 and IPv6 Client

#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.


Example B-4 Recommended Port to IPv6 Client

#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.


Example B-5 IPv4 Server

#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.


Example B-6 Minimal Port To IPv6 Server

#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.


Example B-7 Recommended port to IPv6 Server

#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.


Example B-8 Port of myserver Using an IPv4 Socket and an IPv6 Socket

#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