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 + -
显示快捷键?