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