📄 getaddrinfo.c
字号:
if ( (sin6ptr = calloc(1, sizeof(struct sockaddr_in6))) == NULL) error(EAI_MEMORY);#ifdef SIN6_LEN sin6ptr->sin6_len = sizeof(struct sockaddr_in6);#endif sin6ptr->sin6_family = AF_INET6; memcpy(&sin6ptr->sin6_addr, *ap, sizeof(struct in6_addr)); ai->ai_addr = (struct sockaddr *) sin6ptr; ai->ai_addrlen = sizeof(struct sockaddr_in6); break;#endif /* IPV6 */ } } /* "aihead" points to the first structure in the linked list */ if (hints.ai_flags & AI_CANONNAME) { /* * Posix.1g doesn't say what to do if this flag is set and * multiple addrinfo structures are returned. * We return the canonical name only in the first addrinfo{}. */ if (hptr->h_name != NULL) { if ( (aihead->ai_canonname = strdup(hptr->h_name)) == NULL) error(EAI_MEMORY); } else { /* * Posix.1g says we can just set ai_canonname to point to the * "host" argument, but that makes freeaddrinfo() harder. * We dynamically allocate room for a copy of "host". */ if ( (aihead->ai_canonname = strdup(host)) == NULL) error(EAI_MEMORY); } } /* * Now look up the service, if specified. */ if (serv != NULL && serv[0] != '\0') { if ( (rc = getaddrinfo_serv(aihead, &hints, serv, &sent, hentbuf, HENTBUFSIZ)) != 0) error(rc); } *result = aihead; /* pointer to first structure in linked list */ return(0);bad: freeaddrinfo(aihead); /* free any alloc'ed memory */ return(error);}/* * This function handles the host string. */static intgetaddrinfo_host(const char *host, struct hostent *hptr, struct hostent **hptrptr, char *buf, int bufsiz, int family){#ifdef REENTRANT int h_errno;#endif /* REENTRANT */#ifdef IPV4 /* * We explicitly check for an IPv4 dotted-decimal string. * Recent versions of gethostbyname(), starting around BIND 4.9.2 * do this too, but we have the check here so we don't depend on * this newer feature. (You wouldn't believe the ancient versions * of BIND that some vendors ship.) */ if (isdigit(host[0])) { if (inet_pton(AF_INET, host, hptr->h_addr_list[0]) == 1) { /* Success. Finish making up the hostent{} as though we had called gethostbyname(). */ strncpy(hptr->h_name, host, bufsiz-1); buf[bufsiz-1] = '\0'; hptr->h_addrtype = AF_INET; hptr->h_length = sizeof(struct in_addr); return(0); } }#endif /* IPV4 */#ifdef IPV6 /* * Check for an IPv6 hex string. */ if (isxdigit(host[0]) || host[0] == ':') { if (inet_pton(AF_INET6, host, hptr->h_addr_list[0]) == 1) { /* Success. Finish making up a hostent{} as though we had called gethostbyname(). */ strncpy(buf, host, bufsiz-1); buf[bufsiz-1] = '\0'; hptr->h_addrtype = AF_INET6; hptr->h_length = sizeof(struct in6_addr); return(0); } }#endif /* IPV6 */ /* * Not an address, must be a hostname, try the DNS. * Initialize the resolver, if not already initialized. */ if ((_res.options & RES_INIT) == 0) res_init(); /* need this to set _res.options below */#ifdef IPV6 /* * Notice that the following might be considered optional, and * could be #ifdef'ed out if your <resolv.h> does not define * RES_USE_INET6. But I am assuming you have BIND-4.9.4 installed * and want the IPv4/IPv6 semantics that it defines for gethostbyname(). */#ifndef RES_USE_INET6 /* This is a gross hack; following line from BIND-4.9.4 release ... */ /* (if you're using 4.9.4, but have not installed the include files) */#define RES_USE_INET6 0x00002000 /* use/map IPv6 in gethostbyname() */#endif if (family == AF_INET6) _res.options |= RES_USE_INET6;#endif /* IPV6 */#ifdef REENTRANT hptr = gethostbyname_r(host, hptr, buf, bufsiz, &h_errno);#else hptr = gethostbyname(host);#endif /* REENTRANT */ if (hptr == NULL) { switch (h_errno) { case HOST_NOT_FOUND: return(EAI_NONAME); case TRY_AGAIN: return(EAI_AGAIN); case NO_RECOVERY: return(EAI_FAIL); case NO_DATA: return(EAI_NODATA); default: return(EAI_NONAME); } } *hptrptr = hptr; return(0);}/* * This function handles the service string. */static intgetaddrinfo_serv(struct addrinfo *aihead, const struct addrinfo *hintsptr, const char *serv, struct servent *sptrarg, char *buf, int bufsiz){ int port, rc; int nfound = 0; struct servent *sptr; /* * We allow the service to be a numeric string, which we * interpret as a decimal port number. Posix.1g doesn't * explicitly say to do this, but it just makes sense. * But to do this the caller must specify a socket type, * else there's no way to return values for socket(). */ if (isdigit(serv[0]) && hintsptr->ai_socktype != 0) { port = htons(atoi(serv)); if ( (rc = getaddrinfo_port(aihead, port, hintsptr->ai_socktype)) == 0) return(EAI_NONAME); else if (rc < 0) return(EAI_MEMORY); else return(0); } /* * Not a special case, try the "/etc/services" file (or whatever). * We first try TCP, if applicable. */ if (hintsptr->ai_socktype == 0 || hintsptr->ai_socktype == SOCK_STREAM) {#ifdef REENTRANT sptr = getservbyname_r(serv, "tcp", sptrarg, buf, bufsiz);#else sptr = getservbyname(serv, "tcp");#endif /* REENTRANT */ if (sptr != NULL) { rc = getaddrinfo_port(aihead, sptr->s_port, SOCK_STREAM); if (rc < 0) return(EAI_MEMORY); nfound += rc; } } /* * Now try UDP, if applicable. */ if (hintsptr->ai_socktype == 0 || hintsptr->ai_socktype == SOCK_DGRAM) {#ifdef REENTRANT sptr = getservbyname_r(serv, "udp", sptrarg, buf, bufsiz);#else sptr = getservbyname(serv, "udp");#endif /* REENTRANT */ if (sptr != NULL) { rc = getaddrinfo_port(aihead, sptr->s_port, SOCK_DGRAM); if (rc < 0) return(EAI_MEMORY); nfound += rc; } } if (nfound == 0) { /* You could call getservbyname() one more time, with no protocol specified, but "tcp" and "udp" are all that are supported today. */ if (hintsptr->ai_socktype == 0) return(EAI_NONAME); /* all calls to getservbyname() failed */ else return(EAI_SERVICE);/* service not supported for socket type */ } return(0);}/* * Go through all the addrinfo structures, checking for a match of the * socket type and filling in the socket type, and then the port number * in the corresponding socket address structures. * * The AI_CLONE flag works as follows. Consider a multihomed host with * two IP addresses and no socket type specified by the caller. After * the "host" search there are two addrinfo structures, one per IP address. * Assuming a service supported by both TCP and UDP (say the daytime * service) we need to return *four* addrinfo structures: * IP#1, SOCK_STREAM, TCP port, * IP#1, SOCK_DGRAM, UDP port, * IP#2, SOCK_STREAM, TCP port, * IP#2, SOCK_DGRAM, UDP port. * To do this, when the "host" loop creates an addrinfo structure, if the * caller has not specified a socket type (hints->ai_socktype == 0), the * AI_CLONE flag is set. When the following function finds an entry like * this it is handled as follows: If the entry's ai_socktype is still 0, * this is the first use of the structure, and the ai_socktype field is set. * But, if the entry's ai_socktype is nonzero, then we clone a new addrinfo * structure and set it's ai_socktype to the new value. Although we only * need two socket types today (SOCK_STREAM and SOCK_DGRAM) this algorithm * will handle any number. Also notice that Posix.1g requires all socket * types to be nonzero. */static intgetaddrinfo_port(struct addrinfo *aihead, int port, int socktype) /* port must be in network byte order */{ int nfound = 0; struct addrinfo *ai; for (ai = aihead; ai != NULL; ai = ai->ai_next) { /* * We set the socket type but not the protocol, because if a * port number is specified, the protocol must be TCP or UDP, * and a protocol of 0 for socket() is fine for TCP and UDP. * The only time a nonzero protocol argument is required by * socket() is for a raw socket, in which case a service will * not be specified to getaddrinfo(). */ if (ai->ai_flags & AI_CLONE) { if (ai->ai_socktype != 0) { if ( (ai = getaddrinfo_clone(ai)) == NULL) return(-1); /* tell caller it's a memory allocation error */ /* ai points to newly cloned entry, which is what we want */ } } else if (ai->ai_socktype != socktype) continue; /* ignore if mismatch on socket type */ ai->ai_socktype = socktype; switch (ai->ai_family) {#ifdef IPV4 case AF_INET: ((struct sockaddr_in *) ai->ai_addr)->sin_port = port; nfound++; break;#endif#ifdef IPV6 case AF_INET6: ((struct sockaddr_in6 *) ai->ai_addr)->sin6_port = port; nfound++; break;#endif } } return(nfound);}/* * Clone a new addrinfo structure from an existing one. */static struct addrinfo *getaddrinfo_clone(struct addrinfo *ai){ struct addrinfo *new; if ( (new = calloc(1, sizeof(struct addrinfo))) == NULL) return(NULL); new->ai_next = ai->ai_next; ai->ai_next = new; new->ai_flags = 0; /* make sure AI_CLONE is off */ new->ai_family = ai->ai_family; new->ai_socktype = ai->ai_socktype; new->ai_protocol = ai->ai_protocol; new->ai_canonname = NULL; new->ai_addrlen = ai->ai_addrlen; if ( (new->ai_addr = malloc(ai->ai_addrlen)) == NULL) return(NULL); memcpy(new->ai_addr, ai->ai_addr, ai->ai_addrlen); return(new);}#ifdef LOCAL/* * Do everything for a Unix domain socket. * Only one addrinfo{} is returned. */static intaddrinfo_local(const char *path, struct addrinfo *hints, struct addrinfo **result){ struct addrinfo *ai; struct sockaddr_un *unp; if (hints->ai_socktype == 0) return(EAI_SOCKTYPE); /* we cannot tell socket type from service */ if ( (ai = calloc(1, sizeof(struct addrinfo))) == NULL) return(NULL); ai->ai_flags = 0; ai->ai_family = AF_LOCAL; ai->ai_socktype = hints->ai_socktype; ai->ai_protocol = 0; /* allocate and fill in a socket address structure */ ai->ai_addrlen = sizeof(struct sockaddr_un); if ( (ai->ai_addr = malloc(ai->ai_addrlen)) == NULL) return(EAI_MEMORY); unp = (struct sockaddr_un *) ai->ai_addr; unp->sun_family = AF_UNIX; strncpy(unp->sun_path, path, sizeof(unp->sun_path)); ai->ai_canonname = NULL; /* maybe return the i-node number :-) */ ai->ai_next = NULL; *result = ai; if (hints->ai_flags & AI_PASSIVE) unlink(path); /* OK if this fails */ return(0); /* success */}#endif /* LOCAL */voidfreeaddrinfo(struct addrinfo *aihead){ struct addrinfo *ai, *ainext; for (ai = aihead; ai != NULL; ai = ainext) { if (ai->ai_addr != NULL) free(ai->ai_addr); /* the socket address structure */ if (ai->ai_canonname != NULL) free(ai->ai_canonname); /* the canonical name */ ainext = ai->ai_next; /* can't fetch ai_next after free() */ free(ai); /* the addrinfo{} itself */ }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -