1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h>
29 #endif
30 #ifdef HAVE_NETDB_H
31 #include <netdb.h>
32 #endif
33 #ifdef HAVE_ARPA_INET_H
34 #include <arpa/inet.h>
35 #endif
36 #ifdef HAVE_NET_IF_H
37 #include <net/if.h>
38 #endif
39 #ifdef HAVE_IPHLPAPI_H
40 #include <Iphlpapi.h>
41 #endif
42 #ifdef HAVE_SYS_IOCTL_H
43 #include <sys/ioctl.h>
44 #endif
45 #ifdef HAVE_SYS_PARAM_H
46 #include <sys/param.h>
47 #endif
48
49 #ifdef __VMS
50 #include <in.h>
51 #include <inet.h>
52 #endif
53
54 #ifdef HAVE_SYS_UN_H
55 #include <sys/un.h>
56 #endif
57
58 #ifndef HAVE_SOCKET
59 #error "We cannot compile without socket() support!"
60 #endif
61
62 #include <limits.h>
63
64 #include "doh.h"
65 #include "urldata.h"
66 #include "netrc.h"
67 #include "formdata.h"
68 #include "mime.h"
69 #include "vtls/vtls.h"
70 #include "hostip.h"
71 #include "transfer.h"
72 #include "sendf.h"
73 #include "progress.h"
74 #include "cookie.h"
75 #include "strcase.h"
76 #include "strerror.h"
77 #include "escape.h"
78 #include "strtok.h"
79 #include "share.h"
80 #include "content_encoding.h"
81 #include "http_digest.h"
82 #include "http_negotiate.h"
83 #include "select.h"
84 #include "multiif.h"
85 #include "easyif.h"
86 #include "speedcheck.h"
87 #include "warnless.h"
88 #include "getinfo.h"
89 #include "urlapi-int.h"
90 #include "system_win32.h"
91 #include "hsts.h"
92 #include "noproxy.h"
93 #include "cfilters.h"
94 #include "idn.h"
95
96 /* And now for the protocols */
97 #include "ftp.h"
98 #include "dict.h"
99 #include "telnet.h"
100 #include "tftp.h"
101 #include "http.h"
102 #include "http2.h"
103 #include "file.h"
104 #include "curl_ldap.h"
105 #include "vssh/ssh.h"
106 #include "imap.h"
107 #include "url.h"
108 #include "connect.h"
109 #include "inet_ntop.h"
110 #include "http_ntlm.h"
111 #include "curl_rtmp.h"
112 #include "gopher.h"
113 #include "mqtt.h"
114 #include "http_proxy.h"
115 #include "conncache.h"
116 #include "multihandle.h"
117 #include "strdup.h"
118 #include "setopt.h"
119 #include "altsvc.h"
120 #include "dynbuf.h"
121 #include "headers.h"
122
123 /* The last 3 #include files should be in this order */
124 #include "curl_printf.h"
125 #include "curl_memory.h"
126 #include "memdebug.h"
127
128 #ifndef ARRAYSIZE
129 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
130 #endif
131
132 #ifdef USE_NGHTTP2
133 static void data_priority_cleanup(struct Curl_easy *data);
134 #else
135 #define data_priority_cleanup(x)
136 #endif
137
138 /* Some parts of the code (e.g. chunked encoding) assume this buffer has at
139 * more than just a few bytes to play with. Do not let it become too small or
140 * bad things will happen.
141 */
142 #if READBUFFER_SIZE < READBUFFER_MIN
143 # error READBUFFER_SIZE is too small
144 #endif
145
146 #ifdef USE_UNIX_SOCKETS
147 #define UNIX_SOCKET_PREFIX "localhost"
148 #endif
149
150 /* Reject URLs exceeding this length */
151 #define MAX_URL_LEN 0xffff
152
153 /*
154 * get_protocol_family()
155 *
156 * This is used to return the protocol family for a given protocol.
157 *
158 * Parameters:
159 *
160 * 'h' [in] - struct Curl_handler pointer.
161 *
162 * Returns the family as a single bit protocol identifier.
163 */
get_protocol_family(const struct Curl_handler * h)164 static curl_prot_t get_protocol_family(const struct Curl_handler *h)
165 {
166 DEBUGASSERT(h);
167 DEBUGASSERT(h->family);
168 return h->family;
169 }
170
Curl_freeset(struct Curl_easy * data)171 void Curl_freeset(struct Curl_easy *data)
172 {
173 /* Free all dynamic strings stored in the data->set substructure. */
174 enum dupstring i;
175 enum dupblob j;
176
177 for(i = (enum dupstring)0; i < STRING_LAST; i++) {
178 Curl_safefree(data->set.str[i]);
179 }
180
181 for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
182 Curl_safefree(data->set.blobs[j]);
183 }
184
185 if(data->state.referer_alloc) {
186 Curl_safefree(data->state.referer);
187 data->state.referer_alloc = FALSE;
188 }
189 data->state.referer = NULL;
190 if(data->state.url_alloc) {
191 Curl_safefree(data->state.url);
192 data->state.url_alloc = FALSE;
193 }
194 data->state.url = NULL;
195
196 Curl_mime_cleanpart(&data->set.mimepost);
197
198 #ifndef CURL_DISABLE_COOKIES
199 curl_slist_free_all(data->state.cookielist);
200 data->state.cookielist = NULL;
201 #endif
202 }
203
204 /* free the URL pieces */
up_free(struct Curl_easy * data)205 static void up_free(struct Curl_easy *data)
206 {
207 struct urlpieces *up = &data->state.up;
208 Curl_safefree(up->scheme);
209 Curl_safefree(up->hostname);
210 Curl_safefree(up->port);
211 Curl_safefree(up->user);
212 Curl_safefree(up->password);
213 Curl_safefree(up->options);
214 Curl_safefree(up->path);
215 Curl_safefree(up->query);
216 curl_url_cleanup(data->state.uh);
217 data->state.uh = NULL;
218 }
219
220 /*
221 * This is the internal function curl_easy_cleanup() calls. This should
222 * cleanup and free all resources associated with this sessionhandle.
223 *
224 * We ignore SIGPIPE when this is called from curl_easy_cleanup.
225 */
226
Curl_close(struct Curl_easy ** datap)227 CURLcode Curl_close(struct Curl_easy **datap)
228 {
229 struct Curl_easy *data;
230
231 if(!datap || !*datap)
232 return CURLE_OK;
233
234 data = *datap;
235 *datap = NULL;
236
237 /* Detach connection if any is left. This should not be normal, but can be
238 the case for example with CONNECT_ONLY + recv/send (test 556) */
239 Curl_detach_connection(data);
240 if(!data->state.internal) {
241 if(data->multi)
242 /* This handle is still part of a multi handle, take care of this first
243 and detach this handle from there. */
244 curl_multi_remove_handle(data->multi, data);
245
246 if(data->multi_easy) {
247 /* when curl_easy_perform() is used, it creates its own multi handle to
248 use and this is the one */
249 curl_multi_cleanup(data->multi_easy);
250 data->multi_easy = NULL;
251 }
252 }
253
254 Curl_expire_clear(data); /* shut off any timers left */
255
256 data->magic = 0; /* force a clear AFTER the possibly enforced removal from
257 the multi handle, since that function uses the magic
258 field! */
259
260 if(data->state.rangestringalloc)
261 free(data->state.range);
262
263 /* freed here just in case DONE was not called */
264 Curl_req_free(&data->req, data);
265
266 /* Close down all open SSL info and sessions */
267 Curl_ssl_close_all(data);
268 Curl_safefree(data->state.first_host);
269 Curl_ssl_free_certinfo(data);
270
271 if(data->state.referer_alloc) {
272 Curl_safefree(data->state.referer);
273 data->state.referer_alloc = FALSE;
274 }
275 data->state.referer = NULL;
276
277 up_free(data);
278 Curl_dyn_free(&data->state.headerb);
279 Curl_flush_cookies(data, TRUE);
280 #ifndef CURL_DISABLE_ALTSVC
281 Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
282 Curl_altsvc_cleanup(&data->asi);
283 #endif
284 #ifndef CURL_DISABLE_HSTS
285 Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]);
286 if(!data->share || !data->share->hsts)
287 Curl_hsts_cleanup(&data->hsts);
288 curl_slist_free_all(data->state.hstslist); /* clean up list */
289 #endif
290 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
291 Curl_http_auth_cleanup_digest(data);
292 #endif
293 Curl_safefree(data->info.contenttype);
294 Curl_safefree(data->info.wouldredirect);
295
296 /* this destroys the channel and we cannot use it anymore after this */
297 Curl_resolver_cancel(data);
298 Curl_resolver_cleanup(data->state.async.resolver);
299
300 data_priority_cleanup(data);
301
302 /* No longer a dirty share, if it exists */
303 if(data->share) {
304 Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
305 data->share->dirty--;
306 Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
307 }
308
309 #ifndef CURL_DISABLE_PROXY
310 Curl_safefree(data->state.aptr.proxyuserpwd);
311 #endif
312 Curl_safefree(data->state.aptr.uagent);
313 Curl_safefree(data->state.aptr.userpwd);
314 Curl_safefree(data->state.aptr.accept_encoding);
315 Curl_safefree(data->state.aptr.te);
316 Curl_safefree(data->state.aptr.rangeline);
317 Curl_safefree(data->state.aptr.ref);
318 Curl_safefree(data->state.aptr.host);
319 #ifndef CURL_DISABLE_COOKIES
320 Curl_safefree(data->state.aptr.cookiehost);
321 #endif
322 #ifndef CURL_DISABLE_RTSP
323 Curl_safefree(data->state.aptr.rtsp_transport);
324 #endif
325 Curl_safefree(data->state.aptr.user);
326 Curl_safefree(data->state.aptr.passwd);
327 #ifndef CURL_DISABLE_PROXY
328 Curl_safefree(data->state.aptr.proxyuser);
329 Curl_safefree(data->state.aptr.proxypasswd);
330 #endif
331
332 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API)
333 Curl_mime_cleanpart(data->state.formp);
334 Curl_safefree(data->state.formp);
335 #endif
336
337 /* destruct wildcard structures if it is needed */
338 Curl_wildcard_dtor(&data->wildcard);
339 Curl_freeset(data);
340 Curl_headers_cleanup(data);
341 Curl_netrc_cleanup(&data->state.netrc);
342 free(data);
343 return CURLE_OK;
344 }
345
346 /*
347 * Initialize the UserDefined fields within a Curl_easy.
348 * This may be safely called on a new or existing Curl_easy.
349 */
Curl_init_userdefined(struct Curl_easy * data)350 CURLcode Curl_init_userdefined(struct Curl_easy *data)
351 {
352 struct UserDefined *set = &data->set;
353 CURLcode result = CURLE_OK;
354
355 set->out = stdout; /* default output to stdout */
356 set->in_set = stdin; /* default input from stdin */
357 set->err = stderr; /* default stderr to stderr */
358
359 /* use fwrite as default function to store output */
360 set->fwrite_func = (curl_write_callback)fwrite;
361
362 /* use fread as default function to read input */
363 set->fread_func_set = (curl_read_callback)fread;
364 set->is_fread_set = 0;
365
366 set->seek_client = ZERO_NULL;
367
368 set->filesize = -1; /* we do not know the size */
369 set->postfieldsize = -1; /* unknown size */
370 set->maxredirs = 30; /* sensible default */
371
372 set->method = HTTPREQ_GET; /* Default HTTP request */
373 #ifndef CURL_DISABLE_RTSP
374 set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */
375 #endif
376 #ifndef CURL_DISABLE_FTP
377 set->ftp_use_epsv = TRUE; /* FTP defaults to EPSV operations */
378 set->ftp_use_eprt = TRUE; /* FTP defaults to EPRT operations */
379 set->ftp_use_pret = FALSE; /* mainly useful for drftpd servers */
380 set->ftp_filemethod = FTPFILE_MULTICWD;
381 set->ftp_skip_ip = TRUE; /* skip PASV IP by default */
382 #endif
383 set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
384
385 /* Set the default size of the SSL session ID cache */
386 set->general_ssl.max_ssl_sessions = 5;
387 /* Timeout every 24 hours by default */
388 set->general_ssl.ca_cache_timeout = 24 * 60 * 60;
389
390 set->httpauth = CURLAUTH_BASIC; /* defaults to basic */
391
392 #ifndef CURL_DISABLE_PROXY
393 set->proxyport = 0;
394 set->proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
395 set->proxyauth = CURLAUTH_BASIC; /* defaults to basic */
396 /* SOCKS5 proxy auth defaults to username/password + GSS-API */
397 set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI;
398 #endif
399
400 /* make libcurl quiet by default: */
401 set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */
402
403 Curl_mime_initpart(&set->mimepost);
404
405 Curl_ssl_easy_config_init(data);
406 #ifndef CURL_DISABLE_DOH
407 set->doh_verifyhost = TRUE;
408 set->doh_verifypeer = TRUE;
409 #endif
410 #ifdef USE_SSH
411 /* defaults to any auth type */
412 set->ssh_auth_types = CURLSSH_AUTH_DEFAULT;
413 set->new_directory_perms = 0755; /* Default permissions */
414 #endif
415
416 set->new_file_perms = 0644; /* Default permissions */
417 set->allowed_protocols = (curl_prot_t) CURLPROTO_ALL;
418 set->redir_protocols = CURLPROTO_REDIR;
419
420 #if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
421 /*
422 * disallow unprotected protection negotiation NEC reference implementation
423 * seem not to follow rfc1961 section 4.3/4.4
424 */
425 set->socks5_gssapi_nec = FALSE;
426 #endif
427
428 /* Set the default CA cert bundle/path detected/specified at build time.
429 *
430 * If Schannel or Secure Transport is the selected SSL backend then these
431 * locations are ignored. We allow setting CA location for Schannel and
432 * Secure Transport when explicitly specified by the user via
433 * CURLOPT_CAINFO / --cacert.
434 */
435 if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL &&
436 Curl_ssl_backend() != CURLSSLBACKEND_SECURETRANSPORT) {
437 #if defined(CURL_CA_BUNDLE)
438 result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE);
439 if(result)
440 return result;
441 #ifndef CURL_DISABLE_PROXY
442 result = Curl_setstropt(&set->str[STRING_SSL_CAFILE_PROXY],
443 CURL_CA_BUNDLE);
444 if(result)
445 return result;
446 #endif
447 #endif
448 #if defined(CURL_CA_PATH)
449 result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH);
450 if(result)
451 return result;
452 #ifndef CURL_DISABLE_PROXY
453 result = Curl_setstropt(&set->str[STRING_SSL_CAPATH_PROXY], CURL_CA_PATH);
454 if(result)
455 return result;
456 #endif
457 #endif
458 }
459
460 #ifndef CURL_DISABLE_FTP
461 set->wildcard_enabled = FALSE;
462 set->chunk_bgn = ZERO_NULL;
463 set->chunk_end = ZERO_NULL;
464 set->fnmatch = ZERO_NULL;
465 #endif
466 set->tcp_keepalive = FALSE;
467 set->tcp_keepintvl = 60;
468 set->tcp_keepidle = 60;
469 set->tcp_keepcnt = 9;
470 set->tcp_fastopen = FALSE;
471 set->tcp_nodelay = TRUE;
472 set->ssl_enable_alpn = TRUE;
473 set->expect_100_timeout = 1000L; /* Wait for a second by default. */
474 set->sep_headers = TRUE; /* separated header lists by default */
475 set->buffer_size = READBUFFER_SIZE;
476 set->upload_buffer_size = UPLOADBUFFER_DEFAULT;
477 set->happy_eyeballs_timeout = CURL_HET_DEFAULT;
478 set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT;
479 set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
480 set->maxage_conn = 118;
481 set->maxlifetime_conn = 0;
482 set->http09_allowed = FALSE;
483 #ifdef USE_HTTP2
484 set->httpwant = CURL_HTTP_VERSION_2TLS
485 #else
486 set->httpwant = CURL_HTTP_VERSION_1_1
487 #endif
488 ;
489 #if defined(USE_HTTP2) || defined(USE_HTTP3)
490 memset(&set->priority, 0, sizeof(set->priority));
491 #endif
492 set->quick_exit = 0L;
493 return result;
494 }
495
496 /**
497 * Curl_open()
498 *
499 * @param curl is a pointer to a sessionhandle pointer that gets set by this
500 * function.
501 * @return CURLcode
502 */
503
Curl_open(struct Curl_easy ** curl)504 CURLcode Curl_open(struct Curl_easy **curl)
505 {
506 CURLcode result;
507 struct Curl_easy *data;
508
509 /* simple start-up: alloc the struct, init it with zeroes and return */
510 data = calloc(1, sizeof(struct Curl_easy));
511 if(!data) {
512 /* this is a serious error */
513 DEBUGF(fprintf(stderr, "Error: calloc of Curl_easy failed\n"));
514 return CURLE_OUT_OF_MEMORY;
515 }
516
517 data->magic = CURLEASY_MAGIC_NUMBER;
518
519 Curl_req_init(&data->req);
520
521 result = Curl_resolver_init(data, &data->state.async.resolver);
522 if(result) {
523 DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
524 Curl_req_free(&data->req, data);
525 free(data);
526 return result;
527 }
528
529 result = Curl_init_userdefined(data);
530 if(!result) {
531 Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
532 Curl_initinfo(data);
533
534 /* most recent connection is not yet defined */
535 data->state.lastconnect_id = -1;
536 data->state.recent_conn_id = -1;
537 /* and not assigned an id yet */
538 data->id = -1;
539 data->mid = -1;
540 #ifndef CURL_DISABLE_DOH
541 data->set.dohfor_mid = -1;
542 #endif
543
544 data->progress.flags |= PGRS_HIDE;
545 data->state.current_speed = -1; /* init to negative == impossible */
546 #ifndef CURL_DISABLE_HTTP
547 Curl_llist_init(&data->state.httphdrs, NULL);
548 #endif
549 Curl_netrc_init(&data->state.netrc);
550 }
551
552 if(result) {
553 Curl_resolver_cleanup(data->state.async.resolver);
554 Curl_dyn_free(&data->state.headerb);
555 Curl_freeset(data);
556 Curl_req_free(&data->req, data);
557 free(data);
558 data = NULL;
559 }
560 else
561 *curl = data;
562 return result;
563 }
564
Curl_conn_free(struct Curl_easy * data,struct connectdata * conn)565 void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
566 {
567 size_t i;
568
569 DEBUGASSERT(conn);
570
571 for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
572 Curl_conn_cf_discard_all(data, conn, (int)i);
573 }
574
575 Curl_free_idnconverted_hostname(&conn->host);
576 Curl_free_idnconverted_hostname(&conn->conn_to_host);
577 #ifndef CURL_DISABLE_PROXY
578 Curl_free_idnconverted_hostname(&conn->http_proxy.host);
579 Curl_free_idnconverted_hostname(&conn->socks_proxy.host);
580 Curl_safefree(conn->http_proxy.user);
581 Curl_safefree(conn->socks_proxy.user);
582 Curl_safefree(conn->http_proxy.passwd);
583 Curl_safefree(conn->socks_proxy.passwd);
584 Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
585 Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
586 #endif
587 Curl_safefree(conn->user);
588 Curl_safefree(conn->passwd);
589 Curl_safefree(conn->sasl_authzid);
590 Curl_safefree(conn->options);
591 Curl_safefree(conn->oauth_bearer);
592 Curl_safefree(conn->host.rawalloc); /* hostname buffer */
593 Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */
594 Curl_safefree(conn->hostname_resolve);
595 Curl_safefree(conn->secondaryhostname);
596 Curl_safefree(conn->localdev);
597 Curl_ssl_conn_config_cleanup(conn);
598
599 #ifdef USE_UNIX_SOCKETS
600 Curl_safefree(conn->unix_domain_socket);
601 #endif
602 Curl_safefree(conn->destination);
603
604 free(conn); /* free all the connection oriented data */
605 }
606
607 /*
608 * Disconnects the given connection. Note the connection may not be the
609 * primary connection, like when freeing room in the connection pool or
610 * killing of a dead old connection.
611 *
612 * A connection needs an easy handle when closing down. We support this passed
613 * in separately since the connection to get closed here is often already
614 * disassociated from an easy handle.
615 *
616 * This function MUST NOT reset state in the Curl_easy struct if that
617 * is not strictly bound to the life-time of *this* particular connection.
618 */
Curl_on_disconnect(struct Curl_easy * data,struct connectdata * conn,bool aborted)619 bool Curl_on_disconnect(struct Curl_easy *data,
620 struct connectdata *conn, bool aborted)
621 {
622 /* there must be a connection to close */
623 DEBUGASSERT(conn);
624
625 /* it must be removed from the connection pool */
626 DEBUGASSERT(!conn->bits.in_cpool);
627
628 /* there must be an associated transfer */
629 DEBUGASSERT(data);
630
631 /* the transfer must be detached from the connection */
632 DEBUGASSERT(!data->conn);
633
634 DEBUGF(infof(data, "Curl_disconnect(conn #%" FMT_OFF_T ", aborted=%d)",
635 conn->connection_id, aborted));
636
637 if(conn->dns_entry)
638 Curl_resolv_unlink(data, &conn->dns_entry);
639
640 /* Cleanup NTLM connection-related data */
641 Curl_http_auth_cleanup_ntlm(conn);
642
643 /* Cleanup NEGOTIATE connection-related data */
644 Curl_http_auth_cleanup_negotiate(conn);
645
646 if(conn->connect_only)
647 /* treat the connection as aborted in CONNECT_ONLY situations */
648 aborted = TRUE;
649
650 return aborted;
651 }
652
653 /*
654 * xfer_may_multiplex()
655 *
656 * Return a TRUE, iff the transfer can be done over an (appropriate)
657 * multiplexed connection.
658 */
xfer_may_multiplex(const struct Curl_easy * data,const struct connectdata * conn)659 static bool xfer_may_multiplex(const struct Curl_easy *data,
660 const struct connectdata *conn)
661 {
662 /* If an HTTP protocol and multiplexing is enabled */
663 if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
664 (!conn->bits.protoconnstart || !conn->bits.close)) {
665
666 if(Curl_multiplex_wanted(data->multi) &&
667 (data->state.httpwant >= CURL_HTTP_VERSION_2))
668 /* allows HTTP/2 or newer */
669 return TRUE;
670 }
671 return FALSE;
672 }
673
674 #ifndef CURL_DISABLE_PROXY
675 static bool
proxy_info_matches(const struct proxy_info * data,const struct proxy_info * needle)676 proxy_info_matches(const struct proxy_info *data,
677 const struct proxy_info *needle)
678 {
679 if((data->proxytype == needle->proxytype) &&
680 (data->port == needle->port) &&
681 strcasecompare(data->host.name, needle->host.name))
682 return TRUE;
683
684 return FALSE;
685 }
686
687 static bool
socks_proxy_info_matches(const struct proxy_info * data,const struct proxy_info * needle)688 socks_proxy_info_matches(const struct proxy_info *data,
689 const struct proxy_info *needle)
690 {
691 if(!proxy_info_matches(data, needle))
692 return FALSE;
693
694 /* the user information is case-sensitive
695 or at least it is not defined as case-insensitive
696 see https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.1 */
697
698 /* curl_strequal does a case insensitive comparison,
699 so do not use it here! */
700 if(Curl_timestrcmp(data->user, needle->user) ||
701 Curl_timestrcmp(data->passwd, needle->passwd))
702 return FALSE;
703 return TRUE;
704 }
705 #else
706 /* disabled, will not get called */
707 #define proxy_info_matches(x,y) FALSE
708 #define socks_proxy_info_matches(x,y) FALSE
709 #endif
710
711 /* A connection has to have been idle for a shorter time than 'maxage_conn'
712 (the success rate is just too low after this), or created less than
713 'maxlifetime_conn' ago, to be subject for reuse. */
714
conn_maxage(struct Curl_easy * data,struct connectdata * conn,struct curltime now)715 static bool conn_maxage(struct Curl_easy *data,
716 struct connectdata *conn,
717 struct curltime now)
718 {
719 timediff_t idletime, lifetime;
720
721 idletime = Curl_timediff(now, conn->lastused);
722 idletime /= 1000; /* integer seconds is fine */
723
724 if(idletime > data->set.maxage_conn) {
725 infof(data, "Too old connection (%" FMT_TIMEDIFF_T
726 " seconds idle), disconnect it", idletime);
727 return TRUE;
728 }
729
730 lifetime = Curl_timediff(now, conn->created);
731 lifetime /= 1000; /* integer seconds is fine */
732
733 if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) {
734 infof(data,
735 "Too old connection (%" FMT_TIMEDIFF_T
736 " seconds since creation), disconnect it", lifetime);
737 return TRUE;
738 }
739
740
741 return FALSE;
742 }
743
744 /*
745 * Return TRUE iff the given connection is considered dead.
746 */
Curl_conn_seems_dead(struct connectdata * conn,struct Curl_easy * data,struct curltime * pnow)747 bool Curl_conn_seems_dead(struct connectdata *conn,
748 struct Curl_easy *data,
749 struct curltime *pnow)
750 {
751 DEBUGASSERT(!data->conn);
752 if(!CONN_INUSE(conn)) {
753 /* The check for a dead socket makes sense only if the connection is not in
754 use */
755 bool dead;
756 struct curltime now;
757 if(!pnow) {
758 now = Curl_now();
759 pnow = &now;
760 }
761
762 if(conn_maxage(data, conn, *pnow)) {
763 /* avoid check if already too old */
764 dead = TRUE;
765 }
766 else if(conn->handler->connection_check) {
767 /* The protocol has a special method for checking the state of the
768 connection. Use it to check if the connection is dead. */
769 unsigned int state;
770
771 /* briefly attach the connection to this transfer for the purpose of
772 checking it */
773 Curl_attach_connection(data, conn);
774
775 state = conn->handler->connection_check(data, conn, CONNCHECK_ISDEAD);
776 dead = (state & CONNRESULT_DEAD);
777 /* detach the connection again */
778 Curl_detach_connection(data);
779
780 }
781 else {
782 bool input_pending = FALSE;
783
784 Curl_attach_connection(data, conn);
785 dead = !Curl_conn_is_alive(data, conn, &input_pending);
786 if(input_pending) {
787 /* For reuse, we want a "clean" connection state. The includes
788 * that we expect - in general - no waiting input data. Input
789 * waiting might be a TLS Notify Close, for example. We reject
790 * that.
791 * For protocols where data from other end may arrive at
792 * any time (HTTP/2 PING for example), the protocol handler needs
793 * to install its own `connection_check` callback.
794 */
795 DEBUGF(infof(data, "connection has input pending, not reusable"));
796 dead = TRUE;
797 }
798 Curl_detach_connection(data);
799 }
800
801 if(dead) {
802 /* remove connection from cpool */
803 infof(data, "Connection %" FMT_OFF_T " seems to be dead",
804 conn->connection_id);
805 return TRUE;
806 }
807 }
808 return FALSE;
809 }
810
Curl_conn_upkeep(struct Curl_easy * data,struct connectdata * conn,struct curltime * now)811 CURLcode Curl_conn_upkeep(struct Curl_easy *data,
812 struct connectdata *conn,
813 struct curltime *now)
814 {
815 CURLcode result = CURLE_OK;
816 if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
817 return result;
818
819 /* briefly attach for action */
820 Curl_attach_connection(data, conn);
821 if(conn->handler->connection_check) {
822 /* Do a protocol-specific keepalive check on the connection. */
823 unsigned int rc;
824 rc = conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE);
825 if(rc & CONNRESULT_DEAD)
826 result = CURLE_RECV_ERROR;
827 }
828 else {
829 /* Do the generic action on the FIRSTSOCKET filter chain */
830 result = Curl_conn_keep_alive(data, conn, FIRSTSOCKET);
831 }
832 Curl_detach_connection(data);
833
834 conn->keepalive = *now;
835 return result;
836 }
837
838 #ifdef USE_SSH
ssh_config_matches(struct connectdata * one,struct connectdata * two)839 static bool ssh_config_matches(struct connectdata *one,
840 struct connectdata *two)
841 {
842 return (Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) &&
843 Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub));
844 }
845 #else
846 #define ssh_config_matches(x,y) FALSE
847 #endif
848
849 struct url_conn_match {
850 struct connectdata *found;
851 struct Curl_easy *data;
852 struct connectdata *needle;
853 BIT(may_multiplex);
854 BIT(want_ntlm_http);
855 BIT(want_proxy_ntlm_http);
856
857 BIT(wait_pipe);
858 BIT(force_reuse);
859 BIT(seen_pending_conn);
860 BIT(seen_single_use_conn);
861 BIT(seen_multiplex_conn);
862 };
863
url_match_conn(struct connectdata * conn,void * userdata)864 static bool url_match_conn(struct connectdata *conn, void *userdata)
865 {
866 struct url_conn_match *match = userdata;
867 struct Curl_easy *data = match->data;
868 struct connectdata *needle = match->needle;
869
870 /* Check if `conn` can be used for transfer `data` */
871
872 if(conn->connect_only || conn->bits.close)
873 /* connect-only or to-be-closed connections will not be reused */
874 return FALSE;
875
876 if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
877 && data->set.ipver != conn->ip_version) {
878 /* skip because the connection is not via the requested IP version */
879 return FALSE;
880 }
881
882 if(needle->localdev || needle->localport) {
883 /* If we are bound to a specific local end (IP+port), we must not reuse a
884 random other one, although if we did not ask for a particular one we
885 can reuse one that was bound.
886
887 This comparison is a bit rough and too strict. Since the input
888 parameters can be specified in numerous ways and still end up the same
889 it would take a lot of processing to make it really accurate. Instead,
890 this matching will assume that reuses of bound connections will most
891 likely also reuse the exact same binding parameters and missing out a
892 few edge cases should not hurt anyone much.
893 */
894 if((conn->localport != needle->localport) ||
895 (conn->localportrange != needle->localportrange) ||
896 (needle->localdev &&
897 (!conn->localdev || strcmp(conn->localdev, needle->localdev))))
898 return FALSE;
899 }
900
901 if(needle->bits.conn_to_host != conn->bits.conn_to_host)
902 /* do not mix connections that use the "connect to host" feature and
903 * connections that do not use this feature */
904 return FALSE;
905
906 if(needle->bits.conn_to_port != conn->bits.conn_to_port)
907 /* do not mix connections that use the "connect to port" feature and
908 * connections that do not use this feature */
909 return FALSE;
910
911 if(!Curl_conn_is_connected(conn, FIRSTSOCKET) ||
912 conn->bits.asks_multiplex) {
913 /* Not yet connected, or not yet decided if it multiplexes. The later
914 * happens for HTTP/2 Upgrade: requests that need a response. */
915 if(match->may_multiplex) {
916 match->seen_pending_conn = TRUE;
917 /* Do not pick a connection that has not connected yet */
918 infof(data, "Connection #%" FMT_OFF_T
919 " is not open enough, cannot reuse", conn->connection_id);
920 }
921 /* Do not pick a connection that has not connected yet */
922 return FALSE;
923 }
924 /* `conn` is connected. If it has transfers, can we add ours to it? */
925
926 if(CONN_INUSE(conn)) {
927 if(!conn->bits.multiplex) {
928 /* conn busy and conn cannot take more transfers */
929 match->seen_single_use_conn = TRUE;
930 return FALSE;
931 }
932 match->seen_multiplex_conn = TRUE;
933 if(!match->may_multiplex)
934 /* conn busy and transfer cannot be multiplexed */
935 return FALSE;
936 else {
937 /* transfer and conn multiplex. Are they on the same multi? */
938 struct Curl_llist_node *e = Curl_llist_head(&conn->easyq);
939 struct Curl_easy *entry = Curl_node_elem(e);
940 if(entry->multi != data->multi)
941 return FALSE;
942 }
943 }
944 /* `conn` is connected and we could add the transfer to it, if
945 * all the other criteria do match. */
946
947 /* Does `conn` use the correct protocol? */
948 #ifdef USE_UNIX_SOCKETS
949 if(needle->unix_domain_socket) {
950 if(!conn->unix_domain_socket)
951 return FALSE;
952 if(strcmp(needle->unix_domain_socket, conn->unix_domain_socket))
953 return FALSE;
954 if(needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
955 return FALSE;
956 }
957 else if(conn->unix_domain_socket)
958 return FALSE;
959 #endif
960
961 if((needle->handler->flags&PROTOPT_SSL) !=
962 (conn->handler->flags&PROTOPT_SSL))
963 /* do not do mixed SSL and non-SSL connections */
964 if(get_protocol_family(conn->handler) !=
965 needle->handler->protocol || !conn->bits.tls_upgraded)
966 /* except protocols that have been upgraded via TLS */
967 return FALSE;
968
969 #ifndef CURL_DISABLE_PROXY
970 if(needle->bits.httpproxy != conn->bits.httpproxy ||
971 needle->bits.socksproxy != conn->bits.socksproxy)
972 return FALSE;
973
974 if(needle->bits.socksproxy &&
975 !socks_proxy_info_matches(&needle->socks_proxy,
976 &conn->socks_proxy))
977 return FALSE;
978
979 if(needle->bits.httpproxy) {
980 if(needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
981 return FALSE;
982
983 if(!proxy_info_matches(&needle->http_proxy, &conn->http_proxy))
984 return FALSE;
985
986 if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) {
987 /* https proxies come in different types, http/1.1, h2, ... */
988 if(needle->http_proxy.proxytype != conn->http_proxy.proxytype)
989 return FALSE;
990 /* match SSL config to proxy */
991 if(!Curl_ssl_conn_config_match(data, conn, TRUE)) {
992 DEBUGF(infof(data,
993 "Connection #%" FMT_OFF_T
994 " has different SSL proxy parameters, cannot reuse",
995 conn->connection_id));
996 return FALSE;
997 }
998 /* the SSL config to the server, which may apply here is checked
999 * further below */
1000 }
1001 }
1002 #endif
1003
1004 if(match->may_multiplex &&
1005 (data->state.httpwant == CURL_HTTP_VERSION_2_0) &&
1006 (needle->handler->protocol & CURLPROTO_HTTP) &&
1007 !conn->httpversion) {
1008 if(data->set.pipewait) {
1009 infof(data, "Server upgrade does not support multiplex yet, wait");
1010 match->found = NULL;
1011 match->wait_pipe = TRUE;
1012 return TRUE; /* stop searching, we want to wait */
1013 }
1014 infof(data, "Server upgrade cannot be used");
1015 return FALSE;
1016 }
1017
1018 if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
1019 /* This protocol requires credentials per connection,
1020 so verify that we are using the same name and password as well */
1021 if(Curl_timestrcmp(needle->user, conn->user) ||
1022 Curl_timestrcmp(needle->passwd, conn->passwd) ||
1023 Curl_timestrcmp(needle->sasl_authzid, conn->sasl_authzid) ||
1024 Curl_timestrcmp(needle->oauth_bearer, conn->oauth_bearer)) {
1025 /* one of them was different */
1026 return FALSE;
1027 }
1028 }
1029
1030 /* GSS delegation differences do not actually affect every connection
1031 and auth method, but this check takes precaution before efficiency */
1032 if(needle->gssapi_delegation != conn->gssapi_delegation)
1033 return FALSE;
1034
1035 /* If looking for HTTP and the HTTP version we want is less
1036 * than the HTTP version of conn, continue looking.
1037 * CURL_HTTP_VERSION_2TLS is default which indicates no preference,
1038 * so we take any existing connection. */
1039 if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
1040 (data->state.httpwant != CURL_HTTP_VERSION_2TLS)) {
1041 if((conn->httpversion >= 20) &&
1042 (data->state.httpwant < CURL_HTTP_VERSION_2_0)) {
1043 DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
1044 " with httpversion=%d, we want a version less than h2",
1045 conn->connection_id, conn->httpversion));
1046 }
1047 if((conn->httpversion >= 30) &&
1048 (data->state.httpwant < CURL_HTTP_VERSION_3)) {
1049 DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
1050 " with httpversion=%d, we want a version less than h3",
1051 conn->connection_id, conn->httpversion));
1052 return FALSE;
1053 }
1054 }
1055 #ifdef USE_SSH
1056 else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
1057 if(!ssh_config_matches(needle, conn))
1058 return FALSE;
1059 }
1060 #endif
1061 #ifndef CURL_DISABLE_FTP
1062 else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
1063 /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
1064 if(Curl_timestrcmp(needle->proto.ftpc.account,
1065 conn->proto.ftpc.account) ||
1066 Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
1067 conn->proto.ftpc.alternative_to_user) ||
1068 (needle->proto.ftpc.use_ssl != conn->proto.ftpc.use_ssl) ||
1069 (needle->proto.ftpc.ccc != conn->proto.ftpc.ccc))
1070 return FALSE;
1071 }
1072 #endif
1073
1074 /* Additional match requirements if talking TLS OR
1075 * not talking to an HTTP proxy OR using a tunnel through a proxy */
1076 if((needle->handler->flags&PROTOPT_SSL)
1077 #ifndef CURL_DISABLE_PROXY
1078 || !needle->bits.httpproxy || needle->bits.tunnel_proxy
1079 #endif
1080 ) {
1081 /* Talking the same protocol scheme or a TLS upgraded protocol in the
1082 * same protocol family? */
1083 if(!strcasecompare(needle->handler->scheme, conn->handler->scheme) &&
1084 (get_protocol_family(conn->handler) !=
1085 needle->handler->protocol || !conn->bits.tls_upgraded))
1086 return FALSE;
1087
1088 /* If needle has "conn_to_*" set, conn must match this */
1089 if((needle->bits.conn_to_host && !strcasecompare(
1090 needle->conn_to_host.name, conn->conn_to_host.name)) ||
1091 (needle->bits.conn_to_port &&
1092 needle->conn_to_port != conn->conn_to_port))
1093 return FALSE;
1094
1095 /* hostname and port must match */
1096 if(!strcasecompare(needle->host.name, conn->host.name) ||
1097 needle->remote_port != conn->remote_port)
1098 return FALSE;
1099
1100 /* If talking TLS, conn needs to use the same SSL options. */
1101 if((needle->handler->flags & PROTOPT_SSL) &&
1102 !Curl_ssl_conn_config_match(data, conn, FALSE)) {
1103 DEBUGF(infof(data,
1104 "Connection #%" FMT_OFF_T
1105 " has different SSL parameters, cannot reuse",
1106 conn->connection_id));
1107 return FALSE;
1108 }
1109 }
1110
1111 #if defined(USE_NTLM)
1112 /* If we are looking for an HTTP+NTLM connection, check if this is
1113 already authenticating with the right credentials. If not, keep
1114 looking so that we can reuse NTLM connections if
1115 possible. (Especially we must not reuse the same connection if
1116 partway through a handshake!) */
1117 if(match->want_ntlm_http) {
1118 if(Curl_timestrcmp(needle->user, conn->user) ||
1119 Curl_timestrcmp(needle->passwd, conn->passwd)) {
1120
1121 /* we prefer a credential match, but this is at least a connection
1122 that can be reused and "upgraded" to NTLM */
1123 if(conn->http_ntlm_state == NTLMSTATE_NONE)
1124 match->found = conn;
1125 return FALSE;
1126 }
1127 }
1128 else if(conn->http_ntlm_state != NTLMSTATE_NONE) {
1129 /* Connection is using NTLM auth but we do not want NTLM */
1130 return FALSE;
1131 }
1132
1133 #ifndef CURL_DISABLE_PROXY
1134 /* Same for Proxy NTLM authentication */
1135 if(match->want_proxy_ntlm_http) {
1136 /* Both conn->http_proxy.user and conn->http_proxy.passwd can be
1137 * NULL */
1138 if(!conn->http_proxy.user || !conn->http_proxy.passwd)
1139 return FALSE;
1140
1141 if(Curl_timestrcmp(needle->http_proxy.user,
1142 conn->http_proxy.user) ||
1143 Curl_timestrcmp(needle->http_proxy.passwd,
1144 conn->http_proxy.passwd))
1145 return FALSE;
1146 }
1147 else if(conn->proxy_ntlm_state != NTLMSTATE_NONE) {
1148 /* Proxy connection is using NTLM auth but we do not want NTLM */
1149 return FALSE;
1150 }
1151 #endif
1152 if(match->want_ntlm_http || match->want_proxy_ntlm_http) {
1153 /* Credentials are already checked, we may use this connection.
1154 * With NTLM being weird as it is, we MUST use a
1155 * connection where it has already been fully negotiated.
1156 * If it has not, we keep on looking for a better one. */
1157 match->found = conn;
1158
1159 if((match->want_ntlm_http &&
1160 (conn->http_ntlm_state != NTLMSTATE_NONE)) ||
1161 (match->want_proxy_ntlm_http &&
1162 (conn->proxy_ntlm_state != NTLMSTATE_NONE))) {
1163 /* We must use this connection, no other */
1164 match->force_reuse = TRUE;
1165 return TRUE;
1166 }
1167 /* Continue look up for a better connection */
1168 return FALSE;
1169 }
1170 #endif
1171
1172 if(CONN_INUSE(conn)) {
1173 DEBUGASSERT(match->may_multiplex);
1174 DEBUGASSERT(conn->bits.multiplex);
1175 /* If multiplexed, make sure we do not go over concurrency limit */
1176 if(CONN_INUSE(conn) >=
1177 Curl_multi_max_concurrent_streams(data->multi)) {
1178 infof(data, "client side MAX_CONCURRENT_STREAMS reached"
1179 ", skip (%zu)", CONN_INUSE(conn));
1180 return FALSE;
1181 }
1182 if(CONN_INUSE(conn) >=
1183 Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
1184 infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
1185 CONN_INUSE(conn));
1186 return FALSE;
1187 }
1188 /* When not multiplexed, we have a match here! */
1189 infof(data, "Multiplexed connection found");
1190 }
1191 else if(Curl_conn_seems_dead(conn, data, NULL)) {
1192 /* removed and disconnect. Do not treat as aborted. */
1193 Curl_cpool_disconnect(data, conn, FALSE);
1194 return FALSE;
1195 }
1196
1197 /* We have found a connection. Let's stop searching. */
1198 match->found = conn;
1199 return TRUE;
1200 }
1201
url_match_result(bool result,void * userdata)1202 static bool url_match_result(bool result, void *userdata)
1203 {
1204 struct url_conn_match *match = userdata;
1205 (void)result;
1206 if(match->found) {
1207 /* Attach it now while still under lock, so the connection does
1208 * no longer appear idle and can be reaped. */
1209 Curl_attach_connection(match->data, match->found);
1210 return TRUE;
1211 }
1212 else if(match->seen_single_use_conn && !match->seen_multiplex_conn) {
1213 /* We've seen a single-use, existing connection to the destination and
1214 * no multiplexed one. It seems safe to assume that the server does
1215 * not support multiplexing. */
1216 match->wait_pipe = FALSE;
1217 }
1218 else if(match->seen_pending_conn && match->data->set.pipewait) {
1219 infof(match->data,
1220 "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set");
1221 match->wait_pipe = TRUE;
1222 }
1223 match->force_reuse = FALSE;
1224 return FALSE;
1225 }
1226
1227 /*
1228 * Given one filled in connection struct (named needle), this function should
1229 * detect if there already is one that has all the significant details
1230 * exactly the same and thus should be used instead.
1231 *
1232 * If there is a match, this function returns TRUE - and has marked the
1233 * connection as 'in-use'. It must later be called with ConnectionDone() to
1234 * return back to 'idle' (unused) state.
1235 *
1236 * The force_reuse flag is set if the connection must be used.
1237 */
1238 static bool
ConnectionExists(struct Curl_easy * data,struct connectdata * needle,struct connectdata ** usethis,bool * force_reuse,bool * waitpipe)1239 ConnectionExists(struct Curl_easy *data,
1240 struct connectdata *needle,
1241 struct connectdata **usethis,
1242 bool *force_reuse,
1243 bool *waitpipe)
1244 {
1245 struct url_conn_match match;
1246 bool result;
1247
1248 memset(&match, 0, sizeof(match));
1249 match.data = data;
1250 match.needle = needle;
1251 match.may_multiplex = xfer_may_multiplex(data, needle);
1252
1253 #ifdef USE_NTLM
1254 match.want_ntlm_http = ((data->state.authhost.want & CURLAUTH_NTLM) &&
1255 (needle->handler->protocol & PROTO_FAMILY_HTTP));
1256 #ifndef CURL_DISABLE_PROXY
1257 match.want_proxy_ntlm_http =
1258 (needle->bits.proxy_user_passwd &&
1259 (data->state.authproxy.want & CURLAUTH_NTLM) &&
1260 (needle->handler->protocol & PROTO_FAMILY_HTTP));
1261 #endif
1262 #endif
1263
1264 /* Find a connection in the pool that matches what "data + needle"
1265 * requires. If a suitable candidate is found, it is attached to "data". */
1266 result = Curl_cpool_find(data, needle->destination, needle->destination_len,
1267 url_match_conn, url_match_result, &match);
1268
1269 /* wait_pipe is TRUE if we encounter a bundle that is undecided. There
1270 * is no matching connection then, yet. */
1271 *usethis = match.found;
1272 *force_reuse = match.force_reuse;
1273 *waitpipe = match.wait_pipe;
1274 return result;
1275 }
1276
1277 /*
1278 * verboseconnect() displays verbose information after a connect
1279 */
1280 #ifndef CURL_DISABLE_VERBOSE_STRINGS
Curl_verboseconnect(struct Curl_easy * data,struct connectdata * conn,int sockindex)1281 void Curl_verboseconnect(struct Curl_easy *data,
1282 struct connectdata *conn, int sockindex)
1283 {
1284 if(data->set.verbose && sockindex == SECONDARYSOCKET)
1285 infof(data, "Connected 2nd connection to %s port %u",
1286 conn->secondary.remote_ip, conn->secondary.remote_port);
1287 else
1288 infof(data, "Connected to %s (%s) port %u",
1289 CURL_CONN_HOST_DISPNAME(conn), conn->primary.remote_ip,
1290 conn->primary.remote_port);
1291 #if !defined(CURL_DISABLE_HTTP)
1292 if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
1293 switch(conn->alpn) {
1294 case CURL_HTTP_VERSION_3:
1295 infof(data, "using HTTP/3");
1296 break;
1297 case CURL_HTTP_VERSION_2:
1298 infof(data, "using HTTP/2");
1299 break;
1300 default:
1301 infof(data, "using HTTP/1.x");
1302 break;
1303 }
1304 }
1305 #endif
1306 }
1307 #endif
1308
1309 /*
1310 * Allocate and initialize a new connectdata object.
1311 */
allocate_conn(struct Curl_easy * data)1312 static struct connectdata *allocate_conn(struct Curl_easy *data)
1313 {
1314 struct connectdata *conn = calloc(1, sizeof(struct connectdata));
1315 if(!conn)
1316 return NULL;
1317
1318 /* and we setup a few fields in case we end up actually using this struct */
1319
1320 conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
1321 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
1322 conn->sockfd = CURL_SOCKET_BAD;
1323 conn->writesockfd = CURL_SOCKET_BAD;
1324 conn->connection_id = -1; /* no ID */
1325 conn->primary.remote_port = -1; /* unknown at this point */
1326 conn->remote_port = -1; /* unknown at this point */
1327
1328 /* Default protocol-independent behavior does not support persistent
1329 connections, so we set this to force-close. Protocols that support
1330 this need to set this to FALSE in their "curl_do" functions. */
1331 connclose(conn, "Default to force-close");
1332
1333 /* Store creation time to help future close decision making */
1334 conn->created = Curl_now();
1335
1336 /* Store current time to give a baseline to keepalive connection times. */
1337 conn->keepalive = conn->created;
1338
1339 #ifndef CURL_DISABLE_PROXY
1340 conn->http_proxy.proxytype = data->set.proxytype;
1341 conn->socks_proxy.proxytype = CURLPROXY_SOCKS4;
1342
1343 /* note that these two proxy bits are now just on what looks to be
1344 requested, they may be altered down the road */
1345 conn->bits.proxy = (data->set.str[STRING_PROXY] &&
1346 *data->set.str[STRING_PROXY]);
1347 conn->bits.httpproxy = (conn->bits.proxy &&
1348 (conn->http_proxy.proxytype == CURLPROXY_HTTP ||
1349 conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0 ||
1350 IS_HTTPS_PROXY(conn->http_proxy.proxytype)));
1351 conn->bits.socksproxy = (conn->bits.proxy && !conn->bits.httpproxy);
1352
1353 if(data->set.str[STRING_PRE_PROXY] && *data->set.str[STRING_PRE_PROXY]) {
1354 conn->bits.proxy = TRUE;
1355 conn->bits.socksproxy = TRUE;
1356 }
1357
1358 conn->bits.proxy_user_passwd = !!data->state.aptr.proxyuser;
1359 conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;
1360 #endif /* CURL_DISABLE_PROXY */
1361
1362 #ifndef CURL_DISABLE_FTP
1363 conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
1364 conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
1365 #endif
1366 conn->ip_version = data->set.ipver;
1367 conn->connect_only = data->set.connect_only;
1368 conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */
1369
1370 /* Initialize the easy handle list */
1371 Curl_llist_init(&conn->easyq, NULL);
1372
1373 #ifdef HAVE_GSSAPI
1374 conn->data_prot = PROT_CLEAR;
1375 #endif
1376
1377 /* Store the local bind parameters that will be used for this connection */
1378 if(data->set.str[STRING_DEVICE]) {
1379 conn->localdev = strdup(data->set.str[STRING_DEVICE]);
1380 if(!conn->localdev)
1381 goto error;
1382 }
1383 #ifndef CURL_DISABLE_BINDLOCAL
1384 conn->localportrange = data->set.localportrange;
1385 conn->localport = data->set.localport;
1386 #endif
1387
1388 /* the close socket stuff needs to be copied to the connection struct as
1389 it may live on without (this specific) Curl_easy */
1390 conn->fclosesocket = data->set.fclosesocket;
1391 conn->closesocket_client = data->set.closesocket_client;
1392 conn->lastused = conn->created;
1393 conn->gssapi_delegation = data->set.gssapi_delegation;
1394
1395 return conn;
1396 error:
1397
1398 free(conn->localdev);
1399 free(conn);
1400 return NULL;
1401 }
1402
Curl_get_scheme_handler(const char * scheme)1403 const struct Curl_handler *Curl_get_scheme_handler(const char *scheme)
1404 {
1405 return Curl_getn_scheme_handler(scheme, strlen(scheme));
1406 }
1407
1408 /* returns the handler if the given scheme is built-in */
Curl_getn_scheme_handler(const char * scheme,size_t len)1409 const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
1410 size_t len)
1411 {
1412 /* table generated by schemetable.c:
1413 1. gcc schemetable.c && ./a.out
1414 2. check how small the table gets
1415 3. tweak the hash algorithm, then rerun from 1
1416 4. when the table is good enough
1417 5. copy the table into this source code
1418 6. make sure this function uses the same hash function that worked for
1419 schemetable.c
1420 7. if needed, adjust the #ifdefs in schemetable.c and rerun
1421 */
1422 static const struct Curl_handler * const protocols[67] = {
1423 #ifndef CURL_DISABLE_FILE
1424 &Curl_handler_file,
1425 #else
1426 NULL,
1427 #endif
1428 NULL, NULL,
1429 #if defined(USE_SSL) && !defined(CURL_DISABLE_GOPHER)
1430 &Curl_handler_gophers,
1431 #else
1432 NULL,
1433 #endif
1434 NULL,
1435 #ifdef USE_LIBRTMP
1436 &Curl_handler_rtmpe,
1437 #else
1438 NULL,
1439 #endif
1440 #ifndef CURL_DISABLE_SMTP
1441 &Curl_handler_smtp,
1442 #else
1443 NULL,
1444 #endif
1445 #if defined(USE_SSH)
1446 &Curl_handler_sftp,
1447 #else
1448 NULL,
1449 #endif
1450 #if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
1451 (SIZEOF_CURL_OFF_T > 4)
1452 &Curl_handler_smb,
1453 #else
1454 NULL,
1455 #endif
1456 #if defined(USE_SSL) && !defined(CURL_DISABLE_SMTP)
1457 &Curl_handler_smtps,
1458 #else
1459 NULL,
1460 #endif
1461 #ifndef CURL_DISABLE_TELNET
1462 &Curl_handler_telnet,
1463 #else
1464 NULL,
1465 #endif
1466 #ifndef CURL_DISABLE_GOPHER
1467 &Curl_handler_gopher,
1468 #else
1469 NULL,
1470 #endif
1471 #ifndef CURL_DISABLE_TFTP
1472 &Curl_handler_tftp,
1473 #else
1474 NULL,
1475 #endif
1476 NULL, NULL, NULL,
1477 #if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
1478 &Curl_handler_ftps,
1479 #else
1480 NULL,
1481 #endif
1482 #ifndef CURL_DISABLE_HTTP
1483 &Curl_handler_http,
1484 #else
1485 NULL,
1486 #endif
1487 #ifndef CURL_DISABLE_IMAP
1488 &Curl_handler_imap,
1489 #else
1490 NULL,
1491 #endif
1492 #ifdef USE_LIBRTMP
1493 &Curl_handler_rtmps,
1494 #else
1495 NULL,
1496 #endif
1497 #ifdef USE_LIBRTMP
1498 &Curl_handler_rtmpt,
1499 #else
1500 NULL,
1501 #endif
1502 NULL, NULL, NULL,
1503 #if !defined(CURL_DISABLE_LDAP) && \
1504 !defined(CURL_DISABLE_LDAPS) && \
1505 ((defined(USE_OPENLDAP) && defined(USE_SSL)) || \
1506 (!defined(USE_OPENLDAP) && defined(HAVE_LDAP_SSL)))
1507 &Curl_handler_ldaps,
1508 #else
1509 NULL,
1510 #endif
1511 #if !defined(CURL_DISABLE_WEBSOCKETS) && \
1512 defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
1513 &Curl_handler_wss,
1514 #else
1515 NULL,
1516 #endif
1517 #if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
1518 &Curl_handler_https,
1519 #else
1520 NULL,
1521 #endif
1522 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1523 #ifndef CURL_DISABLE_RTSP
1524 &Curl_handler_rtsp,
1525 #else
1526 NULL,
1527 #endif
1528 #if defined(USE_SSL) && !defined(CURL_DISABLE_SMB) && \
1529 defined(USE_CURL_NTLM_CORE) && (SIZEOF_CURL_OFF_T > 4)
1530 &Curl_handler_smbs,
1531 #else
1532 NULL,
1533 #endif
1534 #if defined(USE_SSH) && !defined(USE_WOLFSSH)
1535 &Curl_handler_scp,
1536 #else
1537 NULL,
1538 #endif
1539 NULL, NULL, NULL,
1540 #ifndef CURL_DISABLE_POP3
1541 &Curl_handler_pop3,
1542 #else
1543 NULL,
1544 #endif
1545 NULL, NULL,
1546 #ifdef USE_LIBRTMP
1547 &Curl_handler_rtmp,
1548 #else
1549 NULL,
1550 #endif
1551 NULL, NULL, NULL,
1552 #ifdef USE_LIBRTMP
1553 &Curl_handler_rtmpte,
1554 #else
1555 NULL,
1556 #endif
1557 NULL, NULL, NULL,
1558 #ifndef CURL_DISABLE_DICT
1559 &Curl_handler_dict,
1560 #else
1561 NULL,
1562 #endif
1563 NULL, NULL, NULL,
1564 #ifndef CURL_DISABLE_MQTT
1565 &Curl_handler_mqtt,
1566 #else
1567 NULL,
1568 #endif
1569 #if defined(USE_SSL) && !defined(CURL_DISABLE_POP3)
1570 &Curl_handler_pop3s,
1571 #else
1572 NULL,
1573 #endif
1574 #if defined(USE_SSL) && !defined(CURL_DISABLE_IMAP)
1575 &Curl_handler_imaps,
1576 #else
1577 NULL,
1578 #endif
1579 NULL,
1580 #if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
1581 &Curl_handler_ws,
1582 #else
1583 NULL,
1584 #endif
1585 NULL,
1586 #ifdef USE_LIBRTMP
1587 &Curl_handler_rtmpts,
1588 #else
1589 NULL,
1590 #endif
1591 #ifndef CURL_DISABLE_LDAP
1592 &Curl_handler_ldap,
1593 #else
1594 NULL,
1595 #endif
1596 NULL, NULL,
1597 #ifndef CURL_DISABLE_FTP
1598 &Curl_handler_ftp,
1599 #else
1600 NULL,
1601 #endif
1602 };
1603
1604 if(len && (len <= 7)) {
1605 const char *s = scheme;
1606 size_t l = len;
1607 const struct Curl_handler *h;
1608 unsigned int c = 978;
1609 while(l) {
1610 c <<= 5;
1611 c += (unsigned int)Curl_raw_tolower(*s);
1612 s++;
1613 l--;
1614 }
1615
1616 h = protocols[c % 67];
1617 if(h && strncasecompare(scheme, h->scheme, len) && !h->scheme[len])
1618 return h;
1619 }
1620 return NULL;
1621 }
1622
findprotocol(struct Curl_easy * data,struct connectdata * conn,const char * protostr)1623 static CURLcode findprotocol(struct Curl_easy *data,
1624 struct connectdata *conn,
1625 const char *protostr)
1626 {
1627 const struct Curl_handler *p = Curl_get_scheme_handler(protostr);
1628
1629 if(p && /* Protocol found in table. Check if allowed */
1630 (data->set.allowed_protocols & p->protocol)) {
1631
1632 /* it is allowed for "normal" request, now do an extra check if this is
1633 the result of a redirect */
1634 if(data->state.this_is_a_follow &&
1635 !(data->set.redir_protocols & p->protocol))
1636 /* nope, get out */
1637 ;
1638 else {
1639 /* Perform setup complement if some. */
1640 conn->handler = conn->given = p;
1641 /* 'port' and 'remote_port' are set in setup_connection_internals() */
1642 return CURLE_OK;
1643 }
1644 }
1645
1646 /* The protocol was not found in the table, but we do not have to assign it
1647 to anything since it is already assigned to a dummy-struct in the
1648 create_conn() function when the connectdata struct is allocated. */
1649 failf(data, "Protocol \"%s\" %s%s", protostr,
1650 p ? "disabled" : "not supported",
1651 data->state.this_is_a_follow ? " (in redirect)":"");
1652
1653 return CURLE_UNSUPPORTED_PROTOCOL;
1654 }
1655
1656
Curl_uc_to_curlcode(CURLUcode uc)1657 CURLcode Curl_uc_to_curlcode(CURLUcode uc)
1658 {
1659 switch(uc) {
1660 default:
1661 return CURLE_URL_MALFORMAT;
1662 case CURLUE_UNSUPPORTED_SCHEME:
1663 return CURLE_UNSUPPORTED_PROTOCOL;
1664 case CURLUE_OUT_OF_MEMORY:
1665 return CURLE_OUT_OF_MEMORY;
1666 case CURLUE_USER_NOT_ALLOWED:
1667 return CURLE_LOGIN_DENIED;
1668 }
1669 }
1670
1671 #ifdef USE_IPV6
1672 /*
1673 * If the URL was set with an IPv6 numerical address with a zone id part, set
1674 * the scope_id based on that!
1675 */
1676
zonefrom_url(CURLU * uh,struct Curl_easy * data,struct connectdata * conn)1677 static void zonefrom_url(CURLU *uh, struct Curl_easy *data,
1678 struct connectdata *conn)
1679 {
1680 char *zoneid;
1681 CURLUcode uc = curl_url_get(uh, CURLUPART_ZONEID, &zoneid, 0);
1682 #ifdef CURL_DISABLE_VERBOSE_STRINGS
1683 (void)data;
1684 #endif
1685
1686 if(!uc && zoneid) {
1687 char *endp;
1688 unsigned long scope = strtoul(zoneid, &endp, 10);
1689 if(!*endp && (scope < UINT_MAX))
1690 /* A plain number, use it directly as a scope id. */
1691 conn->scope_id = (unsigned int)scope;
1692 #if defined(HAVE_IF_NAMETOINDEX)
1693 else {
1694 #elif defined(_WIN32)
1695 else if(Curl_if_nametoindex) {
1696 #endif
1697
1698 #if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32)
1699 /* Zone identifier is not numeric */
1700 unsigned int scopeidx = 0;
1701 #if defined(_WIN32)
1702 scopeidx = Curl_if_nametoindex(zoneid);
1703 #else
1704 scopeidx = if_nametoindex(zoneid);
1705 #endif
1706 if(!scopeidx) {
1707 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1708 char buffer[STRERROR_LEN];
1709 infof(data, "Invalid zoneid: %s; %s", zoneid,
1710 Curl_strerror(errno, buffer, sizeof(buffer)));
1711 #endif
1712 }
1713 else
1714 conn->scope_id = scopeidx;
1715 }
1716 #endif /* HAVE_IF_NAMETOINDEX || _WIN32 */
1717
1718 free(zoneid);
1719 }
1720 }
1721 #else
1722 #define zonefrom_url(a,b,c) Curl_nop_stmt
1723 #endif
1724
1725 /*
1726 * Parse URL and fill in the relevant members of the connection struct.
1727 */
1728 static CURLcode parseurlandfillconn(struct Curl_easy *data,
1729 struct connectdata *conn)
1730 {
1731 CURLcode result;
1732 CURLU *uh;
1733 CURLUcode uc;
1734 char *hostname;
1735 bool use_set_uh = (data->set.uh && !data->state.this_is_a_follow);
1736
1737 up_free(data); /* cleanup previous leftovers first */
1738
1739 /* parse the URL */
1740 if(use_set_uh) {
1741 uh = data->state.uh = curl_url_dup(data->set.uh);
1742 }
1743 else {
1744 uh = data->state.uh = curl_url();
1745 }
1746
1747 if(!uh)
1748 return CURLE_OUT_OF_MEMORY;
1749
1750 if(data->set.str[STRING_DEFAULT_PROTOCOL] &&
1751 !Curl_is_absolute_url(data->state.url, NULL, 0, TRUE)) {
1752 char *url = aprintf("%s://%s", data->set.str[STRING_DEFAULT_PROTOCOL],
1753 data->state.url);
1754 if(!url)
1755 return CURLE_OUT_OF_MEMORY;
1756 if(data->state.url_alloc)
1757 free(data->state.url);
1758 data->state.url = url;
1759 data->state.url_alloc = TRUE;
1760 }
1761
1762 if(!use_set_uh) {
1763 char *newurl;
1764 uc = curl_url_set(uh, CURLUPART_URL, data->state.url, (unsigned int)
1765 (CURLU_GUESS_SCHEME |
1766 CURLU_NON_SUPPORT_SCHEME |
1767 (data->set.disallow_username_in_url ?
1768 CURLU_DISALLOW_USER : 0) |
1769 (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
1770 if(uc) {
1771 failf(data, "URL rejected: %s", curl_url_strerror(uc));
1772 return Curl_uc_to_curlcode(uc);
1773 }
1774
1775 /* after it was parsed, get the generated normalized version */
1776 uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0);
1777 if(uc)
1778 return Curl_uc_to_curlcode(uc);
1779 if(data->state.url_alloc)
1780 free(data->state.url);
1781 data->state.url = newurl;
1782 data->state.url_alloc = TRUE;
1783 }
1784
1785 uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0);
1786 if(uc)
1787 return Curl_uc_to_curlcode(uc);
1788
1789 uc = curl_url_get(uh, CURLUPART_HOST, &data->state.up.hostname, 0);
1790 if(uc) {
1791 if(!strcasecompare("file", data->state.up.scheme))
1792 return CURLE_OUT_OF_MEMORY;
1793 }
1794 else if(strlen(data->state.up.hostname) > MAX_URL_LEN) {
1795 failf(data, "Too long hostname (maximum is %d)", MAX_URL_LEN);
1796 return CURLE_URL_MALFORMAT;
1797 }
1798 hostname = data->state.up.hostname;
1799
1800 if(hostname && hostname[0] == '[') {
1801 /* This looks like an IPv6 address literal. See if there is an address
1802 scope. */
1803 size_t hlen;
1804 conn->bits.ipv6_ip = TRUE;
1805 /* cut off the brackets! */
1806 hostname++;
1807 hlen = strlen(hostname);
1808 hostname[hlen - 1] = 0;
1809
1810 zonefrom_url(uh, data, conn);
1811 }
1812
1813 /* make sure the connect struct gets its own copy of the hostname */
1814 conn->host.rawalloc = strdup(hostname ? hostname : "");
1815 if(!conn->host.rawalloc)
1816 return CURLE_OUT_OF_MEMORY;
1817 conn->host.name = conn->host.rawalloc;
1818
1819 /*************************************************************
1820 * IDN-convert the hostnames
1821 *************************************************************/
1822 result = Curl_idnconvert_hostname(&conn->host);
1823 if(result)
1824 return result;
1825
1826 #ifndef CURL_DISABLE_HSTS
1827 /* HSTS upgrade */
1828 if(data->hsts && strcasecompare("http", data->state.up.scheme)) {
1829 /* This MUST use the IDN decoded name */
1830 if(Curl_hsts(data->hsts, conn->host.name, TRUE)) {
1831 char *url;
1832 Curl_safefree(data->state.up.scheme);
1833 uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0);
1834 if(uc)
1835 return Curl_uc_to_curlcode(uc);
1836 if(data->state.url_alloc)
1837 Curl_safefree(data->state.url);
1838 /* after update, get the updated version */
1839 uc = curl_url_get(uh, CURLUPART_URL, &url, 0);
1840 if(uc)
1841 return Curl_uc_to_curlcode(uc);
1842 uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0);
1843 if(uc) {
1844 free(url);
1845 return Curl_uc_to_curlcode(uc);
1846 }
1847 data->state.url = url;
1848 data->state.url_alloc = TRUE;
1849 infof(data, "Switched from HTTP to HTTPS due to HSTS => %s",
1850 data->state.url);
1851 }
1852 }
1853 #endif
1854
1855 result = findprotocol(data, conn, data->state.up.scheme);
1856 if(result)
1857 return result;
1858
1859 /*
1860 * username and password set with their own options override the credentials
1861 * possibly set in the URL, but netrc does not.
1862 */
1863 if(!data->state.aptr.passwd || (data->state.creds_from != CREDS_OPTION)) {
1864 uc = curl_url_get(uh, CURLUPART_PASSWORD, &data->state.up.password, 0);
1865 if(!uc) {
1866 char *decoded;
1867 result = Curl_urldecode(data->state.up.password, 0, &decoded, NULL,
1868 conn->handler->flags&PROTOPT_USERPWDCTRL ?
1869 REJECT_ZERO : REJECT_CTRL);
1870 if(result)
1871 return result;
1872 conn->passwd = decoded;
1873 result = Curl_setstropt(&data->state.aptr.passwd, decoded);
1874 if(result)
1875 return result;
1876 data->state.creds_from = CREDS_URL;
1877 }
1878 else if(uc != CURLUE_NO_PASSWORD)
1879 return Curl_uc_to_curlcode(uc);
1880 }
1881
1882 if(!data->state.aptr.user || (data->state.creds_from != CREDS_OPTION)) {
1883 /* we do not use the URL API's URL decoder option here since it rejects
1884 control codes and we want to allow them for some schemes in the user
1885 and password fields */
1886 uc = curl_url_get(uh, CURLUPART_USER, &data->state.up.user, 0);
1887 if(!uc) {
1888 char *decoded;
1889 result = Curl_urldecode(data->state.up.user, 0, &decoded, NULL,
1890 conn->handler->flags&PROTOPT_USERPWDCTRL ?
1891 REJECT_ZERO : REJECT_CTRL);
1892 if(result)
1893 return result;
1894 conn->user = decoded;
1895 result = Curl_setstropt(&data->state.aptr.user, decoded);
1896 data->state.creds_from = CREDS_URL;
1897 }
1898 else if(uc != CURLUE_NO_USER)
1899 return Curl_uc_to_curlcode(uc);
1900 if(result)
1901 return result;
1902 }
1903
1904 uc = curl_url_get(uh, CURLUPART_OPTIONS, &data->state.up.options,
1905 CURLU_URLDECODE);
1906 if(!uc) {
1907 conn->options = strdup(data->state.up.options);
1908 if(!conn->options)
1909 return CURLE_OUT_OF_MEMORY;
1910 }
1911 else if(uc != CURLUE_NO_OPTIONS)
1912 return Curl_uc_to_curlcode(uc);
1913
1914 uc = curl_url_get(uh, CURLUPART_PATH, &data->state.up.path,
1915 CURLU_URLENCODE);
1916 if(uc)
1917 return Curl_uc_to_curlcode(uc);
1918
1919 uc = curl_url_get(uh, CURLUPART_PORT, &data->state.up.port,
1920 CURLU_DEFAULT_PORT);
1921 if(uc) {
1922 if(!strcasecompare("file", data->state.up.scheme))
1923 return CURLE_OUT_OF_MEMORY;
1924 }
1925 else {
1926 unsigned long port = strtoul(data->state.up.port, NULL, 10);
1927 conn->primary.remote_port = conn->remote_port =
1928 (data->set.use_port && data->state.allow_port) ?
1929 data->set.use_port : curlx_ultous(port);
1930 }
1931
1932 (void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0);
1933
1934 #ifdef USE_IPV6
1935 if(data->set.scope_id)
1936 /* Override any scope that was set above. */
1937 conn->scope_id = data->set.scope_id;
1938 #endif
1939
1940 return CURLE_OK;
1941 }
1942
1943
1944 /*
1945 * If we are doing a resumed transfer, we need to setup our stuff
1946 * properly.
1947 */
1948 static CURLcode setup_range(struct Curl_easy *data)
1949 {
1950 struct UrlState *s = &data->state;
1951 s->resume_from = data->set.set_resume_from;
1952 if(s->resume_from || data->set.str[STRING_SET_RANGE]) {
1953 if(s->rangestringalloc)
1954 free(s->range);
1955
1956 if(s->resume_from)
1957 s->range = aprintf("%" FMT_OFF_T "-", s->resume_from);
1958 else
1959 s->range = strdup(data->set.str[STRING_SET_RANGE]);
1960
1961 if(!s->range)
1962 return CURLE_OUT_OF_MEMORY;
1963
1964 s->rangestringalloc = TRUE;
1965
1966 /* tell ourselves to fetch this range */
1967 s->use_range = TRUE; /* enable range download */
1968 }
1969 else
1970 s->use_range = FALSE; /* disable range download */
1971
1972 return CURLE_OK;
1973 }
1974
1975
1976 /*
1977 * setup_connection_internals() -
1978 *
1979 * Setup connection internals specific to the requested protocol in the
1980 * Curl_easy. This is inited and setup before the connection is made but
1981 * is about the particular protocol that is to be used.
1982 *
1983 * This MUST get called after proxy magic has been figured out.
1984 */
1985 static CURLcode setup_connection_internals(struct Curl_easy *data,
1986 struct connectdata *conn)
1987 {
1988 const struct Curl_handler *p;
1989 const char *hostname;
1990 int port;
1991 CURLcode result;
1992
1993 /* Perform setup complement if some. */
1994 p = conn->handler;
1995
1996 if(p->setup_connection) {
1997 result = (*p->setup_connection)(data, conn);
1998
1999 if(result)
2000 return result;
2001
2002 p = conn->handler; /* May have changed. */
2003 }
2004
2005 if(conn->primary.remote_port < 0)
2006 /* we check for -1 here since if proxy was detected already, this was
2007 likely already set to the proxy port */
2008 conn->primary.remote_port = p->defport;
2009
2010 /* Now create the destination name */
2011 #ifndef CURL_DISABLE_PROXY
2012 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
2013 hostname = conn->http_proxy.host.name;
2014 port = conn->primary.remote_port;
2015 }
2016 else
2017 #endif
2018 {
2019 port = conn->remote_port;
2020 if(conn->bits.conn_to_host)
2021 hostname = conn->conn_to_host.name;
2022 else
2023 hostname = conn->host.name;
2024 }
2025
2026 #ifdef USE_IPV6
2027 conn->destination = aprintf("%u/%d/%s", conn->scope_id, port, hostname);
2028 #else
2029 conn->destination = aprintf("%d/%s", port, hostname);
2030 #endif
2031 if(!conn->destination)
2032 return CURLE_OUT_OF_MEMORY;
2033
2034 conn->destination_len = strlen(conn->destination) + 1;
2035 Curl_strntolower(conn->destination, conn->destination,
2036 conn->destination_len - 1);
2037
2038 return CURLE_OK;
2039 }
2040
2041
2042 #ifndef CURL_DISABLE_PROXY
2043
2044 #ifndef CURL_DISABLE_HTTP
2045 /****************************************************************
2046 * Detect what (if any) proxy to use. Remember that this selects a host
2047 * name and is not limited to HTTP proxies only.
2048 * The returned pointer must be freed by the caller (unless NULL)
2049 ****************************************************************/
2050 static char *detect_proxy(struct Curl_easy *data,
2051 struct connectdata *conn)
2052 {
2053 char *proxy = NULL;
2054
2055 /* If proxy was not specified, we check for default proxy environment
2056 * variables, to enable i.e Lynx compliance:
2057 *
2058 * http_proxy=http://some.server.dom:port/
2059 * https_proxy=http://some.server.dom:port/
2060 * ftp_proxy=http://some.server.dom:port/
2061 * no_proxy=domain1.dom,host.domain2.dom
2062 * (a comma-separated list of hosts which should
2063 * not be proxied, or an asterisk to override
2064 * all proxy variables)
2065 * all_proxy=http://some.server.dom:port/
2066 * (seems to exist for the CERN www lib. Probably
2067 * the first to check for.)
2068 *
2069 * For compatibility, the all-uppercase versions of these variables are
2070 * checked if the lowercase versions do not exist.
2071 */
2072 char proxy_env[20];
2073 char *envp = proxy_env;
2074 #ifdef CURL_DISABLE_VERBOSE_STRINGS
2075 (void)data;
2076 #endif
2077
2078 msnprintf(proxy_env, sizeof(proxy_env), "%s_proxy", conn->handler->scheme);
2079
2080 /* read the protocol proxy: */
2081 proxy = curl_getenv(proxy_env);
2082
2083 /*
2084 * We do not try the uppercase version of HTTP_PROXY because of
2085 * security reasons:
2086 *
2087 * When curl is used in a webserver application
2088 * environment (cgi or php), this environment variable can
2089 * be controlled by the web server user by setting the
2090 * http header 'Proxy:' to some value.
2091 *
2092 * This can cause 'internal' http/ftp requests to be
2093 * arbitrarily redirected by any external attacker.
2094 */
2095 if(!proxy && !strcasecompare("http_proxy", proxy_env)) {
2096 /* There was no lowercase variable, try the uppercase version: */
2097 Curl_strntoupper(proxy_env, proxy_env, sizeof(proxy_env));
2098 proxy = curl_getenv(proxy_env);
2099 }
2100
2101 if(!proxy) {
2102 #ifndef CURL_DISABLE_WEBSOCKETS
2103 /* websocket proxy fallbacks */
2104 if(strcasecompare("ws_proxy", proxy_env)) {
2105 proxy = curl_getenv("http_proxy");
2106 }
2107 else if(strcasecompare("wss_proxy", proxy_env)) {
2108 proxy = curl_getenv("https_proxy");
2109 if(!proxy)
2110 proxy = curl_getenv("HTTPS_PROXY");
2111 }
2112 if(!proxy) {
2113 #endif
2114 envp = (char *)"all_proxy";
2115 proxy = curl_getenv(envp); /* default proxy to use */
2116 if(!proxy) {
2117 envp = (char *)"ALL_PROXY";
2118 proxy = curl_getenv(envp);
2119 }
2120 #ifndef CURL_DISABLE_WEBSOCKETS
2121 }
2122 #endif
2123 }
2124 if(proxy)
2125 infof(data, "Uses proxy env variable %s == '%s'", envp, proxy);
2126
2127 return proxy;
2128 }
2129 #endif /* CURL_DISABLE_HTTP */
2130
2131 /*
2132 * If this is supposed to use a proxy, we need to figure out the proxy
2133 * hostname, so that we can reuse an existing connection
2134 * that may exist registered to the same proxy host.
2135 */
2136 static CURLcode parse_proxy(struct Curl_easy *data,
2137 struct connectdata *conn, char *proxy,
2138 curl_proxytype proxytype)
2139 {
2140 char *portptr = NULL;
2141 int port = -1;
2142 char *proxyuser = NULL;
2143 char *proxypasswd = NULL;
2144 char *host = NULL;
2145 bool sockstype;
2146 CURLUcode uc;
2147 struct proxy_info *proxyinfo;
2148 CURLU *uhp = curl_url();
2149 CURLcode result = CURLE_OK;
2150 char *scheme = NULL;
2151 #ifdef USE_UNIX_SOCKETS
2152 char *path = NULL;
2153 bool is_unix_proxy = FALSE;
2154 #endif
2155
2156
2157 if(!uhp) {
2158 result = CURLE_OUT_OF_MEMORY;
2159 goto error;
2160 }
2161
2162 /* When parsing the proxy, allowing non-supported schemes since we have
2163 these made up ones for proxies. Guess scheme for URLs without it. */
2164 uc = curl_url_set(uhp, CURLUPART_URL, proxy,
2165 CURLU_NON_SUPPORT_SCHEME|CURLU_GUESS_SCHEME);
2166 if(!uc) {
2167 /* parsed okay as a URL */
2168 uc = curl_url_get(uhp, CURLUPART_SCHEME, &scheme, 0);
2169 if(uc) {
2170 result = CURLE_OUT_OF_MEMORY;
2171 goto error;
2172 }
2173
2174 if(strcasecompare("https", scheme)) {
2175 if(proxytype != CURLPROXY_HTTPS2)
2176 proxytype = CURLPROXY_HTTPS;
2177 else
2178 proxytype = CURLPROXY_HTTPS2;
2179 }
2180 else if(strcasecompare("socks5h", scheme))
2181 proxytype = CURLPROXY_SOCKS5_HOSTNAME;
2182 else if(strcasecompare("socks5", scheme))
2183 proxytype = CURLPROXY_SOCKS5;
2184 else if(strcasecompare("socks4a", scheme))
2185 proxytype = CURLPROXY_SOCKS4A;
2186 else if(strcasecompare("socks4", scheme) ||
2187 strcasecompare("socks", scheme))
2188 proxytype = CURLPROXY_SOCKS4;
2189 else if(strcasecompare("http", scheme))
2190 ; /* leave it as HTTP or HTTP/1.0 */
2191 else {
2192 /* Any other xxx:// reject! */
2193 failf(data, "Unsupported proxy scheme for \'%s\'", proxy);
2194 result = CURLE_COULDNT_CONNECT;
2195 goto error;
2196 }
2197 }
2198 else {
2199 failf(data, "Unsupported proxy syntax in \'%s\': %s", proxy,
2200 curl_url_strerror(uc));
2201 result = CURLE_COULDNT_RESOLVE_PROXY;
2202 goto error;
2203 }
2204
2205 #ifdef USE_SSL
2206 if(!Curl_ssl_supports(data, SSLSUPP_HTTPS_PROXY))
2207 #endif
2208 if(IS_HTTPS_PROXY(proxytype)) {
2209 failf(data, "Unsupported proxy \'%s\', libcurl is built without the "
2210 "HTTPS-proxy support.", proxy);
2211 result = CURLE_NOT_BUILT_IN;
2212 goto error;
2213 }
2214
2215 sockstype =
2216 proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
2217 proxytype == CURLPROXY_SOCKS5 ||
2218 proxytype == CURLPROXY_SOCKS4A ||
2219 proxytype == CURLPROXY_SOCKS4;
2220
2221 proxyinfo = sockstype ? &conn->socks_proxy : &conn->http_proxy;
2222 proxyinfo->proxytype = (unsigned char)proxytype;
2223
2224 /* Is there a username and password given in this proxy url? */
2225 uc = curl_url_get(uhp, CURLUPART_USER, &proxyuser, CURLU_URLDECODE);
2226 if(uc && (uc != CURLUE_NO_USER))
2227 goto error;
2228 uc = curl_url_get(uhp, CURLUPART_PASSWORD, &proxypasswd, CURLU_URLDECODE);
2229 if(uc && (uc != CURLUE_NO_PASSWORD))
2230 goto error;
2231
2232 if(proxyuser || proxypasswd) {
2233 Curl_safefree(proxyinfo->user);
2234 proxyinfo->user = proxyuser;
2235 result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser);
2236 proxyuser = NULL;
2237 if(result)
2238 goto error;
2239 Curl_safefree(proxyinfo->passwd);
2240 if(!proxypasswd) {
2241 proxypasswd = strdup("");
2242 if(!proxypasswd) {
2243 result = CURLE_OUT_OF_MEMORY;
2244 goto error;
2245 }
2246 }
2247 proxyinfo->passwd = proxypasswd;
2248 result = Curl_setstropt(&data->state.aptr.proxypasswd, proxypasswd);
2249 proxypasswd = NULL;
2250 if(result)
2251 goto error;
2252 conn->bits.proxy_user_passwd = TRUE; /* enable it */
2253 }
2254
2255 (void)curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
2256
2257 if(portptr) {
2258 port = (int)strtol(portptr, NULL, 10);
2259 free(portptr);
2260 }
2261 else {
2262 if(data->set.proxyport)
2263 /* None given in the proxy string, then get the default one if it is
2264 given */
2265 port = (int)data->set.proxyport;
2266 else {
2267 if(IS_HTTPS_PROXY(proxytype))
2268 port = CURL_DEFAULT_HTTPS_PROXY_PORT;
2269 else
2270 port = CURL_DEFAULT_PROXY_PORT;
2271 }
2272 }
2273 if(port >= 0) {
2274 proxyinfo->port = port;
2275 if(conn->primary.remote_port < 0 || sockstype ||
2276 !conn->socks_proxy.host.rawalloc)
2277 conn->primary.remote_port = port;
2278 }
2279
2280 /* now, clone the proxy hostname */
2281 uc = curl_url_get(uhp, CURLUPART_HOST, &host, CURLU_URLDECODE);
2282 if(uc) {
2283 result = CURLE_OUT_OF_MEMORY;
2284 goto error;
2285 }
2286 #ifdef USE_UNIX_SOCKETS
2287 if(sockstype && strcasecompare(UNIX_SOCKET_PREFIX, host)) {
2288 uc = curl_url_get(uhp, CURLUPART_PATH, &path, CURLU_URLDECODE);
2289 if(uc) {
2290 result = CURLE_OUT_OF_MEMORY;
2291 goto error;
2292 }
2293 /* path will be "/", if no path was found */
2294 if(strcmp("/", path)) {
2295 is_unix_proxy = TRUE;
2296 free(host);
2297 host = aprintf(UNIX_SOCKET_PREFIX"%s", path);
2298 if(!host) {
2299 result = CURLE_OUT_OF_MEMORY;
2300 goto error;
2301 }
2302 Curl_safefree(proxyinfo->host.rawalloc);
2303 proxyinfo->host.rawalloc = host;
2304 proxyinfo->host.name = host;
2305 host = NULL;
2306 }
2307 }
2308
2309 if(!is_unix_proxy) {
2310 #endif
2311 Curl_safefree(proxyinfo->host.rawalloc);
2312 proxyinfo->host.rawalloc = host;
2313 if(host[0] == '[') {
2314 /* this is a numerical IPv6, strip off the brackets */
2315 size_t len = strlen(host);
2316 host[len-1] = 0; /* clear the trailing bracket */
2317 host++;
2318 zonefrom_url(uhp, data, conn);
2319 }
2320 proxyinfo->host.name = host;
2321 host = NULL;
2322 #ifdef USE_UNIX_SOCKETS
2323 }
2324 #endif
2325
2326 error:
2327 free(proxyuser);
2328 free(proxypasswd);
2329 free(host);
2330 free(scheme);
2331 #ifdef USE_UNIX_SOCKETS
2332 free(path);
2333 #endif
2334 curl_url_cleanup(uhp);
2335 return result;
2336 }
2337
2338 /*
2339 * Extract the user and password from the authentication string
2340 */
2341 static CURLcode parse_proxy_auth(struct Curl_easy *data,
2342 struct connectdata *conn)
2343 {
2344 const char *proxyuser = data->state.aptr.proxyuser ?
2345 data->state.aptr.proxyuser : "";
2346 const char *proxypasswd = data->state.aptr.proxypasswd ?
2347 data->state.aptr.proxypasswd : "";
2348 CURLcode result = CURLE_OUT_OF_MEMORY;
2349
2350 conn->http_proxy.user = strdup(proxyuser);
2351 if(conn->http_proxy.user) {
2352 conn->http_proxy.passwd = strdup(proxypasswd);
2353 if(conn->http_proxy.passwd)
2354 result = CURLE_OK;
2355 else
2356 Curl_safefree(conn->http_proxy.user);
2357 }
2358 return result;
2359 }
2360
2361 /* create_conn helper to parse and init proxy values. to be called after Unix
2362 socket init but before any proxy vars are evaluated. */
2363 static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
2364 struct connectdata *conn)
2365 {
2366 char *proxy = NULL;
2367 char *socksproxy = NULL;
2368 char *no_proxy = NULL;
2369 CURLcode result = CURLE_OK;
2370
2371 /*************************************************************
2372 * Extract the user and password from the authentication string
2373 *************************************************************/
2374 if(conn->bits.proxy_user_passwd) {
2375 result = parse_proxy_auth(data, conn);
2376 if(result)
2377 goto out;
2378 }
2379
2380 /*************************************************************
2381 * Detect what (if any) proxy to use
2382 *************************************************************/
2383 if(data->set.str[STRING_PROXY]) {
2384 proxy = strdup(data->set.str[STRING_PROXY]);
2385 /* if global proxy is set, this is it */
2386 if(!proxy) {
2387 failf(data, "memory shortage");
2388 result = CURLE_OUT_OF_MEMORY;
2389 goto out;
2390 }
2391 }
2392
2393 if(data->set.str[STRING_PRE_PROXY]) {
2394 socksproxy = strdup(data->set.str[STRING_PRE_PROXY]);
2395 /* if global socks proxy is set, this is it */
2396 if(!socksproxy) {
2397 failf(data, "memory shortage");
2398 result = CURLE_OUT_OF_MEMORY;
2399 goto out;
2400 }
2401 }
2402
2403 if(!data->set.str[STRING_NOPROXY]) {
2404 const char *p = "no_proxy";
2405 no_proxy = curl_getenv(p);
2406 if(!no_proxy) {
2407 p = "NO_PROXY";
2408 no_proxy = curl_getenv(p);
2409 }
2410 if(no_proxy) {
2411 infof(data, "Uses proxy env variable %s == '%s'", p, no_proxy);
2412 }
2413 }
2414
2415 if(Curl_check_noproxy(conn->host.name, data->set.str[STRING_NOPROXY] ?
2416 data->set.str[STRING_NOPROXY] : no_proxy)) {
2417 Curl_safefree(proxy);
2418 Curl_safefree(socksproxy);
2419 }
2420 #ifndef CURL_DISABLE_HTTP
2421 else if(!proxy && !socksproxy)
2422 /* if the host is not in the noproxy list, detect proxy. */
2423 proxy = detect_proxy(data, conn);
2424 #endif /* CURL_DISABLE_HTTP */
2425 Curl_safefree(no_proxy);
2426
2427 #ifdef USE_UNIX_SOCKETS
2428 /* For the time being do not mix proxy and Unix domain sockets. See #1274 */
2429 if(proxy && conn->unix_domain_socket) {
2430 free(proxy);
2431 proxy = NULL;
2432 }
2433 #endif
2434
2435 if(proxy && (!*proxy || (conn->handler->flags & PROTOPT_NONETWORK))) {
2436 free(proxy); /* Do not bother with an empty proxy string or if the
2437 protocol does not work with network */
2438 proxy = NULL;
2439 }
2440 if(socksproxy && (!*socksproxy ||
2441 (conn->handler->flags & PROTOPT_NONETWORK))) {
2442 free(socksproxy); /* Do not bother with an empty socks proxy string or if
2443 the protocol does not work with network */
2444 socksproxy = NULL;
2445 }
2446
2447 /***********************************************************************
2448 * If this is supposed to use a proxy, we need to figure out the proxy host
2449 * name, proxy type and port number, so that we can reuse an existing
2450 * connection that may exist registered to the same proxy host.
2451 ***********************************************************************/
2452 if(proxy || socksproxy) {
2453 curl_proxytype ptype = (curl_proxytype)conn->http_proxy.proxytype;
2454 if(proxy) {
2455 result = parse_proxy(data, conn, proxy, ptype);
2456 Curl_safefree(proxy); /* parse_proxy copies the proxy string */
2457 if(result)
2458 goto out;
2459 }
2460
2461 if(socksproxy) {
2462 result = parse_proxy(data, conn, socksproxy, ptype);
2463 /* parse_proxy copies the socks proxy string */
2464 Curl_safefree(socksproxy);
2465 if(result)
2466 goto out;
2467 }
2468
2469 if(conn->http_proxy.host.rawalloc) {
2470 #ifdef CURL_DISABLE_HTTP
2471 /* asking for an HTTP proxy is a bit funny when HTTP is disabled... */
2472 result = CURLE_UNSUPPORTED_PROTOCOL;
2473 goto out;
2474 #else
2475 /* force this connection's protocol to become HTTP if compatible */
2476 if(!(conn->handler->protocol & PROTO_FAMILY_HTTP)) {
2477 if((conn->handler->flags & PROTOPT_PROXY_AS_HTTP) &&
2478 !conn->bits.tunnel_proxy)
2479 conn->handler = &Curl_handler_http;
2480 else
2481 /* if not converting to HTTP over the proxy, enforce tunneling */
2482 conn->bits.tunnel_proxy = TRUE;
2483 }
2484 conn->bits.httpproxy = TRUE;
2485 #endif
2486 }
2487 else {
2488 conn->bits.httpproxy = FALSE; /* not an HTTP proxy */
2489 conn->bits.tunnel_proxy = FALSE; /* no tunneling if not HTTP */
2490 }
2491
2492 if(conn->socks_proxy.host.rawalloc) {
2493 if(!conn->http_proxy.host.rawalloc) {
2494 /* once a socks proxy */
2495 if(!conn->socks_proxy.user) {
2496 conn->socks_proxy.user = conn->http_proxy.user;
2497 conn->http_proxy.user = NULL;
2498 Curl_safefree(conn->socks_proxy.passwd);
2499 conn->socks_proxy.passwd = conn->http_proxy.passwd;
2500 conn->http_proxy.passwd = NULL;
2501 }
2502 }
2503 conn->bits.socksproxy = TRUE;
2504 }
2505 else
2506 conn->bits.socksproxy = FALSE; /* not a socks proxy */
2507 }
2508 else {
2509 conn->bits.socksproxy = FALSE;
2510 conn->bits.httpproxy = FALSE;
2511 }
2512 conn->bits.proxy = conn->bits.httpproxy || conn->bits.socksproxy;
2513
2514 if(!conn->bits.proxy) {
2515 /* we are not using the proxy after all... */
2516 conn->bits.proxy = FALSE;
2517 conn->bits.httpproxy = FALSE;
2518 conn->bits.socksproxy = FALSE;
2519 conn->bits.proxy_user_passwd = FALSE;
2520 conn->bits.tunnel_proxy = FALSE;
2521 /* CURLPROXY_HTTPS does not have its own flag in conn->bits, yet we need
2522 to signal that CURLPROXY_HTTPS is not used for this connection */
2523 conn->http_proxy.proxytype = CURLPROXY_HTTP;
2524 }
2525
2526 out:
2527
2528 free(socksproxy);
2529 free(proxy);
2530 return result;
2531 }
2532 #endif /* CURL_DISABLE_PROXY */
2533
2534 /*
2535 * Curl_parse_login_details()
2536 *
2537 * This is used to parse a login string for username, password and options in
2538 * the following formats:
2539 *
2540 * user
2541 * user:password
2542 * user:password;options
2543 * user;options
2544 * user;options:password
2545 * :password
2546 * :password;options
2547 * ;options
2548 * ;options:password
2549 *
2550 * Parameters:
2551 *
2552 * login [in] - login string.
2553 * len [in] - length of the login string.
2554 * userp [in/out] - address where a pointer to newly allocated memory
2555 * holding the user will be stored upon completion.
2556 * passwdp [in/out] - address where a pointer to newly allocated memory
2557 * holding the password will be stored upon completion.
2558 * optionsp [in/out] - OPTIONAL address where a pointer to newly allocated
2559 * memory holding the options will be stored upon
2560 * completion.
2561 *
2562 * Returns CURLE_OK on success.
2563 */
2564 CURLcode Curl_parse_login_details(const char *login, const size_t len,
2565 char **userp, char **passwdp,
2566 char **optionsp)
2567 {
2568 char *ubuf = NULL;
2569 char *pbuf = NULL;
2570 const char *psep = NULL;
2571 const char *osep = NULL;
2572 size_t ulen;
2573 size_t plen;
2574 size_t olen;
2575
2576 DEBUGASSERT(userp);
2577 DEBUGASSERT(passwdp);
2578
2579 /* Attempt to find the password separator */
2580 psep = memchr(login, ':', len);
2581
2582 /* Attempt to find the options separator */
2583 if(optionsp)
2584 osep = memchr(login, ';', len);
2585
2586 /* Calculate the portion lengths */
2587 ulen = (psep ?
2588 (size_t)(osep && psep > osep ? osep - login : psep - login) :
2589 (osep ? (size_t)(osep - login) : len));
2590 plen = (psep ?
2591 (osep && osep > psep ? (size_t)(osep - psep) :
2592 (size_t)(login + len - psep)) - 1 : 0);
2593 olen = (osep ?
2594 (psep && psep > osep ? (size_t)(psep - osep) :
2595 (size_t)(login + len - osep)) - 1 : 0);
2596
2597 /* Clone the user portion buffer, which can be zero length */
2598 ubuf = Curl_memdup0(login, ulen);
2599 if(!ubuf)
2600 goto error;
2601
2602 /* Clone the password portion buffer */
2603 if(psep) {
2604 pbuf = Curl_memdup0(&psep[1], plen);
2605 if(!pbuf)
2606 goto error;
2607 }
2608
2609 /* Allocate the options portion buffer */
2610 if(optionsp) {
2611 char *obuf = NULL;
2612 if(olen) {
2613 obuf = Curl_memdup0(&osep[1], olen);
2614 if(!obuf)
2615 goto error;
2616 }
2617 *optionsp = obuf;
2618 }
2619 *userp = ubuf;
2620 *passwdp = pbuf;
2621 return CURLE_OK;
2622 error:
2623 free(ubuf);
2624 free(pbuf);
2625 return CURLE_OUT_OF_MEMORY;
2626 }
2627
2628 /*************************************************************
2629 * Figure out the remote port number and fix it in the URL
2630 *
2631 * No matter if we use a proxy or not, we have to figure out the remote
2632 * port number of various reasons.
2633 *
2634 * The port number embedded in the URL is replaced, if necessary.
2635 *************************************************************/
2636 static CURLcode parse_remote_port(struct Curl_easy *data,
2637 struct connectdata *conn)
2638 {
2639
2640 if(data->set.use_port && data->state.allow_port) {
2641 /* if set, we use this instead of the port possibly given in the URL */
2642 char portbuf[16];
2643 CURLUcode uc;
2644 conn->remote_port = data->set.use_port;
2645 msnprintf(portbuf, sizeof(portbuf), "%d", conn->remote_port);
2646 uc = curl_url_set(data->state.uh, CURLUPART_PORT, portbuf, 0);
2647 if(uc)
2648 return CURLE_OUT_OF_MEMORY;
2649 }
2650
2651 return CURLE_OK;
2652 }
2653
2654 static bool str_has_ctrl(const char *input)
2655 {
2656 const unsigned char *str = (const unsigned char *)input;
2657 while(*str) {
2658 if(*str < 0x20)
2659 return TRUE;
2660 str++;
2661 }
2662 return FALSE;
2663 }
2664
2665 /*
2666 * Override the login details from the URL with that in the CURLOPT_USERPWD
2667 * option or a .netrc file, if applicable.
2668 */
2669 static CURLcode override_login(struct Curl_easy *data,
2670 struct connectdata *conn)
2671 {
2672 CURLUcode uc;
2673 char **userp = &conn->user;
2674 char **passwdp = &conn->passwd;
2675 char **optionsp = &conn->options;
2676
2677 if(data->set.str[STRING_OPTIONS]) {
2678 free(*optionsp);
2679 *optionsp = strdup(data->set.str[STRING_OPTIONS]);
2680 if(!*optionsp)
2681 return CURLE_OUT_OF_MEMORY;
2682 }
2683
2684 #ifndef CURL_DISABLE_NETRC
2685 if(data->set.use_netrc == CURL_NETRC_REQUIRED) {
2686 Curl_safefree(*userp);
2687 Curl_safefree(*passwdp);
2688 }
2689 conn->bits.netrc = FALSE;
2690 if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) {
2691 int ret;
2692 bool url_provided = FALSE;
2693
2694 if(data->state.aptr.user &&
2695 (data->state.creds_from != CREDS_NETRC)) {
2696 /* there was a username with a length in the URL. Use the URL decoded
2697 version */
2698 userp = &data->state.aptr.user;
2699 url_provided = TRUE;
2700 }
2701
2702 if(!*passwdp) {
2703 ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
2704 userp, passwdp,
2705 data->set.str[STRING_NETRC_FILE]);
2706 if(ret > 0) {
2707 infof(data, "Couldn't find host %s in the %s file; using defaults",
2708 conn->host.name,
2709 (data->set.str[STRING_NETRC_FILE] ?
2710 data->set.str[STRING_NETRC_FILE] : ".netrc"));
2711 }
2712 else if(ret < 0) {
2713 failf(data, ".netrc parser error");
2714 return CURLE_READ_ERROR;
2715 }
2716 else {
2717 if(!(conn->handler->flags&PROTOPT_USERPWDCTRL)) {
2718 /* if the protocol can't handle control codes in credentials, make
2719 sure there are none */
2720 if(str_has_ctrl(*userp) || str_has_ctrl(*passwdp)) {
2721 failf(data, "control code detected in .netrc credentials");
2722 return CURLE_READ_ERROR;
2723 }
2724 }
2725 /* set bits.netrc TRUE to remember that we got the name from a .netrc
2726 file, so that it is safe to use even if we followed a Location: to a
2727 different host or similar. */
2728 conn->bits.netrc = TRUE;
2729 }
2730 }
2731 if(url_provided) {
2732 Curl_safefree(conn->user);
2733 conn->user = strdup(*userp);
2734 if(!conn->user)
2735 return CURLE_OUT_OF_MEMORY;
2736 }
2737 /* no user was set but a password, set a blank user */
2738 if(!*userp && *passwdp) {
2739 *userp = strdup("");
2740 if(!*userp)
2741 return CURLE_OUT_OF_MEMORY;
2742 }
2743 }
2744 #endif
2745
2746 /* for updated strings, we update them in the URL */
2747 if(*userp) {
2748 CURLcode result;
2749 if(data->state.aptr.user != *userp) {
2750 /* nothing to do then */
2751 result = Curl_setstropt(&data->state.aptr.user, *userp);
2752 if(result)
2753 return result;
2754 data->state.creds_from = CREDS_NETRC;
2755 }
2756 }
2757 if(data->state.aptr.user) {
2758 uc = curl_url_set(data->state.uh, CURLUPART_USER, data->state.aptr.user,
2759 CURLU_URLENCODE);
2760 if(uc)
2761 return Curl_uc_to_curlcode(uc);
2762 if(!*userp) {
2763 *userp = strdup(data->state.aptr.user);
2764 if(!*userp)
2765 return CURLE_OUT_OF_MEMORY;
2766 }
2767 }
2768 if(*passwdp) {
2769 CURLcode result = Curl_setstropt(&data->state.aptr.passwd, *passwdp);
2770 if(result)
2771 return result;
2772 data->state.creds_from = CREDS_NETRC;
2773 }
2774 if(data->state.aptr.passwd) {
2775 uc = curl_url_set(data->state.uh, CURLUPART_PASSWORD,
2776 data->state.aptr.passwd, CURLU_URLENCODE);
2777 if(uc)
2778 return Curl_uc_to_curlcode(uc);
2779 if(!*passwdp) {
2780 *passwdp = strdup(data->state.aptr.passwd);
2781 if(!*passwdp)
2782 return CURLE_OUT_OF_MEMORY;
2783 }
2784 }
2785
2786 return CURLE_OK;
2787 }
2788
2789 /*
2790 * Set the login details so they are available in the connection
2791 */
2792 static CURLcode set_login(struct Curl_easy *data,
2793 struct connectdata *conn)
2794 {
2795 CURLcode result = CURLE_OK;
2796 const char *setuser = CURL_DEFAULT_USER;
2797 const char *setpasswd = CURL_DEFAULT_PASSWORD;
2798
2799 /* If our protocol needs a password and we have none, use the defaults */
2800 if((conn->handler->flags & PROTOPT_NEEDSPWD) && !data->state.aptr.user)
2801 ;
2802 else {
2803 setuser = "";
2804 setpasswd = "";
2805 }
2806 /* Store the default user */
2807 if(!conn->user) {
2808 conn->user = strdup(setuser);
2809 if(!conn->user)
2810 return CURLE_OUT_OF_MEMORY;
2811 }
2812
2813 /* Store the default password */
2814 if(!conn->passwd) {
2815 conn->passwd = strdup(setpasswd);
2816 if(!conn->passwd)
2817 result = CURLE_OUT_OF_MEMORY;
2818 }
2819
2820 return result;
2821 }
2822
2823 /*
2824 * Parses a "host:port" string to connect to.
2825 * The hostname and the port may be empty; in this case, NULL is returned for
2826 * the hostname and -1 for the port.
2827 */
2828 static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
2829 const char *host,
2830 char **hostname_result,
2831 int *port_result)
2832 {
2833 char *host_dup;
2834 char *hostptr;
2835 char *host_portno;
2836 char *portptr;
2837 int port = -1;
2838 CURLcode result = CURLE_OK;
2839
2840 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
2841 (void) data;
2842 #endif
2843
2844 *hostname_result = NULL;
2845 *port_result = -1;
2846
2847 if(!host || !*host)
2848 return CURLE_OK;
2849
2850 host_dup = strdup(host);
2851 if(!host_dup)
2852 return CURLE_OUT_OF_MEMORY;
2853
2854 hostptr = host_dup;
2855
2856 /* start scanning for port number at this point */
2857 portptr = hostptr;
2858
2859 /* detect and extract RFC6874-style IPv6-addresses */
2860 if(*hostptr == '[') {
2861 #ifdef USE_IPV6
2862 char *ptr = ++hostptr; /* advance beyond the initial bracket */
2863 while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':') || (*ptr == '.')))
2864 ptr++;
2865 if(*ptr == '%') {
2866 /* There might be a zone identifier */
2867 if(strncmp("%25", ptr, 3))
2868 infof(data, "Please URL encode %% as %%25, see RFC 6874.");
2869 ptr++;
2870 /* Allow unreserved characters as defined in RFC 3986 */
2871 while(*ptr && (ISALPHA(*ptr) || ISXDIGIT(*ptr) || (*ptr == '-') ||
2872 (*ptr == '.') || (*ptr == '_') || (*ptr == '~')))
2873 ptr++;
2874 }
2875 if(*ptr == ']')
2876 /* yeps, it ended nicely with a bracket as well */
2877 *ptr++ = '\0';
2878 else
2879 infof(data, "Invalid IPv6 address format");
2880 portptr = ptr;
2881 /* Note that if this did not end with a bracket, we still advanced the
2882 * hostptr first, but I cannot see anything wrong with that as no host
2883 * name nor a numeric can legally start with a bracket.
2884 */
2885 #else
2886 failf(data, "Use of IPv6 in *_CONNECT_TO without IPv6 support built-in");
2887 result = CURLE_NOT_BUILT_IN;
2888 goto error;
2889 #endif
2890 }
2891
2892 /* Get port number off server.com:1080 */
2893 host_portno = strchr(portptr, ':');
2894 if(host_portno) {
2895 char *endp = NULL;
2896 *host_portno = '\0'; /* cut off number from hostname */
2897 host_portno++;
2898 if(*host_portno) {
2899 long portparse = strtol(host_portno, &endp, 10);
2900 if((endp && *endp) || (portparse < 0) || (portparse > 65535)) {
2901 failf(data, "No valid port number in connect to host string (%s)",
2902 host_portno);
2903 result = CURLE_SETOPT_OPTION_SYNTAX;
2904 goto error;
2905 }
2906 else
2907 port = (int)portparse; /* we know it will fit */
2908 }
2909 }
2910
2911 /* now, clone the cleaned hostname */
2912 DEBUGASSERT(hostptr);
2913 *hostname_result = strdup(hostptr);
2914 if(!*hostname_result) {
2915 result = CURLE_OUT_OF_MEMORY;
2916 goto error;
2917 }
2918
2919 *port_result = port;
2920
2921 error:
2922 free(host_dup);
2923 return result;
2924 }
2925
2926 /*
2927 * Parses one "connect to" string in the form:
2928 * "HOST:PORT:CONNECT-TO-HOST:CONNECT-TO-PORT".
2929 */
2930 static CURLcode parse_connect_to_string(struct Curl_easy *data,
2931 struct connectdata *conn,
2932 const char *conn_to_host,
2933 char **host_result,
2934 int *port_result)
2935 {
2936 CURLcode result = CURLE_OK;
2937 const char *ptr = conn_to_host;
2938 bool host_match = FALSE;
2939 bool port_match = FALSE;
2940
2941 *host_result = NULL;
2942 *port_result = -1;
2943
2944 if(*ptr == ':') {
2945 /* an empty hostname always matches */
2946 host_match = TRUE;
2947 ptr++;
2948 }
2949 else {
2950 /* check whether the URL's hostname matches */
2951 size_t hostname_to_match_len;
2952 char *hostname_to_match = aprintf("%s%s%s",
2953 conn->bits.ipv6_ip ? "[" : "",
2954 conn->host.name,
2955 conn->bits.ipv6_ip ? "]" : "");
2956 if(!hostname_to_match)
2957 return CURLE_OUT_OF_MEMORY;
2958 hostname_to_match_len = strlen(hostname_to_match);
2959 host_match = strncasecompare(ptr, hostname_to_match,
2960 hostname_to_match_len);
2961 free(hostname_to_match);
2962 ptr += hostname_to_match_len;
2963
2964 host_match = host_match && *ptr == ':';
2965 ptr++;
2966 }
2967
2968 if(host_match) {
2969 if(*ptr == ':') {
2970 /* an empty port always matches */
2971 port_match = TRUE;
2972 ptr++;
2973 }
2974 else {
2975 /* check whether the URL's port matches */
2976 char *ptr_next = strchr(ptr, ':');
2977 if(ptr_next) {
2978 char *endp = NULL;
2979 long port_to_match = strtol(ptr, &endp, 10);
2980 if((endp == ptr_next) && (port_to_match == conn->remote_port)) {
2981 port_match = TRUE;
2982 ptr = ptr_next + 1;
2983 }
2984 }
2985 }
2986 }
2987
2988 if(host_match && port_match) {
2989 /* parse the hostname and port to connect to */
2990 result = parse_connect_to_host_port(data, ptr, host_result, port_result);
2991 }
2992
2993 return result;
2994 }
2995
2996 /*
2997 * Processes all strings in the "connect to" slist, and uses the "connect
2998 * to host" and "connect to port" of the first string that matches.
2999 */
3000 static CURLcode parse_connect_to_slist(struct Curl_easy *data,
3001 struct connectdata *conn,
3002 struct curl_slist *conn_to_host)
3003 {
3004 CURLcode result = CURLE_OK;
3005 char *host = NULL;
3006 int port = -1;
3007
3008 while(conn_to_host && !host && port == -1) {
3009 result = parse_connect_to_string(data, conn, conn_to_host->data,
3010 &host, &port);
3011 if(result)
3012 return result;
3013
3014 if(host && *host) {
3015 conn->conn_to_host.rawalloc = host;
3016 conn->conn_to_host.name = host;
3017 conn->bits.conn_to_host = TRUE;
3018
3019 infof(data, "Connecting to hostname: %s", host);
3020 }
3021 else {
3022 /* no "connect to host" */
3023 conn->bits.conn_to_host = FALSE;
3024 Curl_safefree(host);
3025 }
3026
3027 if(port >= 0) {
3028 conn->conn_to_port = port;
3029 conn->bits.conn_to_port = TRUE;
3030 infof(data, "Connecting to port: %d", port);
3031 }
3032 else {
3033 /* no "connect to port" */
3034 conn->bits.conn_to_port = FALSE;
3035 port = -1;
3036 }
3037
3038 conn_to_host = conn_to_host->next;
3039 }
3040
3041 #ifndef CURL_DISABLE_ALTSVC
3042 if(data->asi && !host && (port == -1) &&
3043 ((conn->handler->protocol == CURLPROTO_HTTPS) ||
3044 #ifdef DEBUGBUILD
3045 /* allow debug builds to circumvent the HTTPS restriction */
3046 getenv("CURL_ALTSVC_HTTP")
3047 #else
3048 0
3049 #endif
3050 )) {
3051 /* no connect_to match, try alt-svc! */
3052 enum alpnid srcalpnid = ALPN_none;
3053 bool use_alt_svc = FALSE;
3054 bool hit = FALSE;
3055 struct altsvc *as = NULL;
3056 const int allowed_versions = ( ALPN_h1
3057 #ifdef USE_HTTP2
3058 | ALPN_h2
3059 #endif
3060 #ifdef USE_HTTP3
3061 | ALPN_h3
3062 #endif
3063 ) & data->asi->flags;
3064 static enum alpnid alpn_ids[] = {
3065 #ifdef USE_HTTP3
3066 ALPN_h3,
3067 #endif
3068 #ifdef USE_HTTP2
3069 ALPN_h2,
3070 #endif
3071 ALPN_h1,
3072 };
3073 size_t i;
3074
3075 switch(data->state.httpwant) {
3076 case CURL_HTTP_VERSION_1_0:
3077 break;
3078 case CURL_HTTP_VERSION_1_1:
3079 use_alt_svc = TRUE;
3080 srcalpnid = ALPN_h1; /* only regard alt-svc advice for http/1.1 */
3081 break;
3082 case CURL_HTTP_VERSION_2_0:
3083 use_alt_svc = TRUE;
3084 srcalpnid = ALPN_h2; /* only regard alt-svc advice for h2 */
3085 break;
3086 case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
3087 break;
3088 case CURL_HTTP_VERSION_3:
3089 use_alt_svc = TRUE;
3090 srcalpnid = ALPN_h3; /* only regard alt-svc advice for h3 */
3091 break;
3092 case CURL_HTTP_VERSION_3ONLY:
3093 break;
3094 default: /* no specific HTTP version wanted, look at all of alt-svc */
3095 use_alt_svc = TRUE;
3096 srcalpnid = ALPN_none;
3097 break;
3098 }
3099 if(!use_alt_svc)
3100 return CURLE_OK;
3101
3102 host = conn->host.rawalloc;
3103 DEBUGF(infof(data, "check Alt-Svc for host %s", host));
3104 if(srcalpnid == ALPN_none) {
3105 /* scan all alt-svc protocol ids in order or relevance */
3106 for(i = 0; !hit && (i < ARRAYSIZE(alpn_ids)); ++i) {
3107 srcalpnid = alpn_ids[i];
3108 hit = Curl_altsvc_lookup(data->asi,
3109 srcalpnid, host, conn->remote_port, /* from */
3110 &as /* to */,
3111 allowed_versions);
3112 }
3113 }
3114 else {
3115 /* look for a specific alt-svc protocol id */
3116 hit = Curl_altsvc_lookup(data->asi,
3117 srcalpnid, host, conn->remote_port, /* from */
3118 &as /* to */,
3119 allowed_versions);
3120 }
3121
3122
3123 if(hit) {
3124 char *hostd = strdup((char *)as->dst.host);
3125 if(!hostd)
3126 return CURLE_OUT_OF_MEMORY;
3127 conn->conn_to_host.rawalloc = hostd;
3128 conn->conn_to_host.name = hostd;
3129 conn->bits.conn_to_host = TRUE;
3130 conn->conn_to_port = as->dst.port;
3131 conn->bits.conn_to_port = TRUE;
3132 conn->bits.altused = TRUE;
3133 infof(data, "Alt-svc connecting from [%s]%s:%d to [%s]%s:%d",
3134 Curl_alpnid2str(srcalpnid), host, conn->remote_port,
3135 Curl_alpnid2str(as->dst.alpnid), hostd, as->dst.port);
3136 if(srcalpnid != as->dst.alpnid) {
3137 /* protocol version switch */
3138 switch(as->dst.alpnid) {
3139 case ALPN_h1:
3140 conn->httpversion = 11;
3141 break;
3142 case ALPN_h2:
3143 conn->httpversion = 20;
3144 break;
3145 case ALPN_h3:
3146 conn->transport = TRNSPRT_QUIC;
3147 conn->httpversion = 30;
3148 break;
3149 default: /* should not be possible */
3150 break;
3151 }
3152 }
3153 }
3154 }
3155 #endif
3156
3157 return result;
3158 }
3159
3160 #ifdef USE_UNIX_SOCKETS
3161 static CURLcode resolve_unix(struct Curl_easy *data,
3162 struct connectdata *conn,
3163 char *unix_path)
3164 {
3165 struct Curl_dns_entry *hostaddr = NULL;
3166 bool longpath = FALSE;
3167
3168 DEBUGASSERT(unix_path);
3169 DEBUGASSERT(conn->dns_entry == NULL);
3170
3171 /* Unix domain sockets are local. The host gets ignored, just use the
3172 * specified domain socket address. Do not cache "DNS entries". There is
3173 * no DNS involved and we already have the filesystem path available. */
3174 hostaddr = calloc(1, sizeof(struct Curl_dns_entry));
3175 if(!hostaddr)
3176 return CURLE_OUT_OF_MEMORY;
3177
3178 hostaddr->addr = Curl_unix2addr(unix_path, &longpath,
3179 conn->bits.abstract_unix_socket);
3180 if(!hostaddr->addr) {
3181 if(longpath)
3182 /* Long paths are not supported for now */
3183 failf(data, "Unix socket path too long: '%s'", unix_path);
3184 free(hostaddr);
3185 return longpath ? CURLE_COULDNT_RESOLVE_HOST : CURLE_OUT_OF_MEMORY;
3186 }
3187
3188 hostaddr->refcount = 1; /* connection is the only one holding this */
3189 conn->dns_entry = hostaddr;
3190 return CURLE_OK;
3191 }
3192 #endif
3193
3194 /*************************************************************
3195 * Resolve the address of the server or proxy
3196 *************************************************************/
3197 static CURLcode resolve_server(struct Curl_easy *data,
3198 struct connectdata *conn,
3199 bool *async)
3200 {
3201 struct hostname *ehost;
3202 timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
3203 const char *peertype = "host";
3204 int rc;
3205 #ifdef USE_UNIX_SOCKETS
3206 char *unix_path = conn->unix_domain_socket;
3207
3208 #ifndef CURL_DISABLE_PROXY
3209 if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
3210 !strncmp(UNIX_SOCKET_PREFIX"/",
3211 conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
3212 unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
3213 #endif
3214
3215 if(unix_path) {
3216 /* TODO, this only works if previous transport is TRNSPRT_TCP. Check it? */
3217 conn->transport = TRNSPRT_UNIX;
3218 return resolve_unix(data, conn, unix_path);
3219 }
3220 #endif
3221
3222 DEBUGASSERT(conn->dns_entry == NULL);
3223
3224 #ifndef CURL_DISABLE_PROXY
3225 if(CONN_IS_PROXIED(conn)) {
3226 ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
3227 &conn->http_proxy.host;
3228 peertype = "proxy";
3229 }
3230 else
3231 #endif
3232 {
3233 ehost = conn->bits.conn_to_host ? &conn->conn_to_host : &conn->host;
3234 /* If not connecting via a proxy, extract the port from the URL, if it is
3235 * there, thus overriding any defaults that might have been set above. */
3236 conn->primary.remote_port = conn->bits.conn_to_port ? conn->conn_to_port :
3237 conn->remote_port;
3238 }
3239
3240 /* Resolve target host right on */
3241 conn->hostname_resolve = strdup(ehost->name);
3242 if(!conn->hostname_resolve)
3243 return CURLE_OUT_OF_MEMORY;
3244
3245 rc = Curl_resolv_timeout(data, conn->hostname_resolve,
3246 conn->primary.remote_port,
3247 &conn->dns_entry, timeout_ms);
3248 if(rc == CURLRESOLV_PENDING)
3249 *async = TRUE;
3250 else if(rc == CURLRESOLV_TIMEDOUT) {
3251 failf(data, "Failed to resolve %s '%s' with timeout after %"
3252 FMT_TIMEDIFF_T " ms", peertype, ehost->dispname,
3253 Curl_timediff(Curl_now(), data->progress.t_startsingle));
3254 return CURLE_OPERATION_TIMEDOUT;
3255 }
3256 else if(!conn->dns_entry) {
3257 failf(data, "Could not resolve %s: %s", peertype, ehost->dispname);
3258 return CURLE_COULDNT_RESOLVE_HOST;
3259 }
3260
3261 return CURLE_OK;
3262 }
3263
3264 /*
3265 * Cleanup the connection `temp`, just allocated for `data`, before using the
3266 * previously `existing` one for `data`. All relevant info is copied over
3267 * and `temp` is freed.
3268 */
3269 static void reuse_conn(struct Curl_easy *data,
3270 struct connectdata *temp,
3271 struct connectdata *existing)
3272 {
3273 /* get the user+password information from the temp struct since it may
3274 * be new for this request even when we reuse an existing connection */
3275 if(temp->user) {
3276 /* use the new username and password though */
3277 Curl_safefree(existing->user);
3278 Curl_safefree(existing->passwd);
3279 existing->user = temp->user;
3280 existing->passwd = temp->passwd;
3281 temp->user = NULL;
3282 temp->passwd = NULL;
3283 }
3284
3285 #ifndef CURL_DISABLE_PROXY
3286 existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd;
3287 if(existing->bits.proxy_user_passwd) {
3288 /* use the new proxy username and proxy password though */
3289 Curl_safefree(existing->http_proxy.user);
3290 Curl_safefree(existing->socks_proxy.user);
3291 Curl_safefree(existing->http_proxy.passwd);
3292 Curl_safefree(existing->socks_proxy.passwd);
3293 existing->http_proxy.user = temp->http_proxy.user;
3294 existing->socks_proxy.user = temp->socks_proxy.user;
3295 existing->http_proxy.passwd = temp->http_proxy.passwd;
3296 existing->socks_proxy.passwd = temp->socks_proxy.passwd;
3297 temp->http_proxy.user = NULL;
3298 temp->socks_proxy.user = NULL;
3299 temp->http_proxy.passwd = NULL;
3300 temp->socks_proxy.passwd = NULL;
3301 }
3302 #endif
3303
3304 /* Finding a connection for reuse in the cpool matches, among other
3305 * things on the "remote-relevant" hostname. This is not necessarily
3306 * the authority of the URL, e.g. conn->host. For example:
3307 * - we use a proxy (not tunneling). we want to send all requests
3308 * that use the same proxy on this connection.
3309 * - we have a "connect-to" setting that may redirect the hostname of
3310 * a new request to the same remote endpoint of an existing conn.
3311 * We want to reuse an existing conn to the remote endpoint.
3312 * Since connection reuse does not match on conn->host necessarily, we
3313 * switch `existing` conn to `temp` conn's host settings.
3314 * TODO: is this correct in the case of TLS connections that have
3315 * used the original hostname in SNI to negotiate? Do we send
3316 * requests for another host through the different SNI?
3317 */
3318 Curl_free_idnconverted_hostname(&existing->host);
3319 Curl_free_idnconverted_hostname(&existing->conn_to_host);
3320 Curl_safefree(existing->host.rawalloc);
3321 Curl_safefree(existing->conn_to_host.rawalloc);
3322 existing->host = temp->host;
3323 temp->host.rawalloc = NULL;
3324 temp->host.encalloc = NULL;
3325 existing->conn_to_host = temp->conn_to_host;
3326 temp->conn_to_host.rawalloc = NULL;
3327 existing->conn_to_port = temp->conn_to_port;
3328 existing->remote_port = temp->remote_port;
3329 Curl_safefree(existing->hostname_resolve);
3330
3331 existing->hostname_resolve = temp->hostname_resolve;
3332 temp->hostname_resolve = NULL;
3333
3334 /* reuse init */
3335 existing->bits.reuse = TRUE; /* yes, we are reusing here */
3336
3337 Curl_conn_free(data, temp);
3338 }
3339
3340 /**
3341 * create_conn() sets up a new connectdata struct, or reuses an already
3342 * existing one, and resolves hostname.
3343 *
3344 * if this function returns CURLE_OK and *async is set to TRUE, the resolve
3345 * response will be coming asynchronously. If *async is FALSE, the name is
3346 * already resolved.
3347 *
3348 * @param data The sessionhandle pointer
3349 * @param in_connect is set to the next connection data pointer
3350 * @param async is set TRUE when an async DNS resolution is pending
3351 * @see Curl_setup_conn()
3352 *
3353 */
3354
3355 static CURLcode create_conn(struct Curl_easy *data,
3356 struct connectdata **in_connect,
3357 bool *async)
3358 {
3359 CURLcode result = CURLE_OK;
3360 struct connectdata *conn;
3361 struct connectdata *existing = NULL;
3362 bool reuse;
3363 bool connections_available = TRUE;
3364 bool force_reuse = FALSE;
3365 bool waitpipe = FALSE;
3366
3367 *async = FALSE;
3368 *in_connect = NULL;
3369
3370 /*************************************************************
3371 * Check input data
3372 *************************************************************/
3373 if(!data->state.url) {
3374 result = CURLE_URL_MALFORMAT;
3375 goto out;
3376 }
3377
3378 /* First, split up the current URL in parts so that we can use the
3379 parts for checking against the already present connections. In order
3380 to not have to modify everything at once, we allocate a temporary
3381 connection data struct and fill in for comparison purposes. */
3382 conn = allocate_conn(data);
3383
3384 if(!conn) {
3385 result = CURLE_OUT_OF_MEMORY;
3386 goto out;
3387 }
3388
3389 /* We must set the return variable as soon as possible, so that our
3390 parent can cleanup any possible allocs we may have done before
3391 any failure */
3392 *in_connect = conn;
3393
3394 result = parseurlandfillconn(data, conn);
3395 if(result)
3396 goto out;
3397
3398 if(data->set.str[STRING_SASL_AUTHZID]) {
3399 conn->sasl_authzid = strdup(data->set.str[STRING_SASL_AUTHZID]);
3400 if(!conn->sasl_authzid) {
3401 result = CURLE_OUT_OF_MEMORY;
3402 goto out;
3403 }
3404 }
3405
3406 if(data->set.str[STRING_BEARER]) {
3407 conn->oauth_bearer = strdup(data->set.str[STRING_BEARER]);
3408 if(!conn->oauth_bearer) {
3409 result = CURLE_OUT_OF_MEMORY;
3410 goto out;
3411 }
3412 }
3413
3414 #ifdef USE_UNIX_SOCKETS
3415 if(data->set.str[STRING_UNIX_SOCKET_PATH]) {
3416 conn->unix_domain_socket = strdup(data->set.str[STRING_UNIX_SOCKET_PATH]);
3417 if(!conn->unix_domain_socket) {
3418 result = CURLE_OUT_OF_MEMORY;
3419 goto out;
3420 }
3421 conn->bits.abstract_unix_socket = data->set.abstract_unix_socket;
3422 }
3423 #endif
3424
3425 /* After the Unix socket init but before the proxy vars are used, parse and
3426 initialize the proxy vars */
3427 #ifndef CURL_DISABLE_PROXY
3428 result = create_conn_helper_init_proxy(data, conn);
3429 if(result)
3430 goto out;
3431
3432 /*************************************************************
3433 * If the protocol is using SSL and HTTP proxy is used, we set
3434 * the tunnel_proxy bit.
3435 *************************************************************/
3436 if((conn->given->flags&PROTOPT_SSL) && conn->bits.httpproxy)
3437 conn->bits.tunnel_proxy = TRUE;
3438 #endif
3439
3440 /*************************************************************
3441 * Figure out the remote port number and fix it in the URL
3442 *************************************************************/
3443 result = parse_remote_port(data, conn);
3444 if(result)
3445 goto out;
3446
3447 /* Check for overridden login details and set them accordingly so that
3448 they are known when protocol->setup_connection is called! */
3449 result = override_login(data, conn);
3450 if(result)
3451 goto out;
3452
3453 result = set_login(data, conn); /* default credentials */
3454 if(result)
3455 goto out;
3456
3457 /*************************************************************
3458 * Process the "connect to" linked list of hostname/port mappings.
3459 * Do this after the remote port number has been fixed in the URL.
3460 *************************************************************/
3461 result = parse_connect_to_slist(data, conn, data->set.connect_to);
3462 if(result)
3463 goto out;
3464
3465 /*************************************************************
3466 * IDN-convert the proxy hostnames
3467 *************************************************************/
3468 #ifndef CURL_DISABLE_PROXY
3469 if(conn->bits.httpproxy) {
3470 result = Curl_idnconvert_hostname(&conn->http_proxy.host);
3471 if(result)
3472 return result;
3473 }
3474 if(conn->bits.socksproxy) {
3475 result = Curl_idnconvert_hostname(&conn->socks_proxy.host);
3476 if(result)
3477 return result;
3478 }
3479 #endif
3480 if(conn->bits.conn_to_host) {
3481 result = Curl_idnconvert_hostname(&conn->conn_to_host);
3482 if(result)
3483 return result;
3484 }
3485
3486 /*************************************************************
3487 * Check whether the host and the "connect to host" are equal.
3488 * Do this after the hostnames have been IDN-converted.
3489 *************************************************************/
3490 if(conn->bits.conn_to_host &&
3491 strcasecompare(conn->conn_to_host.name, conn->host.name)) {
3492 conn->bits.conn_to_host = FALSE;
3493 }
3494
3495 /*************************************************************
3496 * Check whether the port and the "connect to port" are equal.
3497 * Do this after the remote port number has been fixed in the URL.
3498 *************************************************************/
3499 if(conn->bits.conn_to_port && conn->conn_to_port == conn->remote_port) {
3500 conn->bits.conn_to_port = FALSE;
3501 }
3502
3503 #ifndef CURL_DISABLE_PROXY
3504 /*************************************************************
3505 * If the "connect to" feature is used with an HTTP proxy,
3506 * we set the tunnel_proxy bit.
3507 *************************************************************/
3508 if((conn->bits.conn_to_host || conn->bits.conn_to_port) &&
3509 conn->bits.httpproxy)
3510 conn->bits.tunnel_proxy = TRUE;
3511 #endif
3512
3513 /*************************************************************
3514 * Setup internals depending on protocol. Needs to be done after
3515 * we figured out what/if proxy to use.
3516 *************************************************************/
3517 result = setup_connection_internals(data, conn);
3518 if(result)
3519 goto out;
3520
3521 /***********************************************************************
3522 * file: is a special case in that it does not need a network connection
3523 ***********************************************************************/
3524 #ifndef CURL_DISABLE_FILE
3525 if(conn->handler->flags & PROTOPT_NONETWORK) {
3526 bool done;
3527 /* this is supposed to be the connect function so we better at least check
3528 that the file is present here! */
3529 DEBUGASSERT(conn->handler->connect_it);
3530 data->info.conn_scheme = conn->handler->scheme;
3531 /* conn_protocol can only provide "old" protocols */
3532 data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
3533 result = conn->handler->connect_it(data, &done);
3534
3535 /* Setup a "faked" transfer that will do nothing */
3536 if(!result) {
3537 Curl_attach_connection(data, conn);
3538 result = Curl_cpool_add_conn(data, conn);
3539 if(result)
3540 goto out;
3541
3542 /*
3543 * Setup whatever necessary for a resumed transfer
3544 */
3545 result = setup_range(data);
3546 if(result) {
3547 DEBUGASSERT(conn->handler->done);
3548 /* we ignore the return code for the protocol-specific DONE */
3549 (void)conn->handler->done(data, result, FALSE);
3550 goto out;
3551 }
3552 Curl_xfer_setup_nop(data);
3553 }
3554
3555 /* since we skip do_init() */
3556 Curl_init_do(data, conn);
3557
3558 goto out;
3559 }
3560 #endif
3561
3562 /* Setup filter for network connections */
3563 conn->recv[FIRSTSOCKET] = Curl_cf_recv;
3564 conn->send[FIRSTSOCKET] = Curl_cf_send;
3565 conn->recv[SECONDARYSOCKET] = Curl_cf_recv;
3566 conn->send[SECONDARYSOCKET] = Curl_cf_send;
3567 conn->bits.tcp_fastopen = data->set.tcp_fastopen;
3568
3569 /* Complete the easy's SSL configuration for connection cache matching */
3570 result = Curl_ssl_easy_config_complete(data);
3571 if(result)
3572 goto out;
3573
3574 /* FIXME: do we really want to run this every time we add a transfer? */
3575 Curl_cpool_prune_dead(data);
3576
3577 /*************************************************************
3578 * Check the current list of connections to see if we can
3579 * reuse an already existing one or if we have to create a
3580 * new one.
3581 *************************************************************/
3582
3583 DEBUGASSERT(conn->user);
3584 DEBUGASSERT(conn->passwd);
3585
3586 /* reuse_fresh is TRUE if we are told to use a new connection by force, but
3587 we only acknowledge this option if this is not a reused connection
3588 already (which happens due to follow-location or during an HTTP
3589 authentication phase). CONNECT_ONLY transfers also refuse reuse. */
3590 if((data->set.reuse_fresh && !data->state.followlocation) ||
3591 data->set.connect_only)
3592 reuse = FALSE;
3593 else
3594 reuse = ConnectionExists(data, conn, &existing, &force_reuse, &waitpipe);
3595
3596 if(reuse) {
3597 /*
3598 * We already have a connection for this, we got the former connection in
3599 * `existing` and thus we need to cleanup the one we just
3600 * allocated before we can move along and use `existing`.
3601 */
3602 reuse_conn(data, conn, existing);
3603 conn = existing;
3604 *in_connect = conn;
3605
3606 #ifndef CURL_DISABLE_PROXY
3607 infof(data, "Re-using existing connection with %s %s",
3608 conn->bits.proxy ? "proxy" : "host",
3609 conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
3610 conn->http_proxy.host.name ? conn->http_proxy.host.dispname :
3611 conn->host.dispname);
3612 #else
3613 infof(data, "Re-using existing connection with host %s",
3614 conn->host.dispname);
3615 #endif
3616 }
3617 else {
3618 /* We have decided that we want a new connection. However, we may not
3619 be able to do that if we have reached the limit of how many
3620 connections we are allowed to open. */
3621
3622 if(conn->handler->flags & PROTOPT_ALPN) {
3623 /* The protocol wants it, so set the bits if enabled in the easy handle
3624 (default) */
3625 if(data->set.ssl_enable_alpn)
3626 conn->bits.tls_enable_alpn = TRUE;
3627 }
3628
3629 if(waitpipe)
3630 /* There is a connection that *might* become usable for multiplexing
3631 "soon", and we wait for that */
3632 connections_available = FALSE;
3633 else {
3634 switch(Curl_cpool_check_limits(data, conn)) {
3635 case CPOOL_LIMIT_DEST:
3636 infof(data, "No more connections allowed to host");
3637 connections_available = FALSE;
3638 break;
3639 case CPOOL_LIMIT_TOTAL:
3640 #ifndef CURL_DISABLE_DOH
3641 if(data->set.dohfor_mid >= 0)
3642 infof(data, "Allowing DoH to override max connection limit");
3643 else
3644 #endif
3645 {
3646 infof(data, "No connections available in cache");
3647 connections_available = FALSE;
3648 }
3649 break;
3650 default:
3651 break;
3652 }
3653 }
3654
3655 if(!connections_available) {
3656 infof(data, "No connections available.");
3657
3658 Curl_conn_free(data, conn);
3659 *in_connect = NULL;
3660
3661 result = CURLE_NO_CONNECTION_AVAILABLE;
3662 goto out;
3663 }
3664 else {
3665 /*
3666 * This is a brand new connection, so let's store it in the connection
3667 * cache of ours!
3668 */
3669 result = Curl_ssl_conn_config_init(data, conn);
3670 if(result) {
3671 DEBUGF(fprintf(stderr, "Error: init connection ssl config\n"));
3672 goto out;
3673 }
3674
3675 Curl_attach_connection(data, conn);
3676 result = Curl_cpool_add_conn(data, conn);
3677 if(result)
3678 goto out;
3679 }
3680
3681 #if defined(USE_NTLM)
3682 /* If NTLM is requested in a part of this connection, make sure we do not
3683 assume the state is fine as this is a fresh connection and NTLM is
3684 connection based. */
3685 if((data->state.authhost.picked & CURLAUTH_NTLM) &&
3686 data->state.authhost.done) {
3687 infof(data, "NTLM picked AND auth done set, clear picked");
3688 data->state.authhost.picked = CURLAUTH_NONE;
3689 data->state.authhost.done = FALSE;
3690 }
3691
3692 if((data->state.authproxy.picked & CURLAUTH_NTLM) &&
3693 data->state.authproxy.done) {
3694 infof(data, "NTLM-proxy picked AND auth done set, clear picked");
3695 data->state.authproxy.picked = CURLAUTH_NONE;
3696 data->state.authproxy.done = FALSE;
3697 }
3698 #endif
3699 }
3700
3701 /* Setup and init stuff before DO starts, in preparing for the transfer. */
3702 Curl_init_do(data, conn);
3703
3704 /*
3705 * Setup whatever necessary for a resumed transfer
3706 */
3707 result = setup_range(data);
3708 if(result)
3709 goto out;
3710
3711 /* Continue connectdata initialization here. */
3712
3713 if(conn->bits.reuse) {
3714 /* We are reusing the connection - no need to resolve anything, and
3715 idnconvert_hostname() was called already in create_conn() for the reuse
3716 case. */
3717 *async = FALSE;
3718 }
3719 else {
3720 /*************************************************************
3721 * Resolve the address of the server or proxy
3722 *************************************************************/
3723 result = resolve_server(data, conn, async);
3724 if(result)
3725 goto out;
3726 }
3727
3728 /* persist the scheme and handler the transfer is using */
3729 data->info.conn_scheme = conn->handler->scheme;
3730 /* conn_protocol can only provide "old" protocols */
3731 data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
3732 data->info.used_proxy =
3733 #ifdef CURL_DISABLE_PROXY
3734 0
3735 #else
3736 conn->bits.proxy
3737 #endif
3738 ;
3739
3740 /* Everything general done, inform filters that they need
3741 * to prepare for a data transfer. */
3742 result = Curl_conn_ev_data_setup(data);
3743
3744 out:
3745 return result;
3746 }
3747
3748 /* Curl_setup_conn() is called after the name resolve initiated in
3749 * create_conn() is all done.
3750 *
3751 * Curl_setup_conn() also handles reused connections
3752 */
3753 CURLcode Curl_setup_conn(struct Curl_easy *data,
3754 bool *protocol_done)
3755 {
3756 CURLcode result = CURLE_OK;
3757 struct connectdata *conn = data->conn;
3758
3759 Curl_pgrsTime(data, TIMER_NAMELOOKUP);
3760
3761 if(conn->handler->flags & PROTOPT_NONETWORK) {
3762 /* nothing to setup when not using a network */
3763 *protocol_done = TRUE;
3764 return result;
3765 }
3766
3767 /* set start time here for timeout purposes in the connect procedure, it
3768 is later set again for the progress meter purpose */
3769 conn->now = Curl_now();
3770 if(!conn->bits.reuse)
3771 result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry,
3772 CURL_CF_SSL_DEFAULT);
3773 if(!result)
3774 result = Curl_headers_init(data);
3775
3776 /* not sure we need this flag to be passed around any more */
3777 *protocol_done = FALSE;
3778 return result;
3779 }
3780
3781 CURLcode Curl_connect(struct Curl_easy *data,
3782 bool *asyncp,
3783 bool *protocol_done)
3784 {
3785 CURLcode result;
3786 struct connectdata *conn;
3787
3788 *asyncp = FALSE; /* assume synchronous resolves by default */
3789
3790 /* Set the request to virgin state based on transfer settings */
3791 Curl_req_hard_reset(&data->req, data);
3792
3793 /* call the stuff that needs to be called */
3794 result = create_conn(data, &conn, asyncp);
3795
3796 if(!result) {
3797 if(CONN_INUSE(conn) > 1)
3798 /* multiplexed */
3799 *protocol_done = TRUE;
3800 else if(!*asyncp) {
3801 /* DNS resolution is done: that is either because this is a reused
3802 connection, in which case DNS was unnecessary, or because DNS
3803 really did finish already (synch resolver/fast async resolve) */
3804 result = Curl_setup_conn(data, protocol_done);
3805 }
3806 }
3807
3808 if(result == CURLE_NO_CONNECTION_AVAILABLE) {
3809 return result;
3810 }
3811 else if(result && conn) {
3812 /* We are not allowed to return failure with memory left allocated in the
3813 connectdata struct, free those here */
3814 Curl_detach_connection(data);
3815 Curl_cpool_disconnect(data, conn, TRUE);
3816 }
3817
3818 return result;
3819 }
3820
3821 /*
3822 * Curl_init_do() inits the readwrite session. This is inited each time (in
3823 * the DO function before the protocol-specific DO functions are invoked) for
3824 * a transfer, sometimes multiple times on the same Curl_easy. Make sure
3825 * nothing in here depends on stuff that are setup dynamically for the
3826 * transfer.
3827 *
3828 * Allow this function to get called with 'conn' set to NULL.
3829 */
3830
3831 CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
3832 {
3833 /* if this is a pushed stream, we need this: */
3834 CURLcode result;
3835
3836 if(conn) {
3837 conn->bits.do_more = FALSE; /* by default there is no curl_do_more() to
3838 use */
3839 /* if the protocol used does not support wildcards, switch it off */
3840 if(data->state.wildcardmatch &&
3841 !(conn->handler->flags & PROTOPT_WILDCARD))
3842 data->state.wildcardmatch = FALSE;
3843 }
3844
3845 data->state.done = FALSE; /* *_done() is not called yet */
3846
3847 if(data->req.no_body)
3848 /* in HTTP lingo, no body means using the HEAD request... */
3849 data->state.httpreq = HTTPREQ_HEAD;
3850
3851 result = Curl_req_start(&data->req, data);
3852 if(!result) {
3853 Curl_speedinit(data);
3854 Curl_pgrsSetUploadCounter(data, 0);
3855 Curl_pgrsSetDownloadCounter(data, 0);
3856 }
3857 return result;
3858 }
3859
3860 #if defined(USE_HTTP2) || defined(USE_HTTP3)
3861
3862 #ifdef USE_NGHTTP2
3863
3864 static void priority_remove_child(struct Curl_easy *parent,
3865 struct Curl_easy *child)
3866 {
3867 struct Curl_data_prio_node **pnext = &parent->set.priority.children;
3868 struct Curl_data_prio_node *pnode = parent->set.priority.children;
3869
3870 DEBUGASSERT(child->set.priority.parent == parent);
3871 while(pnode && pnode->data != child) {
3872 pnext = &pnode->next;
3873 pnode = pnode->next;
3874 }
3875
3876 DEBUGASSERT(pnode);
3877 if(pnode) {
3878 *pnext = pnode->next;
3879 free(pnode);
3880 }
3881
3882 child->set.priority.parent = 0;
3883 child->set.priority.exclusive = FALSE;
3884 }
3885
3886 CURLcode Curl_data_priority_add_child(struct Curl_easy *parent,
3887 struct Curl_easy *child,
3888 bool exclusive)
3889 {
3890 if(child->set.priority.parent) {
3891 priority_remove_child(child->set.priority.parent, child);
3892 }
3893
3894 if(parent) {
3895 struct Curl_data_prio_node **tail;
3896 struct Curl_data_prio_node *pnode;
3897
3898 pnode = calloc(1, sizeof(*pnode));
3899 if(!pnode)
3900 return CURLE_OUT_OF_MEMORY;
3901 pnode->data = child;
3902
3903 if(parent->set.priority.children && exclusive) {
3904 /* exclusive: move all existing children underneath the new child */
3905 struct Curl_data_prio_node *node = parent->set.priority.children;
3906 while(node) {
3907 node->data->set.priority.parent = child;
3908 node = node->next;
3909 }
3910
3911 tail = &child->set.priority.children;
3912 while(*tail)
3913 tail = &(*tail)->next;
3914
3915 DEBUGASSERT(!*tail);
3916 *tail = parent->set.priority.children;
3917 parent->set.priority.children = 0;
3918 }
3919
3920 tail = &parent->set.priority.children;
3921 while(*tail) {
3922 (*tail)->data->set.priority.exclusive = FALSE;
3923 tail = &(*tail)->next;
3924 }
3925
3926 DEBUGASSERT(!*tail);
3927 *tail = pnode;
3928 }
3929
3930 child->set.priority.parent = parent;
3931 child->set.priority.exclusive = exclusive;
3932 return CURLE_OK;
3933 }
3934
3935 #endif /* USE_NGHTTP2 */
3936
3937 #ifdef USE_NGHTTP2
3938 static void data_priority_cleanup(struct Curl_easy *data)
3939 {
3940 while(data->set.priority.children) {
3941 struct Curl_easy *tmp = data->set.priority.children->data;
3942 priority_remove_child(data, tmp);
3943 if(data->set.priority.parent)
3944 Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE);
3945 }
3946
3947 if(data->set.priority.parent)
3948 priority_remove_child(data->set.priority.parent, data);
3949 }
3950 #endif
3951
3952 void Curl_data_priority_clear_state(struct Curl_easy *data)
3953 {
3954 memset(&data->state.priority, 0, sizeof(data->state.priority));
3955 }
3956
3957 #endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */
3958