Coverage Report

Created: 2024-02-25 06:14

/src/PROJ/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
#include <setjmp.h>
45
#include <signal.h>
46
47
#include "urldata.h"
48
#include "sendf.h"
49
#include "hostip.h"
50
#include "hash.h"
51
#include "rand.h"
52
#include "share.h"
53
#include "url.h"
54
#include "inet_ntop.h"
55
#include "inet_pton.h"
56
#include "multiif.h"
57
#include "doh.h"
58
#include "warnless.h"
59
#include "strcase.h"
60
#include "easy_lock.h"
61
/* The last 3 #include files should be in this order */
62
#include "curl_printf.h"
63
#include "curl_memory.h"
64
#include "memdebug.h"
65
66
#if defined(CURLRES_SYNCH) &&                   \
67
  defined(HAVE_ALARM) &&                        \
68
  defined(SIGALRM) &&                           \
69
  defined(HAVE_SIGSETJMP) &&                    \
70
  defined(GLOBAL_INIT_IS_THREADSAFE)
71
/* alarm-based timeouts can only be used with all the dependencies satisfied */
72
#define USE_ALARM_TIMEOUT
73
#endif
74
75
#define MAX_HOSTCACHE_LEN (255 + 7) /* max FQDN + colon + port number + zero */
76
77
0
#define MAX_DNS_CACHE_SIZE 29999
78
79
/*
80
 * hostip.c explained
81
 * ==================
82
 *
83
 * The main COMPILE-TIME DEFINES to keep in mind when reading the host*.c
84
 * source file are these:
85
 *
86
 * CURLRES_IPV6 - this host has getaddrinfo() and family, and thus we use
87
 * that. The host may not be able to resolve IPv6, but we don't really have to
88
 * take that into account. Hosts that aren't IPv6-enabled have CURLRES_IPV4
89
 * defined.
90
 *
91
 * CURLRES_ARES - is defined if libcurl is built to use c-ares for
92
 * asynchronous name resolves. This can be Windows or *nix.
93
 *
94
 * CURLRES_THREADED - is defined if libcurl is built to run under (native)
95
 * Windows, and then the name resolve will be done in a new thread, and the
96
 * supported API will be the same as for ares-builds.
97
 *
98
 * If any of the two previous are defined, CURLRES_ASYNCH is defined too. If
99
 * libcurl is not built to use an asynchronous resolver, CURLRES_SYNCH is
100
 * defined.
101
 *
102
 * The host*.c sources files are split up like this:
103
 *
104
 * hostip.c   - method-independent resolver functions and utility functions
105
 * hostasyn.c - functions for asynchronous name resolves
106
 * hostsyn.c  - functions for synchronous name resolves
107
 * hostip4.c  - IPv4 specific functions
108
 * hostip6.c  - IPv6 specific functions
109
 *
110
 * The two asynchronous name resolver backends are implemented in:
111
 * asyn-ares.c   - functions for ares-using name resolves
112
 * asyn-thread.c - functions for threaded name resolves
113
114
 * The hostip.h is the united header file for all this. It defines the
115
 * CURLRES_* defines based on the config*.h and curl_setup.h defines.
116
 */
117
118
static void freednsentry(void *freethis);
119
120
#ifndef CURL_DISABLE_VERBOSE_STRINGS
121
static void show_resolve_info(struct Curl_easy *data,
122
                              struct Curl_dns_entry *dns);
123
#else
124
#define show_resolve_info(x,y) Curl_nop_stmt
125
#endif
126
127
/*
128
 * Curl_printable_address() stores a printable version of the 1st address
129
 * given in the 'ai' argument. The result will be stored in the buf that is
130
 * bufsize bytes big.
131
 *
132
 * If the conversion fails, the target buffer is empty.
133
 */
134
void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
135
                            size_t bufsize)
136
0
{
137
0
  DEBUGASSERT(bufsize);
138
0
  buf[0] = 0;
139
140
0
  switch(ai->ai_family) {
141
0
  case AF_INET: {
142
0
    const struct sockaddr_in *sa4 = (const void *)ai->ai_addr;
143
0
    const struct in_addr *ipaddr4 = &sa4->sin_addr;
144
0
    (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr4, buf, bufsize);
145
0
    break;
146
0
  }
147
0
#ifdef ENABLE_IPV6
148
0
  case AF_INET6: {
149
0
    const struct sockaddr_in6 *sa6 = (const void *)ai->ai_addr;
150
0
    const struct in6_addr *ipaddr6 = &sa6->sin6_addr;
151
0
    (void)Curl_inet_ntop(ai->ai_family, (const void *)ipaddr6, buf, bufsize);
152
0
    break;
153
0
  }
154
0
#endif
155
0
  default:
156
0
    break;
157
0
  }
158
0
}
159
160
/*
161
 * Create a hostcache id string for the provided host + port, to be used by
162
 * the DNS caching. Without alloc. Return length of the id string.
163
 */
164
static size_t
165
create_hostcache_id(const char *name,
166
                    size_t nlen, /* 0 or actual name length */
167
                    int port, char *ptr, size_t buflen)
168
0
{
169
0
  size_t len = nlen ? nlen : strlen(name);
170
0
  size_t olen = 0;
171
0
  DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
172
0
  if(len > (buflen - 7))
173
0
    len = buflen - 7;
174
  /* store and lower case the name */
175
0
  while(len--) {
176
0
    *ptr++ = Curl_raw_tolower(*name++);
177
0
    olen++;
178
0
  }
179
0
  olen += msnprintf(ptr, 7, ":%u", port);
180
0
  return olen;
181
0
}
182
183
struct hostcache_prune_data {
184
  time_t now;
185
  time_t oldest; /* oldest time in cache not pruned. */
186
  int cache_timeout;
187
};
188
189
/*
190
 * This function is set as a callback to be called for every entry in the DNS
191
 * cache when we want to prune old unused entries.
192
 *
193
 * Returning non-zero means remove the entry, return 0 to keep it in the
194
 * cache.
195
 */
196
static int
197
hostcache_timestamp_remove(void *datap, void *hc)
198
0
{
199
0
  struct hostcache_prune_data *prune =
200
0
    (struct hostcache_prune_data *) datap;
201
0
  struct Curl_dns_entry *c = (struct Curl_dns_entry *) hc;
202
203
0
  if(c->timestamp) {
204
    /* age in seconds */
205
0
    time_t age = prune->now - c->timestamp;
206
0
    if(age >= prune->cache_timeout)
207
0
      return TRUE;
208
0
    if(age > prune->oldest)
209
0
      prune->oldest = age;
210
0
  }
211
0
  return FALSE;
212
0
}
213
214
/*
215
 * Prune the DNS cache. This assumes that a lock has already been taken.
216
 * Returns the 'age' of the oldest still kept entry.
217
 */
218
static time_t
219
hostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
220
                time_t now)
221
0
{
222
0
  struct hostcache_prune_data user;
223
224
0
  user.cache_timeout = cache_timeout;
225
0
  user.now = now;
226
0
  user.oldest = 0;
227
228
0
  Curl_hash_clean_with_criterium(hostcache,
229
0
                                 (void *) &user,
230
0
                                 hostcache_timestamp_remove);
231
232
0
  return user.oldest;
233
0
}
234
235
/*
236
 * Library-wide function for pruning the DNS cache. This function takes and
237
 * returns the appropriate locks.
238
 */
239
void Curl_hostcache_prune(struct Curl_easy *data)
240
0
{
241
0
  time_t now;
242
  /* the timeout may be set -1 (forever) */
243
0
  int timeout = data->set.dns_cache_timeout;
244
245
0
  if(!data->dns.hostcache)
246
    /* NULL hostcache means we can't do it */
247
0
    return;
248
249
0
  if(data->share)
250
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
251
252
0
  time(&now);
253
254
0
  do {
255
    /* Remove outdated and unused entries from the hostcache */
256
0
    time_t oldest = hostcache_prune(data->dns.hostcache, timeout, now);
257
258
0
    if(oldest < INT_MAX)
259
0
      timeout = (int)oldest; /* we know it fits */
260
0
    else
261
0
      timeout = INT_MAX - 1;
262
263
    /* if the cache size is still too big, use the oldest age as new
264
       prune limit */
265
0
  } while(timeout && (data->dns.hostcache->size > MAX_DNS_CACHE_SIZE));
266
267
0
  if(data->share)
268
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
269
0
}
270
271
#ifdef USE_ALARM_TIMEOUT
272
/* Beware this is a global and unique instance. This is used to store the
273
   return address that we can jump back to from inside a signal handler. This
274
   is not thread-safe stuff. */
275
static sigjmp_buf curl_jmpenv;
276
static curl_simple_lock curl_jmpenv_lock;
277
#endif
278
279
/* lookup address, returns entry if found and not stale */
280
static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
281
                                         const char *hostname,
282
                                         int port)
283
0
{
284
0
  struct Curl_dns_entry *dns = NULL;
285
0
  char entry_id[MAX_HOSTCACHE_LEN];
286
287
  /* Create an entry id, based upon the hostname and port */
288
0
  size_t entry_len = create_hostcache_id(hostname, 0, port,
289
0
                                         entry_id, sizeof(entry_id));
290
291
  /* See if its already in our dns cache */
292
0
  dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
293
294
  /* No entry found in cache, check if we might have a wildcard entry */
295
0
  if(!dns && data->state.wildcard_resolve) {
296
0
    entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id));
297
298
    /* See if it's already in our dns cache */
299
0
    dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
300
0
  }
301
302
0
  if(dns && (data->set.dns_cache_timeout != -1)) {
303
    /* See whether the returned entry is stale. Done before we release lock */
304
0
    struct hostcache_prune_data user;
305
306
0
    time(&user.now);
307
0
    user.cache_timeout = data->set.dns_cache_timeout;
308
0
    user.oldest = 0;
309
310
0
    if(hostcache_timestamp_remove(&user, dns)) {
311
0
      infof(data, "Hostname in DNS cache was stale, zapped");
312
0
      dns = NULL; /* the memory deallocation is being handled by the hash */
313
0
      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
314
0
    }
315
0
  }
316
317
  /* See if the returned entry matches the required resolve mode */
318
0
  if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) {
319
0
    int pf = PF_INET;
320
0
    bool found = false;
321
0
    struct Curl_addrinfo *addr = dns->addr;
322
323
0
#ifdef PF_INET6
324
0
    if(data->conn->ip_version == CURL_IPRESOLVE_V6)
325
0
      pf = PF_INET6;
326
0
#endif
327
328
0
    while(addr) {
329
0
      if(addr->ai_family == pf) {
330
0
        found = true;
331
0
        break;
332
0
      }
333
0
      addr = addr->ai_next;
334
0
    }
335
336
0
    if(!found) {
337
0
      infof(data, "Hostname in DNS cache doesn't have needed family, zapped");
338
0
      dns = NULL; /* the memory deallocation is being handled by the hash */
339
0
      Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
340
0
    }
341
0
  }
342
0
  return dns;
343
0
}
344
345
/*
346
 * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
347
 *
348
 * Curl_resolv() checks initially and multi_runsingle() checks each time
349
 * it discovers the handle in the state WAITRESOLVE whether the hostname
350
 * has already been resolved and the address has already been stored in
351
 * the DNS cache. This short circuits waiting for a lot of pending
352
 * lookups for the same hostname requested by different handles.
353
 *
354
 * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
355
 *
356
 * The returned data *MUST* be "unlocked" with Curl_resolv_unlock() after
357
 * use, or we'll leak memory!
358
 */
359
struct Curl_dns_entry *
360
Curl_fetch_addr(struct Curl_easy *data,
361
                const char *hostname,
362
                int port)
363
0
{
364
0
  struct Curl_dns_entry *dns = NULL;
365
366
0
  if(data->share)
367
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
368
369
0
  dns = fetch_addr(data, hostname, port);
370
371
0
  if(dns)
372
0
    dns->inuse++; /* we use it! */
373
374
0
  if(data->share)
375
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
376
377
0
  return dns;
378
0
}
379
380
#ifndef CURL_DISABLE_SHUFFLE_DNS
381
/*
382
 * Return # of addresses in a Curl_addrinfo struct
383
 */
384
static int num_addresses(const struct Curl_addrinfo *addr)
385
0
{
386
0
  int i = 0;
387
0
  while(addr) {
388
0
    addr = addr->ai_next;
389
0
    i++;
390
0
  }
391
0
  return i;
392
0
}
393
394
UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
395
                                    struct Curl_addrinfo **addr);
396
/*
397
 * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo'
398
 * struct by re-linking its linked list.
399
 *
400
 * The addr argument should be the address of a pointer to the head node of a
401
 * `Curl_addrinfo` list and it will be modified to point to the new head after
402
 * shuffling.
403
 *
404
 * Not declared static only to make it easy to use in a unit test!
405
 *
406
 * @unittest: 1608
407
 */
408
UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
409
                                    struct Curl_addrinfo **addr)
410
0
{
411
0
  CURLcode result = CURLE_OK;
412
0
  const int num_addrs = num_addresses(*addr);
413
414
0
  if(num_addrs > 1) {
415
0
    struct Curl_addrinfo **nodes;
416
0
    infof(data, "Shuffling %i addresses", num_addrs);
417
418
0
    nodes = malloc(num_addrs*sizeof(*nodes));
419
0
    if(nodes) {
420
0
      int i;
421
0
      unsigned int *rnd;
422
0
      const size_t rnd_size = num_addrs * sizeof(*rnd);
423
424
      /* build a plain array of Curl_addrinfo pointers */
425
0
      nodes[0] = *addr;
426
0
      for(i = 1; i < num_addrs; i++) {
427
0
        nodes[i] = nodes[i-1]->ai_next;
428
0
      }
429
430
0
      rnd = malloc(rnd_size);
431
0
      if(rnd) {
432
        /* Fisher-Yates shuffle */
433
0
        if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) {
434
0
          struct Curl_addrinfo *swap_tmp;
435
0
          for(i = num_addrs - 1; i > 0; i--) {
436
0
            swap_tmp = nodes[rnd[i] % (i + 1)];
437
0
            nodes[rnd[i] % (i + 1)] = nodes[i];
438
0
            nodes[i] = swap_tmp;
439
0
          }
440
441
          /* relink list in the new order */
442
0
          for(i = 1; i < num_addrs; i++) {
443
0
            nodes[i-1]->ai_next = nodes[i];
444
0
          }
445
446
0
          nodes[num_addrs-1]->ai_next = NULL;
447
0
          *addr = nodes[0];
448
0
        }
449
0
        free(rnd);
450
0
      }
451
0
      else
452
0
        result = CURLE_OUT_OF_MEMORY;
453
0
      free(nodes);
454
0
    }
455
0
    else
456
0
      result = CURLE_OUT_OF_MEMORY;
457
0
  }
458
0
  return result;
459
0
}
460
#endif
461
462
/*
463
 * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
464
 *
465
 * When calling Curl_resolv() has resulted in a response with a returned
466
 * address, we call this function to store the information in the dns
467
 * cache etc
468
 *
469
 * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
470
 */
471
struct Curl_dns_entry *
472
Curl_cache_addr(struct Curl_easy *data,
473
                struct Curl_addrinfo *addr,
474
                const char *hostname,
475
                size_t hostlen, /* length or zero */
476
                int port)
477
0
{
478
0
  char entry_id[MAX_HOSTCACHE_LEN];
479
0
  size_t entry_len;
480
0
  struct Curl_dns_entry *dns;
481
0
  struct Curl_dns_entry *dns2;
482
483
0
#ifndef CURL_DISABLE_SHUFFLE_DNS
484
  /* shuffle addresses if requested */
485
0
  if(data->set.dns_shuffle_addresses) {
486
0
    CURLcode result = Curl_shuffle_addr(data, &addr);
487
0
    if(result)
488
0
      return NULL;
489
0
  }
490
0
#endif
491
0
  if(!hostlen)
492
0
    hostlen = strlen(hostname);
493
494
  /* Create a new cache entry */
495
0
  dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
496
0
  if(!dns) {
497
0
    return NULL;
498
0
  }
499
500
  /* Create an entry id, based upon the hostname and port */
501
0
  entry_len = create_hostcache_id(hostname, hostlen, port,
502
0
                                  entry_id, sizeof(entry_id));
503
504
0
  dns->inuse = 1;   /* the cache has the first reference */
505
0
  dns->addr = addr; /* this is the address(es) */
506
0
  time(&dns->timestamp);
507
0
  if(dns->timestamp == 0)
508
0
    dns->timestamp = 1;   /* zero indicates permanent CURLOPT_RESOLVE entry */
509
0
  dns->hostport = port;
510
0
  if(hostlen)
511
0
    memcpy(dns->hostname, hostname, hostlen);
512
513
  /* Store the resolved data in our DNS cache. */
514
0
  dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
515
0
                       (void *)dns);
516
0
  if(!dns2) {
517
0
    free(dns);
518
0
    return NULL;
519
0
  }
520
521
0
  dns = dns2;
522
0
  dns->inuse++;         /* mark entry as in-use */
523
0
  return dns;
524
0
}
525
526
#ifdef ENABLE_IPV6
527
/* return a static IPv6 ::1 for the name */
528
static struct Curl_addrinfo *get_localhost6(int port, const char *name)
529
0
{
530
0
  struct Curl_addrinfo *ca;
531
0
  const size_t ss_size = sizeof(struct sockaddr_in6);
532
0
  const size_t hostlen = strlen(name);
533
0
  struct sockaddr_in6 sa6;
534
0
  unsigned char ipv6[16];
535
0
  unsigned short port16 = (unsigned short)(port & 0xffff);
536
0
  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
537
0
  if(!ca)
538
0
    return NULL;
539
540
0
  sa6.sin6_family = AF_INET6;
541
0
  sa6.sin6_port = htons(port16);
542
0
  sa6.sin6_flowinfo = 0;
543
0
  sa6.sin6_scope_id = 0;
544
0
  if(Curl_inet_pton(AF_INET6, "::1", ipv6) < 1)
545
0
    return NULL;
546
0
  memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
547
548
0
  ca->ai_flags     = 0;
549
0
  ca->ai_family    = AF_INET6;
550
0
  ca->ai_socktype  = SOCK_STREAM;
551
0
  ca->ai_protocol  = IPPROTO_TCP;
552
0
  ca->ai_addrlen   = (curl_socklen_t)ss_size;
553
0
  ca->ai_next      = NULL;
554
0
  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
555
0
  memcpy(ca->ai_addr, &sa6, ss_size);
556
0
  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
557
0
  strcpy(ca->ai_canonname, name);
558
0
  return ca;
559
0
}
560
#else
561
#define get_localhost6(x,y) NULL
562
#endif
563
564
/* return a static IPv4 127.0.0.1 for the given name */
565
static struct Curl_addrinfo *get_localhost(int port, const char *name)
566
0
{
567
0
  struct Curl_addrinfo *ca;
568
0
  struct Curl_addrinfo *ca6;
569
0
  const size_t ss_size = sizeof(struct sockaddr_in);
570
0
  const size_t hostlen = strlen(name);
571
0
  struct sockaddr_in sa;
572
0
  unsigned int ipv4;
573
0
  unsigned short port16 = (unsigned short)(port & 0xffff);
574
575
  /* memset to clear the sa.sin_zero field */
576
0
  memset(&sa, 0, sizeof(sa));
577
0
  sa.sin_family = AF_INET;
578
0
  sa.sin_port = htons(port16);
579
0
  if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
580
0
    return NULL;
581
0
  memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
582
583
0
  ca = calloc(1, sizeof(struct Curl_addrinfo) + ss_size + hostlen + 1);
584
0
  if(!ca)
585
0
    return NULL;
586
0
  ca->ai_flags     = 0;
587
0
  ca->ai_family    = AF_INET;
588
0
  ca->ai_socktype  = SOCK_STREAM;
589
0
  ca->ai_protocol  = IPPROTO_TCP;
590
0
  ca->ai_addrlen   = (curl_socklen_t)ss_size;
591
0
  ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
592
0
  memcpy(ca->ai_addr, &sa, ss_size);
593
0
  ca->ai_canonname = (char *)ca->ai_addr + ss_size;
594
0
  strcpy(ca->ai_canonname, name);
595
596
0
  ca6 = get_localhost6(port, name);
597
0
  if(!ca6)
598
0
    return ca;
599
0
  ca6->ai_next = ca;
600
0
  return ca6;
601
0
}
602
603
#ifdef ENABLE_IPV6
604
/*
605
 * Curl_ipv6works() returns TRUE if IPv6 seems to work.
606
 */
607
bool Curl_ipv6works(struct Curl_easy *data)
608
0
{
609
0
  if(data) {
610
    /* the nature of most system is that IPv6 status doesn't come and go
611
       during a program's lifetime so we only probe the first time and then we
612
       have the info kept for fast reuse */
613
0
    DEBUGASSERT(data);
614
0
    DEBUGASSERT(data->multi);
615
0
    if(data->multi->ipv6_up == IPV6_UNKNOWN) {
616
0
      bool works = Curl_ipv6works(NULL);
617
0
      data->multi->ipv6_up = works ? IPV6_WORKS : IPV6_DEAD;
618
0
    }
619
0
    return data->multi->ipv6_up == IPV6_WORKS;
620
0
  }
621
0
  else {
622
0
    int ipv6_works = -1;
623
    /* probe to see if we have a working IPv6 stack */
624
0
    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
625
0
    if(s == CURL_SOCKET_BAD)
626
      /* an IPv6 address was requested but we can't get/use one */
627
0
      ipv6_works = 0;
628
0
    else {
629
0
      ipv6_works = 1;
630
0
      sclose(s);
631
0
    }
632
0
    return (ipv6_works>0)?TRUE:FALSE;
633
0
  }
634
0
}
635
#endif /* ENABLE_IPV6 */
636
637
/*
638
 * Curl_host_is_ipnum() returns TRUE if the given string is a numerical IPv4
639
 * (or IPv6 if supported) address.
640
 */
641
bool Curl_host_is_ipnum(const char *hostname)
642
0
{
643
0
  struct in_addr in;
644
0
#ifdef ENABLE_IPV6
645
0
  struct in6_addr in6;
646
0
#endif
647
0
  if(Curl_inet_pton(AF_INET, hostname, &in) > 0
648
0
#ifdef ENABLE_IPV6
649
0
     || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
650
0
#endif
651
0
    )
652
0
    return TRUE;
653
0
  return FALSE;
654
0
}
655
656
657
/* return TRUE if 'part' is a case insensitive tail of 'full' */
658
static bool tailmatch(const char *full, const char *part)
659
0
{
660
0
  size_t plen = strlen(part);
661
0
  size_t flen = strlen(full);
662
0
  if(plen > flen)
663
0
    return FALSE;
664
0
  return strncasecompare(part, &full[flen - plen], plen);
665
0
}
666
667
/*
668
 * Curl_resolv() is the main name resolve function within libcurl. It resolves
669
 * a name and returns a pointer to the entry in the 'entry' argument (if one
670
 * is provided). This function might return immediately if we're using asynch
671
 * resolves. See the return codes.
672
 *
673
 * The cache entry we return will get its 'inuse' counter increased when this
674
 * function is used. You MUST call Curl_resolv_unlock() later (when you're
675
 * done using this struct) to decrease the counter again.
676
 *
677
 * Return codes:
678
 *
679
 * CURLRESOLV_ERROR   (-1) = error, no pointer
680
 * CURLRESOLV_RESOLVED (0) = OK, pointer provided
681
 * CURLRESOLV_PENDING  (1) = waiting for response, no pointer
682
 */
683
684
enum resolve_t Curl_resolv(struct Curl_easy *data,
685
                           const char *hostname,
686
                           int port,
687
                           bool allowDOH,
688
                           struct Curl_dns_entry **entry)
689
0
{
690
0
  struct Curl_dns_entry *dns = NULL;
691
0
  CURLcode result;
692
0
  enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
693
0
  struct connectdata *conn = data->conn;
694
  /* We should intentionally error and not resolve .onion TLDs */
695
0
  size_t hostname_len = strlen(hostname);
696
0
  if(hostname_len >= 7 &&
697
0
     (curl_strequal(&hostname[hostname_len - 6], ".onion") ||
698
0
      curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
699
0
    failf(data, "Not resolving .onion address (RFC 7686)");
700
0
    return CURLRESOLV_ERROR;
701
0
  }
702
0
  *entry = NULL;
703
0
#ifndef CURL_DISABLE_DOH
704
0
  conn->bits.doh = FALSE; /* default is not */
705
#else
706
  (void)allowDOH;
707
#endif
708
709
0
  if(data->share)
710
0
    Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
711
712
0
  dns = fetch_addr(data, hostname, port);
713
714
0
  if(dns) {
715
0
    infof(data, "Hostname %s was found in DNS cache", hostname);
716
0
    dns->inuse++; /* we use it! */
717
0
    rc = CURLRESOLV_RESOLVED;
718
0
  }
719
720
0
  if(data->share)
721
0
    Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
722
723
0
  if(!dns) {
724
    /* The entry was not in the cache. Resolve it to IP address */
725
726
0
    struct Curl_addrinfo *addr = NULL;
727
0
    int respwait = 0;
728
0
#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS)
729
0
    struct in_addr in;
730
0
#endif
731
0
#ifndef CURL_DISABLE_DOH
732
0
#ifndef USE_RESOLVE_ON_IPS
733
0
    const
734
0
#endif
735
0
      bool ipnum = FALSE;
736
0
#endif
737
738
    /* notify the resolver start callback */
739
0
    if(data->set.resolver_start) {
740
0
      int st;
741
0
      Curl_set_in_callback(data, true);
742
0
      st = data->set.resolver_start(
743
0
#ifdef USE_CURL_ASYNC
744
0
        data->state.async.resolver,
745
#else
746
        NULL,
747
#endif
748
0
        NULL,
749
0
        data->set.resolver_start_client);
750
0
      Curl_set_in_callback(data, false);
751
0
      if(st)
752
0
        return CURLRESOLV_ERROR;
753
0
    }
754
755
0
#ifndef USE_RESOLVE_ON_IPS
756
    /* First check if this is an IPv4 address string */
757
0
    if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
758
      /* This is a dotted IP address 123.123.123.123-style */
759
0
      addr = Curl_ip2addr(AF_INET, &in, hostname, port);
760
0
      if(!addr)
761
0
        return CURLRESOLV_ERROR;
762
0
    }
763
0
#ifdef ENABLE_IPV6
764
0
    else {
765
0
      struct in6_addr in6;
766
      /* check if this is an IPv6 address string */
767
0
      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
768
        /* This is an IPv6 address literal */
769
0
        addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
770
0
        if(!addr)
771
0
          return CURLRESOLV_ERROR;
772
0
      }
773
0
    }
774
0
#endif /* ENABLE_IPV6 */
775
776
#else /* if USE_RESOLVE_ON_IPS */
777
#ifndef CURL_DISABLE_DOH
778
    /* First check if this is an IPv4 address string */
779
    if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
780
      /* This is a dotted IP address 123.123.123.123-style */
781
      ipnum = TRUE;
782
#ifdef ENABLE_IPV6
783
    else {
784
      struct in6_addr in6;
785
      /* check if this is an IPv6 address string */
786
      if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
787
        /* This is an IPv6 address literal */
788
        ipnum = TRUE;
789
    }
790
#endif /* ENABLE_IPV6 */
791
#endif /* CURL_DISABLE_DOH */
792
793
#endif /* !USE_RESOLVE_ON_IPS */
794
795
0
    if(!addr) {
796
0
      if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
797
0
        return CURLRESOLV_ERROR;
798
799
0
      if(strcasecompare(hostname, "localhost") ||
800
0
         tailmatch(hostname, ".localhost"))
801
0
        addr = get_localhost(port, hostname);
802
0
#ifndef CURL_DISABLE_DOH
803
0
      else if(allowDOH && data->set.doh && !ipnum)
804
0
        addr = Curl_doh(data, hostname, port, &respwait);
805
0
#endif
806
0
      else {
807
        /* Check what IP specifics the app has requested and if we can provide
808
         * it. If not, bail out. */
809
0
        if(!Curl_ipvalid(data, conn))
810
0
          return CURLRESOLV_ERROR;
811
        /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
812
           non-zero value indicating that we need to wait for the response to
813
           the resolve call */
814
0
        addr = Curl_getaddrinfo(data, hostname, port, &respwait);
815
0
      }
816
0
    }
817
0
    if(!addr) {
818
0
      if(respwait) {
819
        /* the response to our resolve call will come asynchronously at
820
           a later time, good or bad */
821
        /* First, check that we haven't received the info by now */
822
0
        result = Curl_resolv_check(data, &dns);
823
0
        if(result) /* error detected */
824
0
          return CURLRESOLV_ERROR;
825
0
        if(dns)
826
0
          rc = CURLRESOLV_RESOLVED; /* pointer provided */
827
0
        else
828
0
          rc = CURLRESOLV_PENDING; /* no info yet */
829
0
      }
830
0
    }
831
0
    else {
832
0
      if(data->share)
833
0
        Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
834
835
      /* we got a response, store it in the cache */
836
0
      dns = Curl_cache_addr(data, addr, hostname, 0, port);
837
838
0
      if(data->share)
839
0
        Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
840
841
0
      if(!dns)
842
        /* returned failure, bail out nicely */
843
0
        Curl_freeaddrinfo(addr);
844
0
      else {
845
0
        rc = CURLRESOLV_RESOLVED;
846
0
        show_resolve_info(data, dns);
847
0
      }
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
CURL_NORETURN 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 - 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
#ifndef CURL_DISABLE_VERBOSE_STRINGS
1293
0
      infof(data, "Added %.*s:%d:%s to DNS cache%s",
1294
0
            (int)hlen, host_begin, port, addresses,
1295
0
            permanent ? "" : " (non-permanent)");
1296
0
#endif
1297
1298
      /* Wildcard hostname */
1299
0
      if((hlen == 1) && (host_begin[0] == '*')) {
1300
0
        infof(data, "RESOLVE *:%d using wildcard", port);
1301
0
        data->state.wildcard_resolve = true;
1302
0
      }
1303
0
    }
1304
0
  }
1305
0
  data->state.resolve = NULL; /* dealt with now */
1306
1307
0
  return CURLE_OK;
1308
0
}
1309
1310
#ifndef CURL_DISABLE_VERBOSE_STRINGS
1311
static void show_resolve_info(struct Curl_easy *data,
1312
                              struct Curl_dns_entry *dns)
1313
0
{
1314
0
  struct Curl_addrinfo *a;
1315
0
  CURLcode result = CURLE_OK;
1316
0
#ifdef CURLRES_IPV6
1317
0
  struct dynbuf out[2];
1318
#else
1319
  struct dynbuf out[1];
1320
#endif
1321
0
  DEBUGASSERT(data);
1322
0
  DEBUGASSERT(dns);
1323
1324
0
  if(!data->set.verbose ||
1325
     /* ignore no name or numerical IP addresses */
1326
0
     !dns->hostname[0] || Curl_host_is_ipnum(dns->hostname))
1327
0
    return;
1328
1329
0
  a = dns->addr;
1330
1331
0
  infof(data, "Host %s:%d was resolved.",
1332
0
        (dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport);
1333
1334
0
  Curl_dyn_init(&out[0], 1024);
1335
0
#ifdef CURLRES_IPV6
1336
0
  Curl_dyn_init(&out[1], 1024);
1337
0
#endif
1338
1339
0
  while(a) {
1340
0
    if(
1341
0
#ifdef CURLRES_IPV6
1342
0
       a->ai_family == PF_INET6 ||
1343
0
#endif
1344
0
       a->ai_family == PF_INET) {
1345
0
      char buf[MAX_IPADR_LEN];
1346
0
      struct dynbuf *d = &out[(a->ai_family != PF_INET)];
1347
0
      Curl_printable_address(a, buf, sizeof(buf));
1348
0
      if(Curl_dyn_len(d))
1349
0
        result = Curl_dyn_addn(d, ", ", 2);
1350
0
      if(!result)
1351
0
        result = Curl_dyn_add(d, buf);
1352
0
      if(result) {
1353
0
        infof(data, "too many IP, can't show");
1354
0
        goto fail;
1355
0
      }
1356
0
    }
1357
0
    a = a->ai_next;
1358
0
  }
1359
1360
0
#ifdef CURLRES_IPV6
1361
0
  infof(data, "IPv6: %s",
1362
0
        (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)"));
1363
0
#endif
1364
0
  infof(data, "IPv4: %s",
1365
0
        (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)"));
1366
1367
0
fail:
1368
0
  Curl_dyn_free(&out[0]);
1369
0
#ifdef CURLRES_IPV6
1370
0
  Curl_dyn_free(&out[1]);
1371
0
#endif
1372
0
}
1373
#endif
1374
1375
CURLcode Curl_resolv_check(struct Curl_easy *data,
1376
                           struct Curl_dns_entry **dns)
1377
0
{
1378
0
  CURLcode result;
1379
#if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
1380
  (void)data;
1381
  (void)dns;
1382
#endif
1383
0
#ifndef CURL_DISABLE_DOH
1384
0
  if(data->conn->bits.doh) {
1385
0
    result = Curl_doh_is_resolved(data, dns);
1386
0
  }
1387
0
  else
1388
0
#endif
1389
0
  result = Curl_resolver_is_resolved(data, dns);
1390
0
  if(*dns)
1391
0
    show_resolve_info(data, *dns);
1392
0
  return result;
1393
0
}
1394
1395
int Curl_resolv_getsock(struct Curl_easy *data,
1396
                        curl_socket_t *socks)
1397
0
{
1398
0
#ifdef CURLRES_ASYNCH
1399
0
#ifndef CURL_DISABLE_DOH
1400
0
  if(data->conn->bits.doh)
1401
    /* nothing to wait for during DoH resolve, those handles have their own
1402
       sockets */
1403
0
    return GETSOCK_BLANK;
1404
0
#endif
1405
0
  return Curl_resolver_getsock(data, socks);
1406
#else
1407
  (void)data;
1408
  (void)socks;
1409
  return GETSOCK_BLANK;
1410
#endif
1411
0
}
1412
1413
/* Call this function after Curl_connect() has returned async=TRUE and
1414
   then a successful name resolve has been received.
1415
1416
   Note: this function disconnects and frees the conn data in case of
1417
   resolve failure */
1418
CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
1419
0
{
1420
0
  CURLcode result;
1421
0
  struct connectdata *conn = data->conn;
1422
1423
0
#ifdef USE_CURL_ASYNC
1424
0
  if(data->state.async.dns) {
1425
0
    conn->dns_entry = data->state.async.dns;
1426
0
    data->state.async.dns = NULL;
1427
0
  }
1428
0
#endif
1429
1430
0
  result = Curl_setup_conn(data, protocol_done);
1431
1432
0
  if(result) {
1433
0
    Curl_detach_connection(data);
1434
0
    Curl_conncache_remove_conn(data, conn, TRUE);
1435
0
    Curl_disconnect(data, conn, TRUE);
1436
0
  }
1437
0
  return result;
1438
0
}
1439
1440
/*
1441
 * Curl_resolver_error() calls failf() with the appropriate message after a
1442
 * resolve error
1443
 */
1444
1445
#ifdef USE_CURL_ASYNC
1446
CURLcode Curl_resolver_error(struct Curl_easy *data)
1447
0
{
1448
0
  const char *host_or_proxy;
1449
0
  CURLcode result;
1450
1451
0
#ifndef CURL_DISABLE_PROXY
1452
0
  struct connectdata *conn = data->conn;
1453
0
  if(conn->bits.httpproxy) {
1454
0
    host_or_proxy = "proxy";
1455
0
    result = CURLE_COULDNT_RESOLVE_PROXY;
1456
0
  }
1457
0
  else
1458
0
#endif
1459
0
  {
1460
0
    host_or_proxy = "host";
1461
0
    result = CURLE_COULDNT_RESOLVE_HOST;
1462
0
  }
1463
1464
0
  failf(data, "Could not resolve %s: %s", host_or_proxy,
1465
0
        data->state.async.hostname);
1466
1467
0
  return result;
1468
0
}
1469
#endif /* USE_CURL_ASYNC */