]> Zhao Yanbai Git Server - minix.git/commitdiff
create the getaddrinfo and getnameinfo library functions and friends
authorErik van der Kouwe <erik@minix3.org>
Thu, 21 Jan 2010 06:38:17 +0000 (06:38 +0000)
committerErik van der Kouwe <erik@minix3.org>
Thu, 21 Jan 2010 06:38:17 +0000 (06:38 +0000)
15 files changed:
commands/dhcpd/dhcpd.c
commands/simple/tcpstat.c
commands/simple/udpstat.c
commands/urlget/net.h
include/arpa/inet.h
include/net/gen/netdb.h
lib/ip/Makefile.in
lib/ip/addrinfo.c [new file with mode: 0755]
lib/ip/gai_strerror.c [new file with mode: 0755]
lib/ip/gethnmadr.c
lib/ip/nameinfo.c [new file with mode: 0755]
man/man3/getaddrinfo.3 [new file with mode: 0755]
test/Makefile
test/run
test/test48.c [new file with mode: 0755]

index 129d9e716ea256ad4afa5d997ddd99a8bd055979..05b315a41ad2e800076549c1d0c3f878a47614aa 100644 (file)
@@ -386,7 +386,7 @@ static udpport_t portbyname(const char *name)
     return se->s_port;
 }
 
-static int send(network_t *np, void *data, size_t len)
+static int sendpacket(network_t *np, void *data, size_t len)
 {
     /* Send out a packet using a filedescriptor that is probably in async mode,
      * so first dup() a sync version, then write.  Return true on success.
@@ -957,7 +957,8 @@ int main(int argc, char **argv)
                if (!(np->flags & NF_BOUND)) {
                    /* Rebind over Ethernet. */
                    udp2ether(bp, np);
-                   if (send(np, bp->eth, sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
+                   if (sendpacket(np, bp->eth, 
+                                       sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
                                        + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
                        if (debug >= 1) {
                            printf("%s: Broadcast DHCP %s\n",
@@ -967,7 +968,7 @@ int main(int argc, char **argv)
                    }
                } else {
                    /* Renew over UDP. */
-                   if (send(np, bp->udpio, sizeof(udp_io_hdr_t)
+                   if (sendpacket(np, bp->udpio, sizeof(udp_io_hdr_t)
                                                        + sizeof(dhcp_t))) {
                        if (debug >= 1) {
                            printf("%s: Sent DHCP %s to %s\n",
@@ -1123,7 +1124,7 @@ int main(int argc, char **argv)
                     * is in use already.
                     */
                    make_arp(bp, np);
-                   if (send(np, bp->eth, sizeof(arp46_t))) {
+                   if (sendpacket(np, bp->eth, sizeof(arp46_t))) {
                        if (debug >= 2) {
                            printf("Sent ARP for %s\n", inet_ntoa(np->ip));
                        }
@@ -1191,7 +1192,8 @@ int main(int argc, char **argv)
                    bp->udpio->uih_data_len= sizeof(dhcp_t);
                    udp2ether(bp, np);
 
-                   if (send(np, bp->eth, sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
+                   if (sendpacket(np, bp->eth, 
+                                       sizeof(eth_hdr_t) + sizeof(ip_hdr_t)
                                        + sizeof(udp_hdr_t) + sizeof(dhcp_t))) {
                        if (debug >= 1) {
                            printf("%s: Broadcast DHCP %s\n",
@@ -1255,7 +1257,7 @@ int main(int argc, char **argv)
                     * by DHCP to my own interface.
                     */
                    icmp_advert(bp, np);
-                   if (send(np, bp->ip, sizeof(ip_hdr_t) + 16)) {
+                   if (sendpacket(np, bp->ip, sizeof(ip_hdr_t) + 16)) {
                        if (debug >= 2) {
                            printf("%s: Sent advert for %s to self\n",
                                np->fdp->device, inet_ntoa(np->gateway));
@@ -1267,7 +1269,7 @@ int main(int argc, char **argv)
                if (np->sol_ct >= 0 && --np->sol_ct >= 0) {
                    /* Send a router solicitation. */
                    icmp_solicit(bp);
-                   if (send(np, bp->ip, sizeof(*bp->ip) + 8)) {
+                   if (sendpacket(np, bp->ip, sizeof(*bp->ip) + 8)) {
                        if (debug >= 2) {
                            printf("%s: Broadcast router solicitation\n",
                                np->fdp->device);
@@ -1365,7 +1367,7 @@ int main(int argc, char **argv)
            /* Can we do something with this DHCP packet? */
            if ((r= servdhcp(np, bp, r)) > 0) {
                /* Yes, we have something to send somewhere. */
-               if (send(np, bp->udpio, r)) {
+               if (sendpacket(np, bp->udpio, r)) {
                    if (debug >= 1) {
                        printf("%s: Sent DHCP packet to %s\n",
                            np->fdp->device,
index 2d2a8f23575f9fe956b80f004b65170573d292fb..0afc85233c858d56b8e4f999f1d77f076ab437c2 100644 (file)
@@ -9,6 +9,7 @@ Created:        June 1995 by Philip Homburg <philip@f-mnx.phicoh.com>
 
 #include <inet/inet.h>
 #undef printf
+#undef send
 
 #include <fcntl.h>
 #include <stdio.h>
index b75484ac3d6dc716110cc4415725302f5968bd25..de26c12824607610b9af7b29ed416b7dc729c2d0 100644 (file)
@@ -9,6 +9,7 @@ Created:        March 2001 by Philip Homburg <philip@f-mnx.phicoh.com>
 
 #include <inet/inet.h>
 #undef printf
+#undef send
 
 #include <assert.h>
 #include <fcntl.h>
index 08e1ac6fd06302fead6a441c985e02ab202afbe9..db9f474dabb2a0009e89d2c8ec54dc04217cbd8f 100644 (file)
@@ -1,4 +1,7 @@
 /* net.h Copyright 2000 by Michael Temari All Rights Reserved */
 /* 04/05/2000 Michael Temari <Michael@TemWare.Com> */
 
+/* avoid clash with POSIX connect */
+#define connect _connect
 _PROTOTYPE(int connect, (char *host, int port));
+
index 4ae82935e7654bc9670f9e0a20ba02390641c863..e024596021f8b2c88676001209035d0a54703533 100644 (file)
@@ -35,4 +35,9 @@ _PROTOTYPE( uint16_t ntohs, (uint16_t _netval)                                );
 _PROTOTYPE( int inet_aton, (const char *_cp, struct in_addr *_pin)     );
 #endif
 
+#ifdef _POSIX_SOURCE
+in_addr_t inet_addr(const char *cp);
+#endif
+
 #endif /* _ARPA__INET_H */
+
index 55585e528df7ae95ff1a5f9fda341cf799f5e0df..b7a8b9f9fdafe26bd70b071609c6aca267dd7539 100644 (file)
@@ -125,4 +125,74 @@ int                servxcheck _ARGS((unsigned long _peer, const char *_service,
 char           *servxfile _ARGS((const char *_file));
 #endif
 
+/*  
+ * The definitions below are based on 
+ * http://www.opengroup.org/onlinepubs/009695399/basedefs/netdb.h.html
+ */
+
+#ifdef _POSIX_SOURCE
+
+/* headers exposed by netdb.h */
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+/* struct for use with getaddrinfo() */
+struct addrinfo
+{
+       int              ai_flags;     /* Input flags */
+       int              ai_family;    /* Address family of socket */
+       int              ai_socktype;  /* Socket type */
+       int              ai_protocol;  /* Protocol of socket */
+       socklen_t        ai_addrlen;   /* Length of socket address */
+       struct sockaddr *ai_addr;      /* Socket address of socket */
+       char            *ai_canonname; /* Canonical name of service location */
+       struct addrinfo *ai_next;      /* Pointer to next in list */
+};
+
+/* values for struct addrinfo.ai_flags */
+#define AI_PASSIVE     0x00000001
+#define AI_CANONNAME   0x00000002
+#define AI_NUMERICHOST 0x00000004
+#define AI_NUMERICSERV 0x00000008
+/* 
+#define AI_V4MAPPED    0x00000010
+#define AI_ALL         0x00000020
+#define AI_ADDRCONFIG  0x00000040
+*/
+
+/* flags for getnameinfo() */
+/* #define NI_NOFQDN   0x00000001 */
+#define NI_NUMERICHOST 0x00000002
+#define NI_NAMEREQD    0x00000004
+#define NI_NUMERICSERV 0x00000008
+/* #define NI_NUMERICSCOPE     0x00000010 */
+#define NI_DGRAM       0x00000020
+
+/* error values for getaddrinfo() and getnameinfo() */
+#define EAI_AGAIN      1
+#define EAI_BADFLAGS   2
+#define EAI_FAIL       3
+#define EAI_FAMILY     4
+#define EAI_MEMORY     5
+#define EAI_NONAME     6
+#define EAI_SERVICE    7
+#define EAI_SOCKTYPE   8
+#define EAI_SYSTEM     9
+#define EAI_OVERFLOW   10
+
+/* getaddrinfo() and friends */
+void freeaddrinfo(struct addrinfo *ai);
+int getaddrinfo(const char *nodename,
+       const char *servname,
+       const struct addrinfo *hints,
+       struct addrinfo **res);
+int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+       char *node, socklen_t nodelen, char *service,
+       socklen_t servicelen, int flags);
+const char *gai_strerror(int ecode);
+
+#endif
+
 #endif /* !_NETDB_H_ */
+
index 784b8fe290f4800864f13f33a5bbefd5e8e82c4f..fdf3a18b46a259257a2d93fa8a8a06252c726ce2 100644 (file)
@@ -8,6 +8,7 @@ LIBRARIES=libc
 
 libc_FILES=" \
        accept.c \
+       addrinfo.c \
        bind.c \
        connect.c \
        dhcp_gettag.c \
@@ -18,6 +19,7 @@ libc_FILES=" \
        ethere2a.c \
        etherh2n.c \
        ethern2h.c \
+       gai_strerror.c \
        getdomain.c \
        gethnmadr.c \
        gethostent.c \
@@ -41,6 +43,7 @@ libc_FILES=" \
        inet_ntoa.c \
        listen.c \
        memcspn.c \
+       nameinfo.c \
        oneC_sum.c \
        rcmd.c \
        recv.c \
diff --git a/lib/ip/addrinfo.c b/lib/ip/addrinfo.c
new file mode 100755 (executable)
index 0000000..25babc1
--- /dev/null
@@ -0,0 +1,322 @@
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+/*
+ * getaddrinfo and freeaddrinfo are based on
+ * http://www.opengroup.org/onlinepubs/009695399/functions/getaddrinfo.html
+ */
+void freeaddrinfo(struct addrinfo *ai)
+{
+       struct addrinfo *next;
+
+       while (ai)
+       {
+               /* preserve next pointer */
+               next = ai->ai_next;
+
+               /* free each member of the struct and the struct itself */
+               if (ai->ai_addr) free(ai->ai_addr);
+               if (ai->ai_canonname) free(ai->ai_canonname);
+               free(ai);
+
+               /* continue to free the next element of the linked list */
+               ai = next;
+       }
+}
+
+static int getaddrinfo_parse_hints(
+       const struct addrinfo *hints, 
+       int *flags,
+       int *socktype,
+       int *protocol)
+{
+       assert(flags);
+       assert(socktype);
+
+       /* 
+        * if hints is not specified, no flags are specified and all 
+        * socktypes must be returned 
+        */
+       if (!hints)
+       {
+               *flags = 0;
+               *socktype = 0;
+               *protocol = 0;
+               return 0;
+       }
+
+       /* check hints correctness */
+       if (hints->ai_addrlen || hints->ai_addr || 
+               hints->ai_canonname || hints->ai_next)
+       {
+               errno = EINVAL;
+               return EAI_SYSTEM;
+       }
+
+       /* check flags */
+       *flags = hints->ai_flags;
+       if (*flags & ~(AI_PASSIVE | AI_CANONNAME | 
+               AI_NUMERICHOST | AI_NUMERICSERV))
+               return EAI_BADFLAGS;
+
+       /* only support IPv4 */
+       if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET)
+               return EAI_FAMILY;
+
+       /* only support SOCK_STREAM and SOCK_DGRAM */
+       *socktype = hints->ai_socktype;
+       switch (*socktype)
+       {
+               case 0:
+               case SOCK_STREAM:
+               case SOCK_DGRAM: break;
+               default: return EAI_SOCKTYPE;
+       }
+
+       /* get protocol */
+       *protocol = hints->ai_protocol;
+
+       return 0;
+}
+
+static int getaddrinfo_resolve_hostname(
+       const char *nodename,
+       int flags,
+       char ***addr_list,
+       const char **canonname)
+{
+       static struct in_addr addr;
+       static char *addr_array[2];
+       struct hostent *hostent;
+
+       assert(addr_list);
+       assert(canonname);
+
+       /* if no hostname is specified, use local address */
+       if (!nodename)
+       {
+               if ((flags & AI_PASSIVE) == AI_PASSIVE)
+                       addr.s_addr = htonl(INADDR_ANY);
+               else
+                       addr.s_addr = htonl(INADDR_LOOPBACK);
+
+               addr_array[0] = (char *) &addr;
+               addr_array[1] = NULL;
+               *addr_list = addr_array;
+               *canonname = "localhost";
+               return 0;
+       }
+
+       if (!*nodename)
+               return EAI_NONAME;
+
+       /* convert literal IP address */
+       addr.s_addr = inet_addr(nodename);
+       if (addr.s_addr != (in_addr_t) -1)
+       {
+               addr_array[0] = (char *) &addr;
+               addr_array[1] = NULL;
+               *addr_list = addr_array;
+               *canonname = NULL;
+               return 0;
+       }
+
+       /* AI_NUMERICHOST avoids DNS lookup */
+       if ((flags & AI_NUMERICHOST) == AI_NUMERICHOST)
+               return EAI_NONAME;
+
+       /* attempt DNS lookup */
+       hostent = gethostbyname(nodename);
+       if (!hostent)
+               switch(h_errno)
+               {
+                       case HOST_NOT_FOUND: return EAI_NONAME;
+                       case NO_DATA:        return EAI_FAIL;
+                       case NO_RECOVERY:    return EAI_FAIL;
+                       case TRY_AGAIN:      return EAI_AGAIN;
+                       default:             assert(0); return EAI_FAIL;
+               }
+
+       /* assumption: only IPv4 addresses returned */
+       assert(hostent->h_addrtype == AF_INET);
+       assert(hostent->h_length == sizeof(addr));
+       *addr_list = hostent->h_addr_list;
+       *canonname = hostent->h_name;
+       return 0;
+}
+
+int getaddrinfo_resolve_servname(
+       const char *servname,
+       int flags, 
+       int socktype, 
+       unsigned short *port, 
+       int *protocol)
+{
+       char *endptr;
+       long port_long;
+       struct protoent *protoent;
+       struct servent *servent;
+
+       assert(port);
+       assert(protocol);
+
+       /* if not specified, set port and protocol to zero */
+       if (!servname)
+       {
+               *port = 0;
+               *protocol = 0;
+               return 0;
+       }
+
+       if (!*servname)
+               return EAI_SERVICE;
+
+       /* try to parse port number */
+       port_long = strtol(servname, &endptr, 0);
+       if (!*endptr)
+       {
+               /* check whether port is within range */
+               if (port_long < 0 || port_long > (unsigned short) ~0)
+                       return EAI_SERVICE;
+
+               *port = htons(port_long);
+               *protocol = 0;
+               return 0;
+       }
+
+       /* AI_NUMERICSERV avoids lookup */
+       if ((flags & AI_NUMERICSERV) == AI_NUMERICSERV)
+               return EAI_SERVICE;
+
+       /* look up port number */
+       servent = getservbyname(servname, 
+               (socktype == SOCK_STREAM) ? "tcp" : "udp");
+       if (!servent) 
+               return EAI_SERVICE;
+
+       *port = servent->s_port;
+
+       /* determine protocol number */
+       protoent = getprotobyname(servent->s_proto);
+       *protocol = protoent ? protoent->p_proto : 0;
+       return 0;
+}
+
+int getaddrinfo(
+       const char *nodename,
+       const char *servname,
+       const struct addrinfo *hints,
+       struct addrinfo **res)
+{
+       struct addrinfo *addrinfo, **addrinfo_p;
+       char **addr_list;
+       const char *canonname;
+       int flags, i, protocol, protocol_spec, r, result;
+       unsigned short port;
+       struct sockaddr_in *sockaddr;
+       int socktype, socktype_spec;
+
+       /* 
+        * The following flags are supported:
+        * - AI_CANONNAME
+        * - AI_PASSIVE
+        * - AI_NUMERICHOST 
+        * - AI_NUMERICSERV 
+        *
+        * The following flags not supported due to lack of IPv6 support:
+        * - AI_ADDRCONFIG 
+        * - AI_ALL 
+        * - AI_V4MAPPED 
+        */
+
+       /* check arguments */
+       if ((!nodename && !servname) || !res)
+               return EAI_NONAME;
+
+       /* parse hints */
+       if ((r = getaddrinfo_parse_hints(hints, &flags, &socktype_spec, &protocol_spec))) 
+               return r;
+
+       /* resolve hostname */
+       if ((r = getaddrinfo_resolve_hostname(nodename, flags, &addr_list, &canonname)))
+               return r;
+
+       /* return a result record for each address */
+       addrinfo_p = res;
+       *addrinfo_p = NULL;
+       result = EAI_NONAME;
+       while (*addr_list)
+       {
+               /* return a result record for each socktype */
+               for (i = 0; i < 2; i++)
+               {
+                       /* should current socktype be selected? */
+                       socktype = (i == 0) ? SOCK_STREAM : SOCK_DGRAM;
+                       if (socktype_spec != 0 && socktype_spec != socktype)
+                               continue;
+
+                       /* resolve port */
+                       if ((r = getaddrinfo_resolve_servname(servname, flags, socktype, &port, &protocol)))
+                       {
+                               freeaddrinfo(*res);
+                               return r;
+                       }
+
+                       /* enforce matching protocol */
+                       if (!protocol)
+                               protocol = protocol_spec;
+                       else if (protocol_spec && protocol != protocol_spec)
+                               continue;
+
+                       /* allocate result */
+                       *addrinfo_p = addrinfo = (struct addrinfo *) calloc(1, sizeof(struct addrinfo));
+                       if (!addrinfo)
+                       {
+                               freeaddrinfo(*res);
+                               return EAI_MEMORY;
+                       }
+
+                       sockaddr = (struct sockaddr_in *) calloc(1, sizeof(struct sockaddr_in));
+                       if (!sockaddr)
+                       {
+                               freeaddrinfo(*res);
+                               return EAI_MEMORY;
+                       }
+
+                       /* provide information in result */
+                       addrinfo->ai_family = AF_INET;
+                       addrinfo->ai_socktype = socktype;
+                       addrinfo->ai_protocol = protocol;
+                       addrinfo->ai_addrlen = sizeof(*sockaddr);
+                       addrinfo->ai_addr = (struct sockaddr *) sockaddr;
+                       sockaddr->sin_family = AF_INET;
+                       sockaddr->sin_port = port;
+                       memcpy(&sockaddr->sin_addr, *addr_list, sizeof(sockaddr->sin_addr));
+                       if (((flags & AI_CANONNAME) == AI_CANONNAME) && canonname)
+                       {
+                               addrinfo->ai_canonname = strdup(canonname);
+                               if (!addrinfo->ai_canonname)
+                               {
+                                       freeaddrinfo(*res);
+                                       return EAI_MEMORY;
+                               }
+                       }
+                       result = 0;
+
+                       /* chain next result to the current one */
+                       addrinfo_p = &addrinfo->ai_next;
+               }
+
+               /* move on to next address */
+               addr_list++;
+       }
+
+       /* found anything meaningful? */
+       return result;
+}
diff --git a/lib/ip/gai_strerror.c b/lib/ip/gai_strerror.c
new file mode 100755 (executable)
index 0000000..ccb0acf
--- /dev/null
@@ -0,0 +1,64 @@
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h> 
+#include <string.h>
+
+/*
+ * gai_strerror is based on
+ * http://www.opengroup.org/onlinepubs/009695399/functions/gai_strerror.html 
+ */
+const char *gai_strerror(int ecode)
+{
+       static char buffer[256];
+
+       /* check for each known error code */
+       switch (ecode)
+       {
+               case EAI_AGAIN:
+                       return "The name could not be resolved at this time";
+
+               case EAI_BADFLAGS:
+                       return "The flags had an invalid value";
+
+               case EAI_FAIL:
+                       return "A non-recoverable error occurred";
+
+               case EAI_FAMILY:
+                       return "The address family was not recognized or the "
+                               "address length was invalid for the specified "
+                               "family";
+
+               case EAI_MEMORY:
+                       return "There was a memory allocation failure";
+
+               case EAI_NONAME:
+                       return "The name does not resolve for the supplied "
+                               "parameters, NI_NAMEREQD is set and the host's "
+                               "name cannot be located, or both nodename and "
+                               "servname were null";
+
+               case EAI_SERVICE:
+                       return "The service passed was not recognized for the "
+                               "specified socket type";
+
+               case EAI_SOCKTYPE:
+                       return "The intended socket type was not recognized";
+
+               case EAI_SYSTEM:
+                       snprintf(buffer, 
+                               sizeof(buffer), 
+                               "A system error occurred: %s",
+                               strerror(errno));
+                       return buffer;
+
+               case EAI_OVERFLOW:
+                       return "An argument buffer overflowed";
+       }
+
+       /* unknown error code */
+       snprintf(buffer, 
+               sizeof(buffer), 
+               "An unknown error code was passed to gai_strerror: %d",
+               ecode);
+       return buffer;
+}
index 5f14875ace66f21568c2c946fd4a072aaf38f1a3..b592a9e5b9da047a57f8825c4aed77ac89465b16 100644 (file)
@@ -54,10 +54,6 @@ static char sccsid[] = "@(#)gethostnamadr.c  6.41 (Berkeley) 6/1/90";
 static char *h_addr_ptrs[MAXADDRS + 1];
 
 #ifdef _MINIX
-struct in_addr
-{
-       ipaddr_t s_addr;
-};
 union querybuf;
 
 extern int dn_skipname _ARGS(( const u_char *comp_dn, const u_char *eom ));
diff --git a/lib/ip/nameinfo.c b/lib/ip/nameinfo.c
new file mode 100755 (executable)
index 0000000..3444c29
--- /dev/null
@@ -0,0 +1,148 @@
+#include <arpa/inet.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netdb.h>
+#include <unistd.h>
+
+static size_t first_component_len(const char *s)
+{
+       const char *first = s;
+
+       /* find the first dot or end of string */
+       while (*s && *s != '.')
+               s++;
+
+       /* return length */
+       return s - first;
+}
+
+static int getnameinfo_get_host(const struct sockaddr_in *sockaddr, 
+       char *node, socklen_t nodelen, int flags)
+{
+       struct hostent *hostent;
+       const char *ipaddr;
+
+       /* perform look-up */
+       if ((flags & NI_NUMERICHOST) != NI_NUMERICHOST)
+       {
+               hostent = gethostbyaddr(
+                       (char *) &sockaddr->sin_addr, 
+                       sizeof(sockaddr->sin_addr), 
+                       AF_INET);
+
+               if (hostent && hostent->h_name)
+               {
+                       /* return the hostname that was found */
+                       if (nodelen <= strlen(hostent->h_name))
+                               return EAI_OVERFLOW;
+
+                       strcpy(node, hostent->h_name);
+                       return 0;
+               }
+       }
+
+       if ((flags & NI_NAMEREQD) == NI_NAMEREQD)
+               return EAI_NONAME;
+
+       /* basic implementation to provide numeric values */
+       ipaddr = inet_ntoa(sockaddr->sin_addr);
+       if (nodelen <= strlen(ipaddr))
+               return EAI_OVERFLOW;
+
+       strcpy(node, ipaddr);
+       return 0;
+}
+
+static int getnameinfo_get_serv(const struct sockaddr_in *sockaddr, 
+       char *service, socklen_t servicelen, int flags)
+{
+       struct servent *servent;
+       unsigned short port;
+
+       /* perform look-up */
+       if ((flags & NI_NUMERICSERV) != NI_NUMERICSERV)
+       {
+               servent = getservbyport(sockaddr->sin_port, 
+                       ((flags & NI_DGRAM) == NI_DGRAM) ? "udp" : "tcp");
+               if (servent && servent->s_name)
+               {
+                       /* return the service name that was found */
+                       if (strlen(servent->s_name) >= servicelen)
+                               return EAI_OVERFLOW;
+
+                       strcpy(service, servent->s_name);
+                       return 0;
+               }
+       }
+
+       /* return port number */
+       port = ntohs(sockaddr->sin_port);
+       if (snprintf(service, servicelen, "%u", port) >= servicelen)
+               return EAI_OVERFLOW;
+
+       return 0;
+}
+
+/*
+ * getnameinfo is based on
+ * http://www.opengroup.org/onlinepubs/009695399/functions/getnameinfo.html
+ */
+int getnameinfo(const struct sockaddr *sa, socklen_t salen,
+       char *node, socklen_t nodelen, char *service,
+       socklen_t servicelen, int flags)
+{
+       int r;
+       const struct sockaddr_in *sockaddr;
+
+       /* 
+        * The following flags are really supported:
+        * - NI_NUMERICHOST
+        * - NI_NAMEREQD 
+        * - NI_NUMERICSERV 
+        * - NI_DGRAM
+        *
+        * The following flag is not supported:
+        * - NI_NUMERICSCOPE
+        *
+        * The following flags could have been supported but is not implemented:
+        * - NI_NOFQDN
+        */
+
+       /* check for invalid parameters; only support IPv4 */
+       if (sa == NULL)
+       {
+               errno = EINVAL;
+               return EAI_SYSTEM;
+       }
+       
+       if (sa->sa_family != AF_INET || salen != sizeof(struct sockaddr_in))
+               return EAI_FAMILY;
+
+       if (flags & ~(NI_NUMERICHOST | NI_NAMEREQD | NI_NUMERICSERV | NI_DGRAM))
+               return EAI_BADFLAGS;
+
+       if ((!node || !nodelen) && (!service || !servicelen))
+               return EAI_NONAME;
+
+       /* look up host */
+       sockaddr = (const struct sockaddr_in *) sa;
+       if (node && nodelen > 0)
+       {
+               r = getnameinfo_get_host(sockaddr, node, nodelen, flags);
+               if (r)
+                       return r;
+       }
+
+       /* look up service */
+       if (service && servicelen > 0)
+       {
+               r = getnameinfo_get_serv(sockaddr, service, servicelen, flags);
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
diff --git a/man/man3/getaddrinfo.3 b/man/man3/getaddrinfo.3
new file mode 100755 (executable)
index 0000000..3dc14dd
--- /dev/null
@@ -0,0 +1,66 @@
+.TH GETADDRINFO 3  "January 19, 2010"
+.UC 4
+.SH NAME
+getaddrinfo, freeaddrinfo, gai_strerror, getnameinfo \- address information
+.SH SYNOPSIS
+.nf
+.ft B
+#include <netdb.h>
+
+void freeaddrinfo(struct addrinfo *\fIai\fP);
+int getaddrinfo(const char *\fInodename\fP,
+       const char *\fIservname\fP,
+       const struct addrinfo *\fIhints\fP,
+       struct addrinfo **\fIres\fP);
+int getnameinfo(const struct sockaddr *\fIsa\fP, socklen_t \fIsalen\fP,
+       char *\fInode\fP, socklen_t \fInodelen\fP, char *\fIservice\fP,
+       socklen_t \fIservicelen\fP, int \fIflags\fP);
+const char *gai_strerror(int \fIecode\fP);
+.fi
+.SH DESCRIPTION
+These functions provide to convert between host and service names on the one
+hand and network addresses and ports on the other. 
+.PP
+getaddrinfo converts the hostname specified in \fInodename\fP and/or the service
+name in \fIservname\fP into a socket address that can be used to connect to that
+service or listen for incoming connections. One of the parameters may be NULL. 
+If \fInodename\fP is NULL, an address for the local host is provided. If 
+\fIservname\fP is NULL, the network port is not filled in (and therefore set to 
+zero). Buffers are allocated to store the results and a pointer to the first 
+element of a linked list of addresses is stored in \fIai\fP. These buffers must
+be freed by calling freeaddrinfo on the pointer returned in \fIai\fP.
+.PP
+getnameinfo converts the specified socket address into a hostname and/or service
+name. These are stored in the buffers pointed to by \fInode\fP and \fIservice\fP
+resepectively. \fInodelen\fP and \fIservicelen\fP specify the sizes of these 
+buffers. If the result string including null terminator does not fit in the
+buffer, the function fails and EAI_OVERFLOW is returned.
+.PP
+For both functions, some flags may be specified to alter their behaviour. For
+getaddrinfo these flags are specified in the ai_flags field of the optional 
+\fIhints\fP parameter. AI_PASSIVE indicates that an address suitable for the 
+bind function should be returned, otherwise an address suitable for connect is 
+provided. AI_CANONNAME requests that the canonical name of the host be retrieved 
+and stored in the ai_canonname field of the result. AI_NUMERICHOST and 
+AI_NUMERICSERV indicate respectively that \fInodename\fP is an IP address and
+\fIservname\fP is a port number, avoiding a lookup. Search can be limited to a
+specific socket type by setting \fIhints\fP\->ai_socktype to SOCK_STREAM or 
+SOCK_DGRAM. \fIhints\fP\->ai_family can be set to AF_UNSPEC or AF_INET but this
+doesn't make a difference as MINIX supports only IPv4. Unused fields of 
+\fIhints\fP must be set to zero.
+.PP
+Flags for getnameinfo are specified in the \fIflags\fP parameter. NI_NUMERICHOST
+and NI_NUMERICSERV respectively cause \fInode\fP to be set to an IP address
+and \fIservice\fP to be set to a port number. NI_NAMEREQD causes the function
+to fail with EAI_NONAME if a name is not found for the host (otherwise the IP
+address is returned). NI_DGRAM indicates that a datagram service is specified,
+the default being a stream service.
+.SH "RETURN VALUE
+If the functions succeed, they return 0. If they fail, one of the EAI_* values
+defined in netdb.h is returned. This value can be converted into a string using
+gai_strerror. The most important ones are EAI_NONAME (host name not found), 
+EAI_SERVICE (service name not found) and EAI_OVERFLOW (buffer too small, only
+for getnameinfo).
+.SH "KNOWN ISSUES
+Since MINIX does not support IPv6, the related flags are not supported. 
+The NI_NOFQDN flag is also not (yet) supported. 
index b748298918dd386993f72ef40cd19c3a0157c07b..80280aa5854b295db48636414dedef771562546b 100644 (file)
@@ -10,7 +10,7 @@ OBJ=  test1  test2  test3  test4  test5  test6  test7  test8  test9  \
               test21 test22 test23        test25 test26 test27 test28 test29 \
        test30 test31 test32        test34 test35 test36 test37 test38 \
        test39 t10a t11a t11b test40 t40a t40b t40c t40d t40e t40f test41 \
-       test42 test44 test45 test47
+       test42 test44 test45 test47 test48
 
 BIGOBJ=  test20 test24
 ROOTOBJ= test11 test33 test43 test46
@@ -95,4 +95,5 @@ test45: test45.c test45.h
 test45-gcc: test45.c test45.h
 test46: test46.c
 test47: test47.c
+test48: test48.c
 
index 1455f3b75d95ad43057092e5c1586e00a6db8bc3..a4dabe1ae86093a3aa873b6496d1d9050f6de887 100755 (executable)
--- a/test/run
+++ b/test/run
@@ -19,7 +19,7 @@ echo " "
 # Run all the tests, keeping track of who failed.
 for i in  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 \
          21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
-         41 42 43 44 45 45-gcc 46 47 sh1.sh sh2.sh
+         41 42 43 44 45 45-gcc 46 47 48 sh1.sh sh2.sh
 do
    if [ -x ./test$i ]
    then
diff --git a/test/test48.c b/test/test48.c
new file mode 100755 (executable)
index 0000000..9825c5b
--- /dev/null
@@ -0,0 +1,566 @@
+#include <arpa/inet.h>
+#include <assert.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAX_ERRORS 8
+
+static int errct;
+
+static void quit(void)
+{
+       if (errct > 0)
+       {
+               printf("%d errors\n", errct);
+               exit(1);
+       }
+       else
+       {
+               printf("ok\n");
+               exit(0);
+       }
+}
+
+static void err(void)
+{
+       if (++errct >= MAX_ERRORS)
+       {
+               printf("aborted, too many errors\n");
+               quit();
+       }
+}
+
+static void printstr(const char *s)
+{
+       if (s)
+               printf("\"%s\"", s); 
+       else 
+               printf("NULL");
+}
+
+static void test_getaddrinfo_err(
+       int n, 
+       const char *nodename, 
+       const char *servname, 
+       int passhints, 
+       int flags, 
+       int family, 
+       int socktype,
+       const char *exp_result, 
+       const char *result)
+{
+       printf("error %d: getaddrinfo(", n);
+       printstr(nodename);
+       printf(", ");
+       printstr(servname);
+       printf(", ");
+       if (passhints) 
+               printf("{ 0x%x, %d, %d }", flags, family, socktype); 
+       else 
+               printf("NULL");
+
+       printf("); result: ");
+       printstr(result);
+       printf("; expected: ");
+       printstr(exp_result);
+       printf("\n");
+       err();
+}
+
+/* yes, this is ugly, but not as ugly as repeating it all every time */
+#define TEST_GETADDRINFO_ERR_PARAMS \
+       nodename, servname, passhints, flags, family, socktype
+
+static void test_getaddrinfo_err_nr(
+       int n, 
+       const char *nodename, 
+       const char *servname, 
+       int passhints, 
+       int flags, 
+       int family, 
+       int socktype,
+       int exp_result, 
+       int result)
+{
+       char exp_result_s[23], result_s[23];
+
+       /* convert result to string */
+       snprintf(exp_result_s, sizeof(exp_result_s), "%d/0x%x", 
+               exp_result, exp_result);
+       snprintf(result_s, sizeof(result_s), "%d/0x%x", result, result);
+       test_getaddrinfo_err(n, TEST_GETADDRINFO_ERR_PARAMS, 
+               exp_result_s, result_s);
+}
+
+static void test_getnameinfo_err(
+       int n, 
+       unsigned long ipaddr,
+       unsigned short port,
+       socklen_t nodelen,
+       socklen_t servicelen,
+       int flags, 
+       const char *exp_result, 
+       const char *result)
+{
+       printf("error %d: getnameinfo(0x%.8x, %d, %d, %d, 0x%x); result: ", 
+               n, ntohl(ipaddr), ntohs(port), nodelen, servicelen, flags);
+       printstr(result);
+       printf("; expected: ");
+       printstr(exp_result);
+       printf("\n");
+       err();
+}
+
+/* yes, this is ugly, but not as ugly as repeating it all every time */
+#define TEST_GETNAMEINFO_ERR_PARAMS ipaddr, port, nodelen, servicelen, flags
+
+static void test_getnameinfo_err_nr(
+       int n, 
+       unsigned long ipaddr,
+       unsigned short port,
+       socklen_t nodelen,
+       socklen_t servicelen,
+       int flags, 
+       int exp_result, 
+       int result)
+{
+       char exp_result_s[23], result_s[23];
+
+       /* convert result to string */
+       snprintf(exp_result_s, sizeof(exp_result_s), "%d/0x%x", 
+               exp_result, exp_result);
+       snprintf(result_s, sizeof(result_s), "%d/0x%x", result, result);
+       test_getnameinfo_err(n, TEST_GETNAMEINFO_ERR_PARAMS, 
+               exp_result_s, result_s);
+}
+
+static void test_getaddrinfo(
+       const char *nodename, 
+       const char *servname, 
+       int passhints, 
+       int flags, 
+       int family,
+       int socktype,
+       int exp_results,
+       unsigned long exp_ip,
+       int exp_canonname,
+       unsigned short exp_port)
+{
+       struct addrinfo *ai, *ai_cur;
+       struct addrinfo hints;
+       struct sockaddr_in *sockaddr_in;
+       int ai_count_dgram, ai_count_stream, r;
+       
+       /* some parameters are only meaningful with hints */
+       assert(passhints || !flags);
+       assert(passhints || family == AF_UNSPEC);
+       assert(passhints || !socktype);
+
+       /* initialize hints */
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_flags = flags;
+       hints.ai_family = family;
+       hints.ai_socktype = socktype;
+       hints.ai_family = family;
+
+       /* perform query and test result */
+       ai = (struct addrinfo *) 0xDEADBEEF;
+       r = getaddrinfo(nodename, servname, passhints ? &hints : NULL, &ai);
+       if (r < 0 || r >= 32 || !((1 << r) & exp_results))
+               test_getaddrinfo_err_nr(1, TEST_GETADDRINFO_ERR_PARAMS, exp_results, r);
+
+       if (r)
+               return;
+       
+       /* the function succeeded; do the results make sense? */
+       ai_cur = ai;
+       ai_count_dgram = 0;
+       ai_count_stream = 0;
+       while (ai_cur)
+       {
+               /* test result fields */
+               if (ai_cur->ai_family != AF_INET) 
+                       test_getaddrinfo_err_nr(2, TEST_GETADDRINFO_ERR_PARAMS,
+                               AF_INET, ai_cur->ai_family);
+
+               if (socktype && ai_cur->ai_socktype != socktype) 
+                       test_getaddrinfo_err_nr(3, TEST_GETADDRINFO_ERR_PARAMS,
+                               socktype, ai_cur->ai_socktype);
+
+               switch (ai_cur->ai_socktype)
+               {
+                       case SOCK_DGRAM:  ai_count_dgram++;  break;
+                       case SOCK_STREAM: ai_count_stream++; break;
+               }
+
+               /* do address and port match? */
+               if (ai_cur->ai_addrlen != sizeof(struct sockaddr_in)) 
+                       test_getaddrinfo_err_nr(4, TEST_GETADDRINFO_ERR_PARAMS, 
+                               sizeof(struct sockaddr_in), 
+                               ai_cur->ai_addrlen);
+               else
+               {
+                       sockaddr_in = (struct sockaddr_in *) ai_cur->ai_addr;
+                       if (sockaddr_in->sin_addr.s_addr != exp_ip) 
+                               test_getaddrinfo_err_nr(5,
+                                       TEST_GETADDRINFO_ERR_PARAMS, 
+                                       ntohl(exp_ip), 
+                                       ntohl(sockaddr_in->sin_addr.s_addr));
+
+                       if (sockaddr_in->sin_port != exp_port) 
+                               test_getaddrinfo_err_nr(6, 
+                                       TEST_GETADDRINFO_ERR_PARAMS, 
+                                       ntohs(exp_port), 
+                                       ntohs(sockaddr_in->sin_port));
+               }
+
+               /* is canonical supplied? */
+               if (exp_canonname && 
+                       (!ai_cur->ai_canonname || !*ai_cur->ai_canonname))
+                       test_getaddrinfo_err(7, 
+                               TEST_GETADDRINFO_ERR_PARAMS, 
+                               "(anything)", ai_cur->ai_canonname);
+
+               if (!exp_canonname && ai_cur->ai_canonname)
+                       test_getaddrinfo_err(8, 
+                               TEST_GETADDRINFO_ERR_PARAMS, 
+                               NULL, ai_cur->ai_canonname);
+       
+               /* move to next result */
+               ai_cur = ai_cur->ai_next;
+       }
+       
+       /* check number of results */
+       if (ai_count_dgram != ((socktype == SOCK_STREAM) ? 0 : 1))
+               test_getaddrinfo_err_nr(9, TEST_GETADDRINFO_ERR_PARAMS, 
+                       (socktype == SOCK_STREAM) ? 0 : 1, ai_count_dgram);
+
+       if (ai_count_stream != ((socktype == SOCK_DGRAM) ? 0 : 1))
+               test_getaddrinfo_err_nr(10, TEST_GETADDRINFO_ERR_PARAMS, 
+                       (socktype == SOCK_DGRAM) ? 0 : 1, ai_count_stream);
+
+       /* clean up */
+       freeaddrinfo(ai);
+}
+
+static void memsetl(void *s, unsigned long c, size_t n)
+{
+       unsigned char *p = (unsigned char *) s;
+       size_t i;
+
+       for (i = 0; i < n; i++)
+               p[i] = c >> (8 * (i % sizeof(c)));
+}
+
+void test_getnameinfo(
+       unsigned long ipaddr, 
+       unsigned short port,
+       const char *exp_node,
+       socklen_t nodelen, 
+       const char *exp_service,
+       socklen_t servicelen, 
+       int flags,
+       int exp_results)
+{
+       struct sockaddr_in sockaddr;
+       char node[256], service[256];
+       int r;
+
+       /* avoid buffer overflows */
+       assert(nodelen <= sizeof(node));
+       assert(servicelen <= sizeof(service));
+
+       /* perform query and test result */
+       sockaddr.sin_family = AF_INET;
+       sockaddr.sin_addr.s_addr = ipaddr;
+       sockaddr.sin_port = port;
+       memsetl(node, 0xDEADBEEF, nodelen);
+       memsetl(service, 0xDEADBEEF, servicelen);
+       r = getnameinfo((struct sockaddr *) &sockaddr, sizeof(sockaddr),
+               node, nodelen, service, servicelen, flags);
+
+       if (r < 0 || r >= 32 || !((1 << r) & exp_results))
+               test_getnameinfo_err_nr(1, TEST_GETNAMEINFO_ERR_PARAMS, 
+                       exp_results, r);
+
+       if (r)
+               return;
+
+       /* check results */
+       if (nodelen && strcmp(exp_node, node) != 0)
+               test_getnameinfo_err(2, TEST_GETNAMEINFO_ERR_PARAMS, 
+                       exp_node, node);
+
+       if (servicelen && strcmp(exp_service, service) != 0)
+               test_getnameinfo_err(2, TEST_GETNAMEINFO_ERR_PARAMS, 
+                       exp_service, service);
+}
+
+static struct
+{
+       const char *nodename;
+       unsigned long ipaddr;
+       int numeric;
+       int canonname;
+       int need_network;
+       int exp_result;
+} hosts[] = {
+       { NULL,              0x7f000001, 1, 1, 0, 0                 },
+       { "0.0.0.0",         0x00000000, 1, 0, 0, 0,                },
+       { "0.0.0.255",       0x000000ff, 1, 0, 0, 0,                },
+       { "0.0.255.0",       0x0000ff00, 1, 0, 0, 0,                },
+       { "0.255.0.0",       0x00ff0000, 1, 0, 0, 0,                },
+       { "255.0.0.0",       0xff000000, 1, 0, 0, 0,                },
+       { "127.0.0.1",       0x7f000001, 1, 0, 0, 0,                },
+       { "localhost",       0x7f000001, 0, 1, 0, 0,                },
+       { "minix3.org",      0x82251414, 0, 1, 1, 0,                },
+       { "",                0x00000000, 1, 0, 0, (1 << EAI_NONAME) },
+       { "256.256.256.256", 0x00000000, 1, 0, 0, (1 << EAI_NONAME) },
+       { "minix3.xxx",      0x00000000, 0, 0, 1, (1 << EAI_NONAME) }};
+
+static struct
+{
+       const char *servname;
+       unsigned short port;
+       int numeric;
+       int socktype;
+       int exp_result;
+} services[] = {
+       { NULL,        0, 1, 0,           0                  },
+       { "0",         0, 1, 0,           0                  },
+       { "1",         1, 1, 0,           0                  },
+       { "32767", 32767, 1, 0,           0                  },
+       { "32768", 32768, 1, 0,           0                  },
+       { "65535", 65535, 1, 0,           0                  },
+       { "echo",      7, 0, 0,           0                  },
+       { "ftp",      21, 0, SOCK_STREAM, 0                  },
+       { "tftp",     69, 0, SOCK_DGRAM , 0                  },
+       { "-1",        0, 1, 0,           (1 << EAI_SERVICE) },
+       { "",          0, 1, 0,           (1 << EAI_SERVICE) },
+       { "65537",     0, 1, 0,           (1 << EAI_SERVICE) },
+       { "XXX",       0, 0, 0,           (1 << EAI_SERVICE) }};
+
+static struct 
+{
+       int value;
+       int exp_result; 
+} families[] = { 
+       { AF_UNSPEC,               0                 },
+       { AF_INET,                 0                 },
+       { AF_UNSPEC + AF_INET + 1, (1 << EAI_FAMILY) }};
+
+static struct 
+{
+       int value;
+       int exp_result; 
+} socktypes[] = { 
+       { 0,                            0                   },
+       { SOCK_STREAM,                  0                   },
+       { SOCK_DGRAM,                   0                   },
+       { SOCK_STREAM + SOCK_DGRAM + 1, (1 << EAI_SOCKTYPE) }};
+
+#define LENGTH(a) (sizeof((a)) / sizeof((a)[0]))
+
+static void test_getaddrinfo_all(int use_network)
+{
+       int flag_PASSIVE, flag_CANONNAME, flag_NUMERICHOST, flag_NUMERICSERV;
+       int exp_results, flags, i, j, k, l, needhints, passhints;
+       unsigned long ipaddr;
+
+       /* loop through various parameter values */
+       for (i = 0; i < LENGTH(hosts);     i++)
+       for (j = 0; j < LENGTH(services);  j++)
+       for (k = 0; k < LENGTH(families);  k++)
+       for (l = 0; l < LENGTH(socktypes); l++)
+       for (flag_PASSIVE     = 0; flag_PASSIVE < 2;     flag_PASSIVE++)
+       for (flag_CANONNAME   = 0; flag_CANONNAME < 2;   flag_CANONNAME++)
+       for (flag_NUMERICHOST = 0; flag_NUMERICHOST < 2; flag_NUMERICHOST++)
+       for (flag_NUMERICSERV = 0; flag_NUMERICSERV < 2; flag_NUMERICSERV++)
+       {
+               /* skip tests that need but cannot use network */
+               if (!use_network && hosts[i].need_network)
+                       continue;
+
+               /* determine flags */
+               flags = (flag_PASSIVE     ? AI_PASSIVE : 0) |
+                       (flag_CANONNAME   ? AI_CANONNAME : 0) |
+                       (flag_NUMERICHOST ? AI_NUMERICHOST : 0) |
+                       (flag_NUMERICSERV ? AI_NUMERICSERV : 0);
+
+               /* flags may influence IP address */
+               ipaddr = hosts[i].ipaddr;
+               if (!hosts[i].nodename && flag_PASSIVE)
+                       ipaddr = INADDR_ANY;
+
+               /* determine expected result */
+               exp_results = 
+                       hosts[i].exp_result |
+                       services[j].exp_result |
+                       families[k].exp_result |
+                       socktypes[l].exp_result;
+               if (!hosts[i].nodename && !services[j].servname)
+                       exp_results |= (1 << EAI_NONAME);
+
+               if (flag_NUMERICHOST && !hosts[i].numeric)
+                       exp_results |= (1 << EAI_NONAME);
+
+               if (flag_NUMERICSERV && !services[j].numeric)
+                       exp_results |= (1 << EAI_SERVICE);
+
+               if (services[j].socktype && socktypes[l].value != services[j].socktype)
+                       exp_results |= (1 << EAI_SERVICE);
+
+               /* with no reason for failure, we demand success */
+               if (!exp_results)
+                       exp_results |= (1 << 0);
+
+               /* some options require hints */
+               needhints = (families[k].value != AF_UNSPEC || 
+                       socktypes[l].value != 0 || flags) ? 1 : 0;
+               for (passhints = needhints; passhints < 2; passhints++)
+               {
+                       /* test getaddrinfo function */
+                       test_getaddrinfo(
+                               hosts[i].nodename, 
+                               services[j].servname, 
+                               passhints, 
+                               flags, 
+                               families[k].value, 
+                               socktypes[l].value, 
+                               exp_results, 
+                               htonl(ipaddr), 
+                               flag_CANONNAME && hosts[i].canonname,  
+                               htons(services[j].port));
+               }
+       }
+}
+
+static struct
+{
+       const char *nodename;
+       const char *nodenum;
+       unsigned long ipaddr;
+       int havename;
+} ipaddrs[] = {
+       { "0.0.0.0",    "0.0.0.0",      0x00000000, 0 },
+       { "0.0.0.255",  "0.0.0.255",    0x000000ff, 0 },
+       { "0.0.255.0",  "0.0.255.0",    0x0000ff00, 0 },
+       { "0.255.0.0",  "0.255.0.0",    0x00ff0000, 0 },
+       { "255.0.0.0",  "255.0.0.0",    0xff000000, 0 },
+       { "localhost",  "127.0.0.1",    0x7f000001, 1 },
+       /* no reverse DNS unfortunately */
+       /* { "minix3.org", "130.37.20.20", 0x82251414, 1 } */};
+
+static struct
+{
+       const char *servname;
+       const char *servnum;
+       unsigned short port;
+       int socktype;
+} ports[] = {
+       { "0",      "0",         0, 0           },
+       { "tcpmux", "1",         1, SOCK_STREAM },
+       { "32767",  "32767", 32767, 0           },
+       { "32768",  "32768", 32768, 0           },
+       { "65535",  "65535", 65535, 0           },
+       { "echo",   "7",         7, 0           },
+       { "ftp",    "21",       21, SOCK_STREAM },
+       { "tftp",   "69",       69, SOCK_DGRAM  }};
+
+static int buflens[] = { 0, 1, 2, 3, 4, 5, 6, 9, 10, 11, 255 };
+
+static void test_getnameinfo_all(void)
+{
+       int flag_NUMERICHOST, flag_NAMEREQD, flag_NUMERICSERV, flag_DGRAM;
+       int exp_results, flags, i, j, k, l, socktypemismatch;
+       const char *nodename, *servname;
+
+       /* loop through various parameter values */
+       for (i = 0; i < LENGTH(ipaddrs); i++)
+       for (j = 0; j < LENGTH(ports);   j++)
+       for (k = 0; k < LENGTH(buflens); k++)
+       for (l = 0; l < LENGTH(buflens); l++)
+       for (flag_NUMERICHOST = 0; flag_NUMERICHOST < 2; flag_NUMERICHOST++)
+       for (flag_NAMEREQD    = 0; flag_NAMEREQD < 2;    flag_NAMEREQD++)
+       for (flag_NUMERICSERV = 0; flag_NUMERICSERV < 2; flag_NUMERICSERV++)
+       for (flag_DGRAM       = 0; flag_DGRAM < 2;       flag_DGRAM++)
+       {
+               /* determine flags */
+               flags = (flag_NUMERICHOST ? NI_NUMERICHOST : 0) |
+                       (flag_NAMEREQD    ? NI_NAMEREQD : 0) |
+                       (flag_NUMERICSERV ? NI_NUMERICSERV : 0) |
+                       (flag_DGRAM       ? NI_DGRAM : 0);
+
+               /* determine expected result */
+               exp_results = 0;
+               if (!buflens[k] && !buflens[l])
+                       exp_results |= (1 << EAI_NONAME);
+               
+               nodename = flag_NUMERICHOST ? ipaddrs[i].nodenum : ipaddrs[i].nodename;
+               if (buflens[k] > 0 && buflens[k] <= strlen(nodename))
+                       exp_results |= (1 << EAI_OVERFLOW);
+
+               socktypemismatch =
+                       (flag_DGRAM && ports[j].socktype == SOCK_STREAM) ||
+                       (!flag_DGRAM && ports[j].socktype == SOCK_DGRAM);
+               servname = (flag_NUMERICSERV || socktypemismatch) ? 
+                       ports[j].servnum : ports[j].servname;
+               if (buflens[l] > 0 && buflens[l] <= strlen(servname))
+                       exp_results |= (1 << EAI_OVERFLOW);
+
+               if (flag_NAMEREQD && (!ipaddrs[i].havename | flag_NUMERICHOST) && buflens[k])
+                       exp_results |= (1 << EAI_NONAME);
+
+               /* with no reason for failure, we demand success */
+               if (!exp_results)
+                       exp_results |= (1 << 0);
+
+               /* perform the test */
+               test_getnameinfo(
+                       htonl(ipaddrs[i].ipaddr), 
+                       htons(ports[j].port),
+                       nodename,
+                       buflens[k], 
+                       servname,
+                       buflens[l], 
+                       flags,
+                       exp_results);
+       }
+}
+
+static int can_use_network(void)
+{
+       pid_t pid;
+       int status;
+
+       /* try to ping minix3.org */
+       status = system("ping www.minix3.org > /dev/nul 2>&1");
+       if (status == 127)
+       {
+               printf("cannot execute ping\n");
+               err();
+       }
+
+       return status == 0;
+}
+
+int main(void)
+{
+       int use_network;
+
+       printf("Test 48 ");
+       fflush(stdout);
+
+       use_network = can_use_network();
+       if (!use_network)
+               printf("Warning: no network\n");
+
+       test_getaddrinfo_all(use_network);
+       test_getnameinfo_all();
+
+       quit();
+       return 0;
+}