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