Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/lib/connect.c
2649 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_ARPA_INET_H
45
#include <arpa/inet.h>
46
#endif
47
48
#ifdef __VMS
49
#include <in.h>
50
#include <inet.h>
51
#endif
52
53
#include "urldata.h"
54
#include "sendf.h"
55
#include "if2ip.h"
56
#include "strerror.h"
57
#include "cfilters.h"
58
#include "connect.h"
59
#include "cf-haproxy.h"
60
#include "cf-https-connect.h"
61
#include "cf-ip-happy.h"
62
#include "cf-socket.h"
63
#include "select.h"
64
#include "url.h" /* for Curl_safefree() */
65
#include "multiif.h"
66
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
67
#include "curlx/inet_ntop.h"
68
#include "curlx/inet_pton.h"
69
#include "vtls/vtls.h" /* for vtsl cfilters */
70
#include "progress.h"
71
#include "curlx/warnless.h"
72
#include "conncache.h"
73
#include "multihandle.h"
74
#include "share.h"
75
#include "http_proxy.h"
76
#include "socks.h"
77
78
/* The last 2 #include files should be in this order */
79
#include "curl_memory.h"
80
#include "memdebug.h"
81
82
#if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
83
84
enum alpnid Curl_alpn2alpnid(const char *name, size_t len)
85
{
86
if(len == 2) {
87
if(curl_strnequal(name, "h1", 2))
88
return ALPN_h1;
89
if(curl_strnequal(name, "h2", 2))
90
return ALPN_h2;
91
if(curl_strnequal(name, "h3", 2))
92
return ALPN_h3;
93
}
94
else if(len == 8) {
95
if(curl_strnequal(name, "http/1.1", 8))
96
return ALPN_h1;
97
}
98
return ALPN_none; /* unknown, probably rubbish input */
99
}
100
101
#endif
102
103
/*
104
* Curl_timeleft() returns the amount of milliseconds left allowed for the
105
* transfer/connection. If the value is 0, there is no timeout (ie there is
106
* infinite time left). If the value is negative, the timeout time has already
107
* elapsed.
108
* @param data the transfer to check on
109
* @param nowp timestamp to use for calculation, NULL to use curlx_now()
110
* @param duringconnect TRUE iff connect timeout is also taken into account.
111
* @unittest: 1303
112
*/
113
timediff_t Curl_timeleft(struct Curl_easy *data,
114
struct curltime *nowp,
115
bool duringconnect)
116
{
117
timediff_t timeleft_ms = 0;
118
timediff_t ctimeleft_ms = 0;
119
struct curltime now;
120
121
/* The duration of a connect and the total transfer are calculated from two
122
different time-stamps. It can end up with the total timeout being reached
123
before the connect timeout expires and we must acknowledge whichever
124
timeout that is reached first. The total timeout is set per entire
125
operation, while the connect timeout is set per connect. */
126
if((!data->set.timeout || data->set.connect_only) && !duringconnect)
127
return 0; /* no timeout in place or checked, return "no limit" */
128
129
if(!nowp) {
130
now = curlx_now();
131
nowp = &now;
132
}
133
134
if(data->set.timeout) {
135
timeleft_ms = data->set.timeout -
136
curlx_timediff(*nowp, data->progress.t_startop);
137
if(!timeleft_ms)
138
timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
139
if(!duringconnect)
140
return timeleft_ms; /* no connect check, this is it */
141
}
142
143
if(duringconnect) {
144
timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
145
data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
146
ctimeleft_ms = ctimeout_ms -
147
curlx_timediff(*nowp, data->progress.t_startsingle);
148
if(!ctimeleft_ms)
149
ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
150
if(!timeleft_ms)
151
return ctimeleft_ms; /* no general timeout, this is it */
152
}
153
/* return minimal time left or max amount already expired */
154
return (ctimeleft_ms < timeleft_ms) ? ctimeleft_ms : timeleft_ms;
155
}
156
157
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
158
int timeout_ms, struct curltime *nowp)
159
{
160
struct curltime now;
161
struct connectdata *conn = data->conn;
162
163
DEBUGASSERT(conn);
164
if(!nowp) {
165
now = curlx_now();
166
nowp = &now;
167
}
168
conn->shutdown.start[sockindex] = *nowp;
169
conn->shutdown.timeout_ms = (timeout_ms > 0) ?
170
(timediff_t)timeout_ms :
171
((data->set.shutdowntimeout > 0) ?
172
data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
173
/* Set a timer, unless we operate on the admin handle */
174
if(data->mid)
175
Curl_expire_ex(data, nowp, conn->shutdown.timeout_ms,
176
EXPIRE_SHUTDOWN);
177
}
178
179
timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
180
struct curltime *nowp)
181
{
182
struct curltime now;
183
timediff_t left_ms;
184
185
if(!conn->shutdown.start[sockindex].tv_sec ||
186
(conn->shutdown.timeout_ms <= 0))
187
return 0; /* not started or no limits */
188
189
if(!nowp) {
190
now = curlx_now();
191
nowp = &now;
192
}
193
left_ms = conn->shutdown.timeout_ms -
194
curlx_timediff(*nowp, conn->shutdown.start[sockindex]);
195
return left_ms ? left_ms : -1;
196
}
197
198
timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
199
struct curltime *nowp)
200
{
201
timediff_t left_ms = 0, ms;
202
struct curltime now;
203
int i;
204
205
for(i = 0; conn->shutdown.timeout_ms && (i < 2); ++i) {
206
if(!conn->shutdown.start[i].tv_sec)
207
continue;
208
if(!nowp) {
209
now = curlx_now();
210
nowp = &now;
211
}
212
ms = Curl_shutdown_timeleft(conn, i, nowp);
213
if(ms && (!left_ms || ms < left_ms))
214
left_ms = ms;
215
}
216
return left_ms;
217
}
218
219
void Curl_shutdown_clear(struct Curl_easy *data, int sockindex)
220
{
221
struct curltime *pt = &data->conn->shutdown.start[sockindex];
222
memset(pt, 0, sizeof(*pt));
223
}
224
225
bool Curl_shutdown_started(struct Curl_easy *data, int sockindex)
226
{
227
struct curltime *pt = &data->conn->shutdown.start[sockindex];
228
return (pt->tv_sec > 0) || (pt->tv_usec > 0);
229
}
230
231
/* retrieves ip address and port from a sockaddr structure. note it calls
232
curlx_inet_ntop which sets errno on fail, not SOCKERRNO. */
233
bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
234
char *addr, int *port)
235
{
236
struct sockaddr_in *si = NULL;
237
#ifdef USE_IPV6
238
struct sockaddr_in6 *si6 = NULL;
239
#endif
240
#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
241
struct sockaddr_un *su = NULL;
242
#else
243
(void)salen;
244
#endif
245
246
switch(sa->sa_family) {
247
case AF_INET:
248
si = (struct sockaddr_in *)(void *) sa;
249
if(curlx_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
250
unsigned short us_port = ntohs(si->sin_port);
251
*port = us_port;
252
return TRUE;
253
}
254
break;
255
#ifdef USE_IPV6
256
case AF_INET6:
257
si6 = (struct sockaddr_in6 *)(void *) sa;
258
if(curlx_inet_ntop(sa->sa_family, &si6->sin6_addr, addr,
259
MAX_IPADR_LEN)) {
260
unsigned short us_port = ntohs(si6->sin6_port);
261
*port = us_port;
262
return TRUE;
263
}
264
break;
265
#endif
266
#if (defined(HAVE_SYS_UN_H) || defined(WIN32_SOCKADDR_UN)) && defined(AF_UNIX)
267
case AF_UNIX:
268
if(salen > (curl_socklen_t)sizeof(CURL_SA_FAMILY_T)) {
269
su = (struct sockaddr_un*)sa;
270
curl_msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
271
}
272
else
273
addr[0] = 0; /* socket with no name */
274
*port = 0;
275
return TRUE;
276
#endif
277
default:
278
break;
279
}
280
281
addr[0] = '\0';
282
*port = 0;
283
CURL_SETERRNO(SOCKEAFNOSUPPORT);
284
return FALSE;
285
}
286
287
/*
288
* Used to extract socket and connectdata struct for the most recent
289
* transfer on the given Curl_easy.
290
*
291
* The returned socket will be CURL_SOCKET_BAD in case of failure!
292
*/
293
curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
294
struct connectdata **connp)
295
{
296
DEBUGASSERT(data);
297
298
/* this works for an easy handle:
299
* - that has been used for curl_easy_perform()
300
* - that is associated with a multi handle, and whose connection
301
* was detached with CURLOPT_CONNECT_ONLY
302
*/
303
if(data->state.lastconnect_id != -1) {
304
struct connectdata *conn;
305
306
conn = Curl_cpool_get_conn(data, data->state.lastconnect_id);
307
if(!conn) {
308
data->state.lastconnect_id = -1;
309
return CURL_SOCKET_BAD;
310
}
311
312
if(connp)
313
/* only store this if the caller cares for it */
314
*connp = conn;
315
return conn->sock[FIRSTSOCKET];
316
}
317
return CURL_SOCKET_BAD;
318
}
319
320
/*
321
* Curl_conncontrol() marks streams or connection for closure.
322
*/
323
void Curl_conncontrol(struct connectdata *conn,
324
int ctrl /* see defines in header */
325
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
326
, const char *reason
327
#endif
328
)
329
{
330
/* close if a connection, or a stream that is not multiplexed. */
331
/* This function will be called both before and after this connection is
332
associated with a transfer. */
333
bool closeit, is_multiplex;
334
DEBUGASSERT(conn);
335
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
336
(void)reason; /* useful for debugging */
337
#endif
338
is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
339
closeit = (ctrl == CONNCTRL_CONNECTION) ||
340
((ctrl == CONNCTRL_STREAM) && !is_multiplex);
341
if((ctrl == CONNCTRL_STREAM) && is_multiplex)
342
; /* stream signal on multiplex conn never affects close state */
343
else if((bit)closeit != conn->bits.close) {
344
conn->bits.close = closeit; /* the only place in the source code that
345
should assign this bit */
346
}
347
}
348
349
typedef enum {
350
CF_SETUP_INIT,
351
CF_SETUP_CNNCT_EYEBALLS,
352
CF_SETUP_CNNCT_SOCKS,
353
CF_SETUP_CNNCT_HTTP_PROXY,
354
CF_SETUP_CNNCT_HAPROXY,
355
CF_SETUP_CNNCT_SSL,
356
CF_SETUP_DONE
357
} cf_setup_state;
358
359
struct cf_setup_ctx {
360
cf_setup_state state;
361
int ssl_mode;
362
int transport;
363
};
364
365
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
366
struct Curl_easy *data,
367
bool *done)
368
{
369
struct cf_setup_ctx *ctx = cf->ctx;
370
CURLcode result = CURLE_OK;
371
struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
372
373
if(cf->connected) {
374
*done = TRUE;
375
return CURLE_OK;
376
}
377
378
/* connect current sub-chain */
379
connect_sub_chain:
380
if(!dns)
381
return CURLE_FAILED_INIT;
382
383
if(cf->next && !cf->next->connected) {
384
result = Curl_conn_cf_connect(cf->next, data, done);
385
if(result || !*done)
386
return result;
387
}
388
389
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
390
result = cf_ip_happy_insert_after(cf, data, ctx->transport);
391
if(result)
392
return result;
393
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
394
if(!cf->next || !cf->next->connected)
395
goto connect_sub_chain;
396
}
397
398
/* sub-chain connected, do we need to add more? */
399
#ifndef CURL_DISABLE_PROXY
400
if(ctx->state < CF_SETUP_CNNCT_SOCKS && cf->conn->bits.socksproxy) {
401
result = Curl_cf_socks_proxy_insert_after(cf, data);
402
if(result)
403
return result;
404
ctx->state = CF_SETUP_CNNCT_SOCKS;
405
if(!cf->next || !cf->next->connected)
406
goto connect_sub_chain;
407
}
408
409
if(ctx->state < CF_SETUP_CNNCT_HTTP_PROXY && cf->conn->bits.httpproxy) {
410
#ifdef USE_SSL
411
if(IS_HTTPS_PROXY(cf->conn->http_proxy.proxytype)
412
&& !Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
413
result = Curl_cf_ssl_proxy_insert_after(cf, data);
414
if(result)
415
return result;
416
}
417
#endif /* USE_SSL */
418
419
#ifndef CURL_DISABLE_HTTP
420
if(cf->conn->bits.tunnel_proxy) {
421
result = Curl_cf_http_proxy_insert_after(cf, data);
422
if(result)
423
return result;
424
}
425
#endif /* !CURL_DISABLE_HTTP */
426
ctx->state = CF_SETUP_CNNCT_HTTP_PROXY;
427
if(!cf->next || !cf->next->connected)
428
goto connect_sub_chain;
429
}
430
#endif /* !CURL_DISABLE_PROXY */
431
432
if(ctx->state < CF_SETUP_CNNCT_HAPROXY) {
433
#ifndef CURL_DISABLE_PROXY
434
if(data->set.haproxyprotocol) {
435
if(Curl_conn_is_ssl(cf->conn, cf->sockindex)) {
436
failf(data, "haproxy protocol not support with SSL "
437
"encryption in place (QUIC?)");
438
return CURLE_UNSUPPORTED_PROTOCOL;
439
}
440
result = Curl_cf_haproxy_insert_after(cf, data);
441
if(result)
442
return result;
443
}
444
#endif /* !CURL_DISABLE_PROXY */
445
ctx->state = CF_SETUP_CNNCT_HAPROXY;
446
if(!cf->next || !cf->next->connected)
447
goto connect_sub_chain;
448
}
449
450
if(ctx->state < CF_SETUP_CNNCT_SSL) {
451
#ifdef USE_SSL
452
if((ctx->ssl_mode == CURL_CF_SSL_ENABLE
453
|| (ctx->ssl_mode != CURL_CF_SSL_DISABLE
454
&& cf->conn->handler->flags & PROTOPT_SSL)) /* we want SSL */
455
&& !Curl_conn_is_ssl(cf->conn, cf->sockindex)) { /* it is missing */
456
result = Curl_cf_ssl_insert_after(cf, data);
457
if(result)
458
return result;
459
}
460
#endif /* USE_SSL */
461
ctx->state = CF_SETUP_CNNCT_SSL;
462
if(!cf->next || !cf->next->connected)
463
goto connect_sub_chain;
464
}
465
466
ctx->state = CF_SETUP_DONE;
467
cf->connected = TRUE;
468
*done = TRUE;
469
return CURLE_OK;
470
}
471
472
static void cf_setup_close(struct Curl_cfilter *cf,
473
struct Curl_easy *data)
474
{
475
struct cf_setup_ctx *ctx = cf->ctx;
476
477
CURL_TRC_CF(data, cf, "close");
478
cf->connected = FALSE;
479
ctx->state = CF_SETUP_INIT;
480
481
if(cf->next) {
482
cf->next->cft->do_close(cf->next, data);
483
Curl_conn_cf_discard_chain(&cf->next, data);
484
}
485
}
486
487
static void cf_setup_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
488
{
489
struct cf_setup_ctx *ctx = cf->ctx;
490
491
(void)data;
492
CURL_TRC_CF(data, cf, "destroy");
493
Curl_safefree(ctx);
494
}
495
496
497
struct Curl_cftype Curl_cft_setup = {
498
"SETUP",
499
0,
500
CURL_LOG_LVL_NONE,
501
cf_setup_destroy,
502
cf_setup_connect,
503
cf_setup_close,
504
Curl_cf_def_shutdown,
505
Curl_cf_def_adjust_pollset,
506
Curl_cf_def_data_pending,
507
Curl_cf_def_send,
508
Curl_cf_def_recv,
509
Curl_cf_def_cntrl,
510
Curl_cf_def_conn_is_alive,
511
Curl_cf_def_conn_keep_alive,
512
Curl_cf_def_query,
513
};
514
515
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
516
struct Curl_easy *data,
517
int transport,
518
int ssl_mode)
519
{
520
struct Curl_cfilter *cf = NULL;
521
struct cf_setup_ctx *ctx;
522
CURLcode result = CURLE_OK;
523
524
(void)data;
525
ctx = calloc(1, sizeof(*ctx));
526
if(!ctx) {
527
result = CURLE_OUT_OF_MEMORY;
528
goto out;
529
}
530
ctx->state = CF_SETUP_INIT;
531
ctx->ssl_mode = ssl_mode;
532
ctx->transport = transport;
533
534
result = Curl_cf_create(&cf, &Curl_cft_setup, ctx);
535
if(result)
536
goto out;
537
ctx = NULL;
538
539
out:
540
*pcf = result ? NULL : cf;
541
if(ctx) {
542
free(ctx);
543
}
544
return result;
545
}
546
547
static CURLcode cf_setup_add(struct Curl_easy *data,
548
struct connectdata *conn,
549
int sockindex,
550
int transport,
551
int ssl_mode)
552
{
553
struct Curl_cfilter *cf;
554
CURLcode result = CURLE_OK;
555
556
DEBUGASSERT(data);
557
result = cf_setup_create(&cf, data, transport, ssl_mode);
558
if(result)
559
goto out;
560
Curl_conn_cf_add(data, conn, sockindex, cf);
561
out:
562
return result;
563
}
564
565
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
566
struct Curl_easy *data,
567
int transport,
568
int ssl_mode)
569
{
570
struct Curl_cfilter *cf;
571
CURLcode result;
572
573
DEBUGASSERT(data);
574
result = cf_setup_create(&cf, data, transport, ssl_mode);
575
if(result)
576
goto out;
577
Curl_conn_cf_insert_after(cf_at, cf);
578
out:
579
return result;
580
}
581
582
CURLcode Curl_conn_setup(struct Curl_easy *data,
583
struct connectdata *conn,
584
int sockindex,
585
struct Curl_dns_entry *dns,
586
int ssl_mode)
587
{
588
CURLcode result = CURLE_OK;
589
590
DEBUGASSERT(data);
591
DEBUGASSERT(conn->handler);
592
DEBUGASSERT(dns);
593
594
Curl_resolv_unlink(data, &data->state.dns[sockindex]);
595
data->state.dns[sockindex] = dns;
596
597
#ifndef CURL_DISABLE_HTTP
598
if(!conn->cfilter[sockindex] &&
599
conn->handler->protocol == CURLPROTO_HTTPS) {
600
DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
601
result = Curl_cf_https_setup(data, conn, sockindex);
602
if(result)
603
goto out;
604
}
605
#endif /* !CURL_DISABLE_HTTP */
606
607
/* Still no cfilter set, apply default. */
608
if(!conn->cfilter[sockindex]) {
609
result = cf_setup_add(data, conn, sockindex,
610
conn->transport_wanted, ssl_mode);
611
if(result)
612
goto out;
613
}
614
615
DEBUGASSERT(conn->cfilter[sockindex]);
616
out:
617
if(result)
618
Curl_resolv_unlink(data, &data->state.dns[sockindex]);
619
return result;
620
}
621
622
void Curl_conn_set_multiplex(struct connectdata *conn)
623
{
624
if(!conn->bits.multiplex) {
625
conn->bits.multiplex = TRUE;
626
if(conn->attached_multi) {
627
Curl_multi_connchanged(conn->attached_multi);
628
}
629
}
630
}
631
632