getaddrinfo.c

来自「非常好的dns解析软件」· C语言 代码 · 共 808 行 · 第 1/2 页

C
808
字号
/* * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC") * Copyright (C) 1999-2001  Internet Software Consortium. * * This code is derived from software contributed to ISC by * Berkeley Software Design, Inc. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND BERKELEY SOFTWARE DESIGN, INC. * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *//* $Id: getaddrinfo.c,v 1.43.18.6 2006/11/14 01:07:28 marka Exp $ *//*! \file *//** *    lwres_getaddrinfo() is used to get a list of IP addresses and port *    numbers for host hostname and service servname. The function is the *    lightweight resolver's implementation of getaddrinfo() as defined in *    RFC2133. hostname and servname are pointers to null-terminated strings *    or NULL. hostname is either a host name or a numeric host address *    string: a dotted decimal IPv4 address or an IPv6 address. servname is *    either a decimal port number or a service name as listed in *    /etc/services. *  *    If the operating system does not provide a struct addrinfo, the *    following structure is used: *  * \code * struct  addrinfo { *         int             ai_flags;       // AI_PASSIVE, AI_CANONNAME *         int             ai_family;      // PF_xxx *         int             ai_socktype;    // SOCK_xxx *         int             ai_protocol;    // 0 or IPPROTO_xxx for IPv4 and IPv6 *         size_t          ai_addrlen;     // length of ai_addr *         char            *ai_canonname;  // canonical name for hostname *         struct sockaddr *ai_addr;       // binary address *         struct addrinfo *ai_next;       // next structure in linked list * }; * \endcode *  *  *    hints is an optional pointer to a struct addrinfo. This structure can *    be used to provide hints concerning the type of socket that the caller *    supports or wishes to use. The caller can supply the following *    structure elements in *hints: *  * <ul> *    <li>ai_family: *           The protocol family that should be used. When ai_family is set *           to PF_UNSPEC, it means the caller will accept any protocol *           family supported by the operating system.</li> *  *    <li>ai_socktype: *           denotes the type of socket -- SOCK_STREAM, SOCK_DGRAM or *           SOCK_RAW -- that is wanted. When ai_socktype is zero the caller *           will accept any socket type.</li> *  *    <li>ai_protocol: *           indicates which transport protocol is wanted: IPPROTO_UDP or *           IPPROTO_TCP. If ai_protocol is zero the caller will accept any *           protocol.</li> *  *    <li>ai_flags: *           Flag bits. If the AI_CANONNAME bit is set, a successful call to *           lwres_getaddrinfo() will return a null-terminated string *           containing the canonical name of the specified hostname in *           ai_canonname of the first addrinfo structure returned. Setting *           the AI_PASSIVE bit indicates that the returned socket address *           structure is intended for used in a call to bind(2). In this *           case, if the hostname argument is a NULL pointer, then the IP *           address portion of the socket address structure will be set to *           INADDR_ANY for an IPv4 address or IN6ADDR_ANY_INIT for an IPv6 *           address.<br /><br /> *  *           When ai_flags does not set the AI_PASSIVE bit, the returned *           socket address structure will be ready for use in a call to *           connect(2) for a connection-oriented protocol or connect(2), *           sendto(2), or sendmsg(2) if a connectionless protocol was *           chosen. The IP address portion of the socket address structure *           will be set to the loopback address if hostname is a NULL *           pointer and AI_PASSIVE is not set in ai_flags.<br /><br /> *  *           If ai_flags is set to AI_NUMERICHOST it indicates that hostname *           should be treated as a numeric string defining an IPv4 or IPv6 *           address and no name resolution should be attempted. * </li></ul> *  *    All other elements of the struct addrinfo passed via hints must be *    zero. *  *    A hints of NULL is treated as if the caller provided a struct addrinfo *    initialized to zero with ai_familyset to PF_UNSPEC. *  *    After a successful call to lwres_getaddrinfo(), *res is a pointer to a *    linked list of one or more addrinfo structures. Each struct addrinfo *    in this list cn be processed by following the ai_next pointer, until a *    NULL pointer is encountered. The three members ai_family, ai_socktype, *    and ai_protocol in each returned addrinfo structure contain the *    corresponding arguments for a call to socket(2). For each addrinfo *    structure in the list, the ai_addr member points to a filled-in socket *    address structure of length ai_addrlen. *  *    All of the information returned by lwres_getaddrinfo() is dynamically *    allocated: the addrinfo structures, and the socket address structures *    and canonical host name strings pointed to by the addrinfostructures. *    Memory allocated for the dynamically allocated structures created by a *    successful call to lwres_getaddrinfo() is released by *    lwres_freeaddrinfo(). ai is a pointer to a struct addrinfo created by *    a call to lwres_getaddrinfo(). *  * \section lwresreturn RETURN VALUES *  *    lwres_getaddrinfo() returns zero on success or one of the error codes *    listed in gai_strerror() if an error occurs. If both hostname and *    servname are NULL lwres_getaddrinfo() returns #EAI_NONAME. *  * \section lwressee SEE ALSO *  *    lwres(3), lwres_getaddrinfo(), lwres_freeaddrinfo(), *    lwres_gai_strerror(), RFC2133, getservbyname(3), connect(2), *    sendto(2), sendmsg(2), socket(2). */#include <config.h>#include <string.h>#include <errno.h>#include <lwres/lwres.h>#include <lwres/net.h>#include <lwres/netdb.h>#include <lwres/stdlib.h>#define SA(addr)	((struct sockaddr *)(addr))#define SIN(addr)	((struct sockaddr_in *)(addr))#define SIN6(addr)	((struct sockaddr_in6 *)(addr))#define SUN(addr)	((struct sockaddr_un *)(addr))/*! \struct addrinfo */static struct addrinfo	*ai_reverse(struct addrinfo *oai),	*ai_clone(struct addrinfo *oai, int family),	*ai_alloc(int family, int addrlen);#ifdef AF_LOCALstatic int get_local(const char *name, int socktype, struct addrinfo **res);#endifstatic int add_ipv4(const char *hostname, int flags, struct addrinfo **aip,    int socktype, int port);static int add_ipv6(const char *hostname, int flags, struct addrinfo **aip,    int socktype, int port);static void set_order(int, int (**)(const char *, int, struct addrinfo **,         int, int));#define FOUND_IPV4	0x1#define FOUND_IPV6	0x2#define FOUND_MAX	2#define ISC_AI_MASK (AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST)/*% Get a list of IP addresses and port numbers for host hostname and service servname. */intlwres_getaddrinfo(const char *hostname, const char *servname,	const struct addrinfo *hints, struct addrinfo **res){	struct servent *sp;	const char *proto;	int family, socktype, flags, protocol;	struct addrinfo *ai, *ai_list;	int port, err, i;	int (*net_order[FOUND_MAX+1])(const char *, int, struct addrinfo **,		 int, int);	if (hostname == NULL && servname == NULL)		return (EAI_NONAME);	proto = NULL;	if (hints != NULL) {		if ((hints->ai_flags & ~(ISC_AI_MASK)) != 0)			return (EAI_BADFLAGS);		if (hints->ai_addrlen || hints->ai_canonname ||		    hints->ai_addr || hints->ai_next) {			errno = EINVAL;			return (EAI_SYSTEM);		}		family = hints->ai_family;		socktype = hints->ai_socktype;		protocol = hints->ai_protocol;		flags = hints->ai_flags;		switch (family) {		case AF_UNSPEC:			switch (hints->ai_socktype) {			case SOCK_STREAM:				proto = "tcp";				break;			case SOCK_DGRAM:				proto = "udp";				break;			}			break;		case AF_INET:		case AF_INET6:			switch (hints->ai_socktype) {			case 0:				break;			case SOCK_STREAM:				proto = "tcp";				break;			case SOCK_DGRAM:				proto = "udp";				break;			case SOCK_RAW:				break;			default:				return (EAI_SOCKTYPE);			}			break;#ifdef	AF_LOCAL		case AF_LOCAL:			switch (hints->ai_socktype) {			case 0:				break;			case SOCK_STREAM:				break;			case SOCK_DGRAM:				break;			default:				return (EAI_SOCKTYPE);			}			break;#endif		default:			return (EAI_FAMILY);		}	} else {		protocol = 0;		family = 0;		socktype = 0;		flags = 0;	}#ifdef	AF_LOCAL	/*!	 * First, deal with AF_LOCAL.  If the family was not set,	 * then assume AF_LOCAL if the first character of the	 * hostname/servname is '/'.	 */	if (hostname != NULL &&	    (family == AF_LOCAL || (family == 0 && *hostname == '/')))		return (get_local(hostname, socktype, res));	if (servname != NULL &&	    (family == AF_LOCAL || (family == 0 && *servname == '/')))		return (get_local(servname, socktype, res));#endif	/*	 * Ok, only AF_INET and AF_INET6 left.	 */	ai_list = NULL;	/*	 * First, look up the service name (port) if it was	 * requested.  If the socket type wasn't specified, then	 * try and figure it out.	 */	if (servname != NULL) {		char *e;		port = strtol(servname, &e, 10);		if (*e == '\0') {			if (socktype == 0)				return (EAI_SOCKTYPE);			if (port < 0 || port > 65535)				return (EAI_SERVICE);			port = htons((unsigned short) port);		} else {			sp = getservbyname(servname, proto);			if (sp == NULL)				return (EAI_SERVICE);			port = sp->s_port;			if (socktype == 0) {				if (strcmp(sp->s_proto, "tcp") == 0)					socktype = SOCK_STREAM;				else if (strcmp(sp->s_proto, "udp") == 0)					socktype = SOCK_DGRAM;			}		}	} else		port = 0;	/*	 * Next, deal with just a service name, and no hostname.	 * (we verified that one of them was non-null up above).	 */	if (hostname == NULL && (flags & AI_PASSIVE) != 0) {		if (family == AF_INET || family == 0) {			ai = ai_alloc(AF_INET, sizeof(struct sockaddr_in));			if (ai == NULL)				return (EAI_MEMORY);			ai->ai_socktype = socktype;			ai->ai_protocol = protocol;			SIN(ai->ai_addr)->sin_port = port;			ai->ai_next = ai_list;			ai_list = ai;		}		if (family == AF_INET6 || family == 0) {			ai = ai_alloc(AF_INET6, sizeof(struct sockaddr_in6));			if (ai == NULL) {				lwres_freeaddrinfo(ai_list);				return (EAI_MEMORY);			}			ai->ai_socktype = socktype;			ai->ai_protocol = protocol;			SIN6(ai->ai_addr)->sin6_port = port;			ai->ai_next = ai_list;			ai_list = ai;		}		*res = ai_list;		return (0);	}	/*	 * If the family isn't specified or AI_NUMERICHOST specified,	 * check first to see if it is a numeric address.	 * Though the gethostbyname2() routine	 * will recognize numeric addresses, it will only recognize	 * the format that it is being called for.  Thus, a numeric	 * AF_INET address will be treated by the AF_INET6 call as	 * a domain name, and vice versa.  Checking for both numerics	 * here avoids that.	 */	if (hostname != NULL &&	    (family == 0 || (flags & AI_NUMERICHOST) != 0)) {		char abuf[sizeof(struct in6_addr)];		char nbuf[NI_MAXHOST];		int addrsize, addroff;#ifdef LWRES_HAVE_SIN6_SCOPE_ID		char *p, *ep;		char ntmp[NI_MAXHOST];		lwres_uint32_t scopeid;#endif#ifdef LWRES_HAVE_SIN6_SCOPE_ID		/*		 * Scope identifier portion.		 */		ntmp[0] = '\0';		if (strchr(hostname, '%') != NULL) {			strncpy(ntmp, hostname, sizeof(ntmp) - 1);			ntmp[sizeof(ntmp) - 1] = '\0';			p = strchr(ntmp, '%');			ep = NULL;			/*			 * Vendors may want to support non-numeric			 * scopeid around here.			 */			if (p != NULL)				scopeid = (lwres_uint32_t)strtoul(p + 1,								  &ep, 10);			if (p != NULL && ep != NULL && ep[0] == '\0')				*p = '\0';			else {				ntmp[0] = '\0';				scopeid = 0;			}		} else			scopeid = 0;#endif               if (lwres_net_pton(AF_INET, hostname, (struct in_addr *)abuf)		   == 1)	       {			if (family == AF_INET6) {				/*				 * Convert to a V4 mapped address.				 */				struct in6_addr *a6 = (struct in6_addr *)abuf;				memcpy(&a6->s6_addr[12], &a6->s6_addr[0], 4);				memset(&a6->s6_addr[10], 0xff, 2);				memset(&a6->s6_addr[0], 0, 10);				goto inet6_addr;			}			addrsize = sizeof(struct in_addr);			addroff = (char *)(&SIN(0)->sin_addr) - (char *)0;			family = AF_INET;			goto common;#ifdef LWRES_HAVE_SIN6_SCOPE_ID		} else if (ntmp[0] != '\0' &&

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?