Coverage Report

Created: 2024-09-08 06:32

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