Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/external/curl/lib/cf-socket.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_NETINET_UDP_H
39
#include <netinet/udp.h>
40
#endif
41
#ifdef HAVE_SYS_IOCTL_H
42
#include <sys/ioctl.h>
43
#endif
44
#ifdef HAVE_NETDB_H
45
#include <netdb.h>
46
#endif
47
#ifdef HAVE_FCNTL_H
48
#include <fcntl.h>
49
#endif
50
#ifdef HAVE_ARPA_INET_H
51
#include <arpa/inet.h>
52
#endif
53
54
#ifdef __VMS
55
#include <in.h>
56
#include <inet.h>
57
#endif
58
59
#ifdef __DragonFly__
60
/* Required for __DragonFly_version */
61
#include <sys/param.h>
62
#endif
63
64
#include "urldata.h"
65
#include "bufq.h"
66
#include "sendf.h"
67
#include "if2ip.h"
68
#include "strerror.h"
69
#include "cfilters.h"
70
#include "cf-socket.h"
71
#include "connect.h"
72
#include "select.h"
73
#include "url.h" /* for Curl_safefree() */
74
#include "multiif.h"
75
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
76
#include "inet_ntop.h"
77
#include "curlx/inet_pton.h"
78
#include "progress.h"
79
#include "curlx/warnless.h"
80
#include "conncache.h"
81
#include "multihandle.h"
82
#include "rand.h"
83
#include "share.h"
84
#include "strdup.h"
85
#include "system_win32.h"
86
#include "curlx/version_win32.h"
87
#include "curlx/strparse.h"
88
89
/* The last 3 #include files should be in this order */
90
#include "curl_printf.h"
91
#include "curl_memory.h"
92
#include "memdebug.h"
93
94
95
#if defined(USE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32)
96
/* It makes support for IPv4-mapped IPv6 addresses.
97
* Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
98
* Windows Vista and later: default is on;
99
* DragonFly BSD: acts like off, and dummy setting;
100
* OpenBSD and earlier Windows: unsupported.
101
* Linux: controlled by /proc/sys/net/ipv6/bindv6only.
102
*/
103
static void set_ipv6_v6only(curl_socket_t sockfd, int on)
104
{
105
(void)setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
106
}
107
#else
108
#define set_ipv6_v6only(x,y)
109
#endif
110
111
static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
112
{
113
#if defined(TCP_NODELAY)
114
curl_socklen_t onoff = (curl_socklen_t) 1;
115
int level = IPPROTO_TCP;
116
char buffer[STRERROR_LEN];
117
118
if(setsockopt(sockfd, level, TCP_NODELAY,
119
(void *)&onoff, sizeof(onoff)) < 0)
120
infof(data, "Could not set TCP_NODELAY: %s",
121
Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
122
#else
123
(void)data;
124
(void)sockfd;
125
#endif
126
}
127
128
#ifdef SO_NOSIGPIPE
129
/* The preferred method on macOS (10.2 and later) to prevent SIGPIPEs when
130
sending data to a dead peer (instead of relying on the 4th argument to send
131
being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
132
systems? */
133
static void nosigpipe(struct Curl_easy *data,
134
curl_socket_t sockfd)
135
{
136
int onoff = 1;
137
(void)data;
138
if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
139
(void *)&onoff, sizeof(onoff)) < 0) {
140
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
141
char buffer[STRERROR_LEN];
142
infof(data, "Could not set SO_NOSIGPIPE: %s",
143
Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
144
#endif
145
}
146
}
147
#else
148
#define nosigpipe(x,y) Curl_nop_stmt
149
#endif
150
151
#if defined(USE_WINSOCK) && \
152
defined(TCP_KEEPIDLE) && defined(TCP_KEEPINTVL) && defined(TCP_KEEPCNT)
153
/* Win 10, v 1709 (10.0.16299) and later can use SetSockOpt TCP_KEEP____
154
* so should use seconds */
155
#define CURL_WINSOCK_KEEP_SSO
156
#define KEEPALIVE_FACTOR(x)
157
#elif defined(USE_WINSOCK) || \
158
(defined(__sun) && !defined(TCP_KEEPIDLE)) || \
159
(defined(__DragonFly__) && __DragonFly_version < 500702) || \
160
(defined(_WIN32) && !defined(TCP_KEEPIDLE))
161
/* Solaris < 11.4, DragonFlyBSD < 500702 and Windows < 10.0.16299
162
* use millisecond units. */
163
#define KEEPALIVE_FACTOR(x) (x *= 1000)
164
#else
165
#define KEEPALIVE_FACTOR(x)
166
#endif
167
168
#if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS)
169
#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
170
171
struct tcp_keepalive {
172
u_long onoff;
173
u_long keepalivetime;
174
u_long keepaliveinterval;
175
};
176
#endif
177
178
static void
179
tcpkeepalive(struct Curl_easy *data,
180
curl_socket_t sockfd)
181
{
182
int optval = data->set.tcp_keepalive ? 1 : 0;
183
184
/* only set IDLE and INTVL if setting KEEPALIVE is successful */
185
if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
186
(void *)&optval, sizeof(optval)) < 0) {
187
infof(data, "Failed to set SO_KEEPALIVE on fd "
188
"%" FMT_SOCKET_T ": errno %d",
189
sockfd, SOCKERRNO);
190
}
191
else {
192
#if defined(SIO_KEEPALIVE_VALS) /* Windows */
193
/* Windows 10, version 1709 (10.0.16299) and later versions */
194
#if defined(CURL_WINSOCK_KEEP_SSO)
195
optval = curlx_sltosi(data->set.tcp_keepidle);
196
KEEPALIVE_FACTOR(optval);
197
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
198
(const char *)&optval, sizeof(optval)) < 0) {
199
infof(data, "Failed to set TCP_KEEPIDLE on fd "
200
"%" FMT_SOCKET_T ": errno %d",
201
sockfd, SOCKERRNO);
202
}
203
optval = curlx_sltosi(data->set.tcp_keepintvl);
204
KEEPALIVE_FACTOR(optval);
205
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
206
(const char *)&optval, sizeof(optval)) < 0) {
207
infof(data, "Failed to set TCP_KEEPINTVL on fd "
208
"%" FMT_SOCKET_T ": errno %d",
209
sockfd, SOCKERRNO);
210
}
211
optval = curlx_sltosi(data->set.tcp_keepcnt);
212
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
213
(const char *)&optval, sizeof(optval)) < 0) {
214
infof(data, "Failed to set TCP_KEEPCNT on fd "
215
"%" FMT_SOCKET_T ": errno %d",
216
sockfd, SOCKERRNO);
217
}
218
#else /* Windows < 10.0.16299 */
219
struct tcp_keepalive vals;
220
DWORD dummy;
221
vals.onoff = 1;
222
optval = curlx_sltosi(data->set.tcp_keepidle);
223
KEEPALIVE_FACTOR(optval);
224
vals.keepalivetime = (u_long)optval;
225
optval = curlx_sltosi(data->set.tcp_keepintvl);
226
KEEPALIVE_FACTOR(optval);
227
vals.keepaliveinterval = (u_long)optval;
228
if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
229
NULL, 0, &dummy, NULL, NULL) != 0) {
230
infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd "
231
"%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
232
}
233
#endif
234
#else /* !Windows */
235
#ifdef TCP_KEEPIDLE
236
optval = curlx_sltosi(data->set.tcp_keepidle);
237
KEEPALIVE_FACTOR(optval);
238
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
239
(void *)&optval, sizeof(optval)) < 0) {
240
infof(data, "Failed to set TCP_KEEPIDLE on fd "
241
"%" FMT_SOCKET_T ": errno %d",
242
sockfd, SOCKERRNO);
243
}
244
#elif defined(TCP_KEEPALIVE)
245
/* macOS style */
246
optval = curlx_sltosi(data->set.tcp_keepidle);
247
KEEPALIVE_FACTOR(optval);
248
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
249
(void *)&optval, sizeof(optval)) < 0) {
250
infof(data, "Failed to set TCP_KEEPALIVE on fd "
251
"%" FMT_SOCKET_T ": errno %d",
252
sockfd, SOCKERRNO);
253
}
254
#elif defined(TCP_KEEPALIVE_THRESHOLD)
255
/* Solaris <11.4 style */
256
optval = curlx_sltosi(data->set.tcp_keepidle);
257
KEEPALIVE_FACTOR(optval);
258
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
259
(void *)&optval, sizeof(optval)) < 0) {
260
infof(data, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd "
261
"%" FMT_SOCKET_T ": errno %d",
262
sockfd, SOCKERRNO);
263
}
264
#endif
265
#ifdef TCP_KEEPINTVL
266
optval = curlx_sltosi(data->set.tcp_keepintvl);
267
KEEPALIVE_FACTOR(optval);
268
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
269
(void *)&optval, sizeof(optval)) < 0) {
270
infof(data, "Failed to set TCP_KEEPINTVL on fd "
271
"%" FMT_SOCKET_T ": errno %d",
272
sockfd, SOCKERRNO);
273
}
274
#elif defined(TCP_KEEPALIVE_ABORT_THRESHOLD)
275
/* Solaris <11.4 style */
276
/* TCP_KEEPALIVE_ABORT_THRESHOLD should equal to
277
* TCP_KEEPCNT * TCP_KEEPINTVL on other platforms.
278
* The default value of TCP_KEEPCNT is 9 on Linux,
279
* 8 on *BSD/macOS, 5 or 10 on Windows. We use the
280
* default config for Solaris <11.4 because there is
281
* no default value for TCP_KEEPCNT on Solaris 11.4.
282
*
283
* Note that the consequent probes will not be sent
284
* at equal intervals on Solaris, but will be sent
285
* using the exponential backoff algorithm. */
286
optval = curlx_sltosi(data->set.tcp_keepcnt) *
287
curlx_sltosi(data->set.tcp_keepintvl);
288
KEEPALIVE_FACTOR(optval);
289
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD,
290
(void *)&optval, sizeof(optval)) < 0) {
291
infof(data, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD on fd "
292
"%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
293
}
294
#endif
295
#ifdef TCP_KEEPCNT
296
optval = curlx_sltosi(data->set.tcp_keepcnt);
297
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
298
(void *)&optval, sizeof(optval)) < 0) {
299
infof(data, "Failed to set TCP_KEEPCNT on fd "
300
"%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
301
}
302
#endif
303
#endif
304
}
305
}
306
307
/**
308
* Assign the address `ai` to the Curl_sockaddr_ex `dest` and
309
* set the transport used.
310
*/
311
CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
312
const struct Curl_addrinfo *ai,
313
int transport)
314
{
315
/*
316
* The Curl_sockaddr_ex structure is basically libcurl's external API
317
* curl_sockaddr structure with enough space available to directly hold
318
* any protocol-specific address structures. The variable declared here
319
* will be used to pass / receive data to/from the fopensocket callback
320
* if this has been set, before that, it is initialized from parameters.
321
*/
322
dest->family = ai->ai_family;
323
switch(transport) {
324
case TRNSPRT_TCP:
325
dest->socktype = SOCK_STREAM;
326
dest->protocol = IPPROTO_TCP;
327
break;
328
case TRNSPRT_UNIX:
329
dest->socktype = SOCK_STREAM;
330
dest->protocol = IPPROTO_IP;
331
break;
332
default: /* UDP and QUIC */
333
dest->socktype = SOCK_DGRAM;
334
dest->protocol = IPPROTO_UDP;
335
break;
336
}
337
dest->addrlen = (unsigned int)ai->ai_addrlen;
338
339
if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) {
340
DEBUGASSERT(0);
341
return CURLE_TOO_LARGE;
342
}
343
344
memcpy(&dest->curl_sa_addr, ai->ai_addr, dest->addrlen);
345
return CURLE_OK;
346
}
347
348
static CURLcode socket_open(struct Curl_easy *data,
349
struct Curl_sockaddr_ex *addr,
350
curl_socket_t *sockfd)
351
{
352
DEBUGASSERT(data);
353
DEBUGASSERT(data->conn);
354
if(data->set.fopensocket) {
355
/*
356
* If the opensocket callback is set, all the destination address
357
* information is passed to the callback. Depending on this information the
358
* callback may opt to abort the connection, this is indicated returning
359
* CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
360
* the callback returns a valid socket the destination address information
361
* might have been changed and this 'new' address will actually be used
362
* here to connect.
363
*/
364
Curl_set_in_callback(data, TRUE);
365
*sockfd = data->set.fopensocket(data->set.opensocket_client,
366
CURLSOCKTYPE_IPCXN,
367
(struct curl_sockaddr *)addr);
368
Curl_set_in_callback(data, FALSE);
369
}
370
else {
371
/* opensocket callback not set, so simply create the socket now */
372
*sockfd = socket(addr->family, addr->socktype, addr->protocol);
373
}
374
375
if(*sockfd == CURL_SOCKET_BAD)
376
/* no socket, no connection */
377
return CURLE_COULDNT_CONNECT;
378
379
#if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
380
if(data->conn->scope_id && (addr->family == AF_INET6)) {
381
struct sockaddr_in6 * const sa6 = (void *)&addr->curl_sa_addr;
382
sa6->sin6_scope_id = data->conn->scope_id;
383
}
384
#endif
385
return CURLE_OK;
386
}
387
388
/*
389
* Create a socket based on info from 'conn' and 'ai'.
390
*
391
* 'addr' should be a pointer to the correct struct to get data back, or NULL.
392
* 'sockfd' must be a pointer to a socket descriptor.
393
*
394
* If the open socket callback is set, used that!
395
*
396
*/
397
CURLcode Curl_socket_open(struct Curl_easy *data,
398
const struct Curl_addrinfo *ai,
399
struct Curl_sockaddr_ex *addr,
400
int transport,
401
curl_socket_t *sockfd)
402
{
403
struct Curl_sockaddr_ex dummy;
404
CURLcode result;
405
406
if(!addr)
407
/* if the caller does not want info back, use a local temp copy */
408
addr = &dummy;
409
410
result = Curl_sock_assign_addr(addr, ai, transport);
411
if(result)
412
return result;
413
414
return socket_open(data, addr, sockfd);
415
}
416
417
static int socket_close(struct Curl_easy *data, struct connectdata *conn,
418
int use_callback, curl_socket_t sock)
419
{
420
if(CURL_SOCKET_BAD == sock)
421
return 0;
422
423
if(use_callback && conn && conn->fclosesocket) {
424
int rc;
425
Curl_multi_will_close(data, sock);
426
Curl_set_in_callback(data, TRUE);
427
rc = conn->fclosesocket(conn->closesocket_client, sock);
428
Curl_set_in_callback(data, FALSE);
429
return rc;
430
}
431
432
if(conn)
433
/* tell the multi-socket code about this */
434
Curl_multi_will_close(data, sock);
435
436
sclose(sock);
437
438
return 0;
439
}
440
441
/*
442
* Close a socket.
443
*
444
* 'conn' can be NULL, beware!
445
*/
446
int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
447
curl_socket_t sock)
448
{
449
return socket_close(data, conn, FALSE, sock);
450
}
451
452
#ifdef USE_WINSOCK
453
/* When you run a program that uses the Windows Sockets API, you may
454
experience slow performance when you copy data to a TCP server.
455
456
https://support.microsoft.com/kb/823764
457
458
Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
459
Buffer Size
460
461
The problem described in this knowledge-base is applied only to pre-Vista
462
Windows. Following function trying to detect OS version and skips
463
SO_SNDBUF adjustment for Windows Vista and above.
464
*/
465
466
void Curl_sndbuf_init(curl_socket_t sockfd)
467
{
468
int val = CURL_MAX_WRITE_SIZE + 32;
469
int curval = 0;
470
int curlen = sizeof(curval);
471
472
if(Curl_isVistaOrGreater)
473
return;
474
475
if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
476
if(curval > val)
477
return;
478
479
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
480
}
481
#endif /* USE_WINSOCK */
482
483
/*
484
* Curl_parse_interface()
485
*
486
* This is used to parse interface argument in the following formats.
487
* In all the examples, `host` can be an IP address or a hostname.
488
*
489
* <iface_or_host> - can be either an interface name or a host.
490
* if!<iface> - interface name.
491
* host!<host> - hostname.
492
* ifhost!<iface>!<host> - interface name and hostname.
493
*
494
* Parameters:
495
*
496
* input [in] - input string.
497
* len [in] - length of the input string.
498
* dev [in/out] - address where a pointer to newly allocated memory
499
* holding the interface-or-host will be stored upon
500
* completion.
501
* iface [in/out] - address where a pointer to newly allocated memory
502
* holding the interface will be stored upon completion.
503
* host [in/out] - address where a pointer to newly allocated memory
504
* holding the host will be stored upon completion.
505
*
506
* Returns CURLE_OK on success.
507
*/
508
CURLcode Curl_parse_interface(const char *input,
509
char **dev, char **iface, char **host)
510
{
511
static const char if_prefix[] = "if!";
512
static const char host_prefix[] = "host!";
513
static const char if_host_prefix[] = "ifhost!";
514
size_t len;
515
516
DEBUGASSERT(dev);
517
DEBUGASSERT(iface);
518
DEBUGASSERT(host);
519
520
len = strlen(input);
521
if(len > 512)
522
return CURLE_BAD_FUNCTION_ARGUMENT;
523
524
if(!strncmp(if_prefix, input, strlen(if_prefix))) {
525
input += strlen(if_prefix);
526
if(!*input)
527
return CURLE_BAD_FUNCTION_ARGUMENT;
528
*iface = Curl_memdup0(input, len - strlen(if_prefix));
529
return *iface ? CURLE_OK : CURLE_OUT_OF_MEMORY;
530
}
531
else if(!strncmp(host_prefix, input, strlen(host_prefix))) {
532
input += strlen(host_prefix);
533
if(!*input)
534
return CURLE_BAD_FUNCTION_ARGUMENT;
535
*host = Curl_memdup0(input, len - strlen(host_prefix));
536
return *host ? CURLE_OK : CURLE_OUT_OF_MEMORY;
537
}
538
else if(!strncmp(if_host_prefix, input, strlen(if_host_prefix))) {
539
const char *host_part;
540
input += strlen(if_host_prefix);
541
len -= strlen(if_host_prefix);
542
host_part = memchr(input, '!', len);
543
if(!host_part || !*(host_part + 1))
544
return CURLE_BAD_FUNCTION_ARGUMENT;
545
*iface = Curl_memdup0(input, host_part - input);
546
if(!*iface)
547
return CURLE_OUT_OF_MEMORY;
548
++host_part;
549
*host = Curl_memdup0(host_part, len - (host_part - input));
550
if(!*host) {
551
free(*iface);
552
*iface = NULL;
553
return CURLE_OUT_OF_MEMORY;
554
}
555
return CURLE_OK;
556
}
557
558
if(!*input)
559
return CURLE_BAD_FUNCTION_ARGUMENT;
560
*dev = Curl_memdup0(input, len);
561
return *dev ? CURLE_OK : CURLE_OUT_OF_MEMORY;
562
}
563
564
#ifndef CURL_DISABLE_BINDLOCAL
565
static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
566
curl_socket_t sockfd, int af, unsigned int scope)
567
{
568
struct Curl_sockaddr_storage sa;
569
struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
570
curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
571
struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
572
#ifdef USE_IPV6
573
struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
574
#endif
575
576
struct Curl_dns_entry *h = NULL;
577
unsigned short port = data->set.localport; /* use this port number, 0 for
578
"random" */
579
/* how many port numbers to try to bind to, increasing one at a time */
580
int portnum = data->set.localportrange;
581
const char *dev = data->set.str[STRING_DEVICE];
582
const char *iface_input = data->set.str[STRING_INTERFACE];
583
const char *host_input = data->set.str[STRING_BINDHOST];
584
const char *iface = iface_input ? iface_input : dev;
585
const char *host = host_input ? host_input : dev;
586
int error;
587
#ifdef IP_BIND_ADDRESS_NO_PORT
588
int on = 1;
589
#endif
590
#ifndef USE_IPV6
591
(void)scope;
592
#endif
593
594
/*************************************************************
595
* Select device to bind socket to
596
*************************************************************/
597
if(!iface && !host && !port)
598
/* no local kind of binding was requested */
599
return CURLE_OK;
600
else if(iface && (strlen(iface) >= 255) )
601
return CURLE_BAD_FUNCTION_ARGUMENT;
602
603
memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
604
605
if(iface || host) {
606
char myhost[256] = "";
607
int done = 0; /* -1 for error, 1 for address found */
608
if2ip_result_t if2ip_result = IF2IP_NOT_FOUND;
609
610
#ifdef SO_BINDTODEVICE
611
if(iface) {
612
/*
613
* This binds the local socket to a particular interface. This will
614
* force even requests to other local interfaces to go out the external
615
* interface. Only bind to the interface when specified as interface,
616
* not just as a hostname or ip address.
617
*
618
* The interface might be a VRF, eg: vrf-blue, which means it cannot be
619
* converted to an IP address and would fail Curl_if2ip. Simply try to
620
* use it straight away.
621
*/
622
if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
623
iface, (curl_socklen_t)strlen(iface) + 1) == 0) {
624
/* This is often "errno 1, error: Operation not permitted" if you are
625
* not running as root or another suitable privileged user. If it
626
* succeeds it means the parameter was a valid interface and not an IP
627
* address. Return immediately.
628
*/
629
if(!host_input) {
630
infof(data, "socket successfully bound to interface '%s'", iface);
631
return CURLE_OK;
632
}
633
}
634
}
635
#endif
636
if(!host_input) {
637
/* Discover IP from input device, then bind to it */
638
if2ip_result = Curl_if2ip(af,
639
#ifdef USE_IPV6
640
scope, conn->scope_id,
641
#endif
642
iface, myhost, sizeof(myhost));
643
}
644
switch(if2ip_result) {
645
case IF2IP_NOT_FOUND:
646
if(iface_input && !host_input) {
647
/* Do not fall back to treating it as a hostname */
648
char buffer[STRERROR_LEN];
649
data->state.os_errno = error = SOCKERRNO;
650
failf(data, "Couldn't bind to interface '%s' with errno %d: %s",
651
iface, error, Curl_strerror(error, buffer, sizeof(buffer)));
652
return CURLE_INTERFACE_FAILED;
653
}
654
break;
655
case IF2IP_AF_NOT_SUPPORTED:
656
/* Signal the caller to try another address family if available */
657
return CURLE_UNSUPPORTED_PROTOCOL;
658
case IF2IP_FOUND:
659
/*
660
* We now have the numerical IP address in the 'myhost' buffer
661
*/
662
host = myhost;
663
infof(data, "Local Interface %s is ip %s using address family %i",
664
iface, host, af);
665
done = 1;
666
break;
667
}
668
if(!iface_input || host_input) {
669
/*
670
* This was not an interface, resolve the name as a hostname
671
* or IP number
672
*
673
* Temporarily force name resolution to use only the address type
674
* of the connection. The resolve functions should really be changed
675
* to take a type parameter instead.
676
*/
677
int ip_version = (af == AF_INET) ?
678
CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER;
679
#ifdef USE_IPV6
680
if(af == AF_INET6)
681
ip_version = CURL_IPRESOLVE_V6;
682
#endif
683
684
(void)Curl_resolv_blocking(data, host, 80, ip_version, &h);
685
if(h) {
686
int h_af = h->addr->ai_family;
687
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
688
Curl_printable_address(h->addr, myhost, sizeof(myhost));
689
infof(data, "Name '%s' family %i resolved to '%s' family %i",
690
host, af, myhost, h_af);
691
Curl_resolv_unlink(data, &h); /* this will NULL, potential free h */
692
if(af != h_af) {
693
/* bad IP version combo, signal the caller to try another address
694
family if available */
695
return CURLE_UNSUPPORTED_PROTOCOL;
696
}
697
done = 1;
698
}
699
else {
700
/*
701
* provided dev was no interface (or interfaces are not supported
702
* e.g. Solaris) no ip address and no domain we fail here
703
*/
704
done = -1;
705
}
706
}
707
708
if(done > 0) {
709
#ifdef USE_IPV6
710
/* IPv6 address */
711
if(af == AF_INET6) {
712
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
713
char *scope_ptr = strchr(myhost, '%');
714
if(scope_ptr)
715
*(scope_ptr++) = '\0';
716
#endif
717
if(curlx_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
718
si6->sin6_family = AF_INET6;
719
si6->sin6_port = htons(port);
720
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
721
if(scope_ptr) {
722
/* The "myhost" string either comes from Curl_if2ip or from
723
Curl_printable_address. The latter returns only numeric scope
724
IDs and the former returns none at all. So the scope ID, if
725
present, is known to be numeric */
726
curl_off_t scope_id;
727
if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr),
728
&scope_id, UINT_MAX))
729
return CURLE_UNSUPPORTED_PROTOCOL;
730
si6->sin6_scope_id = (unsigned int)scope_id;
731
}
732
#endif
733
}
734
sizeof_sa = sizeof(struct sockaddr_in6);
735
}
736
else
737
#endif
738
/* IPv4 address */
739
if((af == AF_INET) &&
740
(curlx_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
741
si4->sin_family = AF_INET;
742
si4->sin_port = htons(port);
743
sizeof_sa = sizeof(struct sockaddr_in);
744
}
745
}
746
747
if(done < 1) {
748
/* errorbuf is set false so failf will overwrite any message already in
749
the error buffer, so the user receives this error message instead of a
750
generic resolve error. */
751
char buffer[STRERROR_LEN];
752
data->state.errorbuf = FALSE;
753
data->state.os_errno = error = SOCKERRNO;
754
failf(data, "Couldn't bind to '%s' with errno %d: %s",
755
host, error, Curl_strerror(error, buffer, sizeof(buffer)));
756
return CURLE_INTERFACE_FAILED;
757
}
758
}
759
else {
760
/* no device was given, prepare sa to match af's needs */
761
#ifdef USE_IPV6
762
if(af == AF_INET6) {
763
si6->sin6_family = AF_INET6;
764
si6->sin6_port = htons(port);
765
sizeof_sa = sizeof(struct sockaddr_in6);
766
}
767
else
768
#endif
769
if(af == AF_INET) {
770
si4->sin_family = AF_INET;
771
si4->sin_port = htons(port);
772
sizeof_sa = sizeof(struct sockaddr_in);
773
}
774
}
775
#ifdef IP_BIND_ADDRESS_NO_PORT
776
(void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
777
#endif
778
for(;;) {
779
if(bind(sockfd, sock, sizeof_sa) >= 0) {
780
/* we succeeded to bind */
781
infof(data, "Local port: %hu", port);
782
conn->bits.bound = TRUE;
783
return CURLE_OK;
784
}
785
786
if(--portnum > 0) {
787
port++; /* try next port */
788
if(port == 0)
789
break;
790
infof(data, "Bind to local port %d failed, trying next", port - 1);
791
/* We reuse/clobber the port variable here below */
792
if(sock->sa_family == AF_INET)
793
si4->sin_port = ntohs(port);
794
#ifdef USE_IPV6
795
else
796
si6->sin6_port = ntohs(port);
797
#endif
798
}
799
else
800
break;
801
}
802
{
803
char buffer[STRERROR_LEN];
804
data->state.os_errno = error = SOCKERRNO;
805
failf(data, "bind failed with errno %d: %s",
806
error, Curl_strerror(error, buffer, sizeof(buffer)));
807
}
808
809
return CURLE_INTERFACE_FAILED;
810
}
811
#endif
812
813
/*
814
* verifyconnect() returns TRUE if the connect really has happened.
815
*/
816
static bool verifyconnect(curl_socket_t sockfd, int *error)
817
{
818
bool rc = TRUE;
819
#ifdef SO_ERROR
820
int err = 0;
821
curl_socklen_t errSize = sizeof(err);
822
823
#ifdef _WIN32
824
/*
825
* In October 2003 we effectively nullified this function on Windows due to
826
* problems with it using all CPU in multi-threaded cases.
827
*
828
* In May 2004, we bring it back to offer more info back on connect failures.
829
* Gisle Vanem could reproduce the former problems with this function, but
830
* could avoid them by adding this SleepEx() call below:
831
*
832
* "I do not have Rational Quantify, but the hint from his post was
833
* ntdll::NtRemoveIoCompletion(). I would assume the SleepEx (or maybe
834
* just Sleep(0) would be enough?) would release whatever
835
* mutex/critical-section the ntdll call is waiting on.
836
*
837
* Someone got to verify this on Win-NT 4.0, 2000."
838
*/
839
840
#ifdef UNDER_CE
841
Sleep(0);
842
#else
843
SleepEx(0, FALSE);
844
#endif
845
846
#endif
847
848
if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
849
err = SOCKERRNO;
850
#ifdef UNDER_CE
851
/* Old Windows CE versions do not support SO_ERROR */
852
if(WSAENOPROTOOPT == err) {
853
SET_SOCKERRNO(0);
854
err = 0;
855
}
856
#endif
857
#if defined(EBADIOCTL) && defined(__minix)
858
/* Minix 3.1.x does not support getsockopt on UDP sockets */
859
if(EBADIOCTL == err) {
860
SET_SOCKERRNO(0);
861
err = 0;
862
}
863
#endif
864
if((0 == err) || (SOCKEISCONN == err))
865
/* we are connected, awesome! */
866
rc = TRUE;
867
else
868
/* This was not a successful connect */
869
rc = FALSE;
870
if(error)
871
*error = err;
872
#else
873
(void)sockfd;
874
if(error)
875
*error = SOCKERRNO;
876
#endif
877
return rc;
878
}
879
880
/**
881
* Determine the curl code for a socket connect() == -1 with errno.
882
*/
883
static CURLcode socket_connect_result(struct Curl_easy *data,
884
const char *ipaddress, int error)
885
{
886
switch(error) {
887
case SOCKEINPROGRESS:
888
case SOCKEWOULDBLOCK:
889
#if defined(EAGAIN)
890
#if (EAGAIN) != (SOCKEWOULDBLOCK)
891
/* On some platforms EAGAIN and EWOULDBLOCK are the
892
* same value, and on others they are different, hence
893
* the odd #if
894
*/
895
case EAGAIN:
896
#endif
897
#endif
898
return CURLE_OK;
899
900
default:
901
/* unknown error, fallthrough and try another address! */
902
#ifdef CURL_DISABLE_VERBOSE_STRINGS
903
(void)ipaddress;
904
#else
905
{
906
char buffer[STRERROR_LEN];
907
infof(data, "Immediate connect fail for %s: %s",
908
ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
909
}
910
#endif
911
data->state.os_errno = error;
912
/* connect failed */
913
return CURLE_COULDNT_CONNECT;
914
}
915
}
916
917
struct cf_socket_ctx {
918
int transport;
919
struct Curl_sockaddr_ex addr; /* address to connect to */
920
curl_socket_t sock; /* current attempt socket */
921
struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */
922
struct curltime started_at; /* when socket was created */
923
struct curltime connected_at; /* when socket connected/got first byte */
924
struct curltime first_byte_at; /* when first byte was recvd */
925
#ifdef USE_WINSOCK
926
struct curltime last_sndbuf_query_at; /* when SO_SNDBUF last queried */
927
ULONG sndbuf_size; /* the last set SO_SNDBUF size */
928
#endif
929
int error; /* errno of last failure or 0 */
930
#ifdef DEBUGBUILD
931
int wblock_percent; /* percent of writes doing EAGAIN */
932
int wpartial_percent; /* percent of bytes written in send */
933
int rblock_percent; /* percent of reads doing EAGAIN */
934
size_t recv_max; /* max enforced read size */
935
#endif
936
BIT(got_first_byte); /* if first byte was received */
937
BIT(listening); /* socket is listening */
938
BIT(accepted); /* socket was accepted, not connected */
939
BIT(sock_connected); /* socket is "connected", e.g. in UDP */
940
BIT(active);
941
};
942
943
static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
944
const struct Curl_addrinfo *ai,
945
int transport)
946
{
947
CURLcode result;
948
949
memset(ctx, 0, sizeof(*ctx));
950
ctx->sock = CURL_SOCKET_BAD;
951
ctx->transport = transport;
952
953
result = Curl_sock_assign_addr(&ctx->addr, ai, transport);
954
if(result)
955
return result;
956
957
#ifdef DEBUGBUILD
958
{
959
const char *p = getenv("CURL_DBG_SOCK_WBLOCK");
960
if(p) {
961
curl_off_t l;
962
if(!curlx_str_number(&p, &l, 100))
963
ctx->wblock_percent = (int)l;
964
}
965
p = getenv("CURL_DBG_SOCK_WPARTIAL");
966
if(p) {
967
curl_off_t l;
968
if(!curlx_str_number(&p, &l, 100))
969
ctx->wpartial_percent = (int)l;
970
}
971
p = getenv("CURL_DBG_SOCK_RBLOCK");
972
if(p) {
973
curl_off_t l;
974
if(!curlx_str_number(&p, &l, 100))
975
ctx->rblock_percent = (int)l;
976
}
977
p = getenv("CURL_DBG_SOCK_RMAX");
978
if(p) {
979
curl_off_t l;
980
if(!curlx_str_number(&p, &l, CURL_OFF_T_MAX))
981
ctx->recv_max = (size_t)l;
982
}
983
}
984
#endif
985
986
return result;
987
}
988
989
static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
990
{
991
struct cf_socket_ctx *ctx = cf->ctx;
992
993
if(ctx && CURL_SOCKET_BAD != ctx->sock) {
994
CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock);
995
if(ctx->sock == cf->conn->sock[cf->sockindex])
996
cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
997
socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
998
ctx->sock = CURL_SOCKET_BAD;
999
if(ctx->active && cf->sockindex == FIRSTSOCKET)
1000
cf->conn->remote_addr = NULL;
1001
ctx->active = FALSE;
1002
memset(&ctx->started_at, 0, sizeof(ctx->started_at));
1003
memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
1004
}
1005
1006
cf->connected = FALSE;
1007
}
1008
1009
static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
1010
struct Curl_easy *data,
1011
bool *done)
1012
{
1013
if(cf->connected) {
1014
struct cf_socket_ctx *ctx = cf->ctx;
1015
1016
CURL_TRC_CF(data, cf, "cf_socket_shutdown, fd=%" FMT_SOCKET_T, ctx->sock);
1017
/* On TCP, and when the socket looks well and non-blocking mode
1018
* can be enabled, receive dangling bytes before close to avoid
1019
* entering RST states unnecessarily. */
1020
if(ctx->sock != CURL_SOCKET_BAD &&
1021
ctx->transport == TRNSPRT_TCP &&
1022
(curlx_nonblock(ctx->sock, TRUE) >= 0)) {
1023
unsigned char buf[1024];
1024
(void)sread(ctx->sock, buf, sizeof(buf));
1025
}
1026
}
1027
*done = TRUE;
1028
return CURLE_OK;
1029
}
1030
1031
static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
1032
{
1033
struct cf_socket_ctx *ctx = cf->ctx;
1034
1035
cf_socket_close(cf, data);
1036
CURL_TRC_CF(data, cf, "destroy");
1037
free(ctx);
1038
cf->ctx = NULL;
1039
}
1040
1041
static CURLcode set_local_ip(struct Curl_cfilter *cf,
1042
struct Curl_easy *data)
1043
{
1044
struct cf_socket_ctx *ctx = cf->ctx;
1045
1046
#ifdef HAVE_GETSOCKNAME
1047
if((ctx->sock != CURL_SOCKET_BAD) &&
1048
!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
1049
/* TFTP does not connect, so it cannot get the IP like this */
1050
1051
char buffer[STRERROR_LEN];
1052
struct Curl_sockaddr_storage ssloc;
1053
curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
1054
1055
memset(&ssloc, 0, sizeof(ssloc));
1056
if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
1057
int error = SOCKERRNO;
1058
failf(data, "getsockname() failed with errno %d: %s",
1059
error, Curl_strerror(error, buffer, sizeof(buffer)));
1060
return CURLE_FAILED_INIT;
1061
}
1062
if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
1063
ctx->ip.local_ip, &ctx->ip.local_port)) {
1064
failf(data, "ssloc inet_ntop() failed with errno %d: %s",
1065
errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1066
return CURLE_FAILED_INIT;
1067
}
1068
}
1069
#else
1070
(void)data;
1071
ctx->ip.local_ip[0] = 0;
1072
ctx->ip.local_port = -1;
1073
#endif
1074
return CURLE_OK;
1075
}
1076
1077
static CURLcode set_remote_ip(struct Curl_cfilter *cf,
1078
struct Curl_easy *data)
1079
{
1080
struct cf_socket_ctx *ctx = cf->ctx;
1081
1082
/* store remote address and port used in this connection attempt */
1083
if(!Curl_addr2string(&ctx->addr.curl_sa_addr,
1084
(curl_socklen_t)ctx->addr.addrlen,
1085
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
1086
char buffer[STRERROR_LEN];
1087
1088
ctx->error = errno;
1089
/* malformed address or bug in inet_ntop, try next address */
1090
failf(data, "curl_sa_addr inet_ntop() failed with errno %d: %s",
1091
errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1092
return CURLE_FAILED_INIT;
1093
}
1094
return CURLE_OK;
1095
}
1096
1097
static CURLcode cf_socket_open(struct Curl_cfilter *cf,
1098
struct Curl_easy *data)
1099
{
1100
struct cf_socket_ctx *ctx = cf->ctx;
1101
int error = 0;
1102
bool isconnected = FALSE;
1103
CURLcode result = CURLE_COULDNT_CONNECT;
1104
bool is_tcp;
1105
1106
(void)data;
1107
DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
1108
ctx->started_at = curlx_now();
1109
#ifdef SOCK_NONBLOCK
1110
/* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set
1111
* because we would not know how socketype is about to be used in the
1112
* callback, SOCK_NONBLOCK might get factored out before calling socket().
1113
*/
1114
if(!data->set.fopensocket)
1115
ctx->addr.socktype |= SOCK_NONBLOCK;
1116
#endif
1117
result = socket_open(data, &ctx->addr, &ctx->sock);
1118
#ifdef SOCK_NONBLOCK
1119
/* Restore the socktype after the socket is created. */
1120
if(!data->set.fopensocket)
1121
ctx->addr.socktype &= ~SOCK_NONBLOCK;
1122
#endif
1123
if(result)
1124
goto out;
1125
1126
result = set_remote_ip(cf, data);
1127
if(result)
1128
goto out;
1129
1130
#ifdef USE_IPV6
1131
if(ctx->addr.family == AF_INET6) {
1132
set_ipv6_v6only(ctx->sock, 0);
1133
infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
1134
}
1135
else
1136
#endif
1137
infof(data, " Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
1138
1139
#ifdef USE_IPV6
1140
is_tcp = (ctx->addr.family == AF_INET
1141
|| ctx->addr.family == AF_INET6) &&
1142
ctx->addr.socktype == SOCK_STREAM;
1143
#else
1144
is_tcp = (ctx->addr.family == AF_INET) &&
1145
ctx->addr.socktype == SOCK_STREAM;
1146
#endif
1147
if(is_tcp && data->set.tcp_nodelay)
1148
tcpnodelay(data, ctx->sock);
1149
1150
nosigpipe(data, ctx->sock);
1151
1152
Curl_sndbuf_init(ctx->sock);
1153
1154
if(is_tcp && data->set.tcp_keepalive)
1155
tcpkeepalive(data, ctx->sock);
1156
1157
if(data->set.fsockopt) {
1158
/* activate callback for setting socket options */
1159
Curl_set_in_callback(data, TRUE);
1160
error = data->set.fsockopt(data->set.sockopt_client,
1161
ctx->sock,
1162
CURLSOCKTYPE_IPCXN);
1163
Curl_set_in_callback(data, FALSE);
1164
1165
if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1166
isconnected = TRUE;
1167
else if(error) {
1168
result = CURLE_ABORTED_BY_CALLBACK;
1169
goto out;
1170
}
1171
}
1172
1173
#ifndef CURL_DISABLE_BINDLOCAL
1174
/* possibly bind the local end to an IP, interface or port */
1175
if(ctx->addr.family == AF_INET
1176
#ifdef USE_IPV6
1177
|| ctx->addr.family == AF_INET6
1178
#endif
1179
) {
1180
result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
1181
Curl_ipv6_scope(&ctx->addr.curl_sa_addr));
1182
if(result) {
1183
if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1184
/* The address family is not supported on this interface.
1185
We can continue trying addresses */
1186
result = CURLE_COULDNT_CONNECT;
1187
}
1188
goto out;
1189
}
1190
}
1191
#endif
1192
1193
#ifndef SOCK_NONBLOCK
1194
/* Set socket non-blocking, must be a non-blocking socket for
1195
* a non-blocking connect. */
1196
error = curlx_nonblock(ctx->sock, TRUE);
1197
if(error < 0) {
1198
result = CURLE_UNSUPPORTED_PROTOCOL;
1199
ctx->error = SOCKERRNO;
1200
goto out;
1201
}
1202
#else
1203
if(data->set.fopensocket) {
1204
/* Set socket non-blocking, must be a non-blocking socket for
1205
* a non-blocking connect. */
1206
error = curlx_nonblock(ctx->sock, TRUE);
1207
if(error < 0) {
1208
result = CURLE_UNSUPPORTED_PROTOCOL;
1209
ctx->error = SOCKERRNO;
1210
goto out;
1211
}
1212
}
1213
#endif
1214
ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
1215
out:
1216
if(result) {
1217
if(ctx->sock != CURL_SOCKET_BAD) {
1218
socket_close(data, cf->conn, TRUE, ctx->sock);
1219
ctx->sock = CURL_SOCKET_BAD;
1220
}
1221
}
1222
else if(isconnected) {
1223
set_local_ip(cf, data);
1224
ctx->connected_at = curlx_now();
1225
cf->connected = TRUE;
1226
}
1227
CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
1228
result, ctx->sock);
1229
return result;
1230
}
1231
1232
static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
1233
bool is_tcp_fastopen)
1234
{
1235
struct cf_socket_ctx *ctx = cf->ctx;
1236
#ifdef TCP_FASTOPEN_CONNECT
1237
int optval = 1;
1238
#endif
1239
int rc = -1;
1240
1241
(void)data;
1242
if(is_tcp_fastopen) {
1243
#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1244
# if defined(HAVE_BUILTIN_AVAILABLE)
1245
/* while connectx function is available since macOS 10.11 / iOS 9,
1246
it did not have the interface declared correctly until
1247
Xcode 9 / macOS SDK 10.13 */
1248
if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1249
sa_endpoints_t endpoints;
1250
endpoints.sae_srcif = 0;
1251
endpoints.sae_srcaddr = NULL;
1252
endpoints.sae_srcaddrlen = 0;
1253
endpoints.sae_dstaddr = &ctx->addr.curl_sa_addr;
1254
endpoints.sae_dstaddrlen = ctx->addr.addrlen;
1255
1256
rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY,
1257
CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1258
NULL, 0, NULL, NULL);
1259
}
1260
else {
1261
rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
1262
}
1263
# else
1264
rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
1265
# endif /* HAVE_BUILTIN_AVAILABLE */
1266
#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1267
if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1268
(void *)&optval, sizeof(optval)) < 0)
1269
infof(data, "Failed to enable TCP Fast Open on fd %" FMT_SOCKET_T,
1270
ctx->sock);
1271
1272
rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
1273
#elif defined(MSG_FASTOPEN) /* old Linux */
1274
if(Curl_conn_is_ssl(cf->conn, cf->sockindex))
1275
rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
1276
else
1277
rc = 0; /* Do nothing */
1278
#endif
1279
}
1280
else {
1281
rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,
1282
(curl_socklen_t)ctx->addr.addrlen);
1283
}
1284
return rc;
1285
}
1286
1287
static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
1288
struct Curl_easy *data,
1289
bool *done)
1290
{
1291
struct cf_socket_ctx *ctx = cf->ctx;
1292
CURLcode result = CURLE_COULDNT_CONNECT;
1293
int rc = 0;
1294
1295
(void)data;
1296
if(cf->connected) {
1297
*done = TRUE;
1298
return CURLE_OK;
1299
}
1300
1301
*done = FALSE; /* a negative world view is best */
1302
if(ctx->sock == CURL_SOCKET_BAD) {
1303
int error;
1304
1305
result = cf_socket_open(cf, data);
1306
if(result)
1307
goto out;
1308
1309
if(cf->connected) {
1310
*done = TRUE;
1311
return CURLE_OK;
1312
}
1313
1314
/* Connect TCP socket */
1315
rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
1316
error = SOCKERRNO;
1317
set_local_ip(cf, data);
1318
CURL_TRC_CF(data, cf, "local address %s port %d...",
1319
ctx->ip.local_ip, ctx->ip.local_port);
1320
if(-1 == rc) {
1321
result = socket_connect_result(data, ctx->ip.remote_ip, error);
1322
goto out;
1323
}
1324
}
1325
1326
#ifdef mpeix
1327
/* Call this function once now, and ignore the results. We do this to
1328
"clear" the error state on the socket so that we can later read it
1329
reliably. This is reported necessary on the MPE/iX operating
1330
system. */
1331
(void)verifyconnect(ctx->sock, NULL);
1332
#endif
1333
/* check socket for connect */
1334
rc = SOCKET_WRITABLE(ctx->sock, 0);
1335
1336
if(rc == 0) { /* no connection yet */
1337
CURL_TRC_CF(data, cf, "not connected yet");
1338
return CURLE_OK;
1339
}
1340
else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
1341
if(verifyconnect(ctx->sock, &ctx->error)) {
1342
/* we are connected with TCP, awesome! */
1343
ctx->connected_at = curlx_now();
1344
set_local_ip(cf, data);
1345
*done = TRUE;
1346
cf->connected = TRUE;
1347
CURL_TRC_CF(data, cf, "connected");
1348
return CURLE_OK;
1349
}
1350
}
1351
else if(rc & CURL_CSELECT_ERR) {
1352
(void)verifyconnect(ctx->sock, &ctx->error);
1353
result = CURLE_COULDNT_CONNECT;
1354
}
1355
1356
out:
1357
if(result) {
1358
if(ctx->error) {
1359
set_local_ip(cf, data);
1360
data->state.os_errno = ctx->error;
1361
SET_SOCKERRNO(ctx->error);
1362
#ifndef CURL_DISABLE_VERBOSE_STRINGS
1363
{
1364
char buffer[STRERROR_LEN];
1365
infof(data, "connect to %s port %u from %s port %d failed: %s",
1366
ctx->ip.remote_ip, ctx->ip.remote_port,
1367
ctx->ip.local_ip, ctx->ip.local_port,
1368
Curl_strerror(ctx->error, buffer, sizeof(buffer)));
1369
}
1370
#endif
1371
}
1372
if(ctx->sock != CURL_SOCKET_BAD) {
1373
socket_close(data, cf->conn, TRUE, ctx->sock);
1374
ctx->sock = CURL_SOCKET_BAD;
1375
}
1376
*done = FALSE;
1377
}
1378
return result;
1379
}
1380
1381
static void cf_socket_get_host(struct Curl_cfilter *cf,
1382
struct Curl_easy *data,
1383
const char **phost,
1384
const char **pdisplay_host,
1385
int *pport)
1386
{
1387
struct cf_socket_ctx *ctx = cf->ctx;
1388
(void)data;
1389
*phost = cf->conn->host.name;
1390
*pdisplay_host = cf->conn->host.dispname;
1391
*pport = ctx->ip.remote_port;
1392
}
1393
1394
static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
1395
struct Curl_easy *data,
1396
struct easy_pollset *ps)
1397
{
1398
struct cf_socket_ctx *ctx = cf->ctx;
1399
1400
if(ctx->sock != CURL_SOCKET_BAD) {
1401
/* A listening socket filter needs to be connected before the accept
1402
* for some weird FTP interaction. This should be rewritten, so that
1403
* FTP no longer does the socket checks and accept calls and delegates
1404
* all that to the filter. */
1405
if(ctx->listening) {
1406
Curl_pollset_set_in_only(data, ps, ctx->sock);
1407
CURL_TRC_CF(data, cf, "adjust_pollset, listening, POLLIN fd=%"
1408
FMT_SOCKET_T, ctx->sock);
1409
}
1410
else if(!cf->connected) {
1411
Curl_pollset_set_out_only(data, ps, ctx->sock);
1412
CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%"
1413
FMT_SOCKET_T, ctx->sock);
1414
}
1415
else if(!ctx->active) {
1416
Curl_pollset_add_in(data, ps, ctx->sock);
1417
CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
1418
FMT_SOCKET_T, ctx->sock);
1419
}
1420
}
1421
}
1422
1423
static bool cf_socket_data_pending(struct Curl_cfilter *cf,
1424
const struct Curl_easy *data)
1425
{
1426
struct cf_socket_ctx *ctx = cf->ctx;
1427
int readable;
1428
1429
(void)data;
1430
readable = SOCKET_READABLE(ctx->sock, 0);
1431
return readable > 0 && (readable & CURL_CSELECT_IN);
1432
}
1433
1434
#ifdef USE_WINSOCK
1435
1436
#ifndef SIO_IDEAL_SEND_BACKLOG_QUERY
1437
#define SIO_IDEAL_SEND_BACKLOG_QUERY 0x4004747B
1438
#endif
1439
1440
static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
1441
{
1442
ULONG ideal;
1443
DWORD ideallen;
1444
struct curltime n = curlx_now();
1445
1446
if(curlx_timediff(n, ctx->last_sndbuf_query_at) > 1000) {
1447
if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0,
1448
&ideal, sizeof(ideal), &ideallen, 0, 0) &&
1449
ideal != ctx->sndbuf_size &&
1450
!setsockopt(ctx->sock, SOL_SOCKET, SO_SNDBUF,
1451
(const char *)&ideal, sizeof(ideal))) {
1452
ctx->sndbuf_size = ideal;
1453
}
1454
ctx->last_sndbuf_query_at = n;
1455
}
1456
}
1457
1458
#endif /* USE_WINSOCK */
1459
1460
static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1461
const void *buf, size_t len, bool eos,
1462
CURLcode *err)
1463
{
1464
struct cf_socket_ctx *ctx = cf->ctx;
1465
curl_socket_t fdsave;
1466
ssize_t nwritten;
1467
size_t orig_len = len;
1468
1469
(void)eos; /* unused */
1470
*err = CURLE_OK;
1471
fdsave = cf->conn->sock[cf->sockindex];
1472
cf->conn->sock[cf->sockindex] = ctx->sock;
1473
1474
#ifdef DEBUGBUILD
1475
/* simulate network blocking/partial writes */
1476
if(ctx->wblock_percent > 0) {
1477
unsigned char c = 0;
1478
Curl_rand_bytes(data, FALSE, &c, 1);
1479
if(c >= ((100-ctx->wblock_percent)*256/100)) {
1480
CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
1481
*err = CURLE_AGAIN;
1482
nwritten = -1;
1483
cf->conn->sock[cf->sockindex] = fdsave;
1484
return nwritten;
1485
}
1486
}
1487
if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
1488
len = len * ctx->wpartial_percent / 100;
1489
if(!len)
1490
len = 1;
1491
CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes",
1492
orig_len, len);
1493
}
1494
#endif
1495
1496
#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
1497
if(cf->conn->bits.tcp_fastopen) {
1498
nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
1499
&cf->conn->remote_addr->curl_sa_addr,
1500
cf->conn->remote_addr->addrlen);
1501
cf->conn->bits.tcp_fastopen = FALSE;
1502
}
1503
else
1504
#endif
1505
nwritten = swrite(ctx->sock, buf, len);
1506
1507
if(-1 == nwritten) {
1508
int sockerr = SOCKERRNO;
1509
1510
if(
1511
#ifdef USE_WINSOCK
1512
/* This is how Windows does it */
1513
(SOCKEWOULDBLOCK == sockerr)
1514
#else
1515
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
1516
due to its inability to send off data without blocking. We therefore
1517
treat both error codes the same here */
1518
(SOCKEWOULDBLOCK == sockerr) ||
1519
(EAGAIN == sockerr) || (SOCKEINTR == sockerr) ||
1520
(SOCKEINPROGRESS == sockerr)
1521
#endif
1522
) {
1523
/* this is just a case of EWOULDBLOCK */
1524
*err = CURLE_AGAIN;
1525
}
1526
else {
1527
char buffer[STRERROR_LEN];
1528
failf(data, "Send failure: %s",
1529
Curl_strerror(sockerr, buffer, sizeof(buffer)));
1530
data->state.os_errno = sockerr;
1531
*err = CURLE_SEND_ERROR;
1532
}
1533
}
1534
1535
#if defined(USE_WINSOCK)
1536
if(!*err)
1537
win_update_sndbuf_size(ctx);
1538
#endif
1539
1540
CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
1541
orig_len, (int)nwritten, *err);
1542
cf->conn->sock[cf->sockindex] = fdsave;
1543
return nwritten;
1544
}
1545
1546
static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1547
char *buf, size_t len, CURLcode *err)
1548
{
1549
struct cf_socket_ctx *ctx = cf->ctx;
1550
ssize_t nread;
1551
1552
*err = CURLE_OK;
1553
1554
#ifdef DEBUGBUILD
1555
/* simulate network blocking/partial reads */
1556
if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
1557
unsigned char c = 0;
1558
Curl_rand(data, &c, 1);
1559
if(c >= ((100-ctx->rblock_percent)*256/100)) {
1560
CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
1561
*err = CURLE_AGAIN;
1562
return -1;
1563
}
1564
}
1565
if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
1566
size_t orig_len = len;
1567
len = ctx->recv_max;
1568
CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes",
1569
orig_len, len);
1570
}
1571
#endif
1572
1573
*err = CURLE_OK;
1574
nread = sread(ctx->sock, buf, len);
1575
1576
if(-1 == nread) {
1577
int sockerr = SOCKERRNO;
1578
1579
if(
1580
#ifdef USE_WINSOCK
1581
/* This is how Windows does it */
1582
(SOCKEWOULDBLOCK == sockerr)
1583
#else
1584
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
1585
due to its inability to send off data without blocking. We therefore
1586
treat both error codes the same here */
1587
(SOCKEWOULDBLOCK == sockerr) ||
1588
(EAGAIN == sockerr) || (SOCKEINTR == sockerr)
1589
#endif
1590
) {
1591
/* this is just a case of EWOULDBLOCK */
1592
*err = CURLE_AGAIN;
1593
}
1594
else {
1595
char buffer[STRERROR_LEN];
1596
1597
failf(data, "Recv failure: %s",
1598
Curl_strerror(sockerr, buffer, sizeof(buffer)));
1599
data->state.os_errno = sockerr;
1600
*err = CURLE_RECV_ERROR;
1601
}
1602
}
1603
1604
CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
1605
*err);
1606
if(nread > 0 && !ctx->got_first_byte) {
1607
ctx->first_byte_at = curlx_now();
1608
ctx->got_first_byte = TRUE;
1609
}
1610
return nread;
1611
}
1612
1613
static void cf_socket_update_data(struct Curl_cfilter *cf,
1614
struct Curl_easy *data)
1615
{
1616
/* Update the IP info held in the transfer, if we have that. */
1617
if(cf->connected && (cf->sockindex == FIRSTSOCKET)) {
1618
struct cf_socket_ctx *ctx = cf->ctx;
1619
data->info.primary = ctx->ip;
1620
/* not sure if this is redundant... */
1621
data->info.conn_remote_port = cf->conn->remote_port;
1622
}
1623
}
1624
1625
static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
1626
{
1627
struct cf_socket_ctx *ctx = cf->ctx;
1628
1629
/* use this socket from now on */
1630
cf->conn->sock[cf->sockindex] = ctx->sock;
1631
set_local_ip(cf, data);
1632
if(cf->sockindex == FIRSTSOCKET) {
1633
cf->conn->primary = ctx->ip;
1634
cf->conn->remote_addr = &ctx->addr;
1635
#ifdef USE_IPV6
1636
cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6);
1637
#endif
1638
}
1639
else {
1640
cf->conn->secondary = ctx->ip;
1641
}
1642
ctx->active = TRUE;
1643
}
1644
1645
static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
1646
struct Curl_easy *data,
1647
int event, int arg1, void *arg2)
1648
{
1649
struct cf_socket_ctx *ctx = cf->ctx;
1650
1651
(void)arg1;
1652
(void)arg2;
1653
switch(event) {
1654
case CF_CTRL_CONN_INFO_UPDATE:
1655
cf_socket_active(cf, data);
1656
cf_socket_update_data(cf, data);
1657
break;
1658
case CF_CTRL_DATA_SETUP:
1659
cf_socket_update_data(cf, data);
1660
break;
1661
case CF_CTRL_FORGET_SOCKET:
1662
ctx->sock = CURL_SOCKET_BAD;
1663
break;
1664
}
1665
return CURLE_OK;
1666
}
1667
1668
static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
1669
struct Curl_easy *data,
1670
bool *input_pending)
1671
{
1672
struct cf_socket_ctx *ctx = cf->ctx;
1673
struct pollfd pfd[1];
1674
int r;
1675
1676
*input_pending = FALSE;
1677
(void)data;
1678
if(!ctx || ctx->sock == CURL_SOCKET_BAD)
1679
return FALSE;
1680
1681
/* Check with 0 timeout if there are any events pending on the socket */
1682
pfd[0].fd = ctx->sock;
1683
pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
1684
pfd[0].revents = 0;
1685
1686
r = Curl_poll(pfd, 1, 0);
1687
if(r < 0) {
1688
CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead");
1689
return FALSE;
1690
}
1691
else if(r == 0) {
1692
CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive");
1693
return TRUE;
1694
}
1695
else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) {
1696
CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead");
1697
return FALSE;
1698
}
1699
1700
CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive");
1701
*input_pending = TRUE;
1702
return TRUE;
1703
}
1704
1705
static CURLcode cf_socket_query(struct Curl_cfilter *cf,
1706
struct Curl_easy *data,
1707
int query, int *pres1, void *pres2)
1708
{
1709
struct cf_socket_ctx *ctx = cf->ctx;
1710
1711
switch(query) {
1712
case CF_QUERY_SOCKET:
1713
DEBUGASSERT(pres2);
1714
*((curl_socket_t *)pres2) = ctx->sock;
1715
return CURLE_OK;
1716
case CF_QUERY_CONNECT_REPLY_MS:
1717
if(ctx->got_first_byte) {
1718
timediff_t ms = curlx_timediff(ctx->first_byte_at, ctx->started_at);
1719
*pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
1720
}
1721
else
1722
*pres1 = -1;
1723
return CURLE_OK;
1724
case CF_QUERY_TIMER_CONNECT: {
1725
struct curltime *when = pres2;
1726
switch(ctx->transport) {
1727
case TRNSPRT_UDP:
1728
case TRNSPRT_QUIC:
1729
/* Since UDP connected sockets work different from TCP, we use the
1730
* time of the first byte from the peer as the "connect" time. */
1731
if(ctx->got_first_byte) {
1732
*when = ctx->first_byte_at;
1733
break;
1734
}
1735
FALLTHROUGH();
1736
default:
1737
*when = ctx->connected_at;
1738
break;
1739
}
1740
return CURLE_OK;
1741
}
1742
case CF_QUERY_IP_INFO:
1743
#ifdef USE_IPV6
1744
*pres1 = (ctx->addr.family == AF_INET6);
1745
#else
1746
*pres1 = FALSE;
1747
#endif
1748
*(struct ip_quadruple *)pres2 = ctx->ip;
1749
return CURLE_OK;
1750
default:
1751
break;
1752
}
1753
return cf->next ?
1754
cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1755
CURLE_UNKNOWN_OPTION;
1756
}
1757
1758
struct Curl_cftype Curl_cft_tcp = {
1759
"TCP",
1760
CF_TYPE_IP_CONNECT,
1761
CURL_LOG_LVL_NONE,
1762
cf_socket_destroy,
1763
cf_tcp_connect,
1764
cf_socket_close,
1765
cf_socket_shutdown,
1766
cf_socket_get_host,
1767
cf_socket_adjust_pollset,
1768
cf_socket_data_pending,
1769
cf_socket_send,
1770
cf_socket_recv,
1771
cf_socket_cntrl,
1772
cf_socket_conn_is_alive,
1773
Curl_cf_def_conn_keep_alive,
1774
cf_socket_query,
1775
};
1776
1777
CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
1778
struct Curl_easy *data,
1779
struct connectdata *conn,
1780
const struct Curl_addrinfo *ai,
1781
int transport)
1782
{
1783
struct cf_socket_ctx *ctx = NULL;
1784
struct Curl_cfilter *cf = NULL;
1785
CURLcode result;
1786
1787
(void)data;
1788
(void)conn;
1789
DEBUGASSERT(transport == TRNSPRT_TCP);
1790
ctx = calloc(1, sizeof(*ctx));
1791
if(!ctx) {
1792
result = CURLE_OUT_OF_MEMORY;
1793
goto out;
1794
}
1795
1796
result = cf_socket_ctx_init(ctx, ai, transport);
1797
if(result)
1798
goto out;
1799
1800
result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
1801
1802
out:
1803
*pcf = (!result) ? cf : NULL;
1804
if(result) {
1805
Curl_safefree(cf);
1806
Curl_safefree(ctx);
1807
}
1808
1809
return result;
1810
}
1811
1812
static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
1813
struct Curl_easy *data)
1814
{
1815
struct cf_socket_ctx *ctx = cf->ctx;
1816
int rc;
1817
int one = 1;
1818
1819
(void)one;
1820
1821
/* QUIC needs a connected socket, nonblocking */
1822
DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
1823
1824
/* error: The 1st argument to 'connect' is -1 but should be >= 0
1825
NOLINTNEXTLINE(clang-analyzer-unix.StdCLibraryFunctions) */
1826
rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,
1827
(curl_socklen_t)ctx->addr.addrlen);
1828
if(-1 == rc) {
1829
return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
1830
}
1831
ctx->sock_connected = TRUE;
1832
set_local_ip(cf, data);
1833
CURL_TRC_CF(data, cf, "%s socket %" FMT_SOCKET_T
1834
" connected: [%s:%d] -> [%s:%d]",
1835
(ctx->transport == TRNSPRT_QUIC) ? "QUIC" : "UDP",
1836
ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
1837
ctx->ip.remote_ip, ctx->ip.remote_port);
1838
1839
/* Currently, cf->ctx->sock is always non-blocking because the only
1840
* caller to cf_udp_setup_quic() is cf_udp_connect() that passes the
1841
* non-blocking socket created by cf_socket_open() to it. Thus, we
1842
* do not need to call curlx_nonblock() in cf_udp_setup_quic() anymore.
1843
*/
1844
#ifdef __linux__
1845
switch(ctx->addr.family) {
1846
#ifdef IP_MTU_DISCOVER
1847
case AF_INET: {
1848
int val = IP_PMTUDISC_DO;
1849
(void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
1850
sizeof(val));
1851
break;
1852
}
1853
#endif
1854
#ifdef IPV6_MTU_DISCOVER
1855
case AF_INET6: {
1856
int val = IPV6_PMTUDISC_DO;
1857
(void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
1858
sizeof(val));
1859
break;
1860
}
1861
#endif
1862
}
1863
1864
#if defined(UDP_GRO) && \
1865
(defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \
1866
((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE))
1867
(void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one,
1868
(socklen_t)sizeof(one));
1869
#endif
1870
#endif
1871
1872
return CURLE_OK;
1873
}
1874
1875
static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
1876
struct Curl_easy *data,
1877
bool *done)
1878
{
1879
struct cf_socket_ctx *ctx = cf->ctx;
1880
CURLcode result = CURLE_COULDNT_CONNECT;
1881
1882
if(cf->connected) {
1883
*done = TRUE;
1884
return CURLE_OK;
1885
}
1886
*done = FALSE;
1887
if(ctx->sock == CURL_SOCKET_BAD) {
1888
result = cf_socket_open(cf, data);
1889
if(result) {
1890
CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result);
1891
goto out;
1892
}
1893
1894
if(ctx->transport == TRNSPRT_QUIC) {
1895
result = cf_udp_setup_quic(cf, data);
1896
if(result)
1897
goto out;
1898
CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1899
FMT_SOCKET_T " (%s:%d)",
1900
ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
1901
}
1902
else {
1903
CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1904
FMT_SOCKET_T " (unconnected)", ctx->sock);
1905
}
1906
*done = TRUE;
1907
cf->connected = TRUE;
1908
}
1909
out:
1910
return result;
1911
}
1912
1913
struct Curl_cftype Curl_cft_udp = {
1914
"UDP",
1915
CF_TYPE_IP_CONNECT,
1916
CURL_LOG_LVL_NONE,
1917
cf_socket_destroy,
1918
cf_udp_connect,
1919
cf_socket_close,
1920
cf_socket_shutdown,
1921
cf_socket_get_host,
1922
cf_socket_adjust_pollset,
1923
cf_socket_data_pending,
1924
cf_socket_send,
1925
cf_socket_recv,
1926
cf_socket_cntrl,
1927
cf_socket_conn_is_alive,
1928
Curl_cf_def_conn_keep_alive,
1929
cf_socket_query,
1930
};
1931
1932
CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
1933
struct Curl_easy *data,
1934
struct connectdata *conn,
1935
const struct Curl_addrinfo *ai,
1936
int transport)
1937
{
1938
struct cf_socket_ctx *ctx = NULL;
1939
struct Curl_cfilter *cf = NULL;
1940
CURLcode result;
1941
1942
(void)data;
1943
(void)conn;
1944
DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
1945
ctx = calloc(1, sizeof(*ctx));
1946
if(!ctx) {
1947
result = CURLE_OUT_OF_MEMORY;
1948
goto out;
1949
}
1950
1951
result = cf_socket_ctx_init(ctx, ai, transport);
1952
if(result)
1953
goto out;
1954
1955
result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
1956
1957
out:
1958
*pcf = (!result) ? cf : NULL;
1959
if(result) {
1960
Curl_safefree(cf);
1961
Curl_safefree(ctx);
1962
}
1963
1964
return result;
1965
}
1966
1967
/* this is the TCP filter which can also handle this case */
1968
struct Curl_cftype Curl_cft_unix = {
1969
"UNIX",
1970
CF_TYPE_IP_CONNECT,
1971
CURL_LOG_LVL_NONE,
1972
cf_socket_destroy,
1973
cf_tcp_connect,
1974
cf_socket_close,
1975
cf_socket_shutdown,
1976
cf_socket_get_host,
1977
cf_socket_adjust_pollset,
1978
cf_socket_data_pending,
1979
cf_socket_send,
1980
cf_socket_recv,
1981
cf_socket_cntrl,
1982
cf_socket_conn_is_alive,
1983
Curl_cf_def_conn_keep_alive,
1984
cf_socket_query,
1985
};
1986
1987
CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
1988
struct Curl_easy *data,
1989
struct connectdata *conn,
1990
const struct Curl_addrinfo *ai,
1991
int transport)
1992
{
1993
struct cf_socket_ctx *ctx = NULL;
1994
struct Curl_cfilter *cf = NULL;
1995
CURLcode result;
1996
1997
(void)data;
1998
(void)conn;
1999
DEBUGASSERT(transport == TRNSPRT_UNIX);
2000
ctx = calloc(1, sizeof(*ctx));
2001
if(!ctx) {
2002
result = CURLE_OUT_OF_MEMORY;
2003
goto out;
2004
}
2005
2006
result = cf_socket_ctx_init(ctx, ai, transport);
2007
if(result)
2008
goto out;
2009
2010
result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
2011
2012
out:
2013
*pcf = (!result) ? cf : NULL;
2014
if(result) {
2015
Curl_safefree(cf);
2016
Curl_safefree(ctx);
2017
}
2018
2019
return result;
2020
}
2021
2022
static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
2023
struct Curl_easy *data)
2024
{
2025
struct cf_socket_ctx *ctx = cf->ctx;
2026
timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
2027
timediff_t other;
2028
struct curltime now;
2029
2030
#ifndef CURL_DISABLE_FTP
2031
if(data->set.accepttimeout > 0)
2032
timeout_ms = data->set.accepttimeout;
2033
#endif
2034
2035
now = curlx_now();
2036
/* check if the generic timeout possibly is set shorter */
2037
other = Curl_timeleft(data, &now, FALSE);
2038
if(other && (other < timeout_ms))
2039
/* note that this also works fine for when other happens to be negative
2040
due to it already having elapsed */
2041
timeout_ms = other;
2042
else {
2043
/* subtract elapsed time */
2044
timeout_ms -= curlx_timediff(now, ctx->started_at);
2045
if(!timeout_ms)
2046
/* avoid returning 0 as that means no timeout! */
2047
timeout_ms = -1;
2048
}
2049
return timeout_ms;
2050
}
2051
2052
static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf,
2053
struct Curl_easy *data)
2054
{
2055
struct cf_socket_ctx *ctx = cf->ctx;
2056
#ifdef HAVE_GETPEERNAME
2057
char buffer[STRERROR_LEN];
2058
struct Curl_sockaddr_storage ssrem;
2059
curl_socklen_t plen;
2060
2061
ctx->ip.remote_ip[0] = 0;
2062
ctx->ip.remote_port = 0;
2063
plen = sizeof(ssrem);
2064
memset(&ssrem, 0, plen);
2065
if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
2066
int error = SOCKERRNO;
2067
failf(data, "getpeername() failed with errno %d: %s",
2068
error, Curl_strerror(error, buffer, sizeof(buffer)));
2069
return;
2070
}
2071
if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
2072
ctx->ip.remote_ip, &ctx->ip.remote_port)) {
2073
failf(data, "ssrem inet_ntop() failed with errno %d: %s",
2074
errno, Curl_strerror(errno, buffer, sizeof(buffer)));
2075
return;
2076
}
2077
#else
2078
ctx->ip.remote_ip[0] = 0;
2079
ctx->ip.remote_port = 0;
2080
(void)data;
2081
#endif
2082
}
2083
2084
static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
2085
struct Curl_easy *data,
2086
bool *done)
2087
{
2088
struct cf_socket_ctx *ctx = cf->ctx;
2089
#ifdef USE_IPV6
2090
struct Curl_sockaddr_storage add;
2091
#else
2092
struct sockaddr_in add;
2093
#endif
2094
curl_socklen_t size = (curl_socklen_t) sizeof(add);
2095
curl_socket_t s_accepted = CURL_SOCKET_BAD;
2096
timediff_t timeout_ms;
2097
int socketstate = 0;
2098
bool incoming = FALSE;
2099
2100
/* we start accepted, if we ever close, we cannot go on */
2101
(void)data;
2102
if(cf->connected) {
2103
*done = TRUE;
2104
return CURLE_OK;
2105
}
2106
2107
timeout_ms = cf_tcp_accept_timeleft(cf, data);
2108
if(timeout_ms < 0) {
2109
/* if a timeout was already reached, bail out */
2110
failf(data, "Accept timeout occurred while waiting server connect");
2111
return CURLE_FTP_ACCEPT_TIMEOUT;
2112
}
2113
2114
CURL_TRC_CF(data, cf, "Checking for incoming on fd=%" FMT_SOCKET_T
2115
" ip=%s:%d", ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
2116
socketstate = Curl_socket_check(ctx->sock, CURL_SOCKET_BAD,
2117
CURL_SOCKET_BAD, 0);
2118
CURL_TRC_CF(data, cf, "socket_check -> %x", socketstate);
2119
switch(socketstate) {
2120
case -1: /* error */
2121
/* let's die here */
2122
failf(data, "Error while waiting for server connect");
2123
return CURLE_FTP_ACCEPT_FAILED;
2124
default:
2125
if(socketstate & CURL_CSELECT_IN) {
2126
infof(data, "Ready to accept data connection from server");
2127
incoming = TRUE;
2128
}
2129
break;
2130
}
2131
2132
if(!incoming) {
2133
CURL_TRC_CF(data, cf, "nothing heard from the server yet");
2134
*done = FALSE;
2135
return CURLE_OK;
2136
}
2137
2138
if(0 == getsockname(ctx->sock, (struct sockaddr *) &add, &size)) {
2139
size = sizeof(add);
2140
#ifdef HAVE_ACCEPT4
2141
s_accepted = accept4(ctx->sock, (struct sockaddr *) &add, &size,
2142
SOCK_NONBLOCK | SOCK_CLOEXEC);
2143
#else
2144
s_accepted = accept(ctx->sock, (struct sockaddr *) &add, &size);
2145
#endif
2146
}
2147
2148
if(CURL_SOCKET_BAD == s_accepted) {
2149
failf(data, "Error accept()ing server connect");
2150
return CURLE_FTP_PORT_FAILED;
2151
}
2152
2153
infof(data, "Connection accepted from server");
2154
#ifndef HAVE_ACCEPT4
2155
(void)curlx_nonblock(s_accepted, TRUE); /* enable non-blocking */
2156
#endif
2157
/* Replace any filter on SECONDARY with one listening on this socket */
2158
ctx->listening = FALSE;
2159
ctx->accepted = TRUE;
2160
socket_close(data, cf->conn, TRUE, ctx->sock);
2161
ctx->sock = s_accepted;
2162
2163
cf->conn->sock[cf->sockindex] = ctx->sock;
2164
cf_tcp_set_accepted_remote_ip(cf, data);
2165
set_local_ip(cf, data);
2166
ctx->active = TRUE;
2167
ctx->connected_at = curlx_now();
2168
cf->connected = TRUE;
2169
CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T
2170
", remote=%s port=%d)",
2171
ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port);
2172
2173
if(data->set.fsockopt) {
2174
int error = 0;
2175
2176
/* activate callback for setting socket options */
2177
Curl_set_in_callback(data, true);
2178
error = data->set.fsockopt(data->set.sockopt_client,
2179
ctx->sock, CURLSOCKTYPE_ACCEPT);
2180
Curl_set_in_callback(data, false);
2181
2182
if(error)
2183
return CURLE_ABORTED_BY_CALLBACK;
2184
}
2185
*done = TRUE;
2186
return CURLE_OK;
2187
}
2188
2189
struct Curl_cftype Curl_cft_tcp_accept = {
2190
"TCP-ACCEPT",
2191
CF_TYPE_IP_CONNECT,
2192
CURL_LOG_LVL_NONE,
2193
cf_socket_destroy,
2194
cf_tcp_accept_connect,
2195
cf_socket_close,
2196
cf_socket_shutdown,
2197
cf_socket_get_host,
2198
cf_socket_adjust_pollset,
2199
cf_socket_data_pending,
2200
cf_socket_send,
2201
cf_socket_recv,
2202
cf_socket_cntrl,
2203
cf_socket_conn_is_alive,
2204
Curl_cf_def_conn_keep_alive,
2205
cf_socket_query,
2206
};
2207
2208
CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
2209
struct connectdata *conn,
2210
int sockindex, curl_socket_t *s)
2211
{
2212
CURLcode result;
2213
struct Curl_cfilter *cf = NULL;
2214
struct cf_socket_ctx *ctx = NULL;
2215
2216
/* replace any existing */
2217
Curl_conn_cf_discard_all(data, conn, sockindex);
2218
DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
2219
2220
ctx = calloc(1, sizeof(*ctx));
2221
if(!ctx) {
2222
result = CURLE_OUT_OF_MEMORY;
2223
goto out;
2224
}
2225
ctx->transport = conn->transport;
2226
ctx->sock = *s;
2227
ctx->listening = TRUE;
2228
ctx->accepted = FALSE;
2229
result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
2230
if(result)
2231
goto out;
2232
Curl_conn_cf_add(data, conn, sockindex, cf);
2233
2234
ctx->started_at = curlx_now();
2235
conn->sock[sockindex] = ctx->sock;
2236
set_local_ip(cf, data);
2237
CURL_TRC_CF(data, cf, "set filter for listen socket fd=%" FMT_SOCKET_T
2238
" ip=%s:%d", ctx->sock,
2239
ctx->ip.local_ip, ctx->ip.local_port);
2240
2241
out:
2242
if(result) {
2243
Curl_safefree(cf);
2244
Curl_safefree(ctx);
2245
}
2246
return result;
2247
}
2248
2249
bool Curl_conn_is_tcp_listen(struct Curl_easy *data,
2250
int sockindex)
2251
{
2252
struct Curl_cfilter *cf = data->conn->cfilter[sockindex];
2253
while(cf) {
2254
if(cf->cft == &Curl_cft_tcp_accept)
2255
return TRUE;
2256
cf = cf->next;
2257
}
2258
return FALSE;
2259
}
2260
2261
/**
2262
* Return TRUE iff `cf` is a socket filter.
2263
*/
2264
static bool cf_is_socket(struct Curl_cfilter *cf)
2265
{
2266
return cf && (cf->cft == &Curl_cft_tcp ||
2267
cf->cft == &Curl_cft_udp ||
2268
cf->cft == &Curl_cft_unix ||
2269
cf->cft == &Curl_cft_tcp_accept);
2270
}
2271
2272
CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
2273
struct Curl_easy *data,
2274
curl_socket_t *psock,
2275
const struct Curl_sockaddr_ex **paddr,
2276
struct ip_quadruple *pip)
2277
{
2278
(void)data;
2279
if(cf_is_socket(cf) && cf->ctx) {
2280
struct cf_socket_ctx *ctx = cf->ctx;
2281
2282
if(psock)
2283
*psock = ctx->sock;
2284
if(paddr)
2285
*paddr = &ctx->addr;
2286
if(pip)
2287
*pip = ctx->ip;
2288
return CURLE_OK;
2289
}
2290
return CURLE_FAILED_INIT;
2291
}
2292
2293