Coverage Report

Created: 2022-10-16 06:45

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