doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
hostip.c
Go to the documentation of this file.
1 /***************************************************************************
2  * _ _ ____ _
3  * Project ___| | | | _ \| |
4  * / __| | | | |_) | |
5  * | (__| |_| | _ <| |___
6  * \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id: hostip.c,v 1.130 2004/03/17 12:46:46 bagder Exp $
22  ***************************************************************************/
23 
24 #include "setup.h"
25 
26 #include <string.h>
27 #include <errno.h>
28 
29 #define _REENTRANT
30 
31 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
32 #include <malloc.h>
33 #else
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h>
36 #endif
37 #ifdef HAVE_SYS_SOCKET_H
38 #include <sys/socket.h>
39 #endif
40 #ifdef HAVE_NETINET_IN_H
41 #include <netinet/in.h>
42 #endif
43 #ifdef HAVE_NETDB_H
44 #include <netdb.h>
45 #endif
46 #ifdef HAVE_ARPA_INET_H
47 #include <arpa/inet.h>
48 #endif
49 #ifdef HAVE_STDLIB_H
50 #include <stdlib.h> /* required for free() prototypes */
51 #endif
52 #ifdef VMS
53 #include <in.h>
54 #include <inet.h>
55 #include <stdlib.h>
56 #endif
57 #endif
58 
59 #ifdef HAVE_SETJMP_H
60 #include <setjmp.h>
61 #endif
62 
63 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
64 #undef in_addr_t
65 #define in_addr_t unsigned long
66 #endif
67 
68 #include "urldata.h"
69 #include "sendf.h"
70 #include "hostip.h"
71 #include "hash.h"
72 #include "share.h"
73 #include "url.h"
74 
75 #define _MPRINTF_REPLACE /* use our functions only */
76 #include <curl/mprintf.h>
77 
78 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
79 #include "inet_ntoa_r.h"
80 #endif
81 
82 /* The last #include file should be: */
83 #ifdef CURLDEBUG
84 #include "memdebug.h"
85 #endif
86 
87 #ifndef ARES_SUCCESS
88 #define ARES_SUCCESS CURLE_OK
89 #endif
90 
91 static curl_hash hostname_cache;
92 static int host_cache_initialized;
93 
94 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
95  char *hostname,
96  int port,
97  int *waitp);
98 #ifndef ENABLE_IPV6
99 #if !defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES) || \
100  defined(USE_THREADING_GETHOSTBYNAME)
101 static struct hostent* pack_hostent(char** buf, struct hostent* orig);
102 #endif
103 #endif
104 
105 #ifdef USE_THREADING_GETHOSTBYNAME
106 #ifdef DEBUG_THREADING_GETHOSTBYNAME
107 /* If this is defined, provide tracing */
108 #define TRACE(args) \
109  do { trace_it("%u: ", __LINE__); trace_it args; } while (0)
110 
111 static void trace_it (const char *fmt, ...);
112 #else
113 #define TRACE(x)
114 #endif
115 
116 static struct hostent* pack_hostent (char** buf, struct hostent* orig);
117 static bool init_gethostbyname_thread (struct connectdata *conn,
118  const char *hostname, int port);
119 struct thread_data {
120  HANDLE thread_hnd;
121  DWORD thread_id;
122  DWORD thread_status;
123 };
124 #endif
125 
127 {
128  if (!host_cache_initialized) {
129  Curl_hash_init(&hostname_cache, 7, Curl_freednsinfo);
130  host_cache_initialized = 1;
131  }
132 }
133 
135 {
136  return &hostname_cache;
137 }
138 
140 {
141  if (host_cache_initialized) {
142  Curl_hash_clean(&hostname_cache);
143  host_cache_initialized = 0;
144  }
145 }
146 
147 /* count the number of characters that an integer takes up */
148 static int _num_chars(int i)
149 {
150  int chars = 0;
151 
152  /* While the number divided by 10 is greater than one,
153  * re-divide the number by 10, and increment the number of
154  * characters by 1.
155  *
156  * this relies on the fact that for every multiple of 10,
157  * a new digit is added onto every number
158  */
159  do {
160  chars++;
161 
162  i = (int) i / 10;
163  } while (i >= 1);
164 
165  return chars;
166 }
167 
168 /* Create a hostcache id */
169 static char *
170 create_hostcache_id(char *server, int port, size_t *entry_len)
171 {
172  char *id = NULL;
173 
174  /* Get the length of the new entry id */
175  *entry_len = strlen(server) + /* Hostname length */
176  1 + /* ':' seperator */
177  _num_chars(port); /* number of characters the port will take up */
178 
179  /* Allocate the new entry id */
180  id = malloc(*entry_len + 1); /* 1 extra for the zero terminator */
181  if (!id)
182  return NULL;
183 
184  /* Create the new entry */
185  sprintf(id, "%s:%d", server, port);
186 
187  return id; /* return pointer to the string */
188 }
189 
192  time_t now;
193 };
194 
195 static int
196 hostcache_timestamp_remove(void *datap, void *hc)
197 {
198  struct hostcache_prune_data *data =
199  (struct hostcache_prune_data *) datap;
200  struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
201 
202  if ((data->now - c->timestamp < data->cache_timeout) ||
203  c->inuse) {
204  /* please don't remove */
205  return 0;
206  }
207 
208  /* fine, remove */
209  return 1;
210 }
211 
212 static void
213 hostcache_prune(curl_hash *hostcache, int cache_timeout, time_t now)
214 {
215  struct hostcache_prune_data user;
216 
218  user.now = now;
219 
221  (void *) &user,
222  hostcache_timestamp_remove);
223 }
224 
226 {
227  time_t now;
228 
229  if(data->set.dns_cache_timeout == -1)
230  /* cache forever means never prune! */
231  return;
232 
233  if(data->share)
235 
236  time(&now);
237 
238  /* Remove outdated and unused entries from the hostcache */
239  hostcache_prune(data->hostcache,
240  data->set.dns_cache_timeout,
241  now);
242 
243  if(data->share)
245 }
246 
247 #ifdef HAVE_SIGSETJMP
248 /* Beware this is a global and unique instance */
249 sigjmp_buf curl_jmpenv;
250 #endif
251 
252 
253 /* When calling Curl_resolv() has resulted in a response with a returned
254  address, we call this function to store the information in the dns
255  cache etc */
256 
257 static struct Curl_dns_entry *
258 cache_resolv_response(struct SessionHandle *data,
260  char *hostname,
261  int port)
262 {
263  char *entry_id;
264  size_t entry_len;
265  struct Curl_dns_entry *dns;
266  time_t now;
267 
268  /* Create an entry id, based upon the hostname and port */
269  entry_id = create_hostcache_id(hostname, port, &entry_len);
270  /* If we can't create the entry id, fail */
271  if (!entry_id)
272  return NULL;
273 
274  /* Create a new cache entry */
275  dns = (struct Curl_dns_entry *) malloc(sizeof(struct Curl_dns_entry));
276  if (!dns) {
277  Curl_freeaddrinfo(addr);
278  free(entry_id);
279  return NULL;
280  }
281 
282  dns->inuse = 0; /* init to not used */
283  dns->addr = addr; /* this is the address(es) */
284 
285  /* Store the resolved data in our DNS cache. This function may return a
286  pointer to an existing struct already present in the hash, and it may
287  return the same argument we pass in. Make no assumptions. */
288  dns = Curl_hash_add(data->hostcache, entry_id, entry_len+1, (void *)dns);
289  if(!dns) {
290  /* Major badness, run away. When this happens, the 'dns' data has
291  already been cleared up by Curl_hash_add(). */
292  free(entry_id);
293  return NULL;
294  }
295  time(&now);
296 
297  dns->timestamp = now; /* used now */
298  dns->inuse++; /* mark entry as in-use */
299 
300  /* free the allocated entry_id again */
301  free(entry_id);
302 
303  return dns;
304 }
305 
306 /* Resolve a name and return a pointer in the 'entry' argument if one
307  is available.
308 
309  Return codes:
310 
311  -1 = error, no pointer
312  0 = OK, pointer provided
313  1 = waiting for response, no pointer
314 */
315 int Curl_resolv(struct connectdata *conn,
316  char *hostname,
317  int port,
318  struct Curl_dns_entry **entry)
319 {
320  char *entry_id = NULL;
321  struct Curl_dns_entry *dns = NULL;
322  size_t entry_len;
323  int wait;
324  struct SessionHandle *data = conn->data;
326 
327  /* default to failure */
328  int rc = -1;
329  *entry = NULL;
330 
331 #ifdef HAVE_SIGSETJMP
332  /* this allows us to time-out from the name resolver, as the timeout
333  will generate a signal and we will siglongjmp() from that here */
334  if(!data->set.no_signal && sigsetjmp(curl_jmpenv, 1)) {
335  /* this is coming from a siglongjmp() */
336  failf(data, "name lookup timed out");
337  return -1;
338  }
339 #endif
340 
341  /* Create an entry id, based upon the hostname and port */
342  entry_id = create_hostcache_id(hostname, port, &entry_len);
343  /* If we can't create the entry id, fail */
344  if (!entry_id)
345  return -1;
346 
347  if(data->share)
349 
350  /* See if its already in our dns cache */
351  dns = Curl_hash_pick(data->hostcache, entry_id, entry_len+1);
352 
353  if(data->share)
355 
356  /* free the allocated entry_id again */
357  free(entry_id);
358 
359  if (!dns) {
360  /* The entry was not in the cache. Resolve it to IP address */
361 
362  /* If my_getaddrinfo() returns NULL, 'wait' might be set to a non-zero
363  value indicating that we need to wait for the response to the resolve
364  call */
365  Curl_addrinfo *addr = my_getaddrinfo(conn, hostname, port, &wait);
366 
367  if (!addr) {
368  if(wait) {
369  /* the response to our resolve call will come asynchronously at
370  a later time, good or bad */
371  /* First, check that we haven't received the info by now */
372  result = Curl_is_resolved(conn, &dns);
373  if(result) /* error detected */
374  return -1;
375  if(dns)
376  rc = 0; /* pointer provided */
377  else
378  rc = 1; /* no info yet */
379  }
380  }
381  else {
382  if(data->share)
384 
385  /* we got a response, store it in the cache */
386  dns = cache_resolv_response(data, addr, hostname, port);
387 
388  if(data->share)
390 
391  if(!dns)
392  /* returned failure, bail out nicely */
393  Curl_freeaddrinfo(addr);
394  else
395  rc = 0;
396  }
397  }
398  else {
399  dns->inuse++; /* we use it! */
400  rc = 0;
401  }
402 
403  *entry = dns;
404 
405  return rc;
406 }
407 
408 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
409 {
410  if(data->share)
412 
413  dns->inuse--;
414 
415 #ifdef CURLDEBUG
416  if(dns->inuse < 0) {
417  infof(data, "Interal host cache screw-up!");
418  *(char **)0=NULL;
419  }
420 #endif
421 
422  if(data->share)
424 }
425 
426 /*
427  * This is a wrapper function for freeing name information in a protocol
428  * independent way. This takes care of using the appropriate underlaying
429  * function.
430  */
432 {
433 #ifdef ENABLE_IPV6
434  freeaddrinfo(p);
435 #else
436  free(p); /* works fine for the ARES case too */
437 #endif
438 }
439 
440 /*
441  * Free a cache dns entry.
442  */
443 void Curl_freednsinfo(void *freethis)
444 {
445  struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
446 
448 
449  free(p);
450 }
451 
452 /* --- resolve name or IP-number --- */
453 
454 /* Allocate enough memory to hold the full name information structs and
455  * everything. OSF1 is known to require at least 8872 bytes. The buffer
456  * required for storing all possible aliases and IP numbers is according to
457  * Stevens' Unix Network Programming 2nd edition, p. 304: 8192 bytes!
458  */
459 #define CURL_NAMELOOKUP_SIZE 9000
460 
461 #ifdef USE_ARES
462 
464  fd_set *read_fd_set,
465  fd_set *write_fd_set,
466  int *max_fdp)
467 
468 {
469  int max = ares_fds(conn->data->state.areschannel,
470  read_fd_set, write_fd_set);
471  *max_fdp = max;
472 
473  return CURLE_OK;
474 }
475 
476 /* called to check if the name is resolved now */
478  struct Curl_dns_entry **dns)
479 {
480  fd_set read_fds, write_fds;
481  static const struct timeval tv={0,0};
482  int count;
483  struct SessionHandle *data = conn->data;
484  int nfds;
485 
486  FD_ZERO(&read_fds);
487  FD_ZERO(&write_fds);
488  nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
489 
490  count = select(nfds, &read_fds, &write_fds, NULL,
491  (struct timeval *)&tv);
492 
493  if(count)
494  ares_process(data->state.areschannel, &read_fds, &write_fds);
495 
496  *dns = NULL;
497 
498  if(conn->async.done) {
499  /* we're done, kill the ares handle */
500  if(!conn->async.dns)
502  *dns = conn->async.dns;
503  }
504 
505  return CURLE_OK;
506 }
507 
508 /* This is a function that locks and waits until the name resolve operation
509  has completed.
510 
511  If 'entry' is non-NULL, make it point to the resolved dns entry
512 
513  Return CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and
514  CURLE_OPERATION_TIMEDOUT if a time-out occurred.
515 */
517  struct Curl_dns_entry **entry)
518 {
519  CURLcode rc=CURLE_OK;
520  struct SessionHandle *data = conn->data;
521  struct timeval now = Curl_tvnow();
522  bool timedout = FALSE;
523  long timeout = 300; /* default name resolve timeout in seconds */
524  long elapsed = 0; /* time taken so far */
525 
526  /* now, see if there's a connect timeout or a regular timeout to
527  use instead of the default one */
528  if(conn->data->set.connecttimeout)
529  timeout = conn->data->set.connecttimeout;
530  else if(conn->data->set.timeout)
531  timeout = conn->data->set.timeout;
532 
533  /* Wait for the name resolve query to complete. */
534  while (1) {
535  int nfds=0;
536  fd_set read_fds, write_fds;
537  struct timeval *tvp, tv, store;
538  int count;
539 
540  store.tv_sec = (int)(timeout - elapsed);
541  store.tv_usec = 0;
542 
543  FD_ZERO(&read_fds);
544  FD_ZERO(&write_fds);
545  nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds);
546  if (nfds == 0)
547  break;
548  tvp = ares_timeout(data->state.areschannel,
549  &store, &tv);
550  count = select(nfds, &read_fds, &write_fds, NULL, tvp);
551  if (count < 0 && errno != EINVAL)
552  break;
553  else if(!count) {
554  /* timeout */
555  timedout = TRUE;
556  break;
557  }
558  ares_process(data->state.areschannel, &read_fds, &write_fds);
559 
560  elapsed = Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
561  }
562 
563  /* Operation complete, if the lookup was successful we now have the entry
564  in the cache. */
565 
566  if(entry)
567  *entry = conn->async.dns;
568 
569  if(!conn->async.dns) {
570  /* a name was not resolved */
571  if(timedout || (conn->async.status == ARES_ETIMEOUT)) {
572  failf(data, "Resolving host timed out: %s", conn->name);
574  }
575  else if(conn->async.done) {
576  failf(data, "Could not resolve host: %s (%s)", conn->name,
577  ares_strerror(conn->async.status));
579  }
580  else
582 
583  /* close the connection, since we can't return failure here without
584  cleaning up this connection properly */
585  Curl_disconnect(conn);
586  }
587 
588  return rc;
589 }
590 #endif
591 
592 #if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
593 
594 /* this function gets called by ares/gethostbyname_thread() when we got
595  the name resolved or not */
596 static void host_callback(void *arg, /* "struct connectdata *" */
597  int status,
598  struct hostent *hostent)
599 {
600  struct connectdata *conn = (struct connectdata *)arg;
601  struct Curl_dns_entry *dns = NULL;
602 
603  conn->async.done = TRUE;
604  conn->async.status = status;
605 
606  if(ARES_SUCCESS == status) {
607  /* we got a resolved name in 'hostent' */
608  char *bufp = (char *)malloc(CURL_NAMELOOKUP_SIZE);
609  if(bufp) {
610 
611  /* pack_hostent() copies to and shrinks the target buffer */
612  struct hostent *he = pack_hostent(&bufp, hostent);
613 
614  struct SessionHandle *data = conn->data;
615 
616  if(data->share)
618 
619  dns = cache_resolv_response(data, he,
620  conn->async.hostname, conn->async.port);
621 
622  if(data->share)
624  }
625  }
626 
627  conn->async.dns = dns;
628 
629  /* The input hostent struct will be freed by ares when we return from this
630  function */
631 }
632 #endif
633 
634 #ifdef USE_ARES
635 /*
636  * Return name information about the given hostname and port number. If
637  * successful, the 'hostent' is returned and the forth argument will point to
638  * memory we need to free after use. That meory *MUST* be freed with
639  * Curl_freeaddrinfo(), nothing else.
640  */
641 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
642  char *hostname,
643  int port,
644  int *waitp)
645 {
646  char *bufp;
647  struct SessionHandle *data = conn->data;
648 
649  *waitp = FALSE;
650 
651  bufp = strdup(hostname);
652 
653  if(bufp) {
654  Curl_safefree(conn->async.hostname);
655  conn->async.hostname = bufp;
656  conn->async.port = port;
657  conn->async.done = FALSE; /* not done */
658  conn->async.status = 0; /* clear */
659  conn->async.dns = NULL; /* clear */
660 
661  /* areschannel is already setup in the Curl_open() function */
662  ares_gethostbyname(data->state.areschannel, hostname, PF_INET,
663  host_callback, conn);
664 
665  *waitp = TRUE; /* please wait for the response */
666  }
667  return NULL; /* no struct yet */
668 }
669 #endif
670 
671 #if !defined(USE_ARES) && !defined(USE_THREADING_GETHOSTBYNAME)
672 
673 /* For builds without ARES and threaded gethostbyname, Curl_resolv() can never
674  return wait==TRUE, so this function will never be called. If it still gets
675  called, we return failure at once. */
677  struct Curl_dns_entry **entry)
678 {
679  (void)conn;
680  *entry=NULL;
682 }
683 
685  struct Curl_dns_entry **dns)
686 {
687  (void)conn;
688  *dns = NULL;
689 
691 }
692 #endif
693 
694 #if !defined(USE_ARES)
696  fd_set *read_fd_set,
697  fd_set *write_fd_set,
698  int *max_fdp)
699 {
700  (void)conn;
701  (void)read_fd_set;
702  (void)write_fd_set;
703  (void)max_fdp;
704  return CURLE_OK;
705 }
706 #endif
707 
708 #if defined(ENABLE_IPV6) && !defined(USE_ARES)
709 
710 #ifdef CURLDEBUG
711 /* These two are strictly for memory tracing and are using the same
712  * style as the family otherwise present in memdebug.c. I put these ones
713  * here since they require a bunch of struct types I didn't wanna include
714  * in memdebug.c
715  */
716 int curl_getaddrinfo(char *hostname, char *service,
717  struct addrinfo *hints,
718  struct addrinfo **result,
719  int line, const char *source)
720 {
721  int res=(getaddrinfo)(hostname, service, hints, result);
722  if(0 == res) {
723  /* success */
724  if(logfile)
725  fprintf(logfile, "ADDR %s:%d getaddrinfo() = %p\n",
726  source, line, (void *)*result);
727  }
728  else {
729  if(logfile)
730  fprintf(logfile, "ADDR %s:%d getaddrinfo() failed\n",
731  source, line);
732  }
733  return res;
734 }
735 
736 void curl_freeaddrinfo(struct addrinfo *freethis,
737  int line, const char *source)
738 {
739  (freeaddrinfo)(freethis);
740  if(logfile)
741  fprintf(logfile, "ADDR %s:%d freeaddrinfo(%p)\n",
742  source, line, (void *)freethis);
743 }
744 
745 #endif
746 
747 /*
748  * Return name information about the given hostname and port number. If
749  * successful, the 'addrinfo' is returned and the forth argument will point to
750  * memory we need to free after use. That meory *MUST* be freed with
751  * Curl_freeaddrinfo(), nothing else.
752  */
753 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
754  char *hostname,
755  int port,
756  int *waitp)
757 {
758  struct addrinfo hints, *res;
759  int error;
760  char sbuf[NI_MAXSERV];
761  int s, pf;
762  struct SessionHandle *data = conn->data;
763 
764  *waitp=0; /* don't wait, we have the response now */
765 
766  /* see if we have an IPv6 stack */
767  s = socket(PF_INET6, SOCK_DGRAM, 0);
768  if (s < 0)
769  /* Some non-IPv6 stacks have been found to make very slow name resolves
770  * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
771  * the stack seems to be a non-ipv6 one. */
772  pf = PF_INET;
773  else {
774  /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
775  * possible checks. And close the socket again.
776  */
777  sclose(s);
778 
779  /*
780  * Check if a more limited name resolve has been requested.
781  */
782  switch(data->set.ip_version) {
783  case CURL_IPRESOLVE_V4:
784  pf = PF_INET;
785  break;
786  case CURL_IPRESOLVE_V6:
787  pf = PF_INET6;
788  break;
789  default:
790  pf = PF_UNSPEC;
791  break;
792  }
793  }
794 
795  memset(&hints, 0, sizeof(hints));
796  hints.ai_family = pf;
797  hints.ai_socktype = SOCK_STREAM;
798  hints.ai_flags = AI_CANONNAME;
799  snprintf(sbuf, sizeof(sbuf), "%d", port);
800  error = getaddrinfo(hostname, sbuf, &hints, &res);
801  if (error) {
802  infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
803  return NULL;
804  }
805 
806  return res;
807 }
808 #else /* following code is IPv4-only */
809 
810 #if !defined(HAVE_GETHOSTBYNAME_R) || defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME)
811 static void hostcache_fixoffset(struct hostent *h, long offset);
812 /*
813  * Performs a "deep" copy of a hostent into a buffer (returns a pointer to the
814  * copy). Make absolutely sure the destination buffer is big enough!
815  */
816 static struct hostent* pack_hostent(char** buf, struct hostent* orig)
817 {
818  char *bufptr;
819  char *newbuf;
820  struct hostent* copy;
821 
822  int i;
823  char *str;
824  size_t len;
825 
826  bufptr = *buf;
827  copy = (struct hostent*)bufptr;
828 
829  bufptr += sizeof(struct hostent);
830  copy->h_name = bufptr;
831  len = strlen(orig->h_name) + 1;
832  strncpy(bufptr, orig->h_name, len);
833  bufptr += len;
834 
835  /* we align on even 64bit boundaries for safety */
836 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
837 
838  /* This must be aligned properly to work on many CPU architectures! */
839  bufptr = MEMALIGN(bufptr);
840 
841  copy->h_aliases = (char**)bufptr;
842 
843  /* Figure out how many aliases there are */
844  for (i = 0; orig->h_aliases && orig->h_aliases[i]; ++i);
845 
846  /* Reserve room for the array */
847  bufptr += (i + 1) * sizeof(char*);
848 
849  /* Clone all known aliases */
850  if(orig->h_aliases) {
851  for(i = 0; (str = orig->h_aliases[i]); i++) {
852  len = strlen(str) + 1;
853  strncpy(bufptr, str, len);
854  copy->h_aliases[i] = bufptr;
855  bufptr += len;
856  }
857  }
858  /* if(!orig->h_aliases) i was already set to 0 */
859 
860  /* Terminate the alias list with a NULL */
861  copy->h_aliases[i] = NULL;
862 
863  copy->h_addrtype = orig->h_addrtype;
864  copy->h_length = orig->h_length;
865 
866  /* align it for (at least) 32bit accesses */
867  bufptr = MEMALIGN(bufptr);
868 
869  copy->h_addr_list = (char**)bufptr;
870 
871  /* Figure out how many addresses there are */
872  for (i = 0; orig->h_addr_list[i] != NULL; ++i);
873 
874  /* Reserve room for the array */
875  bufptr += (i + 1) * sizeof(char*);
876 
877  i = 0;
878  len = orig->h_length;
879  str = orig->h_addr_list[i];
880  while (str != NULL) {
881  memcpy(bufptr, str, len);
882  copy->h_addr_list[i] = bufptr;
883  bufptr += len;
884  str = orig->h_addr_list[++i];
885  }
886  copy->h_addr_list[i] = NULL;
887 
888  /* now, shrink the allocated buffer to the size we actually need, which
889  most often is only a fraction of the original alloc */
890  newbuf=(char *)realloc(*buf, (long)bufptr-(long)(*buf));
891 
892  /* if the alloc moved, we need to adjust things again */
893  if(newbuf != *buf)
894  hostcache_fixoffset((struct hostent*)newbuf, (long)newbuf-(long)*buf);
895 
896  /* setup the return */
897  *buf = newbuf;
898  copy = (struct hostent*)newbuf;
899 
900  return copy;
901 }
902 #endif
903 
904 static void hostcache_fixoffset(struct hostent *h, long offset)
905 {
906  int i=0;
907 
908  h->h_name=(char *)((long)h->h_name+offset);
909  if(h->h_aliases) {
910  /* only relocate aliases if there are any! */
911  h->h_aliases=(char **)((long)h->h_aliases+offset);
912  while(h->h_aliases[i]) {
913  h->h_aliases[i]=(char *)((long)h->h_aliases[i]+offset);
914  i++;
915  }
916  }
917 
918  h->h_addr_list=(char **)((long)h->h_addr_list+offset);
919  i=0;
920  while(h->h_addr_list[i]) {
921  h->h_addr_list[i]=(char *)((long)h->h_addr_list[i]+offset);
922  i++;
923  }
924 }
925 
926 #ifndef USE_ARES
927 
928 static char *MakeIP(unsigned long num, char *addr, int addr_len)
929 {
930 #if defined(HAVE_INET_NTOA) || defined(HAVE_INET_NTOA_R)
931  struct in_addr in;
932  in.s_addr = htonl(num);
933 
934 #if defined(HAVE_INET_NTOA_R)
935  inet_ntoa_r(in,addr,addr_len);
936 #else
937  strncpy(addr,inet_ntoa(in),addr_len);
938 #endif
939 #else
940  unsigned char *paddr;
941 
942  num = htonl(num); /* htonl() added to avoid endian probs */
943  paddr = (unsigned char *)&num;
944  sprintf(addr, "%u.%u.%u.%u", paddr[0], paddr[1], paddr[2], paddr[3]);
945 #endif
946  return (addr);
947 }
948 
949 /* The original code to this function was once stolen from the Dancer source
950  code, written by Bjorn Reese, it has since been patched and modified
951  considerably. */
952 static Curl_addrinfo *my_getaddrinfo(struct connectdata *conn,
953  char *hostname,
954  int port,
955  int *waitp)
956 {
957  struct hostent *h = NULL;
958  in_addr_t in;
959  struct SessionHandle *data = conn->data;
960  (void)port; /* unused in IPv4 code */
961 
962  *waitp = 0; /* don't wait, we act synchronously */
963 
964  in=inet_addr(hostname);
965  if (in != CURL_INADDR_NONE) {
966  struct in_addr *addrentry;
967  struct namebuf {
968  struct hostent hostentry;
969  char *h_addr_list[2];
970  struct in_addr addrentry;
971  char h_name[128];
972  } *buf = (struct namebuf *)malloc(sizeof(struct namebuf));
973  if(!buf)
974  return NULL; /* major failure */
975 
976  h = &buf->hostentry;
977  h->h_addr_list = &buf->h_addr_list[0];
978  addrentry = &buf->addrentry;
979  addrentry->s_addr = in;
980  h->h_addr_list[0] = (char*)addrentry;
981  h->h_addr_list[1] = NULL;
982  h->h_addrtype = AF_INET;
983  h->h_length = sizeof(*addrentry);
984  h->h_name = &buf->h_name[0];
985  MakeIP(ntohl(in), (char *)h->h_name, sizeof(buf->h_name));
986  }
987 #if defined(HAVE_GETHOSTBYNAME_R)
988  else {
989  int h_errnop;
990  int res=ERANGE;
991  int step_size=200;
992  int *buf = (int *)malloc(CURL_NAMELOOKUP_SIZE);
993  if(!buf)
994  return NULL; /* major failure */
995 
996  /* Workaround for gethostbyname_r bug in qnx nto. It is also _required_
997  for some of these functions. */
998  memset(buf, 0, CURL_NAMELOOKUP_SIZE);
999 #ifdef HAVE_GETHOSTBYNAME_R_5
1000  /* Solaris, IRIX and more */
1001  (void)res; /* prevent compiler warning */
1002  while(!h) {
1003  h = gethostbyname_r(hostname,
1004  (struct hostent *)buf,
1005  (char *)buf + sizeof(struct hostent),
1006  step_size - sizeof(struct hostent),
1007  &h_errnop);
1008 
1009  /* If the buffer is too small, it returns NULL and sets errno to
1010  ERANGE. The errno is thread safe if this is compiled with
1011  -D_REENTRANT as then the 'errno' variable is a macro defined to
1012  get used properly for threads. */
1013 
1014  if(h || (errno != ERANGE))
1015  break;
1016 
1017  step_size+=200;
1018  }
1019 
1020 #ifdef CURLDEBUG
1021  infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
1022 #endif
1023 
1024  if(h) {
1025  int offset;
1026  h=(struct hostent *)realloc(buf, step_size);
1027  offset=(long)h-(long)buf;
1028  hostcache_fixoffset(h, offset);
1029  buf=(int *)h;
1030  }
1031  else
1032 #endif /* HAVE_GETHOSTBYNAME_R_5 */
1033 #ifdef HAVE_GETHOSTBYNAME_R_6
1034  /* Linux */
1035  do {
1036  res=gethostbyname_r(hostname,
1037  (struct hostent *)buf,
1038  (char *)buf + sizeof(struct hostent),
1039  step_size - sizeof(struct hostent),
1040  &h, /* DIFFERENCE */
1041  &h_errnop);
1042  /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a
1043  sudden this function returns EAGAIN if the given buffer size is too
1044  small. Previous versions are known to return ERANGE for the same
1045  problem.
1046 
1047  This wouldn't be such a big problem if older versions wouldn't
1048  sometimes return EAGAIN on a common failure case. Alas, we can't
1049  assume that EAGAIN *or* ERANGE means ERANGE for any given version of
1050  glibc.
1051 
1052  For now, we do that and thus we may call the function repeatedly and
1053  fail for older glibc versions that return EAGAIN, until we run out
1054  of buffer size (step_size grows beyond CURL_NAMELOOKUP_SIZE).
1055 
1056  If anyone has a better fix, please tell us!
1057 
1058  -------------------------------------------------------------------
1059 
1060  On October 23rd 2003, Dan C dug up more details on the mysteries of
1061  gethostbyname_r() in glibc:
1062 
1063  In glibc 2.2.5 the interface is different (this has also been
1064  discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't
1065  explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32
1066  (shipped/upgraded by Redhat 7.2) don't show this behavior!
1067 
1068  In this "buggy" version, the return code is -1 on error and 'errno'
1069  is set to the ERANGE or EAGAIN code. Note that 'errno' is not a
1070  thread-safe variable.
1071 
1072  */
1073 
1074  if(((ERANGE == res) || (EAGAIN == res)) ||
1075  ((res<0) && ((ERANGE == errno) || (EAGAIN == errno))))
1076  step_size+=200;
1077  else
1078  break;
1079  } while(step_size <= CURL_NAMELOOKUP_SIZE);
1080 
1081  if(!h) /* failure */
1082  res=1;
1083 
1084 #ifdef CURLDEBUG
1085  infof(data, "gethostbyname_r() uses %d bytes\n", step_size);
1086 #endif
1087  if(!res) {
1088  int offset;
1089  h=(struct hostent *)realloc(buf, step_size);
1090  offset=(long)h-(long)buf;
1091  hostcache_fixoffset(h, offset);
1092  buf=(int *)h;
1093  }
1094  else
1095 #endif/* HAVE_GETHOSTBYNAME_R_6 */
1096 #ifdef HAVE_GETHOSTBYNAME_R_3
1097  /* AIX, Digital Unix/Tru64, HPUX 10, more? */
1098 
1099  /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of
1100  the plain fact that it does not return unique full buffers on each
1101  call, but instead several of the pointers in the hostent structs will
1102  point to the same actual data! This have the unfortunate down-side that
1103  our caching system breaks down horribly. Luckily for us though, AIX 4.3
1104  and more recent versions have a completely thread-safe libc where all
1105  the data is stored in thread-specific memory areas making calls to the
1106  plain old gethostbyname() work fine even for multi-threaded programs.
1107 
1108  This AIX 4.3 or later detection is all made in the configure script.
1109 
1110  Troels Walsted Hansen helped us work this out on March 3rd, 2003. */
1111 
1112  if(CURL_NAMELOOKUP_SIZE >=
1113  (sizeof(struct hostent)+sizeof(struct hostent_data))) {
1114 
1115  /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
1116  * that should work! September 20: Richard Prescott worked on the buffer
1117  * size dilemma. */
1118 
1119  res = gethostbyname_r(hostname,
1120  (struct hostent *)buf,
1121  (struct hostent_data *)((char *)buf +
1122  sizeof(struct hostent)));
1123  h_errnop= errno; /* we don't deal with this, but set it anyway */
1124  }
1125  else
1126  res = -1; /* failure, too smallish buffer size */
1127 
1128  if(!res) { /* success */
1129 
1130  h = (struct hostent*)buf; /* result expected in h */
1131 
1132  /* This is the worst kind of the different gethostbyname_r() interfaces.
1133  Since we don't know how big buffer this particular lookup required,
1134  we can't realloc down the huge alloc without doing closer analysis of
1135  the returned data. Thus, we always use CURL_NAMELOOKUP_SIZE for every
1136  name lookup. Fixing this would require an extra malloc() and then
1137  calling pack_hostent() that subsequent realloc()s down the new memory
1138  area to the actually used amount. */
1139  }
1140  else
1141 #endif /* HAVE_GETHOSTBYNAME_R_3 */
1142  {
1143  infof(data, "gethostbyname_r(2) failed for %s\n", hostname);
1144  h = NULL; /* set return code to NULL */
1145  free(buf);
1146  }
1147 #else /* HAVE_GETHOSTBYNAME_R */
1148  else {
1149 
1150 #ifdef USE_THREADING_GETHOSTBYNAME
1151  if (init_gethostbyname_thread(conn,hostname,port)) {
1152  *waitp = TRUE; /* please wait for the response */
1153  return NULL;
1154  }
1155  infof(data, "init_gethostbyname_thread() failed for %s; code %lu\n",
1156  hostname, GetLastError());
1157 #endif
1158  h = gethostbyname(hostname);
1159  if (!h)
1160  infof(data, "gethostbyname(2) failed for %s\n", hostname);
1161  else {
1162  char *buf=(char *)malloc(CURL_NAMELOOKUP_SIZE);
1163  /* we make a copy of the hostent right now, right here, as the static
1164  one we got a pointer to might get removed when we don't want/expect
1165  that */
1166  h = pack_hostent(&buf, h);
1167  }
1168 #endif /*HAVE_GETHOSTBYNAME_R */
1169  }
1170 
1171  return h;
1172 }
1173 
1174 #endif /* end of IPv4-specific code */
1175 
1176 #endif /* end of !USE_ARES */
1177 
1178 
1179 #if defined(USE_THREADING_GETHOSTBYNAME)
1180 #ifdef DEBUG_THREADING_GETHOSTBYNAME
1181 static void trace_it (const char *fmt, ...)
1182 {
1183  static int do_trace = -1;
1184  va_list args;
1185 
1186  if (do_trace == -1)
1187  do_trace = getenv("CURL_TRACE") ? 1 : 0;
1188  if (!do_trace)
1189  return;
1190  va_start (args, fmt);
1191  vfprintf (stderr, fmt, args);
1192  fflush (stderr);
1193  va_end (args);
1194 }
1195 #endif
1196 
1197 /* For builds without ARES/USE_IPV6, create a resolver thread and wait on it.
1198  */
1199 static DWORD WINAPI gethostbyname_thread (void *arg)
1200 {
1201  struct connectdata *conn = (struct connectdata*) arg;
1202  struct hostent *he;
1203  int rc;
1204 
1205  WSASetLastError (conn->async.status = NO_DATA); /* pending status */
1206  he = gethostbyname (conn->async.hostname);
1207  if (he) {
1208  host_callback(conn, ARES_SUCCESS, he);
1209  rc = 1;
1210  }
1211  else {
1212  host_callback(conn, (int)WSAGetLastError(), NULL);
1213  rc = 0;
1214  }
1215  TRACE(("Winsock-error %d, addr %s\n", conn->async.status,
1216  he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown"));
1217  return (rc);
1218  /* An implicit ExitThread() here */
1219 }
1220 
1221 /* complementary of ares_destroy
1222  */
1223 static void destroy_thread_data (struct connectdata *conn)
1224 {
1225  if (conn->async.hostname)
1226  free(conn->async.hostname);
1227  if (conn->async.os_specific)
1228  free(conn->async.os_specific);
1229  conn->async.hostname = NULL;
1230  conn->async.os_specific = NULL;
1231 }
1232 
1233 static bool init_gethostbyname_thread (struct connectdata *conn,
1234  const char *hostname, int port)
1235 {
1236  struct thread_data *td = malloc(sizeof(*td));
1237 
1238  if (!td) {
1239  SetLastError(ENOMEM);
1240  return (0);
1241  }
1242 
1243  memset (td, 0, sizeof(*td));
1244  Curl_safefree(conn->async.hostname);
1245  conn->async.hostname = strdup(hostname);
1246  if (!conn->async.hostname) {
1247  free(td);
1248  SetLastError(ENOMEM);
1249  return (0);
1250  }
1251 
1252  conn->async.port = port;
1253  conn->async.done = FALSE;
1254  conn->async.status = 0;
1255  conn->async.dns = NULL;
1256  conn->async.os_specific = (void*) td;
1257 
1258  td->thread_hnd = CreateThread(NULL, 0, gethostbyname_thread,
1259  conn, 0, &td->thread_id);
1260  if (!td->thread_hnd) {
1261  TRACE(("CreateThread() failed; %lu\n", GetLastError()));
1262  destroy_thread_data(conn);
1263  return (0);
1264  }
1265  return (1);
1266 }
1267 
1268 /* called to check if the name is resolved now */
1270  struct Curl_dns_entry **entry)
1271 {
1272  struct thread_data *td = (struct thread_data*) conn->async.os_specific;
1273  struct SessionHandle *data = conn->data;
1274  long timeout;
1275  DWORD status, ticks;
1276  CURLcode rc;
1277 
1278  curlassert (conn && td);
1279 
1280  /* now, see if there's a connect timeout or a regular timeout to
1281  use instead of the default one */
1282  timeout = conn->data->set.connecttimeout ? conn->data->set.connecttimeout :
1283  conn->data->set.timeout ? conn->data->set.timeout :
1284  300; /* default name resolve timeout in seconds */
1285  ticks = GetTickCount();
1286 
1287  status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout);
1288  if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) {
1289  /* Thread finished before timeout; propagate Winsock error to this thread */
1290  WSASetLastError(conn->async.status);
1291  GetExitCodeThread(td->thread_hnd, &td->thread_status);
1292  TRACE(("status %lu, thread-status %08lX\n", status, td->thread_status));
1293  }
1294  else {
1295  conn->async.done = TRUE;
1296  TerminateThread(td->thread_hnd, (DWORD)-1);
1297  td->thread_status = (DWORD)-1;
1298  }
1299 
1300  TRACE(("gethostbyname_thread() retval %08lX, elapsed %lu ms\n",
1301  td->thread_status, GetTickCount()-ticks));
1302 
1303  if(entry)
1304  *entry = conn->async.dns;
1305 
1306  rc = CURLE_OK;
1307 
1308  if (!conn->async.dns) {
1309  /* a name was not resolved */
1310  if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) {
1311  failf(data, "Resolving host timed out: %s", conn->name);
1313  }
1314  else if(conn->async.done) {
1315  failf(data, "Could not resolve host: %s (code %lu)", conn->name, conn->async.status);
1317  }
1318  else
1320 
1321  destroy_thread_data(conn);
1322  /* close the connection, since we can't return failure here without
1323  cleaning up this connection properly */
1324  Curl_disconnect(conn);
1325  }
1326  return (rc);
1327 }
1328 
1330  struct Curl_dns_entry **entry)
1331 {
1332  *entry = NULL;
1333 
1334  if (conn->async.done) {
1335  /* we're done */
1336  destroy_thread_data(conn);
1337  if (!conn->async.dns) {
1338  TRACE(("Curl_is_resolved(): CURLE_COULDNT_RESOLVE_HOST\n"));
1340  }
1341  *entry = conn->async.dns;
1342  TRACE(("resolved okay, dns %p\n", *entry));
1343  }
1344  else
1345  TRACE(("not yet\n"));
1346  return CURLE_OK;
1347 }
1348 
1349 #endif
#define in_addr_t
#define CURL_IPRESOLVE_V6
Definition: curl.h:802
time_t timestamp
Definition: hostip.h:42
void * Curl_hash_pick(curl_hash *h, char *key, size_t key_len)
Definition: hash.c:195
int Curl_resolv(struct connectdata *conn, char *hostname, int port, struct Curl_dns_entry **entry)
Definition: hostip.c:315
long tv_sec
Definition: timeval.h:37
long connecttimeout
Definition: urldata.h:795
DWORD
Definition: win_qgl.cpp:61
int dns_cache_timeout
Definition: urldata.h:833
#define curlassert(x)
Definition: setup.h:149
#define failf
Definition: sendf.h:32
case const int
Definition: Callbacks.cpp:52
typedef HANDLE(WINAPI *PFNWGLCREATEBUFFERREGIONARBPROC)(HDC hDC
#define CURL_IPRESOLVE_V4
Definition: curl.h:801
bool no_signal
Definition: urldata.h:879
#define WINAPI
Definition: qgl.h:64
CURLcode
Definition: curl.h:209
#define CURL_NAMELOOKUP_SIZE
Definition: hostip.c:459
GLdouble s
Definition: glext.h:2935
GLenum GLsizei len
Definition: glext.h:3472
struct UrlState state
Definition: urldata.h:903
#define CURL_INADDR_NONE
Definition: hostip.h:93
int i
Definition: process.py:33
GLintptr offset
Definition: glext.h:3113
GLuint GLuint num
Definition: glext.h:5390
Boolean result
curl_hash * hostcache
Definition: urldata.h:896
void Curl_global_host_cache_init(void)
Definition: hostip.c:126
long timeout
Definition: urldata.h:794
GLsizei GLsizei GLcharARB * source
Definition: glext.h:3633
char * inet_ntoa_r(const struct in_addr in, char *buffer, int buflen)
long tv_usec
Definition: timeval.h:38
GLuint GLuint GLsizei count
Definition: glext.h:2845
CURLcode Curl_multi_ares_fdset(struct connectdata *conn, fd_set *read_fd_set, fd_set *write_fd_set, int *max_fdp)
Definition: hostip.c:695
void Curl_global_host_cache_dtor(void)
Definition: hostip.c:139
Curl_addrinfo * addr
Definition: hostip.h:41
const GLubyte * c
Definition: glext.h:4677
void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
Definition: hostip.c:408
char * hostname
Definition: urldata.h:432
void Curl_hash_clean(curl_hash *h)
Definition: hash.c:233
curl_hash * Curl_global_host_cache_get(void)
Definition: hostip.c:134
Definition: hostip.h:40
#define NULL
Definition: Lib.h:88
int ip_version
Definition: urldata.h:840
void Curl_freeaddrinfo(Curl_addrinfo *p)
Definition: hostip.c:431
#define select(args...)
Definition: amigaos.h:39
void Curl_hash_clean_with_criterium(curl_hash *h, void *user, int(*comp)(void *, void *))
Definition: hash.c:245
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:2853
void Curl_freednsinfo(void *freethis)
Definition: hostip.c:443
int Curl_hash_init(curl_hash *h, int slots, curl_hash_dtor dtor)
Definition: hash.c:69
struct Curl_share * share
Definition: urldata.h:897
#define MEMALIGN(x)
long Curl_tvdiff(struct timeval newer, struct timeval older)
Definition: timeval.c:92
GLenum const GLvoid * addr
Definition: glext.h:5393
Definition: curl.h:210
struct SessionHandle * data
Definition: urldata.h:403
char * strdup(char *s1)
Definition: main.c:183
#define sclose(x)
Definition: setup.h:220
char * name
Definition: urldata.h:430
CURLcode Curl_disconnect(struct connectdata *conn)
Definition: url.c:1313
GLuint id
Definition: glext.h:3103
GLuint in
Definition: glext.h:5388
#define snprintf
Definition: Str.h:70
CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry)
Definition: hostip.c:676
void Curl_hostcache_prune(struct SessionHandle *data)
Definition: hostip.c:225
#define infof
Definition: sendf.h:31
void Curl_safefree(void *ptr)
Definition: url.c:172
CURLcode Curl_is_resolved(struct connectdata *conn, struct Curl_dns_entry **dns)
Definition: hostip.c:684
typedef void(APIENTRYP PFNGLBLENDCOLORPROC)(GLclampf red
#define FALSE
Definition: mprintf.c:70
struct timeval Curl_tvnow(void)
Definition: timeval.c:81
CURLSHcode Curl_share_lock(struct SessionHandle *data, curl_lock_data type, curl_lock_access accesstype)
Definition: share.c:180
#define inet_ntoa(x)
Definition: amigaos.h:40
struct UserDefined set
Definition: urldata.h:898
struct hostent Curl_addrinfo
Definition: setup.h:275
#define ARES_SUCCESS
Definition: hostip.c:88
long port
Definition: urldata.h:433
GLuint res
Definition: glext.h:5385
void * Curl_hash_add(curl_hash *h, char *key, size_t key_len, void *p)
Definition: hash.c:144
#define TRUE
Definition: mprintf.c:69
long inuse
Definition: hostip.h:43
#define max(x, y)
Definition: os.h:70
GLfloat GLfloat p
Definition: glext.h:4674
CURLSHcode Curl_share_unlock(struct SessionHandle *data, curl_lock_data type)
Definition: share.c:198
int sprintf(idStr &string, const char *fmt,...)
Definition: Str.cpp:1528
#define CURLE_OPERATION_TIMEDOUT
Definition: curl.h:286