create the getaddrinfo and getnameinfo library functions and friends

This commit is contained in:
Erik van der Kouwe 2010-01-21 06:38:17 +00:00
parent 24964aa706
commit a5a2073680
15 changed files with 1262 additions and 14 deletions

View 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,

View 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>

View 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>

View 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));

View 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 */

View 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_ */

View 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 \

322
lib/ip/addrinfo.c Executable file
View file

@ -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;
}

64
lib/ip/gai_strerror.c Executable file
View file

@ -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;
}

View 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 ));

148
lib/ip/nameinfo.c Executable file
View file

@ -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;
}

66
man/man3/getaddrinfo.3 Executable file
View file

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

View 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

View file

@ -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

566
test/test48.c Executable file
View file

@ -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;
}