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