📄 hostip.c
字号:
} } conn->async.dns = dns; /* The input hostent struct will be freed by ares when we return from this function */}/* * Return name information about the given hostname and port number. If * successful, the 'hostent' is returned and the forth argument will point to * memory we need to free after use. That meory *MUST* be freed with * Curl_freeaddrinfo(), nothing else. */static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn, char *hostname, int port, int *waitp){ int rc; char *bufp; struct SessionHandle *data = conn->data; rc = ares_init(&data->state.areschannel); *waitp = FALSE; if(!rc) { /* only if success */ bufp = strdup(hostname); if(bufp) { Curl_safefree(conn->async.hostname); conn->async.hostname = bufp; conn->async.port = port; conn->async.done = FALSE; /* not done */ conn->async.status = 0; /* clear */ conn->async.dns = NULL; /* clear */ ares_gethostbyname(data->state.areschannel, hostname, PF_INET, host_callback, conn); *waitp = TRUE; /* please wait for the response */ } else ares_destroy(data->state.areschannel); } return NULL; /* no struct yet */ }#else/* For builds without ARES, Curl_resolv() can never return wait==TRUE, so this function will never be called. If it still gets called, we return failure at once. */CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry){ (void)conn; *entry=NULL; return CURLE_COULDNT_RESOLVE_HOST;}CURLcode Curl_multi_ares_fdset(struct connectdata *conn, fd_set *read_fd_set, fd_set *write_fd_set, int *max_fdp){ (void)conn; (void)read_fd_set; (void)write_fd_set; (void)max_fdp; return CURLE_OK;}CURLcode Curl_is_resolved(struct connectdata *conn, struct Curl_dns_entry **dns){ (void)conn; *dns = NULL; return CURLE_COULDNT_RESOLVE_HOST;}#endif#if defined(ENABLE_IPV6) && !defined(USE_ARES)#ifdef CURLDEBUG/* These two are strictly for memory tracing and are using the same * style as the family otherwise present in memdebug.c. I put these ones * here since they require a bunch of struct types I didn't wanna include * in memdebug.c */int curl_getaddrinfo(char *hostname, char *service, struct addrinfo *hints, struct addrinfo **result, int line, const char *source){ int res=(getaddrinfo)(hostname, service, hints, result); if(0 == res) { /* success */ if(logfile) fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n", source, line, (void *)*result); } else { if(logfile) fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n", source, line); } return res;}void curl_freeaddrinfo(struct addrinfo *freethis, int line, const char *source){ (freeaddrinfo)(freethis); if(logfile) fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n", source, line, (void *)freethis);}#endif/* * Return name information about the given hostname and port number. If * successful, the 'addrinfo' is returned and the forth argument will point to * memory we need to free after use. That meory *MUST* be freed with * Curl_freeaddrinfo(), nothing else. */static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn, char *hostname, int port, int *waitp){ struct addrinfo hints, *res; int error; char sbuf[NI_MAXSERV]; int s, pf; struct SessionHandle *data = conn->data; *waitp=0; /* don't wait, we have the response now */ /* see if we have an IPv6 stack */ s = socket(PF_INET6, SOCK_DGRAM, 0); if (s < 0) /* Some non-IPv6 stacks have been found to make very slow name resolves * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if * the stack seems to be a non-ipv6 one. */ pf = PF_INET; else { /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest * possible checks. And close the socket again. */ sclose(s); /* * Check if a more limited name resolve has been requested. */ switch(data->set.ip_version) { case CURL_IPRESOLVE_V4: pf = PF_INET; break; case CURL_IPRESOLVE_V6: pf = PF_INET6; break; default: pf = PF_UNSPEC; break; } } memset(&hints, 0, sizeof(hints)); hints.ai_family = pf; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_CANONNAME; snprintf(sbuf, sizeof(sbuf), "%d", port); error = getaddrinfo(hostname, sbuf, &hints, &res); if (error) { infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port); return NULL; } return res;}#else /* following code is IPv4-only */#if !defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES)static void hostcache_fixoffset(struct hostent *h, int offset);/* * Performs a "deep" copy of a hostent into a buffer (returns a pointer to the * copy). Make absolutely sure the destination buffer is big enough! */static struct hostent* pack_hostent(char** buf, struct hostent* orig){ char *bufptr; char *newbuf; struct hostent* copy; int i; char *str; int len; bufptr = *buf; copy = (struct hostent*)bufptr; bufptr += sizeof(struct hostent); copy->h_name = bufptr; len = strlen(orig->h_name) + 1; strncpy(bufptr, orig->h_name, len); bufptr += len; /* we align on even 64bit boundaries for safety */#define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7))) /* This must be aligned properly to work on many CPU architectures! */ bufptr = MEMALIGN(bufptr); copy->h_aliases = (char**)bufptr; /* Figure out how many aliases there are */ for (i = 0; orig->h_aliases && orig->h_aliases[i]; ++i); /* Reserve room for the array */ bufptr += (i + 1) * sizeof(char*); /* Clone all known aliases */ if(orig->h_aliases) { for(i = 0; (str = orig->h_aliases[i]); i++) { len = strlen(str) + 1; strncpy(bufptr, str, len); copy->h_aliases[i] = bufptr; bufptr += len; } } /* if(!orig->h_aliases) i was already set to 0 */ /* Terminate the alias list with a NULL */ copy->h_aliases[i] = NULL; copy->h_addrtype = orig->h_addrtype; copy->h_length = orig->h_length; /* align it for (at least) 32bit accesses */ bufptr = MEMALIGN(bufptr); copy->h_addr_list = (char**)bufptr; /* Figure out how many addresses there are */ for (i = 0; orig->h_addr_list[i] != NULL; ++i); /* Reserve room for the array */ bufptr += (i + 1) * sizeof(char*); i = 0; len = orig->h_length; str = orig->h_addr_list[i]; while (str != NULL) { memcpy(bufptr, str, len); copy->h_addr_list[i] = bufptr; bufptr += len; str = orig->h_addr_list[++i]; } copy->h_addr_list[i] = NULL; /* now, shrink the allocated buffer to the size we actually need, which most often is only a fraction of the original alloc */ newbuf=(char *)realloc(*buf, (int)bufptr-(int)(*buf)); /* if the alloc moved, we need to adjust things again */ if(newbuf != *buf) hostcache_fixoffset((struct hostent*)newbuf, (int)newbuf-(int)*buf); /* setup the return */ *buf = newbuf; copy = (struct hostent*)newbuf; return copy;}#endifstatic void hostcache_fixoffset(struct hostent *h, int offset){ int i=0; h->h_name=(char *)((long)h->h_name+offset); if(h->h_aliases) { /* only relocate aliases if there are any! */ h->h_aliases=(char **)((long)h->h_aliases+offset); while(h->h_aliases[i]) { h->h_aliases[i]=(char *)((long)h->h_aliases[i]+offset); i++; } } h->h_addr_list=(char **)((long)h->h_addr_list+offset); i=0; while(h->h_addr_list[i]) { h->h_addr_list[i]=(char *)((long)h->h_addr_list[i]+offset); i++; }}#ifndef USE_ARESstatic char *MakeIP(unsigned long num, char *addr, int addr_len){#if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R) struct in_addr in; in.s_addr = htonl(num);#if defined(HAVE_INET_NTOA_R) inet_ntoa_r(in,addr,addr_len);#else strncpy(addr,inet_ntoa(in),addr_len);#endif#else unsigned char *paddr; num = htonl(num); /* htonl() added to avoid endian probs */ paddr = (unsigned char *)# sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]);#endif return (addr);}/* The original code to this function was once stolen from the Dancer source code, written by Bjorn Reese, it has since been patched and modified considerably. */static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn, char *hostname, int port, int *waitp){ struct hostent *h = NULL; in_addr_t in; int ret; /* this variable is unused on several platforms but used on some */ struct SessionHandle *data = conn->data; (void)port; /* unused in IPv4 code */ ret = 0; /* to prevent the compiler warning */ *waitp = 0; /* don't wait, we act synchronously */ in=inet_addr(hostname); if (in != CURL_INADDR_NONE) { struct in_addr *addrentry; struct namebuf { struct hostent hostentry; char *h_addr_list[2]; struct in_addr addrentry; char h_name[128]; } *buf = (struct namebuf *)malloc(sizeof(struct namebuf)); if(!buf) return NULL; /* major failure */ h = &buf->hostentry; h->h_addr_list = &buf->h_addr_list[0]; addrentry = &buf->addrentry; addrentry->s_addr = in; h->h_addr_list[0] = (char*)addrentry; h->h_addr_list[1] = NULL; h->h_addrtype = AF_INET; h->h_length = sizeof(*addrentry); h->h_name = &buf->h_name[0]; MakeIP(ntohl(in), (char *)h->h_name, sizeof(buf->h_name)); }#if defined(HAVE_GETHOSTBYNAME_R) else { int h_errnop; int res=ERANGE; int step_size=200; int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE); if(!buf) return NULL; /* major failure */ /* Workaround for gethostbyname_r bug in qnx nto. It is also _required_ for some of these functions. */ memset(buf, 0, CURL_NAMELOOKUP_SIZE);#ifdef HAVE_GETHOSTBYNAME_R_5 /* Solaris, IRIX and more */ (void)res; /* prevent compiler warning */ while(!h) { h = gethostbyname_r(hostname, (struct hostent *)buf, (char *)buf + sizeof(struct hostent), step_size - sizeof(struct hostent), &h_errnop); /* If the buffer is too small, it returns NULL and sets errno to ERANGE. The errno is thread safe if this is compiled with -D_REENTRANT as then the 'errno' variable is a macro defined to get used properly for threads. */ if(h || (errno != ERANGE)) break; step_size+=200; }#ifdef CURLDEBUG infof(data, "gethostbyname_r() uses %d bytes\n", step_size);#endif if(h) { int offset; h=(struct hostent *)realloc(buf, step_size); offset=(long)h-(long)buf; hostcache_fixoffset(h, offset); buf=(int *)h; } else#endif /* HAVE_GETHOSTBYNAME_R_5 */#ifdef HAVE_GETHOSTBYNAME_R_6 /* Linux */ do { res=gethostbyname_r(hostname, (struct hostent *)buf, (char *)buf + sizeof(struct hostent), step_size - sizeof(struct hostent), &h, /* DIFFERENCE */ &h_errnop); /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a sudden this function returns EAGAIN if the given buffer size is too small. Previous versions are known to return ERANGE for the same problem. This wouldn't be such a big problem if older versions wouldn't sometimes return EAGAIN on a common failure case. Alas, we can't assume that EAGAIN *or* ERANGE means ERANGE for any given version of glibc. For now, we do that and thus we may call the function repeatedly and fail for older glibc versions that return EAGAIN, until we run out of buffer size (step_size grows beyond CURL_NAMELOOKUP_SIZE). If anyone has a better fix, please tell us! ------------------------------------------------------------------- On October 23rd 2003, Dan C dug up more details on the mysteries of gethostbyname_r() in glibc: In glibc 2.2.5 the interface is different (this has also been discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32 (shipped/upgraded by Redhat 7.2) don't show this behavior! In this "buggy" version, the return code is -1 on error and 'errno' is set to the ERANGE or EAGAIN code. Note that 'errno' is not a thread-safe variable. */ if(((ERANGE == res) || (EAGAIN == res)) || ((res<0) && ((ERANGE == errno) || (EAGAIN == errno)))) step_size+=200; else break; } while(step_size <= CURL_NAMELOOKUP_SIZE); if(!h) /* failure */ res=1; #ifdef CURLDEBUG infof(data, "gethostbyname_r() uses %d bytes\n", step_size);#endif if(!res) { int offset; h=(struct hostent *)realloc(buf, step_size); offset=(long)h-(long)buf; hostcache_fixoffset(h, offset); buf=(int *)h; } else#endif/* HAVE_GETHOSTBYNAME_R_6 */#ifdef HAVE_GETHOSTBYNAME_R_3 /* AIX, Digital Unix, HPUX 10, more? */ /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of the plain fact that it does not return unique full buffers on each call, but instead several of the pointers in the hostent structs will point to the same actual data! This have the unfortunate down-side that our caching system breaks down horribly. Luckily for us though, AIX 4.3 and more recent versions have a completely thread-safe libc where all the data is stored in thread-specific memory areas making calls to the plain old gethostbyname() work fine even for multi-threaded programs. This AIX 4.3 or later detection is all made in the configure script. Troels Walsted Hansen helped us work this out on March 3rd, 2003. */ if(CURL_NAMELOOKUP_SIZE >= (sizeof(struct hostent)+sizeof(struct hostent_data))) /* August 22nd, 2000: Albert Chin-A-Young brought an updated version * that should work! September 20: Richard Prescott worked on the buffer * size dilemma. */ ret = gethostbyname_r(hostname, (struct hostent *)buf, (struct hostent_data *)((char *)buf + sizeof(struct hostent))); else ret = -1; /* failure, too smallish buffer size */ /* result expected in h */ h = (struct hostent*)buf; h_errnop= errno; /* we don't deal with this, but set it anyway */ if(ret)#endif /* HAVE_GETHOSTBYNAME_R_3 */ { infof(data, "gethostbyname_r(2) failed for %s\n", hostname); h = NULL; /* set return code to NULL */ free(buf); }#else /* HAVE_GETHOSTBYNAME_R */ else { if ((h = gethostbyname(hostname)) == NULL ) { infof(data, "gethostbyname(2) failed for %s\n", hostname); } else { char *buf=(char *)malloc(CURL_NAMELOOKUP_SIZE); /* we make a copy of the hostent right now, right here, as the static one we got a pointer to might get removed when we don't want/expect that */ h = pack_hostent(&buf, h); }#endif /*HAVE_GETHOSTBYNAME_R */ } return (h);}#endif /* end of IPv4-specific code */#endif /* end of !USE_ARES */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -