VMS specific preprocessor symbol checking adjustments
[platform/upstream/curl.git] / lib / hostip.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2009, 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$
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #include <string.h>
27
28 #ifdef HAVE_SYS_SOCKET_H
29 #include <sys/socket.h>
30 #endif
31 #ifdef HAVE_NETINET_IN_H
32 #include <netinet/in.h>
33 #endif
34 #ifdef HAVE_NETDB_H
35 #include <netdb.h>
36 #endif
37 #ifdef HAVE_ARPA_INET_H
38 #include <arpa/inet.h>
39 #endif
40 #ifdef HAVE_STDLIB_H
41 #include <stdlib.h>     /* required for free() prototypes */
42 #endif
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>     /* for the close() proto */
45 #endif
46 #ifdef __VMS
47 #include <in.h>
48 #include <inet.h>
49 #include <stdlib.h>
50 #endif
51
52 #ifdef HAVE_SETJMP_H
53 #include <setjmp.h>
54 #endif
55 #ifdef HAVE_SIGNAL_H
56 #include <signal.h>
57 #endif
58
59 #ifdef HAVE_PROCESS_H
60 #include <process.h>
61 #endif
62
63 #include "urldata.h"
64 #include "sendf.h"
65 #include "hostip.h"
66 #include "hash.h"
67 #include "share.h"
68 #include "strerror.h"
69 #include "url.h"
70 #include "inet_ntop.h"
71
72 #define _MPRINTF_REPLACE /* use our functions only */
73 #include <curl/mprintf.h>
74
75 #include "curl_memory.h"
76 /* The last #include file should be: */
77 #include "memdebug.h"
78
79 #if defined(HAVE_ALARM) && defined(SIGALRM) && defined(HAVE_SIGSETJMP) \
80     && !defined(USE_ARES)
81 /* alarm-based timeouts can only be used with all the dependencies satisfied */
82 #define USE_ALARM_TIMEOUT
83 #endif
84
85 /*
86  * hostip.c explained
87  * ==================
88  *
89  * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
90  * source file are these:
91  *
92  * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
93  * that. The host may not be able to resolve IPv6, but we don't really have to
94  * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
95  * defined.
96  *
97  * CURLRES_ARES - is defined if libcurl is built to use c-ares for
98  * asynchronous name resolves. This can be Windows or *nix.
99  *
100  * CURLRES_THREADED - is defined if libcurl is built to run under (native)
101  * Windows, and then the name resolve will be done in a new thread, and the
102  * supported API will be the same as for ares-builds.
103  *
104  * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
105  * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
106  * defined.
107  *
108  * The host*.c sources files are split up like this:
109  *
110  * hostip.c   - method-independent resolver functions and utility functions
111  * hostasyn.c - functions for asynchronous name resolves
112  * hostsyn.c  - functions for synchronous name resolves
113  * hostares.c - functions for ares-using name resolves
114  * hostthre.c - functions for threaded name resolves
115  * hostip4.c  - ipv4-specific functions
116  * hostip6.c  - ipv6-specific functions
117  *
118  * The hostip.h is the united header file for all this. It defines the
119  * CURLRES_* defines based on the config*.h and setup.h defines.
120  */
121
122 /* These two symbols are for the global DNS cache */
123 static struct curl_hash hostname_cache;
124 static int host_cache_initialized;
125
126 static void freednsentry(void *freethis);
127
128 /*
129  * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
130  * Global DNS cache is general badness. Do not use. This will be removed in
131  * a future version. Use the share interface instead!
132  *
133  * Returns a struct curl_hash pointer on success, NULL on failure.
134  */
135 struct curl_hash *Curl_global_host_cache_init(void)
136 {
137   int rc = 0;
138   if(!host_cache_initialized) {
139     rc = Curl_hash_init(&hostname_cache, 7, Curl_hash_str,
140                         Curl_str_key_compare, freednsentry);
141     if(!rc)
142       host_cache_initialized = 1;
143   }
144   return rc?NULL:&hostname_cache;
145 }
146
147 /*
148  * Destroy and cleanup the global DNS cache
149  */
150 void Curl_global_host_cache_dtor(void)
151 {
152   if(host_cache_initialized) {
153     Curl_hash_clean(&hostname_cache);
154     host_cache_initialized = 0;
155   }
156 }
157
158 /*
159  * Return # of adresses in a Curl_addrinfo struct
160  */
161 int Curl_num_addresses(const Curl_addrinfo *addr)
162 {
163   int i = 0;
164   while(addr) {
165     addr = addr->ai_next;
166     i++;
167   }
168   return i;
169 }
170
171 /*
172  * Curl_printable_address() returns a printable version of the 1st address
173  * given in the 'ai' argument. The result will be stored in the buf that is
174  * bufsize bytes big.
175  *
176  * If the conversion fails, it returns NULL.
177  */
178 const char *
179 Curl_printable_address(const Curl_addrinfo *ai, char *buf, size_t bufsize)
180 {
181   const struct sockaddr_in *sa4;
182   const struct in_addr *ipaddr4;
183 #ifdef ENABLE_IPV6
184   const struct sockaddr_in6 *sa6;
185   const struct in6_addr *ipaddr6;
186 #endif
187
188   switch (ai->ai_family) {
189     case AF_INET:
190       sa4 = (const void *)ai->ai_addr;
191       ipaddr4 = &sa4->sin_addr;
192       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
193 #ifdef ENABLE_IPV6
194     case AF_INET6:
195       sa6 = (const void *)ai->ai_addr;
196       ipaddr6 = &sa6->sin6_addr;
197       return Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
198 #endif
199     default:
200       break;
201   }
202   return NULL;
203 }
204
205 /*
206  * Return a hostcache id string for the providing host + port, to be used by
207  * the DNS caching.
208  */
209 static char *
210 create_hostcache_id(const char *server, int port)
211 {
212   /* create and return the new allocated entry */
213   return aprintf("%s:%d", server, port);
214 }
215
216 struct hostcache_prune_data {
217   long cache_timeout;
218   time_t now;
219 };
220
221 /*
222  * This function is set as a callback to be called for every entry in the DNS
223  * cache when we want to prune old unused entries.
224  *
225  * Returning non-zero means remove the entry, return 0 to keep it in the
226  * cache.
227  */
228 static int
229 hostcache_timestamp_remove(void *datap, void *hc)
230 {
231   struct hostcache_prune_data *data =
232     (struct hostcache_prune_data *) datap;
233   struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
234
235   return (data->now - c->timestamp >= data->cache_timeout);
236 }
237
238 /*
239  * Prune the DNS cache. This assumes that a lock has already been taken.
240  */
241 static void
242 hostcache_prune(struct curl_hash *hostcache, long cache_timeout, time_t now)
243 {
244   struct hostcache_prune_data user;
245
246   user.cache_timeout = cache_timeout;
247   user.now = now;
248
249   Curl_hash_clean_with_criterium(hostcache,
250                                  (void *) &user,
251                                  hostcache_timestamp_remove);
252 }
253
254 /*
255  * Library-wide function for pruning the DNS cache. This function takes and
256  * returns the appropriate locks.
257  */
258 void Curl_hostcache_prune(struct SessionHandle *data)
259 {
260   time_t now;
261
262   if((data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
263     /* cache forever means never prune, and NULL hostcache means
264        we can't do it */
265     return;
266
267   if(data->share)
268     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
269
270   time(&now);
271
272   /* Remove outdated and unused entries from the hostcache */
273   hostcache_prune(data->dns.hostcache,
274                   data->set.dns_cache_timeout,
275                   now);
276
277   if(data->share)
278     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
279 }
280
281 /*
282  * Check if the entry should be pruned. Assumes a locked cache.
283  */
284 static int
285 remove_entry_if_stale(struct SessionHandle *data, struct Curl_dns_entry *dns)
286 {
287   struct hostcache_prune_data user;
288
289   if( !dns || (data->set.dns_cache_timeout == -1) || !data->dns.hostcache)
290     /* cache forever means never prune, and NULL hostcache means
291        we can't do it */
292     return 0;
293
294   time(&user.now);
295   user.cache_timeout = data->set.dns_cache_timeout;
296
297   if( !hostcache_timestamp_remove(&user,dns) )
298     return 0;
299
300   Curl_hash_clean_with_criterium(data->dns.hostcache,
301                                  (void *) &user,
302                                  hostcache_timestamp_remove);
303
304   return 1;
305 }
306
307
308 #ifdef HAVE_SIGSETJMP
309 /* Beware this is a global and unique instance. This is used to store the
310    return address that we can jump back to from inside a signal handler. This
311    is not thread-safe stuff. */
312 sigjmp_buf curl_jmpenv;
313 #endif
314
315
316 /*
317  * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
318  *
319  * When calling Curl_resolv() has resulted in a response with a returned
320  * address, we call this function to store the information in the dns
321  * cache etc
322  *
323  * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
324  */
325 struct Curl_dns_entry *
326 Curl_cache_addr(struct SessionHandle *data,
327                 Curl_addrinfo *addr,
328                 const char *hostname,
329                 int port)
330 {
331   char *entry_id;
332   size_t entry_len;
333   struct Curl_dns_entry *dns;
334   struct Curl_dns_entry *dns2;
335
336   /* Create an entry id, based upon the hostname and port */
337   entry_id = create_hostcache_id(hostname, port);
338   /* If we can't create the entry id, fail */
339   if(!entry_id)
340     return NULL;
341   entry_len = strlen(entry_id);
342
343   /* Create a new cache entry */
344   dns = calloc(1, sizeof(struct Curl_dns_entry));
345   if(!dns) {
346     free(entry_id);
347     return NULL;
348   }
349
350   dns->inuse = 0;   /* init to not used */
351   dns->addr = addr; /* this is the address(es) */
352   time(&dns->timestamp);
353   if(dns->timestamp == 0)
354     dns->timestamp = 1;   /* zero indicates that entry isn't in hash table */
355
356   /* Store the resolved data in our DNS cache. */
357   dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len+1,
358                        (void *)dns);
359   if(!dns2) {
360     free(dns);
361     free(entry_id);
362     return NULL;
363   }
364
365   dns = dns2;
366   dns->inuse++;         /* mark entry as in-use */
367
368   /* free the allocated entry_id again */
369   free(entry_id);
370
371   return dns;
372 }
373
374 /*
375  * Curl_resolv() is the main name resolve function within libcurl. It resolves
376  * a name and returns a pointer to the entry in the 'entry' argument (if one
377  * is provided). This function might return immediately if we're using asynch
378  * resolves. See the return codes.
379  *
380  * The cache entry we return will get its 'inuse' counter increased when this
381  * function is used. You MUST call Curl_resolv_unlock() later (when you're
382  * done using this struct) to decrease the counter again.
383  *
384  * In debug mode, we specifically test for an interface name "LocalHost"
385  * and resolve "localhost" instead as a means to permit test cases
386  * to connect to a local test server with any host name.
387  *
388  * Return codes:
389  *
390  * CURLRESOLV_ERROR   (-1) = error, no pointer
391  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
392  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
393  */
394
395 int Curl_resolv(struct connectdata *conn,
396                 const char *hostname,
397                 int port,
398                 struct Curl_dns_entry **entry)
399 {
400   char *entry_id = NULL;
401   struct Curl_dns_entry *dns = NULL;
402   size_t entry_len;
403   struct SessionHandle *data = conn->data;
404   CURLcode result;
405   int rc = CURLRESOLV_ERROR; /* default to failure */
406
407   *entry = NULL;
408
409   /* Create an entry id, based upon the hostname and port */
410   entry_id = create_hostcache_id(hostname, port);
411   /* If we can't create the entry id, fail */
412   if(!entry_id)
413     return rc;
414
415   entry_len = strlen(entry_id);
416
417   if(data->share)
418     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
419
420   /* See if its already in our dns cache */
421   dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len+1);
422
423   /* See whether the returned entry is stale. Done before we release lock */
424   if( remove_entry_if_stale(data, dns) )
425     dns = NULL; /* the memory deallocation is being handled by the hash */
426
427   if(dns) {
428     dns->inuse++; /* we use it! */
429     rc = CURLRESOLV_RESOLVED;
430   }
431
432   if(data->share)
433     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
434
435   /* free the allocated entry_id again */
436   free(entry_id);
437
438   if(!dns) {
439     /* The entry was not in the cache. Resolve it to IP address */
440
441     Curl_addrinfo *addr;
442     int respwait;
443
444     /* Check what IP specifics the app has requested and if we can provide it.
445      * If not, bail out. */
446     if(!Curl_ipvalid(data))
447       return CURLRESOLV_ERROR;
448
449     /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
450        non-zero value indicating that we need to wait for the response to the
451        resolve call */
452     addr = Curl_getaddrinfo(conn,
453 #ifdef DEBUGBUILD
454                             (data->set.str[STRING_DEVICE]
455                              && !strcmp(data->set.str[STRING_DEVICE],
456                                         "LocalHost"))?"localhost":
457 #endif
458                             hostname, port, &respwait);
459
460     if(!addr) {
461       if(respwait) {
462         /* the response to our resolve call will come asynchronously at
463            a later time, good or bad */
464         /* First, check that we haven't received the info by now */
465         result = Curl_is_resolved(conn, &dns);
466         if(result) /* error detected */
467           return CURLRESOLV_ERROR;
468         if(dns)
469           rc = CURLRESOLV_RESOLVED; /* pointer provided */
470         else
471           rc = CURLRESOLV_PENDING; /* no info yet */
472       }
473     }
474     else {
475       if(data->share)
476         Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
477
478       /* we got a response, store it in the cache */
479       dns = Curl_cache_addr(data, addr, hostname, port);
480
481       if(data->share)
482         Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
483
484       if(!dns)
485         /* returned failure, bail out nicely */
486         Curl_freeaddrinfo(addr);
487       else
488         rc = CURLRESOLV_RESOLVED;
489     }
490   }
491
492   *entry = dns;
493
494   return rc;
495 }
496
497 #ifdef USE_ALARM_TIMEOUT
498 /*
499  * This signal handler jumps back into the main libcurl code and continues
500  * execution.  This effectively causes the remainder of the application to run
501  * within a signal handler which is nonportable and could lead to problems.
502  */
503 static
504 RETSIGTYPE alarmfunc(int sig)
505 {
506   /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
507   (void)sig;
508   siglongjmp(curl_jmpenv, 1);
509   return;
510 }
511 #endif /* USE_ALARM_TIMEOUT */
512
513 /*
514  * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
515  * timeout.  This function might return immediately if we're using asynch
516  * resolves. See the return codes.
517  *
518  * The cache entry we return will get its 'inuse' counter increased when this
519  * function is used. You MUST call Curl_resolv_unlock() later (when you're
520  * done using this struct) to decrease the counter again.
521  *
522  * If built with a synchronous resolver and use of signals is not
523  * disabled by the application, then a nonzero timeout will cause a
524  * timeout after the specified number of milliseconds. Otherwise, timeout
525  * is ignored.
526  *
527  * Return codes:
528  *
529  * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
530  * CURLRESOLV_ERROR   (-1) = error, no pointer
531  * CURLRESOLV_RESOLVED (0) = OK, pointer provided
532  * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
533  */
534
535 int Curl_resolv_timeout(struct connectdata *conn,
536                         const char *hostname,
537                         int port,
538                         struct Curl_dns_entry **entry,
539                         long timeoutms)
540 {
541 #ifdef USE_ALARM_TIMEOUT
542 #ifdef HAVE_SIGACTION
543   struct sigaction keep_sigact;   /* store the old struct here */
544   bool keep_copysig=FALSE;        /* did copy it? */
545   struct sigaction sigact;
546 #else
547 #ifdef HAVE_SIGNAL
548   void (*keep_sigact)(int);       /* store the old handler here */
549 #endif /* HAVE_SIGNAL */
550 #endif /* HAVE_SIGACTION */
551   volatile long timeout;
552   unsigned int prev_alarm=0;
553   struct SessionHandle *data = conn->data;
554 #endif /* USE_ALARM_TIMEOUT */
555   int rc;
556
557   *entry = NULL;
558
559 #ifdef USE_ALARM_TIMEOUT
560   if (data->set.no_signal)
561     /* Ignore the timeout when signals are disabled */
562     timeout = 0;
563   else
564     timeout = timeoutms;
565
566   if(timeout && timeout < 1000)
567     /* The alarm() function only provides integer second resolution, so if
568        we want to wait less than one second we must bail out already now. */
569     return CURLRESOLV_TIMEDOUT;
570
571   if (timeout > 0) {
572     /* This allows us to time-out from the name resolver, as the timeout
573        will generate a signal and we will siglongjmp() from that here.
574        This technique has problems (see alarmfunc). */
575       if(sigsetjmp(curl_jmpenv, 1)) {
576         /* this is coming from a siglongjmp() after an alarm signal */
577         failf(data, "name lookup timed out");
578         return CURLRESOLV_ERROR;
579       }
580
581     /*************************************************************
582      * Set signal handler to catch SIGALRM
583      * Store the old value to be able to set it back later!
584      *************************************************************/
585 #ifdef HAVE_SIGACTION
586     sigaction(SIGALRM, NULL, &sigact);
587     keep_sigact = sigact;
588     keep_copysig = TRUE; /* yes, we have a copy */
589     sigact.sa_handler = alarmfunc;
590 #ifdef SA_RESTART
591     /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
592     sigact.sa_flags &= ~SA_RESTART;
593 #endif
594     /* now set the new struct */
595     sigaction(SIGALRM, &sigact, NULL);
596 #else /* HAVE_SIGACTION */
597     /* no sigaction(), revert to the much lamer signal() */
598 #ifdef HAVE_SIGNAL
599     keep_sigact = signal(SIGALRM, alarmfunc);
600 #endif
601 #endif /* HAVE_SIGACTION */
602
603     /* alarm() makes a signal get sent when the timeout fires off, and that
604        will abort system calls */
605     prev_alarm = alarm((unsigned int) (timeout/1000L));
606   }
607
608 #else
609 #ifndef CURLRES_ASYNCH
610   if(timeoutms)
611     infof(conn->data, "timeout on name lookup is not supported\n");
612 #else
613   (void)timeoutms; /* timeoutms not used with an async resolver */
614 #endif
615 #endif /* USE_ALARM_TIMEOUT */
616
617   /* Perform the actual name resolution. This might be interrupted by an
618    * alarm if it takes too long.
619    */
620   rc = Curl_resolv(conn, hostname, port, entry);
621
622 #ifdef USE_ALARM_TIMEOUT
623   if (timeout > 0) {
624
625 #ifdef HAVE_SIGACTION
626     if(keep_copysig) {
627       /* we got a struct as it looked before, now put that one back nice
628          and clean */
629       sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
630     }
631 #else
632 #ifdef HAVE_SIGNAL
633     /* restore the previous SIGALRM handler */
634     signal(SIGALRM, keep_sigact);
635 #endif
636 #endif /* HAVE_SIGACTION */
637
638     /* switch back the alarm() to either zero or to what it was before minus
639        the time we spent until now! */
640     if(prev_alarm) {
641       /* there was an alarm() set before us, now put it back */
642       unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
643
644       /* the alarm period is counted in even number of seconds */
645       unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
646
647       if(!alarm_set ||
648          ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
649         /* if the alarm time-left reached zero or turned "negative" (counted
650            with unsigned values), we should fire off a SIGALRM here, but we
651            won't, and zero would be to switch it off so we never set it to
652            less than 1! */
653         alarm(1);
654         rc = CURLRESOLV_TIMEDOUT;
655         failf(data, "Previous alarm fired off!");
656       }
657       else
658         alarm((unsigned int)alarm_set);
659     }
660     else
661       alarm(0); /* just shut it off */
662   }
663 #endif /* USE_ALARM_TIMEOUT */
664
665   return rc;
666 }
667
668 /*
669  * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
670  * made, the struct may be destroyed due to pruning. It is important that only
671  * one unlock is made for each Curl_resolv() call.
672  */
673 void Curl_resolv_unlock(struct SessionHandle *data, struct Curl_dns_entry *dns)
674 {
675   DEBUGASSERT(dns && (dns->inuse>0));
676
677   if(data->share)
678     Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
679
680   dns->inuse--;
681   /* only free if nobody is using AND it is not in hostcache (timestamp ==
682      0) */
683   if (dns->inuse == 0 && dns->timestamp == 0) {
684     Curl_freeaddrinfo(dns->addr);
685     free(dns);
686   }
687
688   if(data->share)
689     Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
690 }
691
692 /*
693  * File-internal: free a cache dns entry.
694  */
695 static void freednsentry(void *freethis)
696 {
697   struct Curl_dns_entry *p = (struct Curl_dns_entry *) freethis;
698
699   /* mark the entry as not in hostcache */
700   p->timestamp = 0;
701   if (p->inuse == 0) {
702     Curl_freeaddrinfo(p->addr);
703     free(p);
704   }
705 }
706
707 /*
708  * Curl_mk_dnscache() creates a new DNS cache and returns the handle for it.
709  */
710 struct curl_hash *Curl_mk_dnscache(void)
711 {
712   return Curl_hash_alloc(7, Curl_hash_str, Curl_str_key_compare, freednsentry);
713 }
714
715 #ifdef CURLRES_ADDRINFO_COPY
716
717 /* align on even 64bit boundaries */
718 #define MEMALIGN(x) ((x)+(8-(((unsigned long)(x))&0x7)))
719
720 /*
721  * Curl_addrinfo_copy() performs a "deep" copy of a hostent into a buffer and
722  * returns a pointer to the malloc()ed copy. You need to call free() on the
723  * returned buffer when you're done with it.
724  */
725 Curl_addrinfo *Curl_addrinfo_copy(const void *org, int port)
726 {
727   const struct hostent *orig = org;
728
729   return Curl_he2ai(orig, port);
730 }
731 #endif /* CURLRES_ADDRINFO_COPY */