📄 getaddrinfo.c
字号:
/* * Copyright (c) 1996 W. Richard Stevens. All rights reserved. * * Permission to use or modify this software for educational or * for commercial purposes, and without fee, is hereby granted, * provided that the above copyright notice appears in connection * with any and all uses, with clear indication as to any * modifications made. The author RESERVES the sole rights of * reproduction, publication and distribution and hence permission * to print this source code in any book, reference manual, * magazine, or other type of publication, including any digital * medium, must be granted in writing by W. Richard Stevens. * * The author makes no representations about the suitability of this * software for any purpose. It is provided "as is" without express * or implied warranty. *//* tabs set for 4 spaces, not 8 */#ifdef __osf__#define _SOCKADDR_LEN /* required for this implementation */#define INET6 /* required for this implementation */#endif#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <ctype.h> /* isdigit() */#include <netdb.h> /* hostent{}, servent{}, etc. */#include <arpa/inet.h> /* inet_pton() */#include <arpa/nameser.h>#include <resolv.h> /* DNS resolver */#include <stdlib.h> /* malloc() and calloc() */#include <string.h> /* strdup() and strncpy() */#include <unistd.h> /* unlink() */#include <sys/un.h> /* for Unix domain socket stuff */#ifndef __osf__#include "addrinfo.h" /* defines in here really belong in <netdb.h> */#endif/* NOTE: this code assumes you have the inet_pton() function as defined * in the BIND-4.9.4 release or later (ftp://ftp.vix.com/pub/bind). * If you don't have this in your resolver library, in this tar file is * a copy of it, along with inet_ntop(). *//* We need a way to determine if the compiling host supports IPv4/IPv6. * Cannot test for AF_INET6, as some vendors #define it, even though * they don't support it. */#ifdef AF_INET#define IPV4 /* this is tested throughout the code that follows */#endif /* no one constant that all implementations define, sigh */#if defined(IPV6ADDR_ANY_INIT)#define IPV6 /* this is tested throughout the code that follows */#elif defined(IPV6_FLOWINFO_FLOWLABEL)#define IPV6 /* this is tested throughout the code that follows */#endif/* There is no requirement that getaddrinfo() support Unix domain sockets, * but what the heck, it doesn't take too much code, and it's a real good * test whether an application is *really* protocol-independent. */#ifdef AF_UNIX#define LOCAL /* this is tested throughout the code that follows */#ifndef AF_LOCAL#define AF_LOCAL AF_UNIX /* the Posix.1g correct term */#endif#ifndef PF_LOCAL#define PF_LOCAL PF_UNIX /* the Posix.1g correct term */#endif#endif /* AF_UNIX *//* Define REENTRANT if the reentrant versions of get{host|serv}byname * are to be used. * Note that getaddrinfo{} *must* be reentrant if the underlying * get{host|serv}byname_r functions are provided. * This program has only been tested with the Solaris 2.x functions. * (I have no idea if other vendors have the same functions or not.) * * Long diatribe: Don't define REENTRANT. At least not until you know * what your vendor's gethostbyname_r() function does with regard to * IPv4/IPv6 addresses. If you really need a reentrant version of this * function, because you call it from different threads, then use a * mutex lock to protect the calls. * The problem at the time of this writing is the handling of IPv4/IPv6 * addresses. BIND-4.9.4 does it the "right" way :-), but this won't * be documented until a later revision of the IPv6 BSD API spec. Also * BIND-4.9.4 doesn't provide the reentrant _r() functions, and I have * no idea what the vendors like Sun have done with these functions. * The code far below that calls gethostbyname() sets the resolver * RES_USE_INET6 option if the caller specifies an ai_family of AF_INET6. * This causes 16-byte addresses to be returned, regardless, either * "true" IPv6 address from AAAA records, or IPv4-mapped IPv6 addresses * from A records. If the caller specifies an ai_family of AF_INET6, * then we should return 16-byte addresses. * With BIND-4.9.4 the caller can also force the return of 16-byte addresses * by setting the environment variable RES_OPTIONS, as in * % RES_OPTIONS=inet6 ./a.out arguments... * This way the caller need not pollute the code with code like * ai_family = AF_INET6, making the code protocol-dependent. *//* #define REENTRANT *//* Define following function prototype if your headers don't define it */int inet_pton(int, const char *, void *);#define HENTBUFSIZ 8*1024#define HENTMAXADDR 32 /* max binary address: 16 for IPv4, 24 for IPv6 */ /* following internal flags cannot overlap with other AI_xxx flags */#define AI_CLONE 4 /* clone this entry for other socket types */ /* function prototypes for our own internal functions */static int getaddrinfo_host(const char *, struct hostent *, struct hostent **, char *, int, int);static int getaddrinfo_serv(struct addrinfo *, const struct addrinfo *, const char *, struct servent *, char *, int);static int getaddrinfo_port(struct addrinfo *, int , int);static int addrinfo_local(const char *, struct addrinfo *, struct addrinfo **);static struct addrinfo *getaddrinfo_clone(struct addrinfo *); /* globals for all functions in this file; these *must* be read-only if this function is to be reentrant */static struct addrinfo hints_default;intgetaddrinfo(const char *host, const char *serv, const struct addrinfo *hintsptr, struct addrinfo **result){ int rc, error; struct hostent *hptr, hent; struct servent sent; char hentbuf[HENTBUFSIZ], hent_addr[HENTMAXADDR]; char *hent_aliases[1], *hent_addr_list[2]; char **ap; struct addrinfo hints, *ai, *aihead, **aipnext;#ifdef IPV4 struct sockaddr_in *sinptr;#endif#ifdef IPV6 struct sockaddr_in6 *sin6ptr;#endif/* If we encounter an error we want to free() any dynamic memory that we've allocated. This is our hack to simplify the code. */#define error(e) { error = (e); goto bad; } /* * We must make a copy of the caller's hints structure, so we can * modify ai_family. If the caller doesn't provide a hints structure, * use a default one. This simplifies all the following code. * In the default one, ai_flags, ai_socktype, and ai_protocol are all 0, * but we have to set ai_family to AF_UNSPEC, which isn't guaranteed to * be 0. */ if (hintsptr == NULL) { hints_default.ai_family = AF_UNSPEC; hints = hints_default; /* struct copy */ } else hints = *hintsptr; /* struct copy */ /* * First some error checking. */ if (hints.ai_flags & ~(AI_PASSIVE | AI_CANONNAME)) error(EAI_BADFLAGS); /* unknown flag bits */ /* * Check that the family is valid, and if a socket type is also * specified, check that it's valid for the family. */ if (hints.ai_family != 0) { switch(hints.ai_family) { case AF_UNSPEC: break; /* Actually, AF_UNSPEC is normally defined as 0, but Posix.1g does not require this. */#ifdef IPV4 case AF_INET: if (hints.ai_socktype != 0 && (hints.ai_socktype != SOCK_STREAM && hints.ai_socktype != SOCK_DGRAM && hints.ai_socktype != SOCK_RAW)) error(EAI_SOCKTYPE); /* invalid socket type */ break;#endif#ifdef IPV6 case AF_INET6: if (hints.ai_socktype != 0 && (hints.ai_socktype != SOCK_STREAM && hints.ai_socktype != SOCK_DGRAM && hints.ai_socktype != SOCK_RAW)) error(EAI_SOCKTYPE); /* invalid socket type */ break;#endif#ifdef LOCAL case AF_LOCAL: if (hints.ai_socktype != 0 && (hints.ai_socktype != SOCK_STREAM && hints.ai_socktype != SOCK_DGRAM)) error(EAI_SOCKTYPE); /* invalid socket type */ break;#endif default: error(EAI_FAMILY); /* unknown protocol family */ } } if (host == NULL || host[0] == '\0') { if (serv == NULL || serv[0] == '\0') error(EAI_NONAME); /* either host or serv must be specified */ if (hints.ai_flags & AI_PASSIVE) { /* * No "host" and AI_PASSIVE: the returned address must be * ready for bind(): 0.0.0.0 for IPv4 or 0::0 for IPv6. */ switch (hints.ai_family) {#ifdef IPV4 case AF_INET: host = "0.0.0.0"; break;#endif#ifdef IPV6 case AF_INET6: host = "0::0"; break;#endif#ifdef LOCAL case AF_LOCAL: if (serv[0] != '/') /* allow service to specify path */ error(EAI_ADDRFAMILY); break;#endif case 0: error(EAI_ADDRFAMILY); /* How can we initialize a socket address structure for a passive open if we don't even know the family? */ } } else { /* * No host and not AI_PASSIVE: caller implies connect() to * local host. */ host = "localhost"; } } else if (hints.ai_family == 0) { /* * Caller specifies a host but no address family. * If the host string is really a valid IPv4 dotted-decimal address, * set family to IPv4. Similarly for IPv6 strings. * This allows server applications to be protocol independent * (not having to hard code a protocol family), allowing the * user who starts the program to specify either 0.0.0.0 or 0::0. * * Assumed below is that inet_pton() allows only "valid" strings, * which Paul Vixie put into the BIND-4.9.4 version of this function. */ char temp[16];#ifdef IPV4 if (inet_pton(AF_INET, host, temp) == 1) hints.ai_family = AF_INET;#endif#ifdef IPV6 if (inet_pton(AF_INET6, host, temp) == 1) hints.ai_family = AF_INET6;#endif /* * Note that we could bypass some of the testing done in * getaddrinfo_host(), but it doesn't seem worth complicating * this (already long) function. */ }#ifdef LOCAL /* * For a Unix domain socket only one string can be provided and we * require it to be an absolute pathname. (Using relative pathnames * is asking for trouble.) We allow this string to be specified as * either the hostname or the service name, in which case we ignore * the other string. Notice that a slash is not allowed in a DNS * hostname (see RFC 1912) and a slash does not appear in any of the * service names in /etc/services either. Hence no conflict. * For example, often a protocol-independent server will allow an * argument to specify the service (e.g., port number) and let the * hostname be wildcarded. Similarly, a protocol-independent client * often allows only the hostname as a command-line argument, hardcoding * a service name in the program (which we ignore). */ if ((host != NULL && host[0] == '/')) return(addrinfo_local(host, &hints, result)); if ((serv != NULL && serv[0] == '/')) return(addrinfo_local(serv, &hints, result));#endif /* * Look up the host. The code above guarantees that "host" * is a nonnull pointer to a nonull string. * * We first initialize "hent" assuming "host" is an IPv4/IPv6 address * (instead of a name). This saves passing lots of additional * arguments to getaddrinfo_host(). */ hent.h_name = hentbuf; /* char string specifying address goes here */ hent.h_aliases = hent_aliases; hent_aliases[0] = NULL; /* no aliases */ hent.h_addr_list = hent_addr_list; hent_addr_list[0] = hent_addr; /* one binary address in [0] */ hent_addr_list[1] = NULL; hptr = &hent; if ( (rc = getaddrinfo_host(host, &hent, &hptr, hentbuf, HENTBUFSIZ, hints.ai_family)) != 0) error(rc); /* * "hptr" now points to a filled in hostent{}. * If "host" was an IPv4/IPv6 address, instead of a name, then * "hptr" points to our own "hent" structure. * If gethostbyname_r() was called, then "hptr" points to our own * "hent" structure, which was passed as as an argument to the * reentrant function. * If gethostbyname() was called, then "hptr" points to the static * hostent{} that it returned. * * Check for address family mismatch if the caller specified one. * Note that Posix.1g assumes that AF_foo == PF_foo. */ if (hints.ai_family != AF_UNSPEC && hints.ai_family != hptr->h_addrtype) error(EAI_ADDRFAMILY); /* * Go through the list of returned addresses and create one * addrinfo{} for each one, linking all the structures together. * We still have not looked at the service--that comes after this. */ aihead = NULL; aipnext = &aihead; for (ap = hptr->h_addr_list; *ap != NULL; ap++) { if ( (ai = calloc(1, sizeof(struct addrinfo))) == NULL) error(EAI_MEMORY); *aipnext = ai; /* prev points to this new one */ aipnext = &ai->ai_next; /* pointer to next one goes here */ /* initialize from hints; could be 0 */ if ( (ai->ai_socktype = hints.ai_socktype) == 0) ai->ai_flags |= AI_CLONE; ai->ai_protocol = hints.ai_protocol; ai->ai_family = hptr->h_addrtype; switch (ai->ai_family) { /* * Allocate a socket address structure and fill it in. * The port number will be filled in later, when the service * is processed. */#ifdef IPV4 case AF_INET: if ( (sinptr = calloc(1, sizeof(struct sockaddr_in))) == NULL) error(EAI_MEMORY);#ifdef SIN_LEN sinptr->sin_len = sizeof(struct sockaddr_in);#endif sinptr->sin_family = AF_INET; memcpy(&sinptr->sin_addr, *ap, sizeof(struct in_addr)); ai->ai_addr = (struct sockaddr *) sinptr; ai->ai_addrlen = sizeof(struct sockaddr_in); break;#endif /* IPV4 */#ifdef IPV6 case AF_INET6:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -