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