Coverage Report

Created: 2023-06-07 07:02

/src/curl/lib/hostip.c
Line
Count
Source (jump to first uncovered line)
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) 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 https://curl.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
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
25
#include "curl_setup.h"
26
27
#ifdef HAVE_NETINET_IN_H
28
#include <netinet/in.h>
29
#endif
30
#ifdef HAVE_NETINET_IN6_H
31
#include <netinet/in6.h>
32
#endif
33
#ifdef HAVE_NETDB_H
34
#include <netdb.h>
35
#endif
36
#ifdef HAVE_ARPA_INET_H
37
#include <arpa/inet.h>
38
#endif
39
#ifdef __VMS
40
#include <in.h>
41
#include <inet.h>
42
#endif
43
44
#ifdef HAVE_SETJMP_H
45
#include <setjmp.h>
46
#endif
47
#ifdef HAVE_SIGNAL_H
48
#include <signal.h>
49
#endif
50
51
#include "urldata.h"
52
#include "sendf.h"
53
#include "hostip.h"
54
#include "hash.h"
55
#include "rand.h"
56
#include "share.h"
57
#include "url.h"
58
#include "inet_ntop.h"
59
#include "inet_pton.h"
60
#include "multiif.h"
61
#include "doh.h"
62
#include "warnless.h"
63
#include "strcase.h"
64
#include "easy_lock.h"
65
/* The last 3 #include files should be in this order */
66
#include "curl_printf.h"
67
#include "curl_memory.h"
68
#include "memdebug.h"
69
70
#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
71
#include <SystemConfiguration/SCDynamicStoreCopySpecific.h>
72
#endif
73
74
#if defined(CURLRES_SYNCH) &&                   \
75
  defined(HAVE_ALARM) &&                        \
76
  defined(SIGALRM) &&                           \
77
  defined(HAVE_SIGSETJMP) &&                    \
78
  defined(GLOBAL_INIT_IS_THREADSAFE)
79
/* alarm-based timeouts can only be used with all the dependencies satisfied */
80
#define USE_ALARM_TIMEOUT
81
#endif
82
83
#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
84
85
0
#define MAX_DNS_CACHE_SIZE 29999
86
87
/*
88
 * hostip.c explained
89
 * ==================
90
 *
91
 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
92
 * source file are these:
93
 *
94
 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
95
 * that. The host may not be able to resolve IPv6, but we don't really have to
96
 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
97
 * defined.
98
 *
99
 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
100
 * asynchronous name resolves. This can be Windows or *nix.
101
 *
102
 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
103
 * Windows, and then the name resolve will be done in a new thread, and the
104
 * supported API will be the same as for ares-builds.
105
 *
106
 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
107
 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
108
 * defined.
109
 *
110
 * The host*.c sources files are split up like this:
111
 *
112
 * hostip.c   - method-independent resolver functions and utility functions
113
 * hostasyn.c - functions for asynchronous name resolves
114
 * hostsyn.c  - functions for synchronous name resolves
115
 * hostip4.c  - IPv4 specific functions
116
 * hostip6.c  - IPv6 specific functions
117
 *
118
 * The two asynchronous name resolver backends are implemented in:
119
 * asyn-ares.c   - functions for ares-using name resolves
120
 * asyn-thread.c - functions for threaded name resolves
121
122
 * The hostip.h is the united header file for all this. It defines the
123
 * CURLRES_* defines based on the config*.h and curl_setup.h defines.
124
 */
125
126
static void freednsentry(void *freethis);
127
128
/*
129
 * Return # of addresses in a Curl_addrinfo struct
130
 */
131
static int num_addresses(const struct Curl_addrinfo *addr)
132
0
{
133
0
  int i = 0;
134
0
  while(addr) {
135
0
    addr = addr->ai_next;
136
0
    i++;
137
0
  }
138
0
  return i;
139
0
}
140
141
/*
142
 * Curl_printable_address() stores a printable version of the 1st address
143
 * given in the 'ai' argument. The result will be stored in the buf that is
144
 * bufsize bytes big.
145
 *
146
 * If the conversion fails, the target buffer is empty.
147
 */
148
void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
149
                            size_t bufsize)
150
0
{
151
0
  DEBUGASSERT(bufsize);
152
0
  buf[0] = 0;
153
154
0
  switch(ai->ai_family) {
155
0
  case AF_INET: {
156
0
    const struct sockaddr_in *sa4 = (const void *)ai->ai_addr;
157
0
    const struct in_addr *ipaddr4 = &sa4->sin_addr;
158
0
    (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
159
0
    break;
160
0
  }
161
0
#ifdef ENABLE_IPV6
162
0
  case AF_INET6: {
163
0
    const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr;
164
0
    const struct in6_addr *ipaddr6 = &sa6->sin6_addr;
165
0
    (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
166
0
    break;
167
0
  }
168
0
#endif
169
0
  default:
170
0
    break;
171
0
  }
172
0
}
173
174
/*
175
 * Create a hostcache id string for the provided host + port, to be used by
176
 * the DNS caching. Without alloc. Return length of the id string.
177
 */
178
static size_t
179
create_hostcache_id(const char *name,
180
                    size_t nlen, /* 0 or actual name length */
181
                    int port, char *ptr, size_t buflen)
182
0
{
183
0
  size_t len = nlen ? nlen : strlen(name);
184
0
  size_t olen = 0;
185
0
  DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
186
0
  if(len > (buflen - 7))
187
0
    len = buflen - 7;
188
  /* store and lower case the name */
189
0
  while(len--) {
190
0
    *ptr++ = Curl_raw_tolower(*name++);
191
0
    olen++;
192
0
  }
193
0
  olen += msnprintf(ptr, 7, ":%u", port);
194
0
  return olen;
195
0
}
196
197
struct hostcache_prune_data {
198
  time_t now;
199
  time_t oldest; /* oldest time in cache not pruned. */
200
  int cache_timeout;
201
};
202
203
/*
204
 * This function is set as a callback to be called for every entry in the DNS
205
 * cache when we want to prune old unused entries.
206
 *
207
 * Returning non-zero means remove the entry, return 0 to keep it in the
208
 * cache.
209
 */
210
static int
211
hostcache_timestamp_remove(void *datap, void *hc)
212
0
{
213
0
  struct hostcache_prune_data *prune =
214
0
    (struct hostcache_prune_data *) datap;
215
0
  struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
216
217
0
  if(c->timestamp) {
218
    /* age in seconds */
219
0
    time_t age = prune->now - c->timestamp;
220
0
    if(age >= prune->cache_timeout)
221
0
      return TRUE;
222
0
    if(age > prune->oldest)
223
0
      prune->oldest = age;
224
0
  }
225
0
  return FALSE;
226
0
}
227
228
/*
229
 * Prune the DNS cache. This assumes that a lock has already been taken.
230
 * Returns the 'age' of the oldest still kept entry.
231
 */
232
static time_t
233
hostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
234
                time_t now)
235
0
{
236
0
  struct hostcache_prune_data user;
237
238
0
  user.cache_timeout = cache_timeout;
239
0
  user.now = now;
240
0
  user.oldest = 0;
241
242
0
  Curl_hash_clean_with_criterium(hostcache,
243
0
                                 (void *) &user,
244
0
                                 hostcache_timestamp_remove);
245
246
0
  return user.oldest;
247
0
}
248
249
/*
250
 * Library-wide function for pruning the DNS cache. This function takes and
251
 * returns the appropriate locks.
252
 */
253
void Curl_hostcache_prune(struct Curl_easy *data)
254
0
{
255
0
  time_t now;
256
  /* the timeout may be set -1 (forever) */
257
0
  int timeout = data->set.dns_cache_timeout;
258
259
0
  if(!data->dns.hostcache)
260
    /* NULL hostcache means we can't do it */
261
0
    return;
262
263
0
  if(data->share)
264
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
265
266
0
  time(&now);
267
268
0
  do {
269
    /* Remove outdated and unused entries from the hostcache */
270
0
    time_t oldest = hostcache_prune(data->dns.hostcache, timeout, now);
271
272
0
    if(oldest < INT_MAX)
273
0
      timeout = (int)oldest; /* we know it fits */
274
0
    else
275
0
      timeout = INT_MAX - 1;
276
277
    /* if the cache size is still too big, use the oldest age as new
278
       prune limit */
279
0
  } while(timeout && (data->dns.hostcache->size > MAX_DNS_CACHE_SIZE));
280
281
0
  if(data->share)
282
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
283
0
}
284
285
#ifdef USE_ALARM_TIMEOUT
286
/* Beware this is a global and unique instance. This is used to store the
287
   return address that we can jump back to from inside a signal handler. This
288
   is not thread-safe stuff. */
289
static sigjmp_buf curl_jmpenv;
290
static curl_simple_lock curl_jmpenv_lock;
291
#endif
292
293
/* lookup address, returns entry if found and not stale */
294
static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
295
                                         const char *hostname,
296
                                         int port)
297
0
{
298
0
  struct Curl_dns_entry *dns = NULL;
299
0
  char entry_id[MAX_HOSTCACHE_LEN];
300
301
  /* Create an entry id, based upon the hostname and port */
302
0
  size_t entry_len = create_hostcache_id(hostname, 0, port,
303
0
                                         entry_id, sizeof(entry_id));
304
305
  /* See if its already in our dns cache */
306
0
  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
307
308
  /* No entry found in cache, check if we might have a wildcard entry */
309
0
  if(!dns && data->state.wildcard_resolve) {
310
0
    entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id));
311
312
    /* See if it's already in our dns cache */
313
0
    dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
314
0
  }
315
316
0
  if(dns && (data->set.dns_cache_timeout != -1)) {
317
    /* See whether the returned entry is stale. Done before we release lock */
318
0
    struct hostcache_prune_data user;
319
320
0
    time(&user.now);
321
0
    user.cache_timeout = data->set.dns_cache_timeout;
322
0
    user.oldest = 0;
323
324
0
    if(hostcache_timestamp_remove(&user, dns)) {
325
0
      infof(data, "Hostname in DNS cache was stale, zapped");
326
0
      dns = NULL; /* the memory deallocation is being handled by the hash */
327
0
      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
328
0
    }
329
0
  }
330
331
  /* See if the returned entry matches the required resolve mode */
332
0
  if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) {
333
0
    int pf = PF_INET;
334
0
    bool found = false;
335
0
    struct Curl_addrinfo *addr = dns->addr;
336
337
0
#ifdef PF_INET6
338
0
    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
339
0
      pf = PF_INET6;
340
0
#endif
341
342
0
    while(addr) {
343
0
      if(addr->ai_family == pf) {
344
0
        found = true;
345
0
        break;
346
0
      }
347
0
      addr = addr->ai_next;
348
0
    }
349
350
0
    if(!found) {
351
0
      infof(data, "Hostname in DNS cache doesn't have needed family, zapped");
352
0
      dns = NULL; /* the memory deallocation is being handled by the hash */
353
0
      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
354
0
    }
355
0
  }
356
0
  return dns;
357
0
}
358
359
/*
360
 * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
361
 *
362
 * Curl_resolv() checks initially and multi_runsingle() checks each time
363
 * it discovers the handle in the state WAITRESOLVE whether the hostname
364
 * has already been resolved and the address has already been stored in
365
 * the DNS cache. This short circuits waiting for a lot of pending
366
 * lookups for the same hostname requested by different handles.
367
 *
368
 * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
369
 *
370
 * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
371
 * use, or we'll leak memory!
372
 */
373
struct Curl_dns_entry *
374
Curl_fetch_addr(struct Curl_easy *data,
375
                const char *hostname,
376
                int port)
377
0
{
378
0
  struct Curl_dns_entry *dns = NULL;
379
380
0
  if(data->share)
381
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
382
383
0
  dns = fetch_addr(data, hostname, port);
384
385
0
  if(dns)
386
0
    dns->inuse++; /* we use it! */
387
388
0
  if(data->share)
389
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
390
391
0
  return dns;
392
0
}
393
394
#ifndef CURL_DISABLE_SHUFFLE_DNS
395
UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
396
                                    struct Curl_addrinfo **addr);
397
/*
398
 * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
399
 * struct by re-linking its linked list.
400
 *
401
 * The addr argument should be the address of a pointer to the head node of a
402
 * `Curl_addrinfo` list and it will be modified to point to the new head after
403
 * shuffling.
404
 *
405
 * Not declared static only to make it easy to use in a unit test!
406
 *
407
 * @unittest: 1608
408
 */
409
UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
410
                                    struct Curl_addrinfo **addr)
411
0
{
412
0
  CURLcode result = CURLE_OK;
413
0
  const int num_addrs = num_addresses(*addr);
414
415
0
  if(num_addrs > 1) {
416
0
    struct Curl_addrinfo **nodes;
417
0
    infof(data, "Shuffling %i addresses", num_addrs);
418
419
0
    nodes = malloc(num_addrs*sizeof(*nodes));
420
0
    if(nodes) {
421
0
      int i;
422
0
      unsigned int *rnd;
423
0
      const size_t rnd_size = num_addrs * sizeof(*rnd);
424
425
      /* build a plain array of Curl_addrinfo pointers */
426
0
      nodes[0] = *addr;
427
0
      for(i = 1; i < num_addrs; i++) {
428
0
        nodes[i] = nodes[i-1]->ai_next;
429
0
      }
430
431
0
      rnd = malloc(rnd_size);
432
0
      if(rnd) {
433
        /* Fisher-Yates shuffle */
434
0
        if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
435
0
          struct Curl_addrinfo *swap_tmp;
436
0
          for(i = num_addrs - 1; i > 0; i--) {
437
0
            swap_tmp = nodes[rnd[i] % (i + 1)];
438
0
            nodes[rnd[i] % (i + 1)] = nodes[i];
439
0
            nodes[i] = swap_tmp;
440
0
          }
441
442
          /* relink list in the new order */
443
0
          for(i = 1; i < num_addrs; i++) {
444
0
            nodes[i-1]->ai_next = nodes[i];
445
0
          }
446
447
0
          nodes[num_addrs-1]->ai_next = NULL;
448
0
          *addr = nodes[0];
449
0
        }
450
0
        free(rnd);
451
0
      }
452
0
      else
453
0
        result = CURLE_OUT_OF_MEMORY;
454
0
      free(nodes);
455
0
    }
456
0
    else
457
0
      result = CURLE_OUT_OF_MEMORY;
458
0
  }
459
0
  return result;
460
0
}
461
#endif
462
463
/*
464
 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
465
 *
466
 * When calling Curl_resolv() has resulted in a response with a returned
467
 * address, we call this function to store the information in the dns
468
 * cache etc
469
 *
470
 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
471
 */
472
struct Curl_dns_entry *
473
Curl_cache_addr(struct Curl_easy *data,
474
                struct Curl_addrinfo *addr,
475
                const char *hostname,
476
                size_t hostlen, /* length or zero */
477
                int port)
478
0
{
479
0
  char entry_id[MAX_HOSTCACHE_LEN];
480
0
  size_t entry_len;
481
0
  struct Curl_dns_entry *dns;
482
0
  struct Curl_dns_entry *dns2;
483
484
0
#ifndef CURL_DISABLE_SHUFFLE_DNS
485
  /* shuffle addresses if requested */
486
0
  if(data->set.dns_shuffle_addresses) {
487
0
    CURLcode result = Curl_shuffle_addr(data, &addr);
488
0
    if(result)
489
0
      return NULL;
490
0
  }
491
0
#endif
492
493
  /* Create a new cache entry */
494
0
  dns = calloc(1, sizeof(struct Curl_dns_entry));
495
0
  if(!dns) {
496
0
    return NULL;
497
0
  }
498
499
  /* Create an entry id, based upon the hostname and port */
500
0
  entry_len = create_hostcache_id(hostname, hostlen, port,
501
0
                                  entry_id, sizeof(entry_id));
502
503
0
  dns->inuse = 1;   /* the cache has the first reference */
504
0
  dns->addr = addr; /* this is the address(es) */
505
0
  time(&dns->timestamp);
506
0
  if(dns->timestamp == 0)
507
0
    dns->timestamp = 1;   /* zero indicates permanent CURLOPT_RESOLVE entry */
508
509
  /* Store the resolved data in our DNS cache. */
510
0
  dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
511
0
                       (void *)dns);
512
0
  if(!dns2) {
513
0
    free(dns);
514
0
    return NULL;
515
0
  }
516
517
0
  dns = dns2;
518
0
  dns->inuse++;         /* mark entry as in-use */
519
0
  return dns;
520
0
}
521
522
#ifdef ENABLE_IPV6
523
/* return a static IPv6 ::1 for the name */
524
static struct Curl_addrinfo *get_localhost6(int port, const char *name)
525
0
{
526
0
  struct Curl_addrinfo *ca;
527
0
  const size_t ss_size = sizeof(struct sockaddr_in6);
528
0
  const size_t hostlen = strlen(name);
529
0
  struct sockaddr_in6 sa6;
530
0
  unsigned char ipv6[16];
531
0
  unsigned short port16 = (unsigned short)(port & 0xffff);
532
0
  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
533
0
  if(!ca)
534
0
    return NULL;
535
536
0
  sa6.sin6_family = AF_INET6;
537
0
  sa6.sin6_port = htons(port16);
538
0
  sa6.sin6_flowinfo = 0;
539
0
  sa6.sin6_scope_id = 0;
540
0
  if(Curl_inet_pton(AF_INET6, "::1", ipv6) < 1)
541
0
    return NULL;
542
0
  memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
543
544
0
  ca->ai_flags     = 0;
545
0
  ca->ai_family    = AF_INET6;
546
0
  ca->ai_socktype  = SOCK_STREAM;
547
0
  ca->ai_protocol  = IPPROTO_TCP;
548
0
  ca->ai_addrlen   = (curl_socklen_t)ss_size;
549
0
  ca->ai_next      = NULL;
550
0
  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
551
0
  memcpy(ca->ai_addr, &sa6, ss_size);
552
0
  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
553
0
  strcpy(ca->ai_canonname, name);
554
0
  return ca;
555
0
}
556
#else
557
#define get_localhost6(x,y) NULL
558
#endif
559
560
/* return a static IPv4 127.0.0.1 for the given name */
561
static struct Curl_addrinfo *get_localhost(int port, const char *name)
562
0
{
563
0
  struct Curl_addrinfo *ca;
564
0
  const size_t ss_size = sizeof(struct sockaddr_in);
565
0
  const size_t hostlen = strlen(name);
566
0
  struct sockaddr_in sa;
567
0
  unsigned int ipv4;
568
0
  unsigned short port16 = (unsigned short)(port & 0xffff);
569
570
  /* memset to clear the sa.sin_zero field */
571
0
  memset(&sa, 0, sizeof(sa));
572
0
  sa.sin_family = AF_INET;
573
0
  sa.sin_port = htons(port16);
574
0
  if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
575
0
    return NULL;
576
0
  memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
577
578
0
  ca = calloc(sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1, 1);
579
0
  if(!ca)
580
0
    return NULL;
581
0
  ca->ai_flags     = 0;
582
0
  ca->ai_family    = AF_INET;
583
0
  ca->ai_socktype  = SOCK_STREAM;
584
0
  ca->ai_protocol  = IPPROTO_TCP;
585
0
  ca->ai_addrlen   = (curl_socklen_t)ss_size;
586
0
  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
587
0
  memcpy(ca->ai_addr, &sa, ss_size);
588
0
  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
589
0
  strcpy(ca->ai_canonname, name);
590
0
  ca->ai_next = get_localhost6(port, name);
591
0
  return ca;
592
0
}
593
594
#ifdef ENABLE_IPV6
595
/*
596
 * Curl_ipv6works() returns TRUE if IPv6 seems to work.
597
 */
598
bool Curl_ipv6works(struct Curl_easy *data)
599
0
{
600
0
  if(data) {
601
    /* the nature of most system is that IPv6 status doesn't come and go
602
       during a program's lifetime so we only probe the first time and then we
603
       have the info kept for fast re-use */
604
0
    DEBUGASSERT(data);
605
0
    DEBUGASSERT(data->multi);
606
0
    if(data->multi->ipv6_up == IPV6_UNKNOWN) {
607
0
      bool works = Curl_ipv6works(NULL);
608
0
      data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD;
609
0
    }
610
0
    return data->multi->ipv6_up == IPV6_WORKS;
611
0
  }
612
0
  else {
613
0
    int ipv6_works = -1;
614
    /* probe to see if we have a working IPv6 stack */
615
0
    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
616
0
    if(s == CURL_SOCKET_BAD)
617
      /* an IPv6 address was requested but we can't get/use one */
618
0
      ipv6_works = 0;
619
0
    else {
620
0
      ipv6_works = 1;
621
0
      sclose(s);
622
0
    }
623
0
    return (ipv6_works>0)?TRUE:FALSE;
624
0
  }
625
0
}
626
#endif /* ENABLE_IPV6 */
627
628
/*
629
 * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
630
 * (or IPv6 if supported) address.
631
 */
632
bool Curl_host_is_ipnum(const char *hostname)
633
0
{
634
0
  struct in_addr in;
635
0
#ifdef ENABLE_IPV6
636
0
  struct in6_addr in6;
637
0
#endif
638
0
  if(Curl_inet_pton(AF_INET, hostname, &in) > 0
639
0
#ifdef ENABLE_IPV6
640
0
     || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
641
0
#endif
642
0
    )
643
0
    return TRUE;
644
0
  return FALSE;
645
0
}
646
647
648
/* return TRUE if 'part' is a case insensitive tail of 'full' */
649
static bool tailmatch(const char *full, const char *part)
650
0
{
651
0
  size_t plen = strlen(part);
652
0
  size_t flen = strlen(full);
653
0
  if(plen > flen)
654
0
    return FALSE;
655
0
  return strncasecompare(part, &full[flen - plen], plen);
656
0
}
657
658
/*
659
 * Curl_resolv() is the main name resolve function within libcurl. It resolves
660
 * a name and returns a pointer to the entry in the 'entry' argument (if one
661
 * is provided). This function might return immediately if we're using asynch
662
 * resolves. See the return codes.
663
 *
664
 * The cache entry we return will get its 'inuse' counter increased when this
665
 * function is used. You MUST call Curl_resolv_unlock() later (when you're
666
 * done using this struct) to decrease the counter again.
667
 *
668
 * Return codes:
669
 *
670
 * CURLRESOLV_ERROR   (-1) = error, no pointer
671
 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
672
 * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
673
 */
674
675
enum resolve_t Curl_resolv(struct Curl_easy *data,
676
                           const char *hostname,
677
                           int port,
678
                           bool allowDOH,
679
                           struct Curl_dns_entry **entry)
680
0
{
681
0
  struct Curl_dns_entry *dns = NULL;
682
0
  CURLcode result;
683
0
  enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
684
0
  struct connectdata *conn = data->conn;
685
  /* We should intentionally error and not resolve .onion TLDs */
686
0
  size_t hostname_len = strlen(hostname);
687
0
  if(hostname_len >= 7 &&
688
0
     (curl_strequal(&hostname[hostname_len - 6], ".onion") ||
689
0
      curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
690
0
    failf(data, "Not resolving .onion address (RFC 7686)");
691
0
    return CURLRESOLV_ERROR;
692
0
  }
693
0
  *entry = NULL;
694
0
#ifndef CURL_DISABLE_DOH
695
0
  conn->bits.doh = FALSE; /* default is not */
696
#else
697
  (void)allowDOH;
698
#endif
699
700
0
  if(data->share)
701
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
702
703
0
  dns = fetch_addr(data, hostname, port);
704
705
0
  if(dns) {
706
0
    infof(data, "Hostname %s was found in DNS cache", hostname);
707
0
    dns->inuse++; /* we use it! */
708
0
    rc = CURLRESOLV_RESOLVED;
709
0
  }
710
711
0
  if(data->share)
712
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
713
714
0
  if(!dns) {
715
    /* The entry was not in the cache. Resolve it to IP address */
716
717
0
    struct Curl_addrinfo *addr = NULL;
718
0
    int respwait = 0;
719
0
#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS)
720
0
    struct in_addr in;
721
0
#endif
722
0
#ifndef CURL_DISABLE_DOH
723
0
#ifndef USE_RESOLVE_ON_IPS
724
0
    const
725
0
#endif
726
0
      bool ipnum = FALSE;
727
0
#endif
728
729
    /* notify the resolver start callback */
730
0
    if(data->set.resolver_start) {
731
0
      int st;
732
0
      Curl_set_in_callback(data, true);
733
0
      st = data->set.resolver_start(
734
0
#ifdef USE_CURL_ASYNC
735
0
        data->state.async.resolver,
736
#else
737
        NULL,
738
#endif
739
0
        NULL,
740
0
        data->set.resolver_start_client);
741
0
      Curl_set_in_callback(data, false);
742
0
      if(st)
743
0
        return CURLRESOLV_ERROR;
744
0
    }
745
746
#if defined(ENABLE_IPV6) && defined(CURL_OSX_CALL_COPYPROXIES)
747
    {
748
      /*
749
       * The automagic conversion from IPv4 literals to IPv6 literals only
750
       * works if the SCDynamicStoreCopyProxies system function gets called
751
       * first. As Curl currently doesn't support system-wide HTTP proxies, we
752
       * therefore don't use any value this function might return.
753
       *
754
       * This function is only available on a macOS and is not needed for
755
       * IPv4-only builds, hence the conditions above.
756
       */
757
      CFDictionaryRef dict = SCDynamicStoreCopyProxies(NULL);
758
      if(dict)
759
        CFRelease(dict);
760
    }
761
#endif
762
763
0
#ifndef USE_RESOLVE_ON_IPS
764
    /* First check if this is an IPv4 address string */
765
0
    if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
766
      /* This is a dotted IP address 123.123.123.123-style */
767
0
      addr = Curl_ip2addr(AF_INET, &in, hostname, port);
768
0
#ifdef ENABLE_IPV6
769
0
    if(!addr) {
770
0
      struct in6_addr in6;
771
      /* check if this is an IPv6 address string */
772
0
      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
773
        /* This is an IPv6 address literal */
774
0
        addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
775
0
    }
776
0
#endif /* ENABLE_IPV6 */
777
778
#else /* if USE_RESOLVE_ON_IPS */
779
#ifndef CURL_DISABLE_DOH
780
    /* First check if this is an IPv4 address string */
781
    if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
782
      /* This is a dotted IP address 123.123.123.123-style */
783
      ipnum = TRUE;
784
#ifdef ENABLE_IPV6
785
    else {
786
      struct in6_addr in6;
787
      /* check if this is an IPv6 address string */
788
      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
789
        /* This is an IPv6 address literal */
790
        ipnum = TRUE;
791
    }
792
#endif /* ENABLE_IPV6 */
793
#endif /* CURL_DISABLE_DOH */
794
795
#endif /* !USE_RESOLVE_ON_IPS */
796
797
0
    if(!addr) {
798
0
      if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
799
0
        return CURLRESOLV_ERROR;
800
801
0
      if(strcasecompare(hostname, "localhost") ||
802
0
         tailmatch(hostname, ".localhost"))
803
0
        addr = get_localhost(port, hostname);
804
0
#ifndef CURL_DISABLE_DOH
805
0
      else if(allowDOH && data->set.doh && !ipnum)
806
0
        addr = Curl_doh(data, hostname, port, &respwait);
807
0
#endif
808
0
      else {
809
        /* Check what IP specifics the app has requested and if we can provide
810
         * it. If not, bail out. */
811
0
        if(!Curl_ipvalid(data, conn))
812
0
          return CURLRESOLV_ERROR;
813
        /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
814
           non-zero value indicating that we need to wait for the response to
815
           the resolve call */
816
0
        addr = Curl_getaddrinfo(data, hostname, port, &respwait);
817
0
      }
818
0
    }
819
0
    if(!addr) {
820
0
      if(respwait) {
821
        /* the response to our resolve call will come asynchronously at
822
           a later time, good or bad */
823
        /* First, check that we haven't received the info by now */
824
0
        result = Curl_resolv_check(data, &dns);
825
0
        if(result) /* error detected */
826
0
          return CURLRESOLV_ERROR;
827
0
        if(dns)
828
0
          rc = CURLRESOLV_RESOLVED; /* pointer provided */
829
0
        else
830
0
          rc = CURLRESOLV_PENDING; /* no info yet */
831
0
      }
832
0
    }
833
0
    else {
834
0
      if(data->share)
835
0
        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
836
837
      /* we got a response, store it in the cache */
838
0
      dns = Curl_cache_addr(data, addr, hostname, 0, port);
839
840
0
      if(data->share)
841
0
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
842
843
0
      if(!dns)
844
        /* returned failure, bail out nicely */
845
0
        Curl_freeaddrinfo(addr);
846
0
      else
847
0
        rc = CURLRESOLV_RESOLVED;
848
0
    }
849
0
  }
850
851
0
  *entry = dns;
852
853
0
  return rc;
854
0
}
855
856
#ifdef USE_ALARM_TIMEOUT
857
/*
858
 * This signal handler jumps back into the main libcurl code and continues
859
 * execution.  This effectively causes the remainder of the application to run
860
 * within a signal handler which is nonportable and could lead to problems.
861
 */
862
static
863
void alarmfunc(int sig)
864
{
865
  (void)sig;
866
  siglongjmp(curl_jmpenv, 1);
867
}
868
#endif /* USE_ALARM_TIMEOUT */
869
870
/*
871
 * Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
872
 * timeout.  This function might return immediately if we're using asynch
873
 * resolves. See the return codes.
874
 *
875
 * The cache entry we return will get its 'inuse' counter increased when this
876
 * function is used. You MUST call Curl_resolv_unlock() later (when you're
877
 * done using this struct) to decrease the counter again.
878
 *
879
 * If built with a synchronous resolver and use of signals is not
880
 * disabled by the application, then a nonzero timeout will cause a
881
 * timeout after the specified number of milliseconds. Otherwise, timeout
882
 * is ignored.
883
 *
884
 * Return codes:
885
 *
886
 * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
887
 * CURLRESOLV_ERROR   (-1) = error, no pointer
888
 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
889
 * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
890
 */
891
892
enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
893
                                   const char *hostname,
894
                                   int port,
895
                                   struct Curl_dns_entry **entry,
896
                                   timediff_t timeoutms)
897
0
{
898
#ifdef USE_ALARM_TIMEOUT
899
#ifdef HAVE_SIGACTION
900
  struct sigaction keep_sigact;   /* store the old struct here */
901
  volatile bool keep_copysig = FALSE; /* whether old sigact has been saved */
902
  struct sigaction sigact;
903
#else
904
#ifdef HAVE_SIGNAL
905
  void (*keep_sigact)(int);       /* store the old handler here */
906
#endif /* HAVE_SIGNAL */
907
#endif /* HAVE_SIGACTION */
908
  volatile long timeout;
909
  volatile unsigned int prev_alarm = 0;
910
#endif /* USE_ALARM_TIMEOUT */
911
0
  enum resolve_t rc;
912
913
0
  *entry = NULL;
914
915
0
  if(timeoutms < 0)
916
    /* got an already expired timeout */
917
0
    return CURLRESOLV_TIMEDOUT;
918
919
#ifdef USE_ALARM_TIMEOUT
920
  if(data->set.no_signal)
921
    /* Ignore the timeout when signals are disabled */
922
    timeout = 0;
923
  else
924
    timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
925
926
  if(!timeout)
927
    /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
928
    return Curl_resolv(data, hostname, port, TRUE, entry);
929
930
  if(timeout < 1000) {
931
    /* The alarm() function only provides integer second resolution, so if
932
       we want to wait less than one second we must bail out already now. */
933
    failf(data,
934
        "remaining timeout of %ld too small to resolve via SIGALRM method",
935
        timeout);
936
    return CURLRESOLV_TIMEDOUT;
937
  }
938
  /* This allows us to time-out from the name resolver, as the timeout
939
     will generate a signal and we will siglongjmp() from that here.
940
     This technique has problems (see alarmfunc).
941
     This should be the last thing we do before calling Curl_resolv(),
942
     as otherwise we'd have to worry about variables that get modified
943
     before we invoke Curl_resolv() (and thus use "volatile"). */
944
  curl_simple_lock_lock(&curl_jmpenv_lock);
945
946
  if(sigsetjmp(curl_jmpenv, 1)) {
947
    /* this is coming from a siglongjmp() after an alarm signal */
948
    failf(data, "name lookup timed out");
949
    rc = CURLRESOLV_ERROR;
950
    goto clean_up;
951
  }
952
  else {
953
    /*************************************************************
954
     * Set signal handler to catch SIGALRM
955
     * Store the old value to be able to set it back later!
956
     *************************************************************/
957
#ifdef HAVE_SIGACTION
958
    sigaction(SIGALRM, NULL, &sigact);
959
    keep_sigact = sigact;
960
    keep_copysig = TRUE; /* yes, we have a copy */
961
    sigact.sa_handler = alarmfunc;
962
#ifdef SA_RESTART
963
    /* HPUX doesn't have SA_RESTART but defaults to that behavior! */
964
    sigact.sa_flags &= ~SA_RESTART;
965
#endif
966
    /* now set the new struct */
967
    sigaction(SIGALRM, &sigact, NULL);
968
#else /* HAVE_SIGACTION */
969
    /* no sigaction(), revert to the much lamer signal() */
970
#ifdef HAVE_SIGNAL
971
    keep_sigact = signal(SIGALRM, alarmfunc);
972
#endif
973
#endif /* HAVE_SIGACTION */
974
975
    /* alarm() makes a signal get sent when the timeout fires off, and that
976
       will abort system calls */
977
    prev_alarm = alarm(curlx_sltoui(timeout/1000L));
978
  }
979
980
#else
981
#ifndef CURLRES_ASYNCH
982
  if(timeoutms)
983
    infof(data, "timeout on name lookup is not supported");
984
#else
985
0
  (void)timeoutms; /* timeoutms not used with an async resolver */
986
0
#endif
987
0
#endif /* USE_ALARM_TIMEOUT */
988
989
  /* Perform the actual name resolution. This might be interrupted by an
990
   * alarm if it takes too long.
991
   */
992
0
  rc = Curl_resolv(data, hostname, port, TRUE, entry);
993
994
#ifdef USE_ALARM_TIMEOUT
995
clean_up:
996
997
  if(!prev_alarm)
998
    /* deactivate a possibly active alarm before uninstalling the handler */
999
    alarm(0);
1000
1001
#ifdef HAVE_SIGACTION
1002
  if(keep_copysig) {
1003
    /* we got a struct as it looked before, now put that one back nice
1004
       and clean */
1005
    sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
1006
  }
1007
#else
1008
#ifdef HAVE_SIGNAL
1009
  /* restore the previous SIGALRM handler */
1010
  signal(SIGALRM, keep_sigact);
1011
#endif
1012
#endif /* HAVE_SIGACTION */
1013
1014
  curl_simple_lock_unlock(&curl_jmpenv_lock);
1015
1016
  /* switch back the alarm() to either zero or to what it was before minus
1017
     the time we spent until now! */
1018
  if(prev_alarm) {
1019
    /* there was an alarm() set before us, now put it back */
1020
    timediff_t elapsed_secs = Curl_timediff(Curl_now(),
1021
                                            data->conn->created) / 1000;
1022
1023
    /* the alarm period is counted in even number of seconds */
1024
    unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
1025
1026
    if(!alarm_set ||
1027
       ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
1028
      /* if the alarm time-left reached zero or turned "negative" (counted
1029
         with unsigned values), we should fire off a SIGALRM here, but we
1030
         won't, and zero would be to switch it off so we never set it to
1031
         less than 1! */
1032
      alarm(1);
1033
      rc = CURLRESOLV_TIMEDOUT;
1034
      failf(data, "Previous alarm fired off");
1035
    }
1036
    else
1037
      alarm((unsigned int)alarm_set);
1038
  }
1039
#endif /* USE_ALARM_TIMEOUT */
1040
1041
0
  return rc;
1042
0
}
1043
1044
/*
1045
 * Curl_resolv_unlock() unlocks the given cached DNS entry. When this has been
1046
 * made, the struct may be destroyed due to pruning. It is important that only
1047
 * one unlock is made for each Curl_resolv() call.
1048
 *
1049
 * May be called with 'data' == NULL for global cache.
1050
 */
1051
void Curl_resolv_unlock(struct Curl_easy *data, struct Curl_dns_entry *dns)
1052
0
{
1053
0
  if(data && data->share)
1054
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1055
1056
0
  freednsentry(dns);
1057
1058
0
  if(data && data->share)
1059
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1060
0
}
1061
1062
/*
1063
 * File-internal: release cache dns entry reference, free if inuse drops to 0
1064
 */
1065
static void freednsentry(void *freethis)
1066
0
{
1067
0
  struct Curl_dns_entry *dns = (struct Curl_dns_entry *) freethis;
1068
0
  DEBUGASSERT(dns && (dns->inuse>0));
1069
1070
0
  dns->inuse--;
1071
0
  if(dns->inuse == 0) {
1072
0
    Curl_freeaddrinfo(dns->addr);
1073
0
    free(dns);
1074
0
  }
1075
0
}
1076
1077
/*
1078
 * Curl_init_dnscache() inits a new DNS cache.
1079
 */
1080
void Curl_init_dnscache(struct Curl_hash *hash, int size)
1081
0
{
1082
0
  Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
1083
0
                 freednsentry);
1084
0
}
1085
1086
/*
1087
 * Curl_hostcache_clean()
1088
 *
1089
 * This _can_ be called with 'data' == NULL but then of course no locking
1090
 * can be done!
1091
 */
1092
1093
void Curl_hostcache_clean(struct Curl_easy *data,
1094
                          struct Curl_hash *hash)
1095
0
{
1096
0
  if(data && data->share)
1097
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1098
1099
0
  Curl_hash_clean(hash);
1100
1101
0
  if(data && data->share)
1102
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1103
0
}
1104
1105
1106
CURLcode Curl_loadhostpairs(struct Curl_easy *data)
1107
0
{
1108
0
  struct curl_slist *hostp;
1109
0
  char *host_end;
1110
1111
  /* Default is no wildcard found */
1112
0
  data->state.wildcard_resolve = false;
1113
1114
0
  for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
1115
0
    char entry_id[MAX_HOSTCACHE_LEN];
1116
0
    if(!hostp->data)
1117
0
      continue;
1118
0
    if(hostp->data[0] == '-') {
1119
0
      unsigned long num = 0;
1120
0
      size_t entry_len;
1121
0
      size_t hlen = 0;
1122
0
      host_end = strchr(&hostp->data[1], ':');
1123
1124
0
      if(host_end) {
1125
0
        hlen = host_end - &hostp->data[1];
1126
0
        num = strtoul(++host_end, NULL, 10);
1127
0
        if(!hlen || (num > 0xffff))
1128
0
          host_end = NULL;
1129
0
      }
1130
0
      if(!host_end) {
1131
0
        infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'",
1132
0
              hostp->data);
1133
0
        continue;
1134
0
      }
1135
      /* Create an entry id, based upon the hostname and port */
1136
0
      entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num,
1137
0
                                      entry_id, sizeof(entry_id));
1138
0
      if(data->share)
1139
0
        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1140
1141
      /* delete entry, ignore if it didn't exist */
1142
0
      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
1143
1144
0
      if(data->share)
1145
0
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1146
0
    }
1147
0
    else {
1148
0
      struct Curl_dns_entry *dns;
1149
0
      struct Curl_addrinfo *head = NULL, *tail = NULL;
1150
0
      size_t entry_len;
1151
0
      char address[64];
1152
0
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1153
0
      char *addresses = NULL;
1154
0
#endif
1155
0
      char *addr_begin;
1156
0
      char *addr_end;
1157
0
      char *port_ptr;
1158
0
      int port = 0;
1159
0
      char *end_ptr;
1160
0
      bool permanent = TRUE;
1161
0
      unsigned long tmp_port;
1162
0
      bool error = true;
1163
0
      char *host_begin = hostp->data;
1164
0
      size_t hlen = 0;
1165
1166
0
      if(host_begin[0] == '+') {
1167
0
        host_begin++;
1168
0
        permanent = FALSE;
1169
0
      }
1170
0
      host_end = strchr(host_begin, ':');
1171
0
      if(!host_end)
1172
0
        goto err;
1173
0
      hlen = host_end - host_begin;
1174
1175
0
      port_ptr = host_end + 1;
1176
0
      tmp_port = strtoul(port_ptr, &end_ptr, 10);
1177
0
      if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
1178
0
        goto err;
1179
1180
0
      port = (int)tmp_port;
1181
0
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
1182
0
      addresses = end_ptr + 1;
1183
0
#endif
1184
1185
0
      while(*end_ptr) {
1186
0
        size_t alen;
1187
0
        struct Curl_addrinfo *ai;
1188
1189
0
        addr_begin = end_ptr + 1;
1190
0
        addr_end = strchr(addr_begin, ',');
1191
0
        if(!addr_end)
1192
0
          addr_end = addr_begin + strlen(addr_begin);
1193
0
        end_ptr = addr_end;
1194
1195
        /* allow IP(v6) address within [brackets] */
1196
0
        if(*addr_begin == '[') {
1197
0
          if(addr_end == addr_begin || *(addr_end - 1) != ']')
1198
0
            goto err;
1199
0
          ++addr_begin;
1200
0
          --addr_end;
1201
0
        }
1202
1203
0
        alen = addr_end - addr_begin;
1204
0
        if(!alen)
1205
0
          continue;
1206
1207
0
        if(alen >= sizeof(address))
1208
0
          goto err;
1209
1210
0
        memcpy(address, addr_begin, alen);
1211
0
        address[alen] = '\0';
1212
1213
#ifndef ENABLE_IPV6
1214
        if(strchr(address, ':')) {
1215
          infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
1216
                address);
1217
          continue;
1218
        }
1219
#endif
1220
1221
0
        ai = Curl_str2addr(address, port);
1222
0
        if(!ai) {
1223
0
          infof(data, "Resolve address '%s' found illegal", address);
1224
0
          goto err;
1225
0
        }
1226
1227
0
        if(tail) {
1228
0
          tail->ai_next = ai;
1229
0
          tail = tail->ai_next;
1230
0
        }
1231
0
        else {
1232
0
          head = tail = ai;
1233
0
        }
1234
0
      }
1235
1236
0
      if(!head)
1237
0
        goto err;
1238
1239
0
      error = false;
1240
0
err:
1241
0
      if(error) {
1242
0
        failf(data, "Couldn't parse CURLOPT_RESOLVE entry '%s'",
1243
0
              hostp->data);
1244
0
        Curl_freeaddrinfo(head);
1245
0
        return CURLE_SETOPT_OPTION_SYNTAX;
1246
0
      }
1247
1248
      /* Create an entry id, based upon the hostname and port */
1249
0
      entry_len = create_hostcache_id(host_begin, hlen, port,
1250
0
                                      entry_id, sizeof(entry_id));
1251
1252
0
      if(data->share)
1253
0
        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
1254
1255
      /* See if it's already in our dns cache */
1256
0
      dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
1257
1258
0
      if(dns) {
1259
0
        infof(data, "RESOLVE %.*s:%d is - old addresses discarded",
1260
0
              (int)hlen, host_begin, port);
1261
        /* delete old entry, there are two reasons for this
1262
         1. old entry may have different addresses.
1263
         2. even if entry with correct addresses is already in the cache,
1264
            but if it is close to expire, then by the time next http
1265
            request is made, it can get expired and pruned because old
1266
            entry is not necessarily marked as permanent.
1267
         3. when adding a non-permanent entry, we want it to remove and
1268
            replace an existing permanent entry.
1269
         4. when adding a non-permanent entry, we want it to get a "fresh"
1270
            timeout that starts _now_. */
1271
1272
0
        Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
1273
0
      }
1274
1275
      /* put this new host in the cache */
1276
0
      dns = Curl_cache_addr(data, head, host_begin, hlen, port);
1277
0
      if(dns) {
1278
0
        if(permanent)
1279
0
          dns->timestamp = 0; /* mark as permanent */
1280
        /* release the returned reference; the cache itself will keep the
1281
         * entry alive: */
1282
0
        dns->inuse--;
1283
0
      }
1284
1285
0
      if(data->share)
1286
0
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
1287
1288
0
      if(!dns) {
1289
0
        Curl_freeaddrinfo(head);
1290
0
        return CURLE_OUT_OF_MEMORY;
1291
0
      }
1292
0
      infof(data, "Added %.*s:%d:%s to DNS cache%s",
1293
0
            (int)hlen, host_begin, port, addresses,
1294
0
            permanent ? "" : " (non-permanent)");
1295
1296
      /* Wildcard hostname */
1297
0
      if((hlen == 1) && (host_begin[0] == '*')) {
1298
0
        infof(data, "RESOLVE *:%d using wildcard", port);
1299
0
        data->state.wildcard_resolve = true;
1300
0
      }
1301
0
    }
1302
0
  }
1303
0
  data->state.resolve = NULL; /* dealt with now */
1304
1305
0
  return CURLE_OK;
1306
0
}
1307
1308
CURLcode Curl_resolv_check(struct Curl_easy *data,
1309
                           struct Curl_dns_entry **dns)
1310
0
{
1311
#if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
1312
  (void)data;
1313
  (void)dns;
1314
#endif
1315
0
#ifndef CURL_DISABLE_DOH
1316
0
  if(data->conn->bits.doh)
1317
0
    return Curl_doh_is_resolved(data, dns);
1318
0
#endif
1319
0
  return Curl_resolver_is_resolved(data, dns);
1320
0
}
1321
1322
int Curl_resolv_getsock(struct Curl_easy *data,
1323
                        curl_socket_t *socks)
1324
0
{
1325
0
#ifdef CURLRES_ASYNCH
1326
0
#ifndef CURL_DISABLE_DOH
1327
0
  if(data->conn->bits.doh)
1328
    /* nothing to wait for during DoH resolve, those handles have their own
1329
       sockets */
1330
0
    return GETSOCK_BLANK;
1331
0
#endif
1332
0
  return Curl_resolver_getsock(data, socks);
1333
#else
1334
  (void)data;
1335
  (void)socks;
1336
  return GETSOCK_BLANK;
1337
#endif
1338
0
}
1339
1340
/* Call this function after Curl_connect() has returned async=TRUE and
1341
   then a successful name resolve has been received.
1342
1343
   Note: this function disconnects and frees the conn data in case of
1344
   resolve failure */
1345
CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
1346
0
{
1347
0
  CURLcode result;
1348
0
  struct connectdata *conn = data->conn;
1349
1350
0
#ifdef USE_CURL_ASYNC
1351
0
  if(data->state.async.dns) {
1352
0
    conn->dns_entry = data->state.async.dns;
1353
0
    data->state.async.dns = NULL;
1354
0
  }
1355
0
#endif
1356
1357
0
  result = Curl_setup_conn(data, protocol_done);
1358
1359
0
  if(result) {
1360
0
    Curl_detach_connection(data);
1361
0
    Curl_conncache_remove_conn(data, conn, TRUE);
1362
0
    Curl_disconnect(data, conn, TRUE);
1363
0
  }
1364
0
  return result;
1365
0
}
1366
1367
/*
1368
 * Curl_resolver_error() calls failf() with the appropriate message after a
1369
 * resolve error
1370
 */
1371
1372
#ifdef USE_CURL_ASYNC
1373
CURLcode Curl_resolver_error(struct Curl_easy *data)
1374
0
{
1375
0
  const char *host_or_proxy;
1376
0
  CURLcode result;
1377
1378
0
#ifndef CURL_DISABLE_PROXY
1379
0
  struct connectdata *conn = data->conn;
1380
0
  if(conn->bits.httpproxy) {
1381
0
    host_or_proxy = "proxy";
1382
0
    result = CURLE_COULDNT_RESOLVE_PROXY;
1383
0
  }
1384
0
  else
1385
0
#endif
1386
0
  {
1387
0
    host_or_proxy = "host";
1388
0
    result = CURLE_COULDNT_RESOLVE_HOST;
1389
0
  }
1390
1391
0
  failf(data, "Could not resolve %s: %s", host_or_proxy,
1392
0
        data->state.async.hostname);
1393
1394
0
  return result;
1395
0
}
1396
#endif /* USE_CURL_ASYNC */