Coverage Report

Created: 2026-07-14 07:09

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