Coverage Report

Created: 2026-10-22 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/peer.c
Line
Count
Source
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
 * IDN conversions
26
 */
27
#include "curl_setup.h"
28
29
#ifdef HAVE_NETINET_IN_H
30
#include <netinet/in.h>
31
#endif
32
#ifdef HAVE_NETDB_H
33
#include <netdb.h>
34
#endif
35
#ifdef HAVE_ARPA_INET_H
36
#include <arpa/inet.h>
37
#endif
38
#ifdef HAVE_NET_IF_H
39
#include <net/if.h>
40
#endif
41
#ifdef HAVE_IPHLPAPI_H
42
#include <Iphlpapi.h>
43
#endif
44
#ifdef HAVE_SYS_IOCTL_H
45
#include <sys/ioctl.h>
46
#endif
47
#ifdef HAVE_SYS_PARAM_H
48
#include <sys/param.h>
49
#endif
50
51
#ifdef __VMS
52
#include <in.h>
53
#include <inet.h>
54
#endif
55
56
#ifdef HAVE_SYS_UN_H
57
#include <sys/un.h>
58
#endif
59
60
#if defined(HAVE_IF_NAMETOINDEX) && defined(USE_WINSOCK)
61
#if defined(__MINGW32__) && (__MINGW64_VERSION_MAJOR <= 5)
62
#include <wincrypt.h>  /* workaround for old mingw-w64 missing to include it */
63
#endif
64
#include <iphlpapi.h>
65
#endif
66
67
#include "curl_addrinfo.h"
68
#include "curl_trc.h"
69
#include "protocol.h"
70
#include "http_proxy.h"
71
#include "idn.h"
72
#include "curlx/strdup.h"
73
#include "curlx/strparse.h"
74
#include "peer.h"
75
#include "urldata.h"
76
#include "url.h"
77
#include "urlapi-int.h"
78
#include "vtls/vtls.h"
79
80
struct peer_parse {
81
  const struct Curl_scheme *scheme;
82
  struct Curl_str host_user;
83
  struct Curl_str host;
84
  struct Curl_str zoneid;
85
  char *tmp_host_user;
86
  char *tmp_host;
87
  char *tmp_zoneid;
88
  uint32_t scopeid;
89
  uint16_t port;
90
  bool ipv6;
91
  bool unix_socket;
92
  bool abstract_uds;
93
};
94
95
static void peer_parse_clear(struct peer_parse *pp)
96
67.3k
{
97
67.3k
  curlx_free(pp->tmp_host_user);
98
67.3k
  curlx_free(pp->tmp_host);
99
67.3k
  curlx_free(pp->tmp_zoneid);
100
67.3k
  memset(pp, 0, sizeof(*pp));
101
67.3k
}
102
103
static CURLcode peer_create(struct peer_parse *pp,
104
                            struct Curl_peer **ppeer)
105
67.0k
{
106
67.0k
  struct Curl_peer *peer = NULL;
107
67.0k
  CURLcode result = CURLE_OK;
108
67.0k
  size_t zone_alen = 0, host_alen = 0;
109
110
67.0k
  if(!pp || !pp->scheme)
111
0
    return CURLE_FAILED_INIT;
112
67.0k
  if(!pp->host.len && !(pp->scheme->flags & PROTOPT_NONETWORK))
113
6
    return CURLE_FAILED_INIT;
114
115
66.9k
  if((pp->host.str != pp->host_user.str) ||
116
65.9k
     (pp->host.len != pp->host_user.len)) {
117
1.03k
    host_alen = pp->host.len + 1;
118
1.03k
  }
119
66.9k
  zone_alen = pp->zoneid.len ? (pp->zoneid.len + 1) : 0;
120
121
  /* null-terminator already part of struct */
122
66.9k
  peer = curlx_calloc(1, sizeof(*peer) +
123
66.9k
                         pp->host_user.len + host_alen + zone_alen);
124
66.9k
  if(!peer) {
125
0
    result = CURLE_OUT_OF_MEMORY;
126
0
    goto out;
127
0
  }
128
129
66.9k
  peer->refcount = 1;
130
66.9k
  peer->scheme = pp->scheme;
131
66.9k
  peer->hostname = peer->user_hostname;
132
66.9k
  peer->port = pp->port;
133
66.9k
  peer->scopeid = pp->scopeid;
134
66.9k
  peer->ipv6 = pp->ipv6;
135
66.9k
  peer->unix_socket = pp->unix_socket;
136
66.9k
  peer->abstract_uds = pp->abstract_uds;
137
138
66.9k
  if(pp->host_user.len)
139
66.7k
    memcpy(peer->user_hostname, pp->host_user.str, pp->host_user.len);
140
141
66.9k
  if(host_alen) {
142
1.03k
    peer->hostname = peer->user_hostname + pp->host_user.len + 1;
143
1.03k
    memcpy(peer->hostname, pp->host.str, pp->host.len);
144
1.03k
  }
145
146
66.9k
  if(zone_alen) {
147
266
    peer->zoneid = peer->user_hostname + pp->host_user.len + 1 + host_alen;
148
266
    memcpy(peer->zoneid, pp->zoneid.str, pp->zoneid.len);
149
266
#ifdef USE_IPV6
150
    /* Determine scope_id if not already provided */
151
266
    if(!peer->scopeid) {
152
266
      const char *p = peer->zoneid;
153
266
      curl_off_t scope;
154
266
      if(!curlx_str_number(&p, &scope, UINT_MAX) && !*p) {
155
        /* A plain number, use it directly as a scope id. */
156
67
        peer->scopeid = (uint32_t)scope;
157
67
      }
158
199
#ifdef HAVE_IF_NAMETOINDEX
159
199
      else {
160
        /* Zone identifier is not fully numeric */
161
199
        unsigned int idx = 0;
162
199
        idx = if_nametoindex(peer->zoneid);
163
199
        if(idx) {
164
10
          peer->scopeid = (uint32_t)idx;
165
10
        }
166
189
        else {
167
          /* Do we want to return an error here? */
168
189
        }
169
199
      }
170
266
#endif /* HAVE_IF_NAMETOINDEX */
171
266
    }
172
266
#endif /* USE_IPV6 */
173
266
  }
174
175
66.9k
out:
176
66.9k
  if(!result)
177
66.9k
    *ppeer = peer;
178
0
  else
179
0
    Curl_peer_unlink(&peer);
180
66.9k
  return result;
181
66.9k
}
182
183
static CURLcode peer_parse_host(struct Curl_easy *data,
184
                                struct peer_parse *pp,
185
                                bool scan_for_ipv6)
186
65.7k
{
187
65.7k
  if(!pp || !pp->host_user.str || !pp->host_user.len)
188
0
    return CURLE_FAILED_INIT;
189
190
65.7k
  if(pp->host_user.str[0] == '[') {
191
1.00k
    const char *s = pp->host_user.str + 1;
192
1.00k
    struct Curl_str tmp;
193
1.00k
    if(curlx_str_until(&s, &tmp, pp->host_user.len - 1, ']'))
194
1
      return CURLE_URL_MALFORMAT;
195
196
1.00k
    if(!Curl_looks_like_ipv6(tmp.str, tmp.len, TRUE,
197
1.00k
                             &pp->host, &pp->zoneid)) {
198
1
      failf(data, "Invalid IPv6 address format in '%.*s'",
199
1
            (int)pp->host_user.len, pp->host_user.str);
200
1
      return CURLE_URL_MALFORMAT;
201
1
    }
202
1.00k
    pp->ipv6 = TRUE;
203
1.00k
  }
204
64.7k
  else {
205
64.7k
#ifdef USE_IDN
206
64.7k
    if(!Curl_is_ASCII_str(&pp->host_user)) {
207
97
      CURLcode result;
208
97
      if(!pp->tmp_host_user) {
209
        /* need a null-terminated string for IDN */
210
97
        pp->tmp_host_user = curlx_memdup0(pp->host_user.str,
211
97
                                          pp->host_user.len);
212
97
        if(!pp->tmp_host_user)
213
0
          return CURLE_OUT_OF_MEMORY;
214
97
      }
215
97
      result = Curl_idn_decode(pp->tmp_host_user, &pp->tmp_host);
216
97
      if(result)
217
97
        return result;
218
0
      pp->host.str = pp->tmp_host;
219
0
      pp->host.len = strlen(pp->host.str);
220
0
    }
221
64.6k
    else
222
64.6k
#endif
223
64.6k
    if(scan_for_ipv6 &&
224
1.73k
       Curl_looks_like_ipv6(pp->host_user.str, pp->host_user.len, TRUE,
225
1.73k
                            &pp->host, &pp->zoneid)) {
226
1.41k
      if(pp->host_user.len < MAX_IPADR_LEN) {
227
1.39k
        char tmp[MAX_IPADR_LEN];
228
1.39k
        memcpy(tmp, pp->host_user.str, pp->host_user.len);
229
1.39k
        tmp[pp->host_user.len] = 0;
230
1.39k
        pp->ipv6 = !Curl_is_ipv4addr(tmp);
231
1.39k
      }
232
12
      else
233
12
        pp->ipv6 = TRUE;
234
1.41k
    }
235
63.2k
    else
236
63.2k
      pp->host = pp->host_user;
237
64.7k
  }
238
65.6k
  return CURLE_OK;
239
65.7k
}
240
241
CURLcode Curl_peer_create(struct Curl_easy *data,
242
                          const struct Curl_scheme *scheme,
243
                          const char *hostname,
244
                          uint16_t port,
245
                          struct Curl_peer **ppeer)
246
1.74k
{
247
1.74k
  struct peer_parse pp;
248
1.74k
  CURLcode result;
249
250
1.74k
  Curl_peer_unlink(ppeer);
251
1.74k
  memset(&pp, 0, sizeof(pp));
252
1.74k
  pp.scheme = scheme;
253
1.74k
  pp.host_user.str = hostname;
254
1.74k
  pp.host_user.len = strlen(hostname);
255
1.74k
  pp.port = port;
256
257
1.74k
  result = peer_parse_host(data, &pp, TRUE);
258
1.74k
  if(!result)
259
1.73k
    result = peer_create(&pp, ppeer);
260
261
1.74k
  peer_parse_clear(&pp);
262
1.74k
  return result;
263
1.74k
}
264
265
#ifdef USE_UNIX_SOCKETS
266
CURLcode Curl_peer_uds_create(const struct Curl_scheme *scheme,
267
                              const char *path,
268
                              bool abstract_unix_socket,
269
                              struct Curl_peer **ppeer)
270
1.08k
{
271
1.08k
  struct peer_parse pp;
272
1.08k
  size_t pathlen = path ? strlen(path) : 0;
273
1.08k
  CURLcode result = CURLE_OK;
274
275
1.08k
  Curl_peer_unlink(ppeer);
276
1.08k
  memset(&pp, 0, sizeof(pp));
277
1.08k
  if(!scheme)
278
0
    return CURLE_FAILED_INIT;
279
1.08k
  if(!pathlen)
280
6
    return CURLE_FAILED_INIT;
281
282
1.08k
  pp.scheme = scheme;
283
1.08k
  pp.host_user.str = pp.host.str = path;
284
1.08k
  pp.host_user.len = pp.host.len = pathlen;
285
1.08k
  pp.unix_socket = TRUE;
286
1.08k
  pp.abstract_uds = abstract_unix_socket;
287
288
1.08k
  result = peer_create(&pp, ppeer);
289
1.08k
  peer_parse_clear(&pp);
290
1.08k
  return result;
291
1.08k
}
292
#endif /* USE_UNIX_SOCKETS */
293
294
void Curl_peer_link(struct Curl_peer **pdest, struct Curl_peer *src)
295
307k
{
296
307k
  if(*pdest != src) {
297
245k
    Curl_peer_unlink(pdest);
298
245k
    *pdest = src;
299
245k
    if(src) {
300
245k
      DEBUGASSERT(src->refcount < UINT32_MAX);
301
245k
      src->refcount++;
302
245k
    }
303
245k
  }
304
307k
}
305
306
void Curl_peer_unlink(struct Curl_peer **ppeer)
307
1.26M
{
308
1.26M
  if(*ppeer) {
309
312k
    struct Curl_peer *peer = *ppeer;
310
311
312k
    DEBUGASSERT(peer->refcount);
312
312k
    *ppeer = NULL;
313
312k
    if(peer->refcount)
314
312k
      peer->refcount--;
315
312k
    if(!peer->refcount) {
316
66.9k
      curlx_free(peer);
317
66.9k
    }
318
312k
  }
319
1.26M
}
320
321
bool Curl_peer_equal(struct Curl_peer *p1, struct Curl_peer *p2)
322
161k
{
323
161k
  return (p1 == p2) ||
324
68.1k
         (p1 && p2 &&
325
66.7k
          (p1->scheme == p2->scheme) &&
326
63.9k
          Curl_peer_same_destination(p1, p2));
327
161k
}
328
329
static bool peer_same_hostname(struct Curl_peer *p1, struct Curl_peer *p2)
330
76.5k
{
331
  /* UNIX domain socket paths must be compared case-sensitive,
332
   * as many filesystem are like that. */
333
76.5k
  return (p1->unix_socket == p2->unix_socket) &&
334
76.5k
         (p1->abstract_uds == p2->abstract_uds) &&
335
76.5k
         (p1->ipv6 == p2->ipv6) &&
336
         /* unix_socket paths are case-sensitive, hostnames are not.
337
          * Do strcmp() first on both, since we do a lot of matches
338
          * on the same names and platform strcmp is way faster. */
339
75.4k
         (!strcmp(p1->hostname, p2->hostname) ||
340
28.1k
          (!p1->unix_socket &&
341
28.1k
           curl_strequal(p1->hostname, p2->hostname)));
342
76.5k
}
343
344
static bool peer_same_zoneid(const char *z1, const char *z2)
345
46.0k
{
346
46.0k
  return (z1 == z2) ||
347
562
         (z1 && z2 && !strcmp(z1, z2));
348
46.0k
}
349
350
bool Curl_peer_same_destination(struct Curl_peer *p1, struct Curl_peer *p2)
351
181k
{
352
181k
  return (p1 == p2) ||
353
79.6k
         (p1 && p2 &&
354
79.6k
          (p1->port == p2->port) &&
355
76.5k
          peer_same_hostname(p1, p2) &&
356
47.3k
          (p1->scopeid == p2->scopeid) &&
357
47.2k
          (p1->scopeid || peer_same_zoneid(p1->zoneid, p2->zoneid)));
358
181k
}
359
360
CURLcode Curl_peer_from_url(CURLU *uh, struct Curl_easy *data,
361
                            uint16_t port_override,
362
                            uint32_t scopeid_override,
363
                            struct Curl_peer **ppeer)
364
31.6k
{
365
31.6k
  struct peer_parse pp;
366
31.6k
  char *zoneid = NULL, *scheme = NULL, *hostname = NULL;
367
31.6k
  CURLUcode uc;
368
31.6k
  CURLcode result;
369
370
31.6k
  Curl_peer_unlink(ppeer);
371
31.6k
  memset(&pp, 0, sizeof(pp));
372
373
31.6k
  uc = curl_url_get(uh, CURLUPART_SCHEME, &scheme, 0);
374
31.6k
  if(uc)
375
0
    return Curl_uc_to_curlcode(uc);
376
31.6k
  pp.scheme = Curl_get_scheme(scheme);
377
31.6k
  if(!pp.scheme) {
378
271
    failf(data, "Protocol \"%s\" not supported%s", scheme,
379
271
          data->state.this_is_a_follow ? " (in redirect)" : "");
380
271
    result = CURLE_UNSUPPORTED_PROTOCOL;
381
271
    goto out;
382
271
  }
383
384
31.3k
  uc = curl_url_get(uh, CURLUPART_HOST, &hostname, 0);
385
31.3k
  if(uc) {
386
276
    if((uc == CURLUE_NO_HOST) && (pp.scheme->flags & PROTOPT_NONETWORK))
387
276
      ; /* acceptable */
388
0
    else {
389
0
      result = CURLE_OUT_OF_MEMORY;
390
0
      goto out;
391
0
    }
392
276
  }
393
31.0k
  else if(strlen(hostname) > MAX_URL_LEN) {
394
0
    failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN);
395
0
    result = CURLE_URL_MALFORMAT;
396
0
    goto out;
397
0
  }
398
399
31.3k
  pp.host_user.str = hostname ? hostname : "";
400
31.3k
  pp.host_user.len = strlen(pp.host_user.str);
401
31.3k
  if(pp.host_user.len) {
402
31.0k
    result = peer_parse_host(data, &pp, FALSE);
403
31.0k
    if(result)
404
89
      goto out;
405
31.0k
  }
406
276
  else
407
276
    pp.host = pp.host_user;
408
409
31.2k
  if(port_override) {
410
    /* if set, we use this instead of the port possibly given in the URL */
411
652
    char portbuf[16];
412
652
    curl_msnprintf(portbuf, sizeof(portbuf), "%d", port_override);
413
652
    uc = curl_url_set(uh, CURLUPART_PORT, portbuf, 0);
414
652
    if(uc) {
415
0
      result = CURLE_OUT_OF_MEMORY;
416
0
      goto out;
417
0
    }
418
652
    else
419
652
      pp.port = port_override;
420
652
  }
421
30.6k
  else {
422
30.6k
    uc = Curl_url_get_port(uh, &pp.port);
423
30.6k
    if(uc) {
424
275
      if(uc == CURLUE_OUT_OF_MEMORY) {
425
0
        result = CURLE_OUT_OF_MEMORY;
426
0
        goto out;
427
0
      }
428
275
      else if(!(pp.scheme->flags & PROTOPT_NONETWORK)) {
429
0
        result = CURLE_URL_MALFORMAT;
430
0
        goto out;
431
0
      }
432
      /* no port ok when not a network scheme */
433
275
    }
434
30.6k
  }
435
436
31.2k
  if(scopeid_override)
437
    /* Override any scope id from an URL zone. */
438
353
    pp.scopeid = scopeid_override;
439
30.9k
  else {
440
30.9k
    if(curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0) ==
441
30.9k
       CURLUE_OUT_OF_MEMORY) {
442
0
      result = CURLE_OUT_OF_MEMORY;
443
0
      goto out;
444
0
    }
445
30.9k
    if(zoneid) {
446
232
      pp.zoneid.str = zoneid;
447
232
      pp.zoneid.len = strlen(zoneid);
448
232
    }
449
30.9k
  }
450
451
31.2k
  result = peer_create(&pp, ppeer);
452
31.2k
  if(result)
453
0
    failf(data, "Error %d creating peer for %s:%u",
454
0
          (int)result, pp.host_user.str, pp.port);
455
456
31.6k
out:
457
31.6k
  peer_parse_clear(&pp);
458
31.6k
  curlx_free(scheme);
459
31.6k
  curlx_free(hostname);
460
31.6k
  curlx_free(zoneid);
461
31.6k
  return result;
462
31.2k
}
463
464
/* Parse a "host:port" string to connect to into a peer.
465
 * IPv6 addresses might appear in brackets or without them. */
466
CURLcode Curl_peer_from_connect_to(struct Curl_easy *data,
467
                                   const struct Curl_peer *dest,
468
                                   const char *connect_to,
469
                                   struct Curl_peer **ppeer)
470
29.7k
{
471
29.7k
  struct peer_parse pp;
472
29.7k
  const char *portstr = NULL;
473
29.7k
  bool port_switch = FALSE;
474
29.7k
  CURLcode result;
475
476
29.7k
  Curl_peer_unlink(ppeer);
477
29.7k
  memset(&pp, 0, sizeof(pp));
478
29.7k
  if(!connect_to || !*connect_to)
479
0
    return CURLE_FAILED_INIT;
480
481
29.7k
  pp.scheme = dest->scheme;
482
483
  /* detect and extract RFC6874-style IPv6-addresses */
484
29.7k
  if(connect_to[0] == '[') {
485
0
    const char *s = strchr(connect_to + 1, ']');
486
0
    if(!s) {
487
0
      failf(data, "Invalid IPv6 address format in '%s'", connect_to);
488
0
      result = CURLE_SETOPT_OPTION_SYNTAX;
489
0
      goto out;
490
0
    }
491
0
    portstr = strchr(s, ':');
492
0
    pp.host_user.str = connect_to;
493
0
    pp.host_user.len = s - pp.host_user.str + 1;
494
0
    pp.ipv6 = TRUE;
495
0
  }
496
29.7k
  else {
497
29.7k
    portstr = strchr(connect_to, ':');
498
29.7k
    pp.host_user.str = connect_to;
499
29.7k
    pp.host_user.len = portstr ?
500
29.7k
      (size_t)(portstr - connect_to) : strlen(connect_to);
501
29.7k
  }
502
503
29.7k
  if(!pp.host_user.len) { /* no hostname found, only port switch */
504
0
    port_switch = TRUE;
505
0
    pp.host_user.str = dest->user_hostname;
506
0
    pp.host_user.len = strlen(dest->user_hostname);
507
0
  }
508
509
29.7k
  result = peer_parse_host(data, &pp, FALSE);
510
29.7k
  if(result)
511
0
    goto out;
512
  /* On a port switch, ipv6 must stay the same */
513
29.7k
  DEBUGASSERT(!port_switch || ((bool)pp.ipv6 == (bool)dest->ipv6));
514
515
29.7k
  if(portstr && portstr[1]) {
516
0
    const char *p = portstr + 1;
517
0
    curl_off_t portparse;
518
0
    if(curlx_str_number(&p, &portparse, 0xffff)) {
519
0
      failf(data, "No valid port number in '%s'", connect_to);
520
0
      result = CURLE_SETOPT_OPTION_SYNTAX;
521
0
      goto out;
522
0
    }
523
0
    pp.port = (uint16_t)portparse; /* we know it will fit */
524
0
  }
525
29.7k
  else
526
29.7k
    pp.port = dest->port;
527
528
#ifndef USE_IPV6
529
  if(pp.ipv6) {
530
    failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in");
531
    result = CURLE_NOT_BUILT_IN;
532
    goto out;
533
  }
534
#endif
535
29.7k
  if(pp.ipv6 && port_switch) {
536
0
    if(dest->zoneid) {
537
0
      pp.zoneid.str = dest->zoneid;
538
0
      pp.zoneid.len = strlen(dest->zoneid);
539
0
    }
540
0
    pp.scopeid = dest->scopeid;
541
0
  }
542
543
29.7k
  result = peer_create(&pp, ppeer);
544
29.7k
  CURL_TRC_M(data, "connect-to peer_create2 -> %d", (int)result);
545
546
29.7k
out:
547
29.7k
  CURL_TRC_M(data, "parse connect_to peer: %s -> %d", connect_to, (int)result);
548
29.7k
  peer_parse_clear(&pp);
549
29.7k
  return result;
550
29.7k
}
551
552
#ifndef CURL_DISABLE_PROXY
553
554
#ifdef USE_UNIX_SOCKETS
555
69
#define UNIX_SOCKET_PREFIX "localhost"
556
#endif
557
558
CURLcode Curl_scheme_to_proxytype(struct Curl_easy *data,
559
                                  const char *scheme,
560
                                  uint8_t *proxytype, const char *url)
561
6.38k
{
562
6.38k
  if(!scheme)
563
0
    return CURLE_OK;
564
565
6.38k
  if(curl_strequal("https", scheme)) {
566
0
    if(*proxytype != CURLPROXY_HTTPS2 && *proxytype != CURLPROXY_HTTPS3)
567
0
      *proxytype = CURLPROXY_HTTPS;
568
0
  }
569
6.38k
  else if(curl_strequal("socks5h", scheme))
570
0
    *proxytype = CURLPROXY_SOCKS5_HOSTNAME;
571
6.38k
  else if(curl_strequal("socks5", scheme))
572
138
    *proxytype = CURLPROXY_SOCKS5;
573
6.25k
  else if(curl_strequal("socks4a", scheme))
574
0
    *proxytype = CURLPROXY_SOCKS4A;
575
6.25k
  else if(curl_strequal("socks4", scheme) || curl_strequal("socks", scheme))
576
0
    *proxytype = CURLPROXY_SOCKS4;
577
6.25k
  else if(curl_strequal("http", scheme)) {
578
6.25k
    if(*proxytype != CURLPROXY_HTTP_1_0)
579
6.19k
      *proxytype = CURLPROXY_HTTP;
580
6.25k
  }
581
0
  else {
582
    /* Any other xxx:// reject! */
583
0
    failf(data, "Unsupported proxy scheme for \'%s\'", url);
584
0
    return CURLE_COULDNT_CONNECT;
585
0
  }
586
6.38k
  return CURLE_OK;
587
6.38k
}
588
589
CURLcode Curl_peer_from_proxy_url(CURLU *uh,
590
                                  struct Curl_easy *data,
591
                                  const char *url,
592
                                  uint8_t proxytype,
593
                                  struct Curl_peer **ppeer,
594
                                  uint8_t *pproxytype)
595
3.19k
{
596
3.19k
  struct peer_parse pp;
597
3.19k
  char *scheme = NULL;
598
3.19k
  char *portptr = NULL;
599
3.19k
#ifdef USE_UNIX_SOCKETS
600
3.19k
  bool is_socks = FALSE;
601
3.19k
#endif
602
3.19k
  CURLUcode uc;
603
3.19k
  CURLcode result = CURLE_OK;
604
605
3.19k
  Curl_peer_unlink(ppeer);
606
3.19k
  memset(&pp, 0, sizeof(pp));
607
3.19k
  pp.port = CURL_DEFAULT_PROXY_PORT;
608
3.19k
  uc = curl_url_get(uh, CURLUPART_SCHEME, &scheme,
609
3.19k
                    CURLU_NON_SUPPORT_SCHEME | CURLU_NO_GUESS_SCHEME);
610
3.19k
  if(uc) {
611
0
    if(uc == CURLUE_OUT_OF_MEMORY) {
612
0
      result = CURLE_OUT_OF_MEMORY;
613
0
      goto out;
614
0
    }
615
    /* URL came without scheme, the passed `proxytype` determines it */
616
0
    switch(proxytype) {
617
0
    case CURLPROXY_HTTP:
618
0
    case CURLPROXY_HTTP_1_0:
619
0
      pp.scheme = &Curl_scheme_http;
620
0
      break;
621
0
    case CURLPROXY_HTTPS:
622
0
    case CURLPROXY_HTTPS2:
623
0
    case CURLPROXY_HTTPS3:
624
0
      pp.scheme = &Curl_scheme_https;
625
0
      break;
626
0
    case CURLPROXY_SOCKS4:
627
0
      pp.scheme = &Curl_scheme_socks4;
628
0
      break;
629
0
    case CURLPROXY_SOCKS4A:
630
0
      pp.scheme = &Curl_scheme_socks4a;
631
0
      break;
632
0
    case CURLPROXY_SOCKS5:
633
0
      pp.scheme = &Curl_scheme_socks5;
634
0
      break;
635
0
    case CURLPROXY_SOCKS5_HOSTNAME:
636
0
      pp.scheme = &Curl_scheme_socks5h;
637
0
      break;
638
0
    default:
639
0
      failf(data, "Unsupported proxy type %u for \'%s\'", proxytype, url);
640
0
      result = CURLE_COULDNT_RESOLVE_PROXY;
641
0
      goto out;
642
0
    }
643
0
  }
644
3.19k
  else {
645
3.19k
    pp.scheme = Curl_get_scheme(scheme);
646
3.19k
    result = Curl_scheme_to_proxytype(data, scheme, &proxytype, url);
647
3.19k
    if(result)
648
0
      goto out;
649
3.19k
  }
650
3.19k
  DEBUGASSERT(pp.scheme);
651
652
3.19k
  if(CURL_PROXY_IS_HTTPS(proxytype) &&
653
0
     !Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY)) {
654
0
    failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
655
0
          "HTTPS-proxy support.", url);
656
0
    result = CURLE_NOT_BUILT_IN;
657
0
    goto out;
658
0
  }
659
660
3.19k
  switch(pp.scheme->family) {
661
69
  case CURLPROTO_SOCKS:
662
69
#ifdef USE_UNIX_SOCKETS
663
69
    is_socks = TRUE;
664
69
#endif
665
69
    break;
666
3.12k
  case CURLPROTO_HTTP:
667
3.12k
    break;
668
0
  default:
669
0
    failf(data, "Unsupported proxy protocol for \'%s\'", url);
670
0
    result = CURLE_COULDNT_CONNECT;
671
0
    goto out;
672
3.19k
  }
673
674
3.19k
  uc = curl_url_get(uh, CURLUPART_PORT, &portptr, CURLU_NO_DEFAULT_PORT);
675
3.19k
  if(uc == CURLUE_OUT_OF_MEMORY) {
676
0
    result = CURLE_OUT_OF_MEMORY;
677
0
    goto out;
678
0
  }
679
3.19k
  if(portptr) {
680
0
    curl_off_t num;
681
0
    const char *p = portptr;
682
0
    if(!curlx_str_number(&p, &num, UINT16_MAX))
683
0
      pp.port = (uint16_t)num;
684
    /* Should we not error out when the port number is invalid? */
685
0
    curlx_free(portptr);
686
0
  }
687
3.19k
  else {
688
    /* No port in URL, take the set one or the scheme's default */
689
3.19k
    if(data->set.proxyport)
690
48
      pp.port = data->set.proxyport;
691
3.14k
    else
692
3.14k
      pp.port = pp.scheme->defport;
693
3.19k
  }
694
695
  /* now, clone the proxy hostname */
696
3.19k
  uc = curl_url_get(uh, CURLUPART_HOST, &pp.tmp_host_user, CURLU_URLDECODE);
697
3.19k
  if(uc) {
698
0
    result = CURLE_OUT_OF_MEMORY;
699
0
    goto out;
700
0
  }
701
3.19k
  pp.host_user.str = pp.tmp_host_user;
702
3.19k
  pp.host_user.len = strlen(pp.tmp_host_user);
703
704
3.19k
#ifdef USE_UNIX_SOCKETS
705
3.19k
  if(is_socks && curl_strequal(UNIX_SOCKET_PREFIX, pp.tmp_host_user)) {
706
0
    uc = curl_url_get(uh, CURLUPART_PATH, &pp.tmp_host, CURLU_URLDECODE);
707
0
    if(uc) {
708
0
      result = CURLE_OUT_OF_MEMORY;
709
0
      goto out;
710
0
    }
711
    /* path will be "/", if no path was found */
712
0
    if(strcmp("/", pp.tmp_host)) {
713
0
      pp.host.str = pp.tmp_host;
714
0
      pp.host.len = strlen(pp.tmp_host);
715
0
      pp.unix_socket = TRUE;
716
0
    }
717
0
    else {
718
0
      pp.host = pp.host_user;
719
0
    }
720
0
  }
721
3.19k
#endif /* USE_UNIX_SOCKETS */
722
723
3.19k
  if(!pp.host.len) {
724
3.19k
    result = peer_parse_host(data, &pp, FALSE);
725
3.19k
    if(result)
726
0
      goto out;
727
3.19k
  }
728
729
3.19k
  uc = curl_url_get(uh, CURLUPART_ZONEID, &pp.tmp_zoneid, 0);
730
3.19k
  if(uc == CURLUE_OUT_OF_MEMORY) {
731
0
    result = CURLE_OUT_OF_MEMORY;
732
0
    goto out;
733
0
  }
734
3.19k
  if(pp.tmp_zoneid) {
735
0
    pp.zoneid.str = pp.tmp_zoneid;
736
0
    pp.zoneid.len = strlen(pp.tmp_zoneid);
737
0
  }
738
739
3.19k
  *pproxytype = proxytype;
740
3.19k
  result = peer_create(&pp, ppeer);
741
742
3.19k
out:
743
3.19k
  peer_parse_clear(&pp);
744
3.19k
  curlx_free(scheme);
745
3.19k
#ifdef DEBUGBUILD
746
3.19k
  if(!result)
747
3.19k
    DEBUGASSERT(*ppeer);
748
3.19k
#endif
749
3.19k
  return result;
750
3.19k
}
751
752
#endif /* !CURL_DISABLE_PROXY */