223 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
We need better network address conv helpers.
 | 
						|
This is what our applets want:
 | 
						|
 | 
						|
          sockaddr -> hostname
 | 
						|
udhcp:    hostname -> ipv4 addr
 | 
						|
nslookup: hostname -> list of names - done
 | 
						|
tftp:     host,port -> sockaddr
 | 
						|
nc:       host,port -> sockaddr
 | 
						|
inetd: ?
 | 
						|
traceroute: ?, hostname -> ipv4 addr
 | 
						|
arping    hostname -> ipv4 addr
 | 
						|
ping6     hostname -> ipv6 addr
 | 
						|
ifconfig  hostname -> ipv4 addr (FIXME error check?)
 | 
						|
ipcalc    ipv4 addr -> hostname
 | 
						|
syslogd   hostname -> sockaddr
 | 
						|
inet_common.c: buggy. hostname -> ipv4 addr
 | 
						|
mount     hostname -> sockaddr_in
 | 
						|
 | 
						|
==================
 | 
						|
HOWTO get rid of inet_ntoa/aton:
 | 
						|
 | 
						|
foo.sin_addr.s_addr = inet_addr(cp);
 | 
						|
-
 | 
						|
inet_pton(AF_INET, cp, &foo.sin_addr);
 | 
						|
 | 
						|
inet_aton(cp, &foo.sin_addr);
 | 
						|
-
 | 
						|
inet_pton(AF_INET, cp, &foo.sin_addr);
 | 
						|
 | 
						|
ptr = inet_ntoa(foo.sin_addr);
 | 
						|
-
 | 
						|
char str[INET_ADDRSTRLEN];
 | 
						|
ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
 | 
						|
 | 
						|
===================
 | 
						|
 | 
						|
       struct addrinfo {
 | 
						|
           int     ai_flags;
 | 
						|
           int     ai_family;
 | 
						|
           int     ai_socktype;
 | 
						|
           int     ai_protocol;
 | 
						|
           size_t  ai_addrlen;
 | 
						|
           struct sockaddr *ai_addr;
 | 
						|
           char   *ai_canonname;
 | 
						|
           struct addrinfo *ai_next;
 | 
						|
       };
 | 
						|
       int getaddrinfo(const char *node, const char *service,
 | 
						|
                       const struct addrinfo *hints,
 | 
						|
                       struct addrinfo **res);
 | 
						|
 | 
						|
       void freeaddrinfo(struct addrinfo *res);
 | 
						|
 | 
						|
       const char *gai_strerror(int errcode);
 | 
						|
 | 
						|
       The members ai_family, ai_socktype, and ai_protocol have the same  meaning
 | 
						|
       as  the corresponding parameters in the socket(2) system call.  The getad-
 | 
						|
       drinfo(3) function returns socket addresses in either IPv4 or IPv6 address
 | 
						|
       family, (ai_family will be set to either AF_INET or AF_INET6).
 | 
						|
 | 
						|
       The  hints  parameter specifies the preferred socket type, or protocol.  A
 | 
						|
       NULL hints specifies that any network address or protocol  is  acceptable.
 | 
						|
       If  this  parameter  is  not NULL it points to an addrinfo structure whose
 | 
						|
       ai_family, ai_socktype, and  ai_protocol  members  specify  the  preferred
 | 
						|
       socket type.  AF_UNSPEC in ai_family specifies any protocol family (either
 | 
						|
       IPv4 or IPv6, for example).  0 in  ai_socktype  or  ai_protocol  specifies
 | 
						|
       that any socket type or protocol is acceptable as well.  The ai_flags mem-
 | 
						|
       ber specifies additional options, defined below.  Multiple flags are spec-
 | 
						|
       ified  by  logically  OR-ing  them together.  All the other members in the
 | 
						|
       hints parameter must contain either 0, or a null pointer.
 | 
						|
 | 
						|
       The node or service parameter, but not both, may be NULL.  node  specifies
 | 
						|
       either  a  numerical network address (dotted-decimal format for IPv4, hex-
 | 
						|
       adecimal format for IPv6) or a network hostname, whose  network  addresses
 | 
						|
       are looked up and resolved.  If hints.ai_flags contains the AI_NUMERICHOST
 | 
						|
       flag then the node parameter must be a  numerical  network  address.   The
 | 
						|
       AI_NUMERICHOST  flag  suppresses  any  potentially  lengthy  network  host
 | 
						|
       address lookups.
 | 
						|
 | 
						|
       The getaddrinfo(3) function creates a linked list of addrinfo  structures,
 | 
						|
       one  for  each  network address subject to any restrictions imposed by the
 | 
						|
       hints parameter.  The ai_canonname field of the first  of  these  addrinfo
 | 
						|
       structures  is  set  to  point  to  the  official  name  of  the  host, if
 | 
						|
       hints.ai_flags includes the AI_CANONNAME  flag.   ai_family,  ai_socktype,
 | 
						|
       and  ai_protocol specify the socket creation parameters.  A pointer to the
 | 
						|
       socket address is placed in the ai_addr member,  and  the  length  of  the
 | 
						|
       socket address, in bytes, is placed in the ai_addrlen member.
 | 
						|
 | 
						|
       If  node is NULL, the network address in each socket structure is initial-
 | 
						|
       ized according to the AI_PASSIVE flag, which  is  set  in  hints.ai_flags.
 | 
						|
       The  network  address in each socket structure will be left unspecified if
 | 
						|
       AI_PASSIVE flag is set.  This is used by server applications, which intend
 | 
						|
       to  accept client connections on any network address.  The network address
 | 
						|
       will be set to the loopback interface address if the  AI_PASSIVE  flag  is
 | 
						|
       not  set.  This is used by client applications, which intend to connect to
 | 
						|
       a server running on the same network host.
 | 
						|
 | 
						|
       If hints.ai_flags includes the AI_ADDRCONFIG flag, then IPv4 addresses are
 | 
						|
       returned  in the list pointed to by result only if the local system has at
 | 
						|
       least has at least one IPv4 address configured,  and  IPv6  addresses  are
 | 
						|
       only  returned  if  the local system has at least one IPv6 address config-
 | 
						|
       ured.
 | 
						|
 | 
						|
       If hint.ai_flags specifies the AI_V4MAPPED flag, and  hints.ai_family  was
 | 
						|
       specified as AF_INET6, and no matching IPv6 addresses could be found, then
 | 
						|
       return IPv4-mapped IPv6 addresses in the list pointed to  by  result.   If
 | 
						|
       both  AI_V4MAPPED and AI_ALL are specified in hints.ai_family, then return
 | 
						|
       both IPv6 and IPv4-mapped IPv6 addresses in the list pointed to by result.
 | 
						|
       AI_ALL is ignored if AI_V4MAPPED is not also specified.
 | 
						|
 | 
						|
       service  sets the port number in the network address of each socket struc-
 | 
						|
       ture.  If service is NULL the port number will be left uninitialized.   If
 | 
						|
       AI_NUMERICSERV  is  specified  in  hints.ai_flags and service is not NULL,
 | 
						|
       then service must point to a string  containing  a  numeric  port  number.
 | 
						|
       This  flag  is used to inhibit the invocation of a name resolution service
 | 
						|
       in cases where it is known not to be required.
 | 
						|
 | 
						|
 | 
						|
==============
 | 
						|
 | 
						|
       int getnameinfo(const struct sockaddr *sa, socklen_t salen,
 | 
						|
                       char *host, size_t hostlen,
 | 
						|
                       char *serv, size_t servlen, int flags);
 | 
						|
 | 
						|
       The  getnameinfo(3)  function is defined for protocol-independent
 | 
						|
       address-to-nodename translation.  It combines the functionality
 | 
						|
       of gethostbyaddr(3) and getservbyport(3) and is the inverse of
 | 
						|
       getaddrinfo(3).  The sa argument is a pointer to a generic socket address
 | 
						|
       structure (of type sockaddr_in or  sockaddr_in6)  of  size  salen  that
 | 
						|
       holds  the  input IP address and port number.  The arguments host and
 | 
						|
       serv are pointers to buffers (of size hostlen and servlen respectively)
 | 
						|
       to hold the return values.
 | 
						|
 | 
						|
       The caller can specify that no hostname (or no service name) is required
 | 
						|
       by providing a NULL host (or serv) argument or a zero hostlen (or servlen)
 | 
						|
       parameter. However, at least one of hostname or service name must be requested.
 | 
						|
 | 
						|
       The flags argument modifies the behaviour of getnameinfo(3) as follows:
 | 
						|
 | 
						|
       NI_NOFQDN
 | 
						|
              If set, return only the hostname part of the FQDN for local hosts.
 | 
						|
 | 
						|
       NI_NUMERICHOST
 | 
						|
              If set, then the numeric form of the hostname is returned.
 | 
						|
              (When not set, this will still happen in case the node's name
 | 
						|
              cannot be looked up.)
 | 
						|
 | 
						|
       NI_NAMEREQD
 | 
						|
              If set, then a error is returned if the hostname cannot be looked up.
 | 
						|
 | 
						|
       NI_NUMERICSERV
 | 
						|
              If set, then the service address is returned in numeric form,
 | 
						|
              for example by its port number.
 | 
						|
 | 
						|
       NI_DGRAM
 | 
						|
              If  set, then the service is datagram (UDP) based rather than stream
 | 
						|
              (TCP) based. This is required for the few ports (512-514) that have different
 | 
						|
              services for UDP and TCP.
 | 
						|
 | 
						|
=================
 | 
						|
 | 
						|
Modified IPv6-aware C code:
 | 
						|
 | 
						|
    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);
 | 
						|
 | 
						|
Note that for new applications, if you write address-family-agnostic data structures,
 | 
						|
there is no need for porting.
 | 
						|
 | 
						|
However, when it comes to server-side programming in C/C++, there is an additional wrinkle.
 | 
						|
Namely, depending on whether your application is written for a dual-stack platform, such
 | 
						|
as Solaris or Linux, or a single-stack platform, such as Windows, you would need to
 | 
						|
structure the code differently.
 | 
						|
 | 
						|
Here's the corresponding server C code for a dual-stack platform:
 | 
						|
 | 
						|
    int ServSock, csock;
 | 
						|
    struct sockaddr addr, from;
 | 
						|
    ...
 | 
						|
    ServSock = socket(AF_INET6, SOCK_STREAM, PF_INET6);
 | 
						|
    bind(ServSock, &addr, sizeof(addr));
 | 
						|
    do {
 | 
						|
       csock = accept(ServSocket, &from, sizeof(from));
 | 
						|
       doClientStuff(csock);
 | 
						|
    } while (!finished);
 |