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> /* <netinet/tcp.h> may need it */
29 #endif
30 #ifdef HAVE_SYS_UN_H
31 #include <sys/un.h> /* for sockaddr_un */
32 #endif
33 #ifdef HAVE_LINUX_TCP_H
34 #include <linux/tcp.h>
35 #elif defined(HAVE_NETINET_TCP_H)
36 #include <netinet/tcp.h>
37 #endif
38 #ifdef HAVE_SYS_IOCTL_H
39 #include <sys/ioctl.h>
40 #endif
41 #ifdef HAVE_NETDB_H
42 #include <netdb.h>
43 #endif
44 #ifdef HAVE_FCNTL_H
45 #include <fcntl.h>
46 #endif
47 #ifdef HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
49 #endif
50
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55
56 #include "urldata.h"
57 #include "sendf.h"
58 #include "if2ip.h"
59 #include "strerror.h"
60 #include "cfilters.h"
61 #include "connect.h"
62 #include "cf-haproxy.h"
63 #include "cf-https-connect.h"
64 #include "cf-socket.h"
65 #include "select.h"
66 #include "url.h" /* for Curl_safefree() */
67 #include "multiif.h"
68 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
69 #include "inet_ntop.h"
70 #include "inet_pton.h"
71 #include "vtls/vtls.h" /* for vtsl cfilters */
72 #include "progress.h"
73 #include "warnless.h"
74 #include "conncache.h"
75 #include "multihandle.h"
76 #include "share.h"
77 #include "version_win32.h"
78 #include "vquic/vquic.h" /* for quic cfilters */
79 #include "http_proxy.h"
80 #include "socks.h"
81 #include "strcase.h"
82
83 /* The last 3 #include files should be in this order */
84 #include "curl_printf.h"
85 #include "curl_memory.h"
86 #include "memdebug.h"
87
88 #ifndef ARRAYSIZE
89 #define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
90 #endif
91
92 #if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
93
Curl_alpn2alpnid(char * name,size_t len)94 enum alpnid Curl_alpn2alpnid(char *name, size_t len)
95 {
96 if(len == 2) {
97 if(strncasecompare(name, "h1", 2))
98 return ALPN_h1;
99 if(strncasecompare(name, "h2", 2))
100 return ALPN_h2;
101 if(strncasecompare(name, "h3", 2))
102 return ALPN_h3;
103 }
104 else if(len == 8) {
105 if(strncasecompare(name, "http/1.1", 8))
106 return ALPN_h1;
107 }
108 return ALPN_none; /* unknown, probably rubbish input */
109 }
110
111 #endif
112
113 /*
114 * Curl_timeleft() returns the amount of milliseconds left allowed for the
115 * transfer/connection. If the value is 0, there is no timeout (ie there is
116 * infinite time left). If the value is negative, the timeout time has already
117 * elapsed.
118 * @param data the transfer to check on
119 * @param nowp timestamp to use for calculation, NULL to use Curl_now()
120 * @param duringconnect TRUE iff connect timeout is also taken into account.
121 * @unittest: 1303
122 */
Curl_timeleft(struct Curl_easy * data,struct curltime * nowp,bool duringconnect)123 timediff_t Curl_timeleft(struct Curl_easy *data,
124 struct curltime *nowp,
125 bool duringconnect)
126 {
127 timediff_t timeleft_ms = 0;
128 timediff_t ctimeleft_ms = 0;
129 struct curltime now;
130
131 /* The duration of a connect and the total transfer are calculated from two
132 different time-stamps. It can end up with the total timeout being reached
133 before the connect timeout expires and we must acknowledge whichever
134 timeout that is reached first. The total timeout is set per entire
135 operation, while the connect timeout is set per connect. */
136 if(data->set.timeout <= 0 && !duringconnect)
137 return 0; /* no timeout in place or checked, return "no limit" */
138
139 if(!nowp) {
140 now = Curl_now();
141 nowp = &now;
142 }
143
144 if(data->set.timeout > 0) {
145 timeleft_ms = data->set.timeout -
146 Curl_timediff(*nowp, data->progress.t_startop);
147 if(!timeleft_ms)
148 timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
149 if(!duringconnect)
150 return timeleft_ms; /* no connect check, this is it */
151 }
152
153 if(duringconnect) {
154 timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
155 data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
156 ctimeleft_ms = ctimeout_ms -
157 Curl_timediff(*nowp, data->progress.t_startsingle);
158 if(!ctimeleft_ms)
159 ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
160 if(!timeleft_ms)
161 return ctimeleft_ms; /* no general timeout, this is it */
162 }
163 /* return minimal time left or max amount already expired */
164 return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms;
165 }
166
Curl_shutdown_start(struct Curl_easy * data,int sockindex,struct curltime * nowp)167 void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
168 struct curltime *nowp)
169 {
170 struct curltime now;
171
172 DEBUGASSERT(data->conn);
173 if(!nowp) {
174 now = Curl_now();
175 nowp = &now;
176 }
177 data->conn->shutdown.start[sockindex] = *nowp;
178 data->conn->shutdown.timeout_ms = (data->set.shutdowntimeout > 0) ?
179 data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS;
180 }
181
Curl_shutdown_timeleft(struct connectdata * conn,int sockindex,struct curltime * nowp)182 timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
183 struct curltime *nowp)
184 {
185 struct curltime now;
186 timediff_t left_ms;
187
188 if(!conn->shutdown.start[sockindex].tv_sec || !conn->shutdown.timeout_ms)
189 return 0; /* not started or no limits */
190
191 if(!nowp) {
192 now = Curl_now();
193 nowp = &now;
194 }
195 left_ms = conn->shutdown.timeout_ms -
196 Curl_timediff(*nowp, conn->shutdown.start[sockindex]);
197 return left_ms ? left_ms : -1;
198 }
199
Curl_conn_shutdown_timeleft(struct connectdata * conn,struct curltime * nowp)200 timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
201 struct curltime *nowp)
202 {
203 timediff_t left_ms = 0, ms;
204 struct curltime now;
205 int i;
206
207 for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) {
208 if(!conn->shutdown.start[i].tv_sec)
209 continue;
210 if(!nowp) {
211 now = Curl_now();
212 nowp = &now;
213 }
214 ms = Curl_shutdown_timeleft(conn, i, nowp);
215 if(ms && (!left_ms || ms < left_ms))
216 left_ms = ms;
217 }
218 return left_ms;
219 }
220
Curl_shutdown_clear(struct Curl_easy * data,int sockindex)221 void Curl_shutdown_clear(struct Curl_easy *data, int sockindex)
222 {
223 struct curltime *pt = &data->conn->shutdown.start[sockindex];
224 memset(pt, 0, sizeof(*pt));
225 }
226
Curl_shutdown_started(struct Curl_easy * data,int sockindex)227 bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
228 {
229 struct curltime *pt = &data->conn->shutdown.start[sockindex];
230 return (pt->tv_sec > 0) || (pt->tv_usec > 0);
231 }
232
233 static const struct Curl_addrinfo *
addr_first_match(const struct Curl_addrinfo * addr,int family)234 addr_first_match(const struct Curl_addrinfo *addr, int family)
235 {
236 while(addr) {
237 if(addr->ai_family == family)
238 return addr;
239 addr = addr->ai_next;
240 }
241 return NULL;
242 }
243
244 static const struct Curl_addrinfo *
addr_next_match(const struct Curl_addrinfo * addr,int family)245 addr_next_match(const struct Curl_addrinfo *addr, int family)
246 {
247 while(addr && addr->ai_next) {
248 addr = addr->ai_next;
249 if(addr->ai_family == family)
250 return addr;
251 }
252 return NULL;
253 }
254
255 /* retrieves ip address and port from a sockaddr structure.
256 note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
Curl_addr2string(struct sockaddr * sa,curl_socklen_t salen,char * addr,int * port)257 bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
258 char *addr, int *port)
259 {
260 struct sockaddr_in *si = NULL;
261 #ifdef USE_IPV6
262 struct sockaddr_in6 *si6 = NULL;
263 #endif
264 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
265 struct sockaddr_un *su = NULL;
266 #else
267 (void)salen;
268 #endif
269
270 switch(sa->sa_family) {
271 case AF_INET:
272 si = (struct sockaddr_in *)(void *) sa;
273 if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
274 addr, MAX_IPADR_LEN)) {
275 unsigned short us_port = ntohs(si->sin_port);
276 *port = us_port;
277 return TRUE;
278 }
279 break;
280 #ifdef USE_IPV6
281 case AF_INET6:
282 si6 = (struct sockaddr_in6 *)(void *) sa;
283 if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
284 addr, MAX_IPADR_LEN)) {
285 unsigned short us_port = ntohs(si6->sin6_port);
286 *port = us_port;
287 return TRUE;
288 }
289 break;
290 #endif
291 #if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
292 case AF_UNIX:
293 if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
294 su = (struct sockaddr_un*)sa;
295 msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
296 }
297 else
298 addr[0] = 0; /* socket with no name */
299 *port = 0;
300 return TRUE;
301 #endif
302 default:
303 break;
304 }
305
306 addr[0] = '\0';
307 *port = 0;
308 errno = EAFNOSUPPORT;
309 return FALSE;
310 }
311
312 /*
313 * Used to extract socket and connectdata struct for the most recent
314 * transfer on the given Curl_easy.
315 *
316 * The returned socket will be CURL_SOCKET_BAD in case of failure!
317 */
Curl_getconnectinfo(struct Curl_easy * data,struct connectdata ** connp)318 curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
319 struct connectdata **connp)
320 {
321 DEBUGASSERT(data);
322
323 /* this works for an easy handle:
324 * - that has been used for curl_easy_perform()
325 * - that is associated with a multi handle, and whose connection
326 * was detached with CURLOPT_CONNECT_ONLY
327 */
328 if(data->state.lastconnect_id != -1) {
329 struct connectdata *conn;
330
331 conn = Curl_cpool_get_conn(data, data->state.lastconnect_id);
332 if(!conn) {
333 data->state.lastconnect_id = -1;
334 return CURL_SOCKET_BAD;
335 }
336
337 if(connp)
338 /* only store this if the caller cares for it */
339 *connp = conn;
340 return conn->sock[FIRSTSOCKET];
341 }
342 return CURL_SOCKET_BAD;
343 }
344
345 /*
346 * Curl_conncontrol() marks streams or connection for closure.
347 */
Curl_conncontrol(struct connectdata * conn,int ctrl,const char * reason)348 void Curl_conncontrol(struct connectdata *conn,
349 int ctrl /* see defines in header */
350 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
351 , const char *reason
352 #endif
353 )
354 {
355 /* close if a connection, or a stream that is not multiplexed. */
356 /* This function will be called both before and after this connection is
357 associated with a transfer. */
358 bool closeit, is_multiplex;
359 DEBUGASSERT(conn);
360 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
361 (void)reason; /* useful for debugging */
362 #endif
363 is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
364 closeit = (ctrl == CONNCTRL_CONNECTION) ||
365 ((ctrl == CONNCTRL_STREAM) && !is_multiplex);
366 if((ctrl == CONNCTRL_STREAM) && is_multiplex)
367 ; /* stream signal on multiplex conn never affects close state */
368 else if((bit)closeit != conn->bits.close) {
369 conn->bits.close = closeit; /* the only place in the source code that
370 should assign this bit */
371 }
372 }
373
374 /**
375 * job walking the matching addr infos, creating a sub-cfilter with the
376 * provided method `cf_create` and running setup/connect on it.
377 */
378 struct eyeballer {
379 const char *name;
380 const struct Curl_addrinfo *first; /* complete address list, not owned */
381 const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */
382 int ai_family; /* matching address family only */
383 cf_ip_connect_create *cf_create; /* for creating cf */
384 struct Curl_cfilter *cf; /* current sub-cfilter connecting */
385 struct eyeballer *primary; /* eyeballer this one is backup for */
386 timediff_t delay_ms; /* delay until start */
387 struct curltime started; /* start of current attempt */
388 timediff_t timeoutms; /* timeout for current attempt */
389 expire_id timeout_id; /* ID for Curl_expire() */
390 CURLcode result;
391 int error;
392 BIT(rewinded); /* if we rewinded the addr list */
393 BIT(has_started); /* attempts have started */
394 BIT(is_done); /* out of addresses/time */
395 BIT(connected); /* cf has connected */
396 BIT(shutdown); /* cf has shutdown */
397 BIT(inconclusive); /* connect was not a hard failure, we
398 * might talk to a restarting server */
399 };
400
401
402 typedef enum {
403 SCFST_INIT,
404 SCFST_WAITING,
405 SCFST_DONE
406 } cf_connect_state;
407
408 struct cf_he_ctx {
409 int transport;
410 cf_ip_connect_create *cf_create;
411 const struct Curl_dns_entry *remotehost;
412 cf_connect_state state;
413 struct eyeballer *baller[2];
414 struct eyeballer *winner;
415 struct curltime started;
416 };
417
418 /* when there are more than one IP address left to use, this macro returns how
419 much of the given timeout to spend on *this* attempt */
420 #define TIMEOUT_LARGE 600
421 #define USETIME(ms) ((ms > TIMEOUT_LARGE) ? (ms / 2) : ms)
422
eyeballer_new(struct eyeballer ** pballer,cf_ip_connect_create * cf_create,const struct Curl_addrinfo * addr,int ai_family,struct eyeballer * primary,timediff_t delay_ms,timediff_t timeout_ms,expire_id timeout_id)423 static CURLcode eyeballer_new(struct eyeballer **pballer,
424 cf_ip_connect_create *cf_create,
425 const struct Curl_addrinfo *addr,
426 int ai_family,
427 struct eyeballer *primary,
428 timediff_t delay_ms,
429 timediff_t timeout_ms,
430 expire_id timeout_id)
431 {
432 struct eyeballer *baller;
433
434 *pballer = NULL;
435 baller = calloc(1, sizeof(*baller));
436 if(!baller)
437 return CURLE_OUT_OF_MEMORY;
438
439 baller->name = ((ai_family == AF_INET) ? "ipv4" : (
440 #ifdef USE_IPV6
441 (ai_family == AF_INET6) ? "ipv6" :
442 #endif
443 "ip"));
444 baller->cf_create = cf_create;
445 baller->first = baller->addr = addr;
446 baller->ai_family = ai_family;
447 baller->primary = primary;
448 baller->delay_ms = delay_ms;
449 baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
450 USETIME(timeout_ms) : timeout_ms;
451 baller->timeout_id = timeout_id;
452 baller->result = CURLE_COULDNT_CONNECT;
453
454 *pballer = baller;
455 return CURLE_OK;
456 }
457
baller_close(struct eyeballer * baller,struct Curl_easy * data)458 static void baller_close(struct eyeballer *baller,
459 struct Curl_easy *data)
460 {
461 if(baller && baller->cf) {
462 Curl_conn_cf_discard_chain(&baller->cf, data);
463 }
464 }
465
baller_free(struct eyeballer * baller,struct Curl_easy * data)466 static void baller_free(struct eyeballer *baller,
467 struct Curl_easy *data)
468 {
469 if(baller) {
470 baller_close(baller, data);
471 free(baller);
472 }
473 }
474
baller_rewind(struct eyeballer * baller)475 static void baller_rewind(struct eyeballer *baller)
476 {
477 baller->rewinded = TRUE;
478 baller->addr = baller->first;
479 baller->inconclusive = FALSE;
480 }
481
baller_next_addr(struct eyeballer * baller)482 static void baller_next_addr(struct eyeballer *baller)
483 {
484 baller->addr = addr_next_match(baller->addr, baller->ai_family);
485 }
486
487 /*
488 * Initiate a connect attempt walk.
489 *
490 * Note that even on connect fail it returns CURLE_OK, but with 'sock' set to
491 * CURL_SOCKET_BAD. Other errors will however return proper errors.
492 */
baller_initiate(struct Curl_cfilter * cf,struct Curl_easy * data,struct eyeballer * baller)493 static void baller_initiate(struct Curl_cfilter *cf,
494 struct Curl_easy *data,
495 struct eyeballer *baller)
496 {
497 struct cf_he_ctx *ctx = cf->ctx;
498 struct Curl_cfilter *cf_prev = baller->cf;
499 struct Curl_cfilter *wcf;
500 CURLcode result;
501
502
503 /* Do not close a previous cfilter yet to ensure that the next IP's
504 socket gets a different file descriptor, which can prevent bugs when
505 the curl_multi_socket_action interface is used with certain select()
506 replacements such as kqueue. */
507 result = baller->cf_create(&baller->cf, data, cf->conn, baller->addr,
508 ctx->transport);
509 if(result)
510 goto out;
511
512 /* the new filter might have sub-filters */
513 for(wcf = baller->cf; wcf; wcf = wcf->next) {
514 wcf->conn = cf->conn;
515 wcf->sockindex = cf->sockindex;
516 }
517
518 if(addr_next_match(baller->addr, baller->ai_family)) {
519 Curl_expire(data, baller->timeoutms, baller->timeout_id);
520 }
521
522 out:
523 if(result) {
524 CURL_TRC_CF(data, cf, "%s failed", baller->name);
525 baller_close(baller, data);
526 }
527 if(cf_prev)
528 Curl_conn_cf_discard_chain(&cf_prev, data);
529 baller->result = result;
530 }
531
532 /**
533 * Start a connection attempt on the current baller address.
534 * Will return CURLE_OK on the first address where a socket
535 * could be created and the non-blocking connect started.
536 * Returns error when all remaining addresses have been tried.
537 */
baller_start(struct Curl_cfilter * cf,struct Curl_easy * data,struct eyeballer * baller,timediff_t timeoutms)538 static CURLcode baller_start(struct Curl_cfilter *cf,
539 struct Curl_easy *data,
540 struct eyeballer *baller,
541 timediff_t timeoutms)
542 {
543 baller->error = 0;
544 baller->connected = FALSE;
545 baller->has_started = TRUE;
546
547 while(baller->addr) {
548 baller->started = Curl_now();
549 baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
550 USETIME(timeoutms) : timeoutms;
551 baller_initiate(cf, data, baller);
552 if(!baller->result)
553 break;
554 baller_next_addr(baller);
555 }
556 if(!baller->addr) {
557 baller->is_done = TRUE;
558 }
559 return baller->result;
560 }
561
562
563 /* Used within the multi interface. Try next IP address, returns error if no
564 more address exists or error */
baller_start_next(struct Curl_cfilter * cf,struct Curl_easy * data,struct eyeballer * baller,timediff_t timeoutms)565 static CURLcode baller_start_next(struct Curl_cfilter *cf,
566 struct Curl_easy *data,
567 struct eyeballer *baller,
568 timediff_t timeoutms)
569 {
570 if(cf->sockindex == FIRSTSOCKET) {
571 baller_next_addr(baller);
572 /* If we get inconclusive answers from the server(s), we start
573 * again until this whole thing times out. This allows us to
574 * connect to servers that are gracefully restarting and the
575 * packet routing to the new instance has not happened yet (e.g. QUIC). */
576 if(!baller->addr && baller->inconclusive)
577 baller_rewind(baller);
578 baller_start(cf, data, baller, timeoutms);
579 }
580 else {
581 baller->error = 0;
582 baller->connected = FALSE;
583 baller->has_started = TRUE;
584 baller->is_done = TRUE;
585 baller->result = CURLE_COULDNT_CONNECT;
586 }
587 return baller->result;
588 }
589
baller_connect(struct Curl_cfilter * cf,struct Curl_easy * data,struct eyeballer * baller,struct curltime * now,bool * connected)590 static CURLcode baller_connect(struct Curl_cfilter *cf,
591 struct Curl_easy *data,
592 struct eyeballer *baller,
593 struct curltime *now,
594 bool *connected)
595 {
596 (void)cf;
597 *connected = baller->connected;
598 if(!baller->result && !*connected) {
599 /* evaluate again */
600 baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected);
601
602 if(!baller->result) {
603 if(*connected) {
604 baller->connected = TRUE;
605 baller->is_done = TRUE;
606 }
607 else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) {
608 infof(data, "%s connect timeout after %" FMT_TIMEDIFF_T
609 "ms, move on!", baller->name, baller->timeoutms);
610 #if defined(ETIMEDOUT)
611 baller->error = ETIMEDOUT;
612 #endif
613 baller->result = CURLE_OPERATION_TIMEDOUT;
614 }
615 }
616 else if(baller->result == CURLE_WEIRD_SERVER_REPLY)
617 baller->inconclusive = TRUE;
618 }
619 return baller->result;
620 }
621
622 /*
623 * is_connected() checks if the socket has connected.
624 */
is_connected(struct Curl_cfilter * cf,struct Curl_easy * data,bool * connected)625 static CURLcode is_connected(struct Curl_cfilter *cf,
626 struct Curl_easy *data,
627 bool *connected)
628 {
629 struct cf_he_ctx *ctx = cf->ctx;
630 struct connectdata *conn = cf->conn;
631 CURLcode result;
632 struct curltime now;
633 size_t i;
634 int ongoing, not_started;
635 const char *hostname;
636
637 /* Check if any of the conn->tempsock we use for establishing connections
638 * succeeded and, if so, close any ongoing other ones.
639 * Transfer the successful conn->tempsock to conn->sock[sockindex]
640 * and set conn->tempsock to CURL_SOCKET_BAD.
641 * If transport is QUIC, we need to shutdown the ongoing 'other'
642 * cot ballers in a QUIC appropriate way. */
643 evaluate:
644 *connected = FALSE; /* a negative world view is best */
645 now = Curl_now();
646 ongoing = not_started = 0;
647 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
648 struct eyeballer *baller = ctx->baller[i];
649
650 if(!baller || baller->is_done)
651 continue;
652
653 if(!baller->has_started) {
654 ++not_started;
655 continue;
656 }
657 baller->result = baller_connect(cf, data, baller, &now, connected);
658 CURL_TRC_CF(data, cf, "%s connect -> %d, connected=%d",
659 baller->name, baller->result, *connected);
660
661 if(!baller->result) {
662 if(*connected) {
663 /* connected, declare the winner */
664 ctx->winner = baller;
665 ctx->baller[i] = NULL;
666 break;
667 }
668 else { /* still waiting */
669 ++ongoing;
670 }
671 }
672 else if(!baller->is_done) {
673 /* The baller failed to connect, start its next attempt */
674 if(baller->error) {
675 data->state.os_errno = baller->error;
676 SET_SOCKERRNO(baller->error);
677 }
678 baller_start_next(cf, data, baller, Curl_timeleft(data, &now, TRUE));
679 if(baller->is_done) {
680 CURL_TRC_CF(data, cf, "%s done", baller->name);
681 }
682 else {
683 /* next attempt was started */
684 CURL_TRC_CF(data, cf, "%s trying next", baller->name);
685 ++ongoing;
686 Curl_expire(data, 0, EXPIRE_RUN_NOW);
687 }
688 }
689 }
690
691 if(ctx->winner) {
692 *connected = TRUE;
693 return CURLE_OK;
694 }
695
696 /* Nothing connected, check the time before we might
697 * start new ballers or return ok. */
698 if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
699 failf(data, "Connection timeout after %" FMT_OFF_T " ms",
700 Curl_timediff(now, data->progress.t_startsingle));
701 return CURLE_OPERATION_TIMEDOUT;
702 }
703
704 /* Check if we have any waiting ballers to start now. */
705 if(not_started > 0) {
706 int added = 0;
707
708 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
709 struct eyeballer *baller = ctx->baller[i];
710
711 if(!baller || baller->has_started)
712 continue;
713 /* We start its primary baller has failed to connect or if
714 * its start delay_ms have expired */
715 if((baller->primary && baller->primary->is_done) ||
716 Curl_timediff(now, ctx->started) >= baller->delay_ms) {
717 baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
718 if(baller->is_done) {
719 CURL_TRC_CF(data, cf, "%s done", baller->name);
720 }
721 else {
722 CURL_TRC_CF(data, cf, "%s starting (timeout=%" FMT_TIMEDIFF_T "ms)",
723 baller->name, baller->timeoutms);
724 ++ongoing;
725 ++added;
726 }
727 }
728 }
729 if(added > 0)
730 goto evaluate;
731 }
732
733 if(ongoing > 0) {
734 /* We are still trying, return for more waiting */
735 *connected = FALSE;
736 return CURLE_OK;
737 }
738
739 /* all ballers have failed to connect. */
740 CURL_TRC_CF(data, cf, "all eyeballers failed");
741 result = CURLE_COULDNT_CONNECT;
742 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
743 struct eyeballer *baller = ctx->baller[i];
744 if(!baller)
745 continue;
746 CURL_TRC_CF(data, cf, "%s assess started=%d, result=%d",
747 baller->name, baller->has_started, baller->result);
748 if(baller->has_started && baller->result) {
749 result = baller->result;
750 break;
751 }
752 }
753
754 #ifndef CURL_DISABLE_PROXY
755 if(conn->bits.socksproxy)
756 hostname = conn->socks_proxy.host.name;
757 else if(conn->bits.httpproxy)
758 hostname = conn->http_proxy.host.name;
759 else
760 #endif
761 if(conn->bits.conn_to_host)
762 hostname = conn->conn_to_host.name;
763 else
764 hostname = conn->host.name;
765
766 failf(data, "Failed to connect to %s port %u after "
767 "%" FMT_TIMEDIFF_T " ms: %s",
768 hostname, conn->primary.remote_port,
769 Curl_timediff(now, data->progress.t_startsingle),
770 curl_easy_strerror(result));
771
772 #ifdef WSAETIMEDOUT
773 if(WSAETIMEDOUT == data->state.os_errno)
774 result = CURLE_OPERATION_TIMEDOUT;
775 #elif defined(ETIMEDOUT)
776 if(ETIMEDOUT == data->state.os_errno)
777 result = CURLE_OPERATION_TIMEDOUT;
778 #endif
779
780 return result;
781 }
782
783 /*
784 * Connect to the given host with timeout, proxy or remote does not matter.
785 * There might be more than one IP address to try out.
786 */
start_connect(struct Curl_cfilter * cf,struct Curl_easy * data,const struct Curl_dns_entry * remotehost)787 static CURLcode start_connect(struct Curl_cfilter *cf,
788 struct Curl_easy *data,
789 const struct Curl_dns_entry *remotehost)
790 {
791 struct cf_he_ctx *ctx = cf->ctx;
792 struct connectdata *conn = cf->conn;
793 CURLcode result = CURLE_COULDNT_CONNECT;
794 int ai_family0 = 0, ai_family1 = 0;
795 timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
796 const struct Curl_addrinfo *addr0 = NULL, *addr1 = NULL;
797
798 if(timeout_ms < 0) {
799 /* a precaution, no need to continue if time already is up */
800 failf(data, "Connection time-out");
801 return CURLE_OPERATION_TIMEDOUT;
802 }
803
804 ctx->started = Curl_now();
805
806 /* remotehost->addr is the list of addresses from the resolver, each
807 * with an address family. The list has at least one entry, possibly
808 * many more.
809 * We try at most 2 at a time, until we either get a connection or
810 * run out of addresses to try. Since likelihood of success is tied
811 * to the address family (e.g. IPV6 might not work at all ), we want
812 * the 2 connect attempt ballers to try different families, if possible.
813 *
814 */
815 if(conn->ip_version == CURL_IPRESOLVE_V6) {
816 #ifdef USE_IPV6
817 ai_family0 = AF_INET6;
818 addr0 = addr_first_match(remotehost->addr, ai_family0);
819 #endif
820 }
821 else if(conn->ip_version == CURL_IPRESOLVE_V4) {
822 ai_family0 = AF_INET;
823 addr0 = addr_first_match(remotehost->addr, ai_family0);
824 }
825 else {
826 /* no user preference, we try ipv6 always first when available */
827 #ifdef USE_IPV6
828 ai_family0 = AF_INET6;
829 addr0 = addr_first_match(remotehost->addr, ai_family0);
830 #endif
831 /* next candidate is ipv4 */
832 ai_family1 = AF_INET;
833 addr1 = addr_first_match(remotehost->addr, ai_family1);
834 /* no ip address families, probably AF_UNIX or something, use the
835 * address family given to us */
836 if(!addr1 && !addr0 && remotehost->addr) {
837 ai_family0 = remotehost->addr->ai_family;
838 addr0 = addr_first_match(remotehost->addr, ai_family0);
839 }
840 }
841
842 if(!addr0 && addr1) {
843 /* switch around, so a single baller always uses addr0 */
844 addr0 = addr1;
845 ai_family0 = ai_family1;
846 addr1 = NULL;
847 }
848
849 /* We found no address that matches our criteria, we cannot connect */
850 if(!addr0) {
851 return CURLE_COULDNT_CONNECT;
852 }
853
854 memset(ctx->baller, 0, sizeof(ctx->baller));
855 result = eyeballer_new(&ctx->baller[0], ctx->cf_create, addr0, ai_family0,
856 NULL, 0, /* no primary/delay, start now */
857 timeout_ms, EXPIRE_DNS_PER_NAME);
858 if(result)
859 return result;
860 CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
861 ctx->baller[0]->name, ctx->baller[0]->timeoutms);
862 if(addr1) {
863 /* second one gets a delayed start */
864 result = eyeballer_new(&ctx->baller[1], ctx->cf_create, addr1, ai_family1,
865 ctx->baller[0], /* wait on that to fail */
866 /* or start this delayed */
867 data->set.happy_eyeballs_timeout,
868 timeout_ms, EXPIRE_DNS_PER_NAME2);
869 if(result)
870 return result;
871 CURL_TRC_CF(data, cf, "created %s (timeout %" FMT_TIMEDIFF_T "ms)",
872 ctx->baller[1]->name, ctx->baller[1]->timeoutms);
873 Curl_expire(data, data->set.happy_eyeballs_timeout,
874 EXPIRE_HAPPY_EYEBALLS);
875 }
876
877 return CURLE_OK;
878 }
879
cf_he_ctx_clear(struct Curl_cfilter * cf,struct Curl_easy * data)880 static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
881 {
882 struct cf_he_ctx *ctx = cf->ctx;
883 size_t i;
884
885 DEBUGASSERT(ctx);
886 DEBUGASSERT(data);
887 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
888 baller_free(ctx->baller[i], data);
889 ctx->baller[i] = NULL;
890 }
891 baller_free(ctx->winner, data);
892 ctx->winner = NULL;
893 }
894
cf_he_shutdown(struct Curl_cfilter * cf,struct Curl_easy * data,bool * done)895 static CURLcode cf_he_shutdown(struct Curl_cfilter *cf,
896 struct Curl_easy *data, bool *done)
897 {
898 struct cf_he_ctx *ctx = cf->ctx;
899 size_t i;
900 CURLcode result = CURLE_OK;
901
902 DEBUGASSERT(data);
903 if(cf->connected) {
904 *done = TRUE;
905 return CURLE_OK;
906 }
907
908 /* shutdown all ballers that have not done so already. If one fails,
909 * continue shutting down others until all are shutdown. */
910 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
911 struct eyeballer *baller = ctx->baller[i];
912 bool bdone = FALSE;
913 if(!baller || !baller->cf || baller->shutdown)
914 continue;
915 baller->result = baller->cf->cft->do_shutdown(baller->cf, data, &bdone);
916 if(baller->result || bdone)
917 baller->shutdown = TRUE; /* treat a failed shutdown as done */
918 }
919
920 *done = TRUE;
921 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
922 if(ctx->baller[i] && !ctx->baller[i]->shutdown)
923 *done = FALSE;
924 }
925 if(*done) {
926 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
927 if(ctx->baller[i] && ctx->baller[i]->result)
928 result = ctx->baller[i]->result;
929 }
930 }
931 CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
932 return result;
933 }
934
cf_he_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)935 static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
936 struct Curl_easy *data,
937 struct easy_pollset *ps)
938 {
939 struct cf_he_ctx *ctx = cf->ctx;
940 size_t i;
941
942 if(!cf->connected) {
943 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
944 struct eyeballer *baller = ctx->baller[i];
945 if(!baller || !baller->cf)
946 continue;
947 Curl_conn_cf_adjust_pollset(baller->cf, data, ps);
948 }
949 CURL_TRC_CF(data, cf, "adjust_pollset -> %d socks", ps->num);
950 }
951 }
952
cf_he_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)953 static CURLcode cf_he_connect(struct Curl_cfilter *cf,
954 struct Curl_easy *data,
955 bool blocking, bool *done)
956 {
957 struct cf_he_ctx *ctx = cf->ctx;
958 CURLcode result = CURLE_OK;
959
960 if(cf->connected) {
961 *done = TRUE;
962 return CURLE_OK;
963 }
964
965 (void)blocking; /* TODO: do we want to support this? */
966 DEBUGASSERT(ctx);
967 *done = FALSE;
968
969 switch(ctx->state) {
970 case SCFST_INIT:
971 DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
972 DEBUGASSERT(!cf->connected);
973 result = start_connect(cf, data, ctx->remotehost);
974 if(result)
975 return result;
976 ctx->state = SCFST_WAITING;
977 FALLTHROUGH();
978 case SCFST_WAITING:
979 result = is_connected(cf, data, done);
980 if(!result && *done) {
981 DEBUGASSERT(ctx->winner);
982 DEBUGASSERT(ctx->winner->cf);
983 DEBUGASSERT(ctx->winner->cf->connected);
984 /* we have a winner. Install and activate it.
985 * close/free all others. */
986 ctx->state = SCFST_DONE;
987 cf->connected = TRUE;
988 cf->next = ctx->winner->cf;
989 ctx->winner->cf = NULL;
990 cf_he_ctx_clear(cf, data);
991
992 if(cf->conn->handler->protocol & PROTO_FAMILY_SSH)
993 Curl_pgrsTime(data, TIMER_APPCONNECT); /* we are connected already */
994 if(Curl_trc_cf_is_verbose(cf, data)) {
995 struct ip_quadruple ipquad;
996 int is_ipv6;
997 if(!Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad)) {
998 const char *host, *disphost;
999 int port;
1000 cf->next->cft->get_host(cf->next, data, &host, &disphost, &port);
1001 CURL_TRC_CF(data, cf, "Connected to %s (%s) port %u",
1002 disphost, ipquad.remote_ip, ipquad.remote_port);
1003 }
1004 }
1005 data->info.numconnects++; /* to track the # of connections made */
1006 }
1007 break;
1008 case SCFST_DONE:
1009 *done = TRUE;
1010 break;
1011 }
1012 return result;
1013 }
1014
cf_he_close(struct Curl_cfilter * cf,struct Curl_easy * data)1015 static void cf_he_close(struct Curl_cfilter *cf,
1016 struct Curl_easy *data)
1017 {
1018 struct cf_he_ctx *ctx = cf->ctx;
1019
1020 CURL_TRC_CF(data, cf, "close");
1021 cf_he_ctx_clear(cf, data);
1022 cf->connected = FALSE;
1023 ctx->state = SCFST_INIT;
1024
1025 if(cf->next) {
1026 cf->next->cft->do_close(cf->next, data);
1027 Curl_conn_cf_discard_chain(&cf->next, data);
1028 }
1029 }
1030
cf_he_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)1031 static bool cf_he_data_pending(struct Curl_cfilter *cf,
1032 const struct Curl_easy *data)
1033 {
1034 struct cf_he_ctx *ctx = cf->ctx;
1035 size_t i;
1036
1037 if(cf->connected)
1038 return cf->next->cft->has_data_pending(cf->next, data);
1039
1040 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
1041 struct eyeballer *baller = ctx->baller[i];
1042 if(!baller || !baller->cf)
1043 continue;
1044 if(baller->cf->cft->has_data_pending(baller->cf, data))
1045 return TRUE;
1046 }
1047 return FALSE;
1048 }
1049
get_max_baller_time(struct Curl_cfilter * cf,struct Curl_easy * data,int query)1050 static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
1051 struct Curl_easy *data,
1052 int query)
1053 {
1054 struct cf_he_ctx *ctx = cf->ctx;
1055 struct curltime t, tmax;
1056 size_t i;
1057
1058 memset(&tmax, 0, sizeof(tmax));
1059 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
1060 struct eyeballer *baller = ctx->baller[i];
1061
1062 memset(&t, 0, sizeof(t));
1063 if(baller && baller->cf &&
1064 !baller->cf->cft->query(baller->cf, data, query, NULL, &t)) {
1065 if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
1066 tmax = t;
1067 }
1068 }
1069 return tmax;
1070 }
1071
cf_he_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)1072 static CURLcode cf_he_query(struct Curl_cfilter *cf,
1073 struct Curl_easy *data,
1074 int query, int *pres1, void *pres2)
1075 {
1076 struct cf_he_ctx *ctx = cf->ctx;
1077
1078 if(!cf->connected) {
1079 switch(query) {
1080 case CF_QUERY_CONNECT_REPLY_MS: {
1081 int reply_ms = -1;
1082 size_t i;
1083
1084 for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
1085 struct eyeballer *baller = ctx->baller[i];
1086 int breply_ms;
1087
1088 if(baller && baller->cf &&
1089 !baller->cf->cft->query(baller->cf, data, query,
1090 &breply_ms, NULL)) {
1091 if(breply_ms >= 0 && (reply_ms < 0 || breply_ms < reply_ms))
1092 reply_ms = breply_ms;
1093 }
1094 }
1095 *pres1 = reply_ms;
1096 CURL_TRC_CF(data, cf, "query connect reply: %dms", *pres1);
1097 return CURLE_OK;
1098 }
1099 case CF_QUERY_TIMER_CONNECT: {
1100 struct curltime *when = pres2;
1101 *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_CONNECT);
1102 return CURLE_OK;
1103 }
1104 case CF_QUERY_TIMER_APPCONNECT: {
1105 struct curltime *when = pres2;
1106 *when = get_max_baller_time(cf, data, CF_QUERY_TIMER_APPCONNECT);
1107 return CURLE_OK;
1108 }
1109 default:
1110 break;
1111 }
1112 }
1113
1114 return cf->next ?
1115 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1116 CURLE_UNKNOWN_OPTION;
1117 }
1118
cf_he_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1119 static void cf_he_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1120 {
1121 struct cf_he_ctx *ctx = cf->ctx;
1122
1123 CURL_TRC_CF(data, cf, "destroy");
1124 if(ctx) {
1125 cf_he_ctx_clear(cf, data);
1126 }
1127 /* release any resources held in state */
1128 Curl_safefree(ctx);
1129 }
1130
1131 struct Curl_cftype Curl_cft_happy_eyeballs = {
1132 "HAPPY-EYEBALLS",
1133 0,
1134 CURL_LOG_LVL_NONE,
1135 cf_he_destroy,
1136 cf_he_connect,
1137 cf_he_close,
1138 cf_he_shutdown,
1139 Curl_cf_def_get_host,
1140 cf_he_adjust_pollset,
1141 cf_he_data_pending,
1142 Curl_cf_def_send,
1143 Curl_cf_def_recv,
1144 Curl_cf_def_cntrl,
1145 Curl_cf_def_conn_is_alive,
1146 Curl_cf_def_conn_keep_alive,
1147 cf_he_query,
1148 };
1149
1150 /**
1151 * Create a happy eyeball connection filter that uses the, once resolved,
1152 * address information to connect on ip families based on connection
1153 * configuration.
1154 * @param pcf output, the created cfilter
1155 * @param data easy handle used in creation
1156 * @param conn connection the filter is created for
1157 * @param cf_create method to create the sub-filters performing the
1158 * actual connects.
1159 */
1160 static CURLcode
cf_happy_eyeballs_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,cf_ip_connect_create * cf_create,const struct Curl_dns_entry * remotehost,int transport)1161 cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
1162 struct Curl_easy *data,
1163 struct connectdata *conn,
1164 cf_ip_connect_create *cf_create,
1165 const struct Curl_dns_entry *remotehost,
1166 int transport)
1167 {
1168 struct cf_he_ctx *ctx = NULL;
1169 CURLcode result;
1170
1171 (void)data;
1172 (void)conn;
1173 *pcf = NULL;
1174 ctx = calloc(1, sizeof(*ctx));
1175 if(!ctx) {
1176 result = CURLE_OUT_OF_MEMORY;
1177 goto out;
1178 }
1179 ctx->transport = transport;
1180 ctx->cf_create = cf_create;
1181 ctx->remotehost = remotehost;
1182
1183 result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
1184
1185 out:
1186 if(result) {
1187 Curl_safefree(*pcf);
1188 Curl_safefree(ctx);
1189 }
1190 return result;
1191 }
1192
1193 struct transport_provider {
1194 int transport;
1195 cf_ip_connect_create *cf_create;
1196 };
1197
1198 static
1199 #ifndef UNITTESTS
1200 const
1201 #endif
1202 struct transport_provider transport_providers[] = {
1203 { TRNSPRT_TCP, Curl_cf_tcp_create },
1204 #ifdef USE_HTTP3
1205 { TRNSPRT_QUIC, Curl_cf_quic_create },
1206 #endif
1207 #ifndef CURL_DISABLE_TFTP
1208 { TRNSPRT_UDP, Curl_cf_udp_create },
1209 #endif
1210 #ifdef USE_UNIX_SOCKETS
1211 { TRNSPRT_UNIX, Curl_cf_unix_create },
1212 #endif
1213 };
1214
get_cf_create(int transport)1215 static cf_ip_connect_create *get_cf_create(int transport)
1216 {
1217 size_t i;
1218 for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
1219 if(transport == transport_providers[i].transport)
1220 return transport_providers[i].cf_create;
1221 }
1222 return NULL;
1223 }
1224
cf_he_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data,const struct Curl_dns_entry * remotehost,int transport)1225 static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
1226 struct Curl_easy *data,
1227 const struct Curl_dns_entry *remotehost,
1228 int transport)
1229 {
1230 cf_ip_connect_create *cf_create;
1231 struct Curl_cfilter *cf;
1232 CURLcode result;
1233
1234 /* Need to be first */
1235 DEBUGASSERT(cf_at);
1236 cf_create = get_cf_create(transport);
1237 if(!cf_create) {
1238 CURL_TRC_CF(data, cf_at, "unsupported transport type %d", transport);
1239 return CURLE_UNSUPPORTED_PROTOCOL;
1240 }
1241 result = cf_happy_eyeballs_create(&cf, data, cf_at->conn,
1242 cf_create, remotehost,
1243 transport);
1244 if(result)
1245 return result;
1246
1247 Curl_conn_cf_insert_after(cf_at, cf);
1248 return CURLE_OK;
1249 }
1250
1251 typedef enum {
1252 CF_SETUP_INIT,
1253 CF_SETUP_CNNCT_EYEBALLS,
1254 CF_SETUP_CNNCT_SOCKS,
1255 CF_SETUP_CNNCT_HTTP_PROXY,
1256 CF_SETUP_CNNCT_HAPROXY,
1257 CF_SETUP_CNNCT_SSL,
1258 CF_SETUP_DONE
1259 } cf_setup_state;
1260
1261 struct cf_setup_ctx {
1262 cf_setup_state state;
1263 const struct Curl_dns_entry *remotehost;
1264 int ssl_mode;
1265 int transport;
1266 };
1267
cf_setup_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1268 static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
1269 struct Curl_easy *data,
1270 bool blocking, bool *done)
1271 {
1272 struct cf_setup_ctx *ctx = cf->ctx;
1273 CURLcode result = CURLE_OK;
1274
1275 if(cf->connected) {
1276 *done = TRUE;
1277 return CURLE_OK;
1278 }
1279
1280 /* connect current sub-chain */
1281 connect_sub_chain:
1282 if(cf->next && !cf->next->connected) {
1283 result = Curl_conn_cf_connect(cf->next, data, blocking, done);
1284 if(result || !*done)
1285 return result;
1286 }
1287
1288 if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
1289 result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport);
1290 if(result)
1291 return result;
1292 ctx->state = CF_SETUP_CNNCT_EYEBALLS;
1293 if(!cf->next || !cf->next->connected)
1294 goto connect_sub_chain;
1295 }
1296
1297 /* sub-chain connected, do we need to add more? */
1298 #ifndef CURL_DISABLE_PROXY
1299 if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
1300 result = Curl_cf_socks_proxy_insert_after(cf, data);
1301 if(result)
1302 return result;
1303 ctx->state = CF_SETUP_CNNCT_SOCKS;
1304 if(!cf->next || !cf->next->connected)
1305 goto connect_sub_chain;
1306 }
1307
1308 if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
1309 #ifdef USE_SSL
1310 if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype)
1311 && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
1312 result = Curl_cf_ssl_proxy_insert_after(cf, data);
1313 if(result)
1314 return result;
1315 }
1316 #endif /* USE_SSL */
1317
1318 #if !defined(CURL_DISABLE_HTTP)
1319 if(cf->conn->bits.tunnel_proxy) {
1320 result = Curl_cf_http_proxy_insert_after(cf, data);
1321 if(result)
1322 return result;
1323 }
1324 #endif /* !CURL_DISABLE_HTTP */
1325 ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
1326 if(!cf->next || !cf->next->connected)
1327 goto connect_sub_chain;
1328 }
1329 #endif /* !CURL_DISABLE_PROXY */
1330
1331 if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
1332 #if !defined(CURL_DISABLE_PROXY)
1333 if(data->set.haproxyprotocol) {
1334 if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
1335 failf(data, "haproxy protocol not support with SSL "
1336 "encryption in place (QUIC?)");
1337 return CURLE_UNSUPPORTED_PROTOCOL;
1338 }
1339 result = Curl_cf_haproxy_insert_after(cf, data);
1340 if(result)
1341 return result;
1342 }
1343 #endif /* !CURL_DISABLE_PROXY */
1344 ctx->state = CF_SETUP_CNNCT_HAPROXY;
1345 if(!cf->next || !cf->next->connected)
1346 goto connect_sub_chain;
1347 }
1348
1349 if(ctx->state < CF_SETUP_CNNCT_SSL) {
1350 #ifdef USE_SSL
1351 if((ctx->ssl_mode == CURL_CF_SSL_ENABLE
1352 || (ctx->ssl_mode != CURL_CF_SSL_DISABLE
1353 && cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */
1354 && !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
1355 result = Curl_cf_ssl_insert_after(cf, data);
1356 if(result)
1357 return result;
1358 }
1359 #endif /* USE_SSL */
1360 ctx->state = CF_SETUP_CNNCT_SSL;
1361 if(!cf->next || !cf->next->connected)
1362 goto connect_sub_chain;
1363 }
1364
1365 ctx->state = CF_SETUP_DONE;
1366 cf->connected = TRUE;
1367 *done = TRUE;
1368 return CURLE_OK;
1369 }
1370
cf_setup_close(struct Curl_cfilter * cf,struct Curl_easy * data)1371 static void cf_setup_close(struct Curl_cfilter *cf,
1372 struct Curl_easy *data)
1373 {
1374 struct cf_setup_ctx *ctx = cf->ctx;
1375
1376 CURL_TRC_CF(data, cf, "close");
1377 cf->connected = FALSE;
1378 ctx->state = CF_SETUP_INIT;
1379
1380 if(cf->next) {
1381 cf->next->cft->do_close(cf->next, data);
1382 Curl_conn_cf_discard_chain(&cf->next, data);
1383 }
1384 }
1385
cf_setup_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)1386 static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1387 {
1388 struct cf_setup_ctx *ctx = cf->ctx;
1389
1390 (void)data;
1391 CURL_TRC_CF(data, cf, "destroy");
1392 Curl_safefree(ctx);
1393 }
1394
1395
1396 struct Curl_cftype Curl_cft_setup = {
1397 "SETUP",
1398 0,
1399 CURL_LOG_LVL_NONE,
1400 cf_setup_destroy,
1401 cf_setup_connect,
1402 cf_setup_close,
1403 Curl_cf_def_shutdown,
1404 Curl_cf_def_get_host,
1405 Curl_cf_def_adjust_pollset,
1406 Curl_cf_def_data_pending,
1407 Curl_cf_def_send,
1408 Curl_cf_def_recv,
1409 Curl_cf_def_cntrl,
1410 Curl_cf_def_conn_is_alive,
1411 Curl_cf_def_conn_keep_alive,
1412 Curl_cf_def_query,
1413 };
1414
cf_setup_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,const struct Curl_dns_entry * remotehost,int transport,int ssl_mode)1415 static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
1416 struct Curl_easy *data,
1417 const struct Curl_dns_entry *remotehost,
1418 int transport,
1419 int ssl_mode)
1420 {
1421 struct Curl_cfilter *cf = NULL;
1422 struct cf_setup_ctx *ctx;
1423 CURLcode result = CURLE_OK;
1424
1425 (void)data;
1426 ctx = calloc(1, sizeof(*ctx));
1427 if(!ctx) {
1428 result = CURLE_OUT_OF_MEMORY;
1429 goto out;
1430 }
1431 ctx->state = CF_SETUP_INIT;
1432 ctx->remotehost = remotehost;
1433 ctx->ssl_mode = ssl_mode;
1434 ctx->transport = transport;
1435
1436 result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
1437 if(result)
1438 goto out;
1439 ctx = NULL;
1440
1441 out:
1442 *pcf = result ? NULL : cf;
1443 free(ctx);
1444 return result;
1445 }
1446
cf_setup_add(struct Curl_easy * data,struct connectdata * conn,int sockindex,const struct Curl_dns_entry * remotehost,int transport,int ssl_mode)1447 static CURLcode cf_setup_add(struct Curl_easy *data,
1448 struct connectdata *conn,
1449 int sockindex,
1450 const struct Curl_dns_entry *remotehost,
1451 int transport,
1452 int ssl_mode)
1453 {
1454 struct Curl_cfilter *cf;
1455 CURLcode result = CURLE_OK;
1456
1457 DEBUGASSERT(data);
1458 result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
1459 if(result)
1460 goto out;
1461 Curl_conn_cf_add(data, conn, sockindex, cf);
1462 out:
1463 return result;
1464 }
1465
1466 #ifdef UNITTESTS
1467 /* used by unit2600.c */
Curl_debug_set_transport_provider(int transport,cf_ip_connect_create * cf_create)1468 void Curl_debug_set_transport_provider(int transport,
1469 cf_ip_connect_create *cf_create)
1470 {
1471 size_t i;
1472 for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
1473 if(transport == transport_providers[i].transport) {
1474 transport_providers[i].cf_create = cf_create;
1475 return;
1476 }
1477 }
1478 }
1479 #endif /* UNITTESTS */
1480
Curl_cf_setup_insert_after(struct Curl_cfilter * cf_at,struct Curl_easy * data,const struct Curl_dns_entry * remotehost,int transport,int ssl_mode)1481 CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
1482 struct Curl_easy *data,
1483 const struct Curl_dns_entry *remotehost,
1484 int transport,
1485 int ssl_mode)
1486 {
1487 struct Curl_cfilter *cf;
1488 CURLcode result;
1489
1490 DEBUGASSERT(data);
1491 result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
1492 if(result)
1493 goto out;
1494 Curl_conn_cf_insert_after(cf_at, cf);
1495 out:
1496 return result;
1497 }
1498
Curl_conn_setup(struct Curl_easy * data,struct connectdata * conn,int sockindex,const struct Curl_dns_entry * remotehost,int ssl_mode)1499 CURLcode Curl_conn_setup(struct Curl_easy *data,
1500 struct connectdata *conn,
1501 int sockindex,
1502 const struct Curl_dns_entry *remotehost,
1503 int ssl_mode)
1504 {
1505 CURLcode result = CURLE_OK;
1506
1507 DEBUGASSERT(data);
1508 DEBUGASSERT(conn->handler);
1509
1510 #if !defined(CURL_DISABLE_HTTP)
1511 if(!conn->cfilter[sockindex] &&
1512 conn->handler->protocol == CURLPROTO_HTTPS) {
1513 DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
1514 result = Curl_cf_https_setup(data, conn, sockindex, remotehost);
1515 if(result)
1516 goto out;
1517 }
1518 #endif /* !defined(CURL_DISABLE_HTTP) */
1519
1520 /* Still no cfilter set, apply default. */
1521 if(!conn->cfilter[sockindex]) {
1522 result = cf_setup_add(data, conn, sockindex, remotehost,
1523 conn->transport, ssl_mode);
1524 if(result)
1525 goto out;
1526 }
1527
1528 DEBUGASSERT(conn->cfilter[sockindex]);
1529 out:
1530 return result;
1531 }
1532