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