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.
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",
}
} 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",
* 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));
}
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",
* 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));
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);
/* 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,
#include <inet/inet.h>
#undef printf
+#undef send
#include <fcntl.h>
#include <stdio.h>
#include <inet/inet.h>
#undef printf
+#undef send
#include <assert.h>
#include <fcntl.h>
/* 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));
+
_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 */
+
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_ */
+
libc_FILES=" \
accept.c \
+ addrinfo.c \
bind.c \
connect.c \
dhcp_gettag.c \
ethere2a.c \
etherh2n.c \
ethern2h.c \
+ gai_strerror.c \
getdomain.c \
gethnmadr.c \
gethostent.c \
inet_ntoa.c \
listen.c \
memcspn.c \
+ nameinfo.c \
oneC_sum.c \
rcmd.c \
recv.c \
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
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 ));
--- /dev/null
+#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;
+}
--- /dev/null
+.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.
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
test45-gcc: test45.c test45.h
test46: test46.c
test47: test47.c
+test48: test48.c
# 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
--- /dev/null
+#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;
+}