1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #ifdef HAVE_NETINET_IN_H
28 #include <netinet/in.h> /* <netinet/tcp.h> may need it */
29 #endif
30 #ifdef HAVE_SYS_UN_H
31 #include <sys/un.h> /* for sockaddr_un */
32 #endif
33 #ifdef HAVE_LINUX_TCP_H
34 #include <linux/tcp.h>
35 #elif defined(HAVE_NETINET_TCP_H)
36 #include <netinet/tcp.h>
37 #endif
38 #ifdef HAVE_SYS_IOCTL_H
39 #include <sys/ioctl.h>
40 #endif
41 #ifdef HAVE_NETDB_H
42 #include <netdb.h>
43 #endif
44 #ifdef HAVE_FCNTL_H
45 #include <fcntl.h>
46 #endif
47 #ifdef HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
49 #endif
50
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55
56 #include "urldata.h"
57 #include "bufq.h"
58 #include "sendf.h"
59 #include "if2ip.h"
60 #include "strerror.h"
61 #include "cfilters.h"
62 #include "cf-socket.h"
63 #include "connect.h"
64 #include "select.h"
65 #include "url.h" /* for Curl_safefree() */
66 #include "multiif.h"
67 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
68 #include "inet_ntop.h"
69 #include "inet_pton.h"
70 #include "progress.h"
71 #include "warnless.h"
72 #include "conncache.h"
73 #include "multihandle.h"
74 #include "rand.h"
75 #include "share.h"
76 #include "version_win32.h"
77
78 /* The last 3 #include files should be in this order */
79 #include "curl_printf.h"
80 #include "curl_memory.h"
81 #include "memdebug.h"
82
83
84 #if defined(USE_IPV6) && defined(IPV6_V6ONLY) && defined(_WIN32)
85 /* It makes support for IPv4-mapped IPv6 addresses.
86 * Linux kernel, NetBSD, FreeBSD and Darwin: default is off;
87 * Windows Vista and later: default is on;
88 * DragonFly BSD: acts like off, and dummy setting;
89 * OpenBSD and earlier Windows: unsupported.
90 * Linux: controlled by /proc/sys/net/ipv6/bindv6only.
91 */
set_ipv6_v6only(curl_socket_t sockfd,int on)92 static void set_ipv6_v6only(curl_socket_t sockfd, int on)
93 {
94 (void)setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on));
95 }
96 #else
97 #define set_ipv6_v6only(x,y)
98 #endif
99
tcpnodelay(struct Curl_easy * data,curl_socket_t sockfd)100 static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
101 {
102 #if defined(TCP_NODELAY)
103 curl_socklen_t onoff = (curl_socklen_t) 1;
104 int level = IPPROTO_TCP;
105 char buffer[STRERROR_LEN];
106
107 if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
108 sizeof(onoff)) < 0)
109 infof(data, "Could not set TCP_NODELAY: %s",
110 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
111 #else
112 (void)data;
113 (void)sockfd;
114 #endif
115 }
116
117 #ifdef SO_NOSIGPIPE
118 /* The preferred method on Mac OS X (10.2 and later) to prevent SIGPIPEs when
119 sending data to a dead peer (instead of relying on the 4th argument to send
120 being MSG_NOSIGNAL). Possibly also existing and in use on other BSD
121 systems? */
nosigpipe(struct Curl_easy * data,curl_socket_t sockfd)122 static void nosigpipe(struct Curl_easy *data,
123 curl_socket_t sockfd)
124 {
125 int onoff = 1;
126 (void)data;
127 if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
128 sizeof(onoff)) < 0) {
129 #if !defined(CURL_DISABLE_VERBOSE_STRINGS)
130 char buffer[STRERROR_LEN];
131 infof(data, "Could not set SO_NOSIGPIPE: %s",
132 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
133 #endif
134 }
135 }
136 #else
137 #define nosigpipe(x,y) Curl_nop_stmt
138 #endif
139
140 #if defined(__DragonFly__) || defined(USE_WINSOCK)
141 /* DragonFlyBSD and Windows use millisecond units */
142 #define KEEPALIVE_FACTOR(x) (x *= 1000)
143 #else
144 #define KEEPALIVE_FACTOR(x)
145 #endif
146
147 #if defined(USE_WINSOCK) && !defined(SIO_KEEPALIVE_VALS)
148 #define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR,4)
149
150 struct tcp_keepalive {
151 u_long onoff;
152 u_long keepalivetime;
153 u_long keepaliveinterval;
154 };
155 #endif
156
157 static void
tcpkeepalive(struct Curl_easy * data,curl_socket_t sockfd)158 tcpkeepalive(struct Curl_easy *data,
159 curl_socket_t sockfd)
160 {
161 int optval = data->set.tcp_keepalive?1:0;
162
163 /* only set IDLE and INTVL if setting KEEPALIVE is successful */
164 if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
165 (void *)&optval, sizeof(optval)) < 0) {
166 infof(data, "Failed to set SO_KEEPALIVE on fd "
167 "%" CURL_FORMAT_SOCKET_T ": errno %d",
168 sockfd, SOCKERRNO);
169 }
170 else {
171 #if defined(SIO_KEEPALIVE_VALS)
172 struct tcp_keepalive vals;
173 DWORD dummy;
174 vals.onoff = 1;
175 optval = curlx_sltosi(data->set.tcp_keepidle);
176 KEEPALIVE_FACTOR(optval);
177 vals.keepalivetime = optval;
178 optval = curlx_sltosi(data->set.tcp_keepintvl);
179 KEEPALIVE_FACTOR(optval);
180 vals.keepaliveinterval = optval;
181 if(WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, (LPVOID) &vals, sizeof(vals),
182 NULL, 0, &dummy, NULL, NULL) != 0) {
183 infof(data, "Failed to set SIO_KEEPALIVE_VALS on fd "
184 "%" CURL_FORMAT_SOCKET_T ": errno %d",
185 sockfd, SOCKERRNO);
186 }
187 #else
188 #ifdef TCP_KEEPIDLE
189 optval = curlx_sltosi(data->set.tcp_keepidle);
190 KEEPALIVE_FACTOR(optval);
191 if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
192 (void *)&optval, sizeof(optval)) < 0) {
193 infof(data, "Failed to set TCP_KEEPIDLE on fd "
194 "%" CURL_FORMAT_SOCKET_T ": errno %d",
195 sockfd, SOCKERRNO);
196 }
197 #elif defined(TCP_KEEPALIVE)
198 /* Mac OS X style */
199 optval = curlx_sltosi(data->set.tcp_keepidle);
200 KEEPALIVE_FACTOR(optval);
201 if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
202 (void *)&optval, sizeof(optval)) < 0) {
203 infof(data, "Failed to set TCP_KEEPALIVE on fd "
204 "%" CURL_FORMAT_SOCKET_T ": errno %d",
205 sockfd, SOCKERRNO);
206 }
207 #endif
208 #ifdef TCP_KEEPINTVL
209 optval = curlx_sltosi(data->set.tcp_keepintvl);
210 KEEPALIVE_FACTOR(optval);
211 if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
212 (void *)&optval, sizeof(optval)) < 0) {
213 infof(data, "Failed to set TCP_KEEPINTVL on fd "
214 "%" CURL_FORMAT_SOCKET_T ": errno %d",
215 sockfd, SOCKERRNO);
216 }
217 #endif
218 #endif
219 }
220 }
221
222 /**
223 * Assign the address `ai` to the Curl_sockaddr_ex `dest` and
224 * set the transport used.
225 */
Curl_sock_assign_addr(struct Curl_sockaddr_ex * dest,const struct Curl_addrinfo * ai,int transport)226 void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
227 const struct Curl_addrinfo *ai,
228 int transport)
229 {
230 /*
231 * The Curl_sockaddr_ex structure is basically libcurl's external API
232 * curl_sockaddr structure with enough space available to directly hold
233 * any protocol-specific address structures. The variable declared here
234 * will be used to pass / receive data to/from the fopensocket callback
235 * if this has been set, before that, it is initialized from parameters.
236 */
237 dest->family = ai->ai_family;
238 switch(transport) {
239 case TRNSPRT_TCP:
240 dest->socktype = SOCK_STREAM;
241 dest->protocol = IPPROTO_TCP;
242 break;
243 case TRNSPRT_UNIX:
244 dest->socktype = SOCK_STREAM;
245 dest->protocol = IPPROTO_IP;
246 break;
247 default: /* UDP and QUIC */
248 dest->socktype = SOCK_DGRAM;
249 dest->protocol = IPPROTO_UDP;
250 break;
251 }
252 dest->addrlen = ai->ai_addrlen;
253
254 if(dest->addrlen > sizeof(struct Curl_sockaddr_storage))
255 dest->addrlen = sizeof(struct Curl_sockaddr_storage);
256 memcpy(&dest->sa_addr, ai->ai_addr, dest->addrlen);
257 }
258
socket_open(struct Curl_easy * data,struct Curl_sockaddr_ex * addr,curl_socket_t * sockfd)259 static CURLcode socket_open(struct Curl_easy *data,
260 struct Curl_sockaddr_ex *addr,
261 curl_socket_t *sockfd)
262 {
263 DEBUGASSERT(data);
264 DEBUGASSERT(data->conn);
265 if(data->set.fopensocket) {
266 /*
267 * If the opensocket callback is set, all the destination address
268 * information is passed to the callback. Depending on this information the
269 * callback may opt to abort the connection, this is indicated returning
270 * CURL_SOCKET_BAD; otherwise it will return a not-connected socket. When
271 * the callback returns a valid socket the destination address information
272 * might have been changed and this 'new' address will actually be used
273 * here to connect.
274 */
275 Curl_set_in_callback(data, true);
276 *sockfd = data->set.fopensocket(data->set.opensocket_client,
277 CURLSOCKTYPE_IPCXN,
278 (struct curl_sockaddr *)addr);
279 Curl_set_in_callback(data, false);
280 }
281 else {
282 /* opensocket callback not set, so simply create the socket now */
283 *sockfd = socket(addr->family, addr->socktype, addr->protocol);
284 }
285
286 if(*sockfd == CURL_SOCKET_BAD)
287 /* no socket, no connection */
288 return CURLE_COULDNT_CONNECT;
289
290 #if defined(USE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
291 if(data->conn->scope_id && (addr->family == AF_INET6)) {
292 struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
293 sa6->sin6_scope_id = data->conn->scope_id;
294 }
295 #endif
296 return CURLE_OK;
297 }
298
299 /*
300 * Create a socket based on info from 'conn' and 'ai'.
301 *
302 * 'addr' should be a pointer to the correct struct to get data back, or NULL.
303 * 'sockfd' must be a pointer to a socket descriptor.
304 *
305 * If the open socket callback is set, used that!
306 *
307 */
Curl_socket_open(struct Curl_easy * data,const struct Curl_addrinfo * ai,struct Curl_sockaddr_ex * addr,int transport,curl_socket_t * sockfd)308 CURLcode Curl_socket_open(struct Curl_easy *data,
309 const struct Curl_addrinfo *ai,
310 struct Curl_sockaddr_ex *addr,
311 int transport,
312 curl_socket_t *sockfd)
313 {
314 struct Curl_sockaddr_ex dummy;
315
316 if(!addr)
317 /* if the caller doesn't want info back, use a local temp copy */
318 addr = &dummy;
319
320 Curl_sock_assign_addr(addr, ai, transport);
321 return socket_open(data, addr, sockfd);
322 }
323
socket_close(struct Curl_easy * data,struct connectdata * conn,int use_callback,curl_socket_t sock)324 static int socket_close(struct Curl_easy *data, struct connectdata *conn,
325 int use_callback, curl_socket_t sock)
326 {
327 if(use_callback && conn && conn->fclosesocket) {
328 int rc;
329 Curl_multi_closed(data, sock);
330 Curl_set_in_callback(data, true);
331 rc = conn->fclosesocket(conn->closesocket_client, sock);
332 Curl_set_in_callback(data, false);
333 return rc;
334 }
335
336 if(conn)
337 /* tell the multi-socket code about this */
338 Curl_multi_closed(data, sock);
339
340 sclose(sock);
341
342 return 0;
343 }
344
345 /*
346 * Close a socket.
347 *
348 * 'conn' can be NULL, beware!
349 */
Curl_socket_close(struct Curl_easy * data,struct connectdata * conn,curl_socket_t sock)350 int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
351 curl_socket_t sock)
352 {
353 return socket_close(data, conn, FALSE, sock);
354 }
355
356 #ifdef USE_WINSOCK
357 /* When you run a program that uses the Windows Sockets API, you may
358 experience slow performance when you copy data to a TCP server.
359
360 https://support.microsoft.com/kb/823764
361
362 Work-around: Make the Socket Send Buffer Size Larger Than the Program Send
363 Buffer Size
364
365 The problem described in this knowledge-base is applied only to pre-Vista
366 Windows. Following function trying to detect OS version and skips
367 SO_SNDBUF adjustment for Windows Vista and above.
368 */
369 #define DETECT_OS_NONE 0
370 #define DETECT_OS_PREVISTA 1
371 #define DETECT_OS_VISTA_OR_LATER 2
372
Curl_sndbufset(curl_socket_t sockfd)373 void Curl_sndbufset(curl_socket_t sockfd)
374 {
375 int val = CURL_MAX_WRITE_SIZE + 32;
376 int curval = 0;
377 int curlen = sizeof(curval);
378
379 static int detectOsState = DETECT_OS_NONE;
380
381 if(detectOsState == DETECT_OS_NONE) {
382 if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
383 VERSION_GREATER_THAN_EQUAL))
384 detectOsState = DETECT_OS_VISTA_OR_LATER;
385 else
386 detectOsState = DETECT_OS_PREVISTA;
387 }
388
389 if(detectOsState == DETECT_OS_VISTA_OR_LATER)
390 return;
391
392 if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
393 if(curval > val)
394 return;
395
396 setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&val, sizeof(val));
397 }
398 #endif
399
400 #ifndef CURL_DISABLE_BINDLOCAL
bindlocal(struct Curl_easy * data,struct connectdata * conn,curl_socket_t sockfd,int af,unsigned int scope)401 static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
402 curl_socket_t sockfd, int af, unsigned int scope)
403 {
404 struct Curl_sockaddr_storage sa;
405 struct sockaddr *sock = (struct sockaddr *)&sa; /* bind to this address */
406 curl_socklen_t sizeof_sa = 0; /* size of the data sock points to */
407 struct sockaddr_in *si4 = (struct sockaddr_in *)&sa;
408 #ifdef USE_IPV6
409 struct sockaddr_in6 *si6 = (struct sockaddr_in6 *)&sa;
410 #endif
411
412 struct Curl_dns_entry *h = NULL;
413 unsigned short port = data->set.localport; /* use this port number, 0 for
414 "random" */
415 /* how many port numbers to try to bind to, increasing one at a time */
416 int portnum = data->set.localportrange;
417 const char *dev = data->set.str[STRING_DEVICE];
418 int error;
419 #ifdef IP_BIND_ADDRESS_NO_PORT
420 int on = 1;
421 #endif
422 #ifndef USE_IPV6
423 (void)scope;
424 #endif
425
426 /*************************************************************
427 * Select device to bind socket to
428 *************************************************************/
429 if(!dev && !port)
430 /* no local kind of binding was requested */
431 return CURLE_OK;
432
433 memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
434
435 if(dev && (strlen(dev)<255) ) {
436 char myhost[256] = "";
437 int done = 0; /* -1 for error, 1 for address found */
438 bool is_interface = FALSE;
439 bool is_host = FALSE;
440 static const char *if_prefix = "if!";
441 static const char *host_prefix = "host!";
442
443 if(strncmp(if_prefix, dev, strlen(if_prefix)) == 0) {
444 dev += strlen(if_prefix);
445 is_interface = TRUE;
446 }
447 else if(strncmp(host_prefix, dev, strlen(host_prefix)) == 0) {
448 dev += strlen(host_prefix);
449 is_host = TRUE;
450 }
451
452 /* interface */
453 if(!is_host) {
454 #ifdef SO_BINDTODEVICE
455 /*
456 * This binds the local socket to a particular interface. This will
457 * force even requests to other local interfaces to go out the external
458 * interface. Only bind to the interface when specified as interface,
459 * not just as a hostname or ip address.
460 *
461 * The interface might be a VRF, eg: vrf-blue, which means it cannot be
462 * converted to an IP address and would fail Curl_if2ip. Simply try to
463 * use it straight away.
464 */
465 if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
466 dev, (curl_socklen_t)strlen(dev) + 1) == 0) {
467 /* This is often "errno 1, error: Operation not permitted" if you're
468 * not running as root or another suitable privileged user. If it
469 * succeeds it means the parameter was a valid interface and not an IP
470 * address. Return immediately.
471 */
472 infof(data, "socket successfully bound to interface '%s'", dev);
473 return CURLE_OK;
474 }
475 #endif
476
477 switch(Curl_if2ip(af,
478 #ifdef USE_IPV6
479 scope, conn->scope_id,
480 #endif
481 dev, myhost, sizeof(myhost))) {
482 case IF2IP_NOT_FOUND:
483 if(is_interface) {
484 /* Do not fall back to treating it as a host name */
485 failf(data, "Couldn't bind to interface '%s'", dev);
486 return CURLE_INTERFACE_FAILED;
487 }
488 break;
489 case IF2IP_AF_NOT_SUPPORTED:
490 /* Signal the caller to try another address family if available */
491 return CURLE_UNSUPPORTED_PROTOCOL;
492 case IF2IP_FOUND:
493 is_interface = TRUE;
494 /*
495 * We now have the numerical IP address in the 'myhost' buffer
496 */
497 infof(data, "Local Interface %s is ip %s using address family %i",
498 dev, myhost, af);
499 done = 1;
500 break;
501 }
502 }
503 if(!is_interface) {
504 /*
505 * This was not an interface, resolve the name as a host name
506 * or IP number
507 *
508 * Temporarily force name resolution to use only the address type
509 * of the connection. The resolve functions should really be changed
510 * to take a type parameter instead.
511 */
512 unsigned char ipver = conn->ip_version;
513 int rc;
514
515 if(af == AF_INET)
516 conn->ip_version = CURL_IPRESOLVE_V4;
517 #ifdef USE_IPV6
518 else if(af == AF_INET6)
519 conn->ip_version = CURL_IPRESOLVE_V6;
520 #endif
521
522 rc = Curl_resolv(data, dev, 80, FALSE, &h);
523 if(rc == CURLRESOLV_PENDING)
524 (void)Curl_resolver_wait_resolv(data, &h);
525 conn->ip_version = ipver;
526
527 if(h) {
528 /* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
529 Curl_printable_address(h->addr, myhost, sizeof(myhost));
530 infof(data, "Name '%s' family %i resolved to '%s' family %i",
531 dev, af, myhost, h->addr->ai_family);
532 Curl_resolv_unlock(data, h);
533 if(af != h->addr->ai_family) {
534 /* bad IP version combo, signal the caller to try another address
535 family if available */
536 return CURLE_UNSUPPORTED_PROTOCOL;
537 }
538 done = 1;
539 }
540 else {
541 /*
542 * provided dev was no interface (or interfaces are not supported
543 * e.g. solaris) no ip address and no domain we fail here
544 */
545 done = -1;
546 }
547 }
548
549 if(done > 0) {
550 #ifdef USE_IPV6
551 /* IPv6 address */
552 if(af == AF_INET6) {
553 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
554 char *scope_ptr = strchr(myhost, '%');
555 if(scope_ptr)
556 *(scope_ptr++) = '\0';
557 #endif
558 if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
559 si6->sin6_family = AF_INET6;
560 si6->sin6_port = htons(port);
561 #ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
562 if(scope_ptr) {
563 /* The "myhost" string either comes from Curl_if2ip or from
564 Curl_printable_address. The latter returns only numeric scope
565 IDs and the former returns none at all. So the scope ID, if
566 present, is known to be numeric */
567 unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
568 if(scope_id > UINT_MAX)
569 return CURLE_UNSUPPORTED_PROTOCOL;
570
571 si6->sin6_scope_id = (unsigned int)scope_id;
572 }
573 #endif
574 }
575 sizeof_sa = sizeof(struct sockaddr_in6);
576 }
577 else
578 #endif
579 /* IPv4 address */
580 if((af == AF_INET) &&
581 (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
582 si4->sin_family = AF_INET;
583 si4->sin_port = htons(port);
584 sizeof_sa = sizeof(struct sockaddr_in);
585 }
586 }
587
588 if(done < 1) {
589 /* errorbuf is set false so failf will overwrite any message already in
590 the error buffer, so the user receives this error message instead of a
591 generic resolve error. */
592 data->state.errorbuf = FALSE;
593 failf(data, "Couldn't bind to '%s'", dev);
594 return CURLE_INTERFACE_FAILED;
595 }
596 }
597 else {
598 /* no device was given, prepare sa to match af's needs */
599 #ifdef USE_IPV6
600 if(af == AF_INET6) {
601 si6->sin6_family = AF_INET6;
602 si6->sin6_port = htons(port);
603 sizeof_sa = sizeof(struct sockaddr_in6);
604 }
605 else
606 #endif
607 if(af == AF_INET) {
608 si4->sin_family = AF_INET;
609 si4->sin_port = htons(port);
610 sizeof_sa = sizeof(struct sockaddr_in);
611 }
612 }
613 #ifdef IP_BIND_ADDRESS_NO_PORT
614 (void)setsockopt(sockfd, SOL_IP, IP_BIND_ADDRESS_NO_PORT, &on, sizeof(on));
615 #endif
616 for(;;) {
617 if(bind(sockfd, sock, sizeof_sa) >= 0) {
618 /* we succeeded to bind */
619 infof(data, "Local port: %hu", port);
620 conn->bits.bound = TRUE;
621 return CURLE_OK;
622 }
623
624 if(--portnum > 0) {
625 port++; /* try next port */
626 if(port == 0)
627 break;
628 infof(data, "Bind to local port %d failed, trying next", port - 1);
629 /* We reuse/clobber the port variable here below */
630 if(sock->sa_family == AF_INET)
631 si4->sin_port = ntohs(port);
632 #ifdef USE_IPV6
633 else
634 si6->sin6_port = ntohs(port);
635 #endif
636 }
637 else
638 break;
639 }
640 {
641 char buffer[STRERROR_LEN];
642 data->state.os_errno = error = SOCKERRNO;
643 failf(data, "bind failed with errno %d: %s",
644 error, Curl_strerror(error, buffer, sizeof(buffer)));
645 }
646
647 return CURLE_INTERFACE_FAILED;
648 }
649 #endif
650
651 /*
652 * verifyconnect() returns TRUE if the connect really has happened.
653 */
verifyconnect(curl_socket_t sockfd,int * error)654 static bool verifyconnect(curl_socket_t sockfd, int *error)
655 {
656 bool rc = TRUE;
657 #ifdef SO_ERROR
658 int err = 0;
659 curl_socklen_t errSize = sizeof(err);
660
661 #ifdef _WIN32
662 /*
663 * In October 2003 we effectively nullified this function on Windows due to
664 * problems with it using all CPU in multi-threaded cases.
665 *
666 * In May 2004, we bring it back to offer more info back on connect failures.
667 * Gisle Vanem could reproduce the former problems with this function, but
668 * could avoid them by adding this SleepEx() call below:
669 *
670 * "I don't have Rational Quantify, but the hint from his post was
671 * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
672 * just Sleep(0) would be enough?) would release whatever
673 * mutex/critical-section the ntdll call is waiting on.
674 *
675 * Someone got to verify this on Win-NT 4.0, 2000."
676 */
677
678 #ifdef _WIN32_WCE
679 Sleep(0);
680 #else
681 SleepEx(0, FALSE);
682 #endif
683
684 #endif
685
686 if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
687 err = SOCKERRNO;
688 #ifdef _WIN32_WCE
689 /* Old WinCE versions don't support SO_ERROR */
690 if(WSAENOPROTOOPT == err) {
691 SET_SOCKERRNO(0);
692 err = 0;
693 }
694 #endif
695 #if defined(EBADIOCTL) && defined(__minix)
696 /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
697 if(EBADIOCTL == err) {
698 SET_SOCKERRNO(0);
699 err = 0;
700 }
701 #endif
702 if((0 == err) || (EISCONN == err))
703 /* we are connected, awesome! */
704 rc = TRUE;
705 else
706 /* This wasn't a successful connect */
707 rc = FALSE;
708 if(error)
709 *error = err;
710 #else
711 (void)sockfd;
712 if(error)
713 *error = SOCKERRNO;
714 #endif
715 return rc;
716 }
717
718 /**
719 * Determine the curl code for a socket connect() == -1 with errno.
720 */
socket_connect_result(struct Curl_easy * data,const char * ipaddress,int error)721 static CURLcode socket_connect_result(struct Curl_easy *data,
722 const char *ipaddress, int error)
723 {
724 switch(error) {
725 case EINPROGRESS:
726 case EWOULDBLOCK:
727 #if defined(EAGAIN)
728 #if (EAGAIN) != (EWOULDBLOCK)
729 /* On some platforms EAGAIN and EWOULDBLOCK are the
730 * same value, and on others they are different, hence
731 * the odd #if
732 */
733 case EAGAIN:
734 #endif
735 #endif
736 return CURLE_OK;
737
738 default:
739 /* unknown error, fallthrough and try another address! */
740 #ifdef CURL_DISABLE_VERBOSE_STRINGS
741 (void)ipaddress;
742 #else
743 {
744 char buffer[STRERROR_LEN];
745 infof(data, "Immediate connect fail for %s: %s",
746 ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
747 }
748 #endif
749 data->state.os_errno = error;
750 /* connect failed */
751 return CURLE_COULDNT_CONNECT;
752 }
753 }
754
755 /* We have a recv buffer to enhance reads with len < NW_SMALL_READS.
756 * This happens often on TLS connections where the TLS implementation
757 * tries to read the head of a TLS record, determine the length of the
758 * full record and then make a subsequent read for that.
759 * On large reads, we will not fill the buffer to avoid the double copy. */
760 #define NW_RECV_CHUNK_SIZE (64 * 1024)
761 #define NW_RECV_CHUNKS 1
762 #define NW_SMALL_READS (1024)
763
764 struct cf_socket_ctx {
765 int transport;
766 struct Curl_sockaddr_ex addr; /* address to connect to */
767 curl_socket_t sock; /* current attempt socket */
768 struct bufq recvbuf; /* used when `buffer_recv` is set */
769 struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */
770 struct curltime started_at; /* when socket was created */
771 struct curltime connected_at; /* when socket connected/got first byte */
772 struct curltime first_byte_at; /* when first byte was recvd */
773 int error; /* errno of last failure or 0 */
774 #ifdef DEBUGBUILD
775 int wblock_percent; /* percent of writes doing EAGAIN */
776 int wpartial_percent; /* percent of bytes written in send */
777 int rblock_percent; /* percent of reads doing EAGAIN */
778 size_t recv_max; /* max enforced read size */
779 #endif
780 BIT(got_first_byte); /* if first byte was received */
781 BIT(accepted); /* socket was accepted, not connected */
782 BIT(sock_connected); /* socket is "connected", e.g. in UDP */
783 BIT(active);
784 BIT(buffer_recv);
785 };
786
cf_socket_ctx_init(struct cf_socket_ctx * ctx,const struct Curl_addrinfo * ai,int transport)787 static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
788 const struct Curl_addrinfo *ai,
789 int transport)
790 {
791 memset(ctx, 0, sizeof(*ctx));
792 ctx->sock = CURL_SOCKET_BAD;
793 ctx->transport = transport;
794 Curl_sock_assign_addr(&ctx->addr, ai, transport);
795 Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS);
796 #ifdef DEBUGBUILD
797 {
798 char *p = getenv("CURL_DBG_SOCK_WBLOCK");
799 if(p) {
800 long l = strtol(p, NULL, 10);
801 if(l >= 0 && l <= 100)
802 ctx->wblock_percent = (int)l;
803 }
804 p = getenv("CURL_DBG_SOCK_WPARTIAL");
805 if(p) {
806 long l = strtol(p, NULL, 10);
807 if(l >= 0 && l <= 100)
808 ctx->wpartial_percent = (int)l;
809 }
810 p = getenv("CURL_DBG_SOCK_RBLOCK");
811 if(p) {
812 long l = strtol(p, NULL, 10);
813 if(l >= 0 && l <= 100)
814 ctx->rblock_percent = (int)l;
815 }
816 p = getenv("CURL_DBG_SOCK_RMAX");
817 if(p) {
818 long l = strtol(p, NULL, 10);
819 if(l >= 0)
820 ctx->recv_max = (size_t)l;
821 }
822 }
823 #endif
824 }
825
826 struct reader_ctx {
827 struct Curl_cfilter *cf;
828 struct Curl_easy *data;
829 };
830
nw_in_read(void * reader_ctx,unsigned char * buf,size_t len,CURLcode * err)831 static ssize_t nw_in_read(void *reader_ctx,
832 unsigned char *buf, size_t len,
833 CURLcode *err)
834 {
835 struct reader_ctx *rctx = reader_ctx;
836 struct cf_socket_ctx *ctx = rctx->cf->ctx;
837 ssize_t nread;
838
839 *err = CURLE_OK;
840 nread = sread(ctx->sock, buf, len);
841
842 if(-1 == nread) {
843 int sockerr = SOCKERRNO;
844
845 if(
846 #ifdef WSAEWOULDBLOCK
847 /* This is how Windows does it */
848 (WSAEWOULDBLOCK == sockerr)
849 #else
850 /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
851 due to its inability to send off data without blocking. We therefore
852 treat both error codes the same here */
853 (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
854 #endif
855 ) {
856 /* this is just a case of EWOULDBLOCK */
857 *err = CURLE_AGAIN;
858 nread = -1;
859 }
860 else {
861 char buffer[STRERROR_LEN];
862
863 failf(rctx->data, "Recv failure: %s",
864 Curl_strerror(sockerr, buffer, sizeof(buffer)));
865 rctx->data->state.os_errno = sockerr;
866 *err = CURLE_RECV_ERROR;
867 nread = -1;
868 }
869 }
870 CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu, fd=%"
871 CURL_FORMAT_SOCKET_T ") -> %d, err=%d",
872 len, ctx->sock, (int)nread, *err);
873 return nread;
874 }
875
cf_socket_close(struct Curl_cfilter * cf,struct Curl_easy * data)876 static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
877 {
878 struct cf_socket_ctx *ctx = cf->ctx;
879
880 if(ctx && CURL_SOCKET_BAD != ctx->sock) {
881 CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
882 ")", ctx->sock);
883 if(ctx->sock == cf->conn->sock[cf->sockindex])
884 cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
885 socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
886 ctx->sock = CURL_SOCKET_BAD;
887 if(ctx->active && cf->sockindex == FIRSTSOCKET)
888 cf->conn->remote_addr = NULL;
889 Curl_bufq_reset(&ctx->recvbuf);
890 ctx->active = FALSE;
891 ctx->buffer_recv = FALSE;
892 memset(&ctx->started_at, 0, sizeof(ctx->started_at));
893 memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
894 }
895
896 cf->connected = FALSE;
897 }
898
cf_socket_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)899 static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
900 {
901 struct cf_socket_ctx *ctx = cf->ctx;
902
903 cf_socket_close(cf, data);
904 CURL_TRC_CF(data, cf, "destroy");
905 Curl_bufq_free(&ctx->recvbuf);
906 free(ctx);
907 cf->ctx = NULL;
908 }
909
set_local_ip(struct Curl_cfilter * cf,struct Curl_easy * data)910 static CURLcode set_local_ip(struct Curl_cfilter *cf,
911 struct Curl_easy *data)
912 {
913 struct cf_socket_ctx *ctx = cf->ctx;
914
915 #ifdef HAVE_GETSOCKNAME
916 if((ctx->sock != CURL_SOCKET_BAD) &&
917 !(data->conn->handler->protocol & CURLPROTO_TFTP)) {
918 /* TFTP does not connect, so it cannot get the IP like this */
919
920 char buffer[STRERROR_LEN];
921 struct Curl_sockaddr_storage ssloc;
922 curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
923
924 memset(&ssloc, 0, sizeof(ssloc));
925 if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
926 int error = SOCKERRNO;
927 failf(data, "getsockname() failed with errno %d: %s",
928 error, Curl_strerror(error, buffer, sizeof(buffer)));
929 return CURLE_FAILED_INIT;
930 }
931 if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
932 ctx->ip.local_ip, &ctx->ip.local_port)) {
933 failf(data, "ssloc inet_ntop() failed with errno %d: %s",
934 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
935 return CURLE_FAILED_INIT;
936 }
937 }
938 #else
939 (void)data;
940 ctx->ip.local_ip[0] = 0;
941 ctx->ip.local_port = -1;
942 #endif
943 return CURLE_OK;
944 }
945
set_remote_ip(struct Curl_cfilter * cf,struct Curl_easy * data)946 static CURLcode set_remote_ip(struct Curl_cfilter *cf,
947 struct Curl_easy *data)
948 {
949 struct cf_socket_ctx *ctx = cf->ctx;
950
951 /* store remote address and port used in this connection attempt */
952 if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
953 ctx->ip.remote_ip, &ctx->ip.remote_port)) {
954 char buffer[STRERROR_LEN];
955
956 ctx->error = errno;
957 /* malformed address or bug in inet_ntop, try next address */
958 failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
959 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
960 return CURLE_FAILED_INIT;
961 }
962 return CURLE_OK;
963 }
964
cf_socket_open(struct Curl_cfilter * cf,struct Curl_easy * data)965 static CURLcode cf_socket_open(struct Curl_cfilter *cf,
966 struct Curl_easy *data)
967 {
968 struct cf_socket_ctx *ctx = cf->ctx;
969 int error = 0;
970 bool isconnected = FALSE;
971 CURLcode result = CURLE_COULDNT_CONNECT;
972 bool is_tcp;
973
974 (void)data;
975 DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
976 ctx->started_at = Curl_now();
977 result = socket_open(data, &ctx->addr, &ctx->sock);
978 if(result)
979 goto out;
980
981 result = set_remote_ip(cf, data);
982 if(result)
983 goto out;
984
985 #ifdef USE_IPV6
986 if(ctx->addr.family == AF_INET6) {
987 set_ipv6_v6only(ctx->sock, 0);
988 infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
989 }
990 else
991 #endif
992 infof(data, " Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
993
994 #ifdef USE_IPV6
995 is_tcp = (ctx->addr.family == AF_INET
996 || ctx->addr.family == AF_INET6) &&
997 ctx->addr.socktype == SOCK_STREAM;
998 #else
999 is_tcp = (ctx->addr.family == AF_INET) &&
1000 ctx->addr.socktype == SOCK_STREAM;
1001 #endif
1002 if(is_tcp && data->set.tcp_nodelay)
1003 tcpnodelay(data, ctx->sock);
1004
1005 nosigpipe(data, ctx->sock);
1006
1007 Curl_sndbufset(ctx->sock);
1008
1009 if(is_tcp && data->set.tcp_keepalive)
1010 tcpkeepalive(data, ctx->sock);
1011
1012 if(data->set.fsockopt) {
1013 /* activate callback for setting socket options */
1014 Curl_set_in_callback(data, true);
1015 error = data->set.fsockopt(data->set.sockopt_client,
1016 ctx->sock,
1017 CURLSOCKTYPE_IPCXN);
1018 Curl_set_in_callback(data, false);
1019
1020 if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1021 isconnected = TRUE;
1022 else if(error) {
1023 result = CURLE_ABORTED_BY_CALLBACK;
1024 goto out;
1025 }
1026 }
1027
1028 #ifndef CURL_DISABLE_BINDLOCAL
1029 /* possibly bind the local end to an IP, interface or port */
1030 if(ctx->addr.family == AF_INET
1031 #ifdef USE_IPV6
1032 || ctx->addr.family == AF_INET6
1033 #endif
1034 ) {
1035 result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
1036 Curl_ipv6_scope(&ctx->addr.sa_addr));
1037 if(result) {
1038 if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1039 /* The address family is not supported on this interface.
1040 We can continue trying addresses */
1041 result = CURLE_COULDNT_CONNECT;
1042 }
1043 goto out;
1044 }
1045 }
1046 #endif
1047
1048 /* set socket non-blocking */
1049 (void)curlx_nonblock(ctx->sock, TRUE);
1050 ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
1051 out:
1052 if(result) {
1053 if(ctx->sock != CURL_SOCKET_BAD) {
1054 socket_close(data, cf->conn, TRUE, ctx->sock);
1055 ctx->sock = CURL_SOCKET_BAD;
1056 }
1057 }
1058 else if(isconnected) {
1059 set_local_ip(cf, data);
1060 ctx->connected_at = Curl_now();
1061 cf->connected = TRUE;
1062 }
1063 CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
1064 result, ctx->sock);
1065 return result;
1066 }
1067
do_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool is_tcp_fastopen)1068 static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
1069 bool is_tcp_fastopen)
1070 {
1071 struct cf_socket_ctx *ctx = cf->ctx;
1072 #ifdef TCP_FASTOPEN_CONNECT
1073 int optval = 1;
1074 #endif
1075 int rc = -1;
1076
1077 (void)data;
1078 if(is_tcp_fastopen) {
1079 #if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1080 # if defined(HAVE_BUILTIN_AVAILABLE)
1081 /* while connectx function is available since macOS 10.11 / iOS 9,
1082 it did not have the interface declared correctly until
1083 Xcode 9 / macOS SDK 10.13 */
1084 if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1085 sa_endpoints_t endpoints;
1086 endpoints.sae_srcif = 0;
1087 endpoints.sae_srcaddr = NULL;
1088 endpoints.sae_srcaddrlen = 0;
1089 endpoints.sae_dstaddr = &ctx->addr.sa_addr;
1090 endpoints.sae_dstaddrlen = ctx->addr.addrlen;
1091
1092 rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY,
1093 CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1094 NULL, 0, NULL, NULL);
1095 }
1096 else {
1097 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1098 }
1099 # else
1100 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1101 # endif /* HAVE_BUILTIN_AVAILABLE */
1102 #elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1103 if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1104 (void *)&optval, sizeof(optval)) < 0)
1105 infof(data, "Failed to enable TCP Fast Open on fd %"
1106 CURL_FORMAT_SOCKET_T, ctx->sock);
1107
1108 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1109 #elif defined(MSG_FASTOPEN) /* old Linux */
1110 if(cf->conn->given->flags & PROTOPT_SSL)
1111 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1112 else
1113 rc = 0; /* Do nothing */
1114 #endif
1115 }
1116 else {
1117 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1118 }
1119 return rc;
1120 }
1121
cf_tcp_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1122 static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
1123 struct Curl_easy *data,
1124 bool blocking, bool *done)
1125 {
1126 struct cf_socket_ctx *ctx = cf->ctx;
1127 CURLcode result = CURLE_COULDNT_CONNECT;
1128 int rc = 0;
1129
1130 (void)data;
1131 if(cf->connected) {
1132 *done = TRUE;
1133 return CURLE_OK;
1134 }
1135
1136 /* TODO: need to support blocking connect? */
1137 if(blocking)
1138 return CURLE_UNSUPPORTED_PROTOCOL;
1139
1140 *done = FALSE; /* a very negative world view is best */
1141 if(ctx->sock == CURL_SOCKET_BAD) {
1142 int error;
1143
1144 result = cf_socket_open(cf, data);
1145 if(result)
1146 goto out;
1147
1148 if(cf->connected) {
1149 *done = TRUE;
1150 return CURLE_OK;
1151 }
1152
1153 /* Connect TCP socket */
1154 rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
1155 error = SOCKERRNO;
1156 data->tcp_connect_errno = error;
1157 set_local_ip(cf, data);
1158 CURL_TRC_CF(data, cf, "local address %s port %d...",
1159 ctx->ip.local_ip, ctx->ip.local_port);
1160 if(-1 == rc) {
1161 result = socket_connect_result(data, ctx->ip.remote_ip, error);
1162 goto out;
1163 }
1164 }
1165
1166 #ifdef mpeix
1167 /* Call this function once now, and ignore the results. We do this to
1168 "clear" the error state on the socket so that we can later read it
1169 reliably. This is reported necessary on the MPE/iX operating
1170 system. */
1171 (void)verifyconnect(ctx->sock, NULL);
1172 #endif
1173 /* check socket for connect */
1174 rc = SOCKET_WRITABLE(ctx->sock, 0);
1175
1176 if(rc == 0) { /* no connection yet */
1177 CURL_TRC_CF(data, cf, "not connected yet");
1178 return CURLE_OK;
1179 }
1180 else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
1181 if(verifyconnect(ctx->sock, &ctx->error)) {
1182 /* we are connected with TCP, awesome! */
1183 ctx->connected_at = Curl_now();
1184 set_local_ip(cf, data);
1185 *done = TRUE;
1186 cf->connected = TRUE;
1187 CURL_TRC_CF(data, cf, "connected");
1188 return CURLE_OK;
1189 }
1190 }
1191 else if(rc & CURL_CSELECT_ERR) {
1192 (void)verifyconnect(ctx->sock, &ctx->error);
1193 result = CURLE_COULDNT_CONNECT;
1194 }
1195
1196 out:
1197 if(result) {
1198 if(ctx->error) {
1199 set_local_ip(cf, data);
1200 data->state.os_errno = ctx->error;
1201 SET_SOCKERRNO(ctx->error);
1202 #ifndef CURL_DISABLE_VERBOSE_STRINGS
1203 {
1204 char buffer[STRERROR_LEN];
1205 infof(data, "connect to %s port %u from %s port %d failed: %s",
1206 ctx->ip.remote_ip, ctx->ip.remote_port,
1207 ctx->ip.local_ip, ctx->ip.local_port,
1208 Curl_strerror(ctx->error, buffer, sizeof(buffer)));
1209 }
1210 #endif
1211 }
1212 if(ctx->sock != CURL_SOCKET_BAD) {
1213 socket_close(data, cf->conn, TRUE, ctx->sock);
1214 ctx->sock = CURL_SOCKET_BAD;
1215 }
1216 *done = FALSE;
1217 }
1218 return result;
1219 }
1220
cf_socket_get_host(struct Curl_cfilter * cf,struct Curl_easy * data,const char ** phost,const char ** pdisplay_host,int * pport)1221 static void cf_socket_get_host(struct Curl_cfilter *cf,
1222 struct Curl_easy *data,
1223 const char **phost,
1224 const char **pdisplay_host,
1225 int *pport)
1226 {
1227 struct cf_socket_ctx *ctx = cf->ctx;
1228 (void)data;
1229 *phost = cf->conn->host.name;
1230 *pdisplay_host = cf->conn->host.dispname;
1231 *pport = ctx->ip.remote_port;
1232 }
1233
cf_socket_adjust_pollset(struct Curl_cfilter * cf,struct Curl_easy * data,struct easy_pollset * ps)1234 static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
1235 struct Curl_easy *data,
1236 struct easy_pollset *ps)
1237 {
1238 struct cf_socket_ctx *ctx = cf->ctx;
1239
1240 if(ctx->sock != CURL_SOCKET_BAD) {
1241 if(!cf->connected) {
1242 Curl_pollset_set_out_only(data, ps, ctx->sock);
1243 CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%"
1244 CURL_FORMAT_SOCKET_T, ctx->sock);
1245 }
1246 else if(!ctx->active) {
1247 Curl_pollset_add_in(data, ps, ctx->sock);
1248 CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
1249 CURL_FORMAT_SOCKET_T, ctx->sock);
1250 }
1251 }
1252 }
1253
cf_socket_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)1254 static bool cf_socket_data_pending(struct Curl_cfilter *cf,
1255 const struct Curl_easy *data)
1256 {
1257 struct cf_socket_ctx *ctx = cf->ctx;
1258 int readable;
1259
1260 (void)data;
1261 if(!Curl_bufq_is_empty(&ctx->recvbuf))
1262 return TRUE;
1263
1264 readable = SOCKET_READABLE(ctx->sock, 0);
1265 return (readable > 0 && (readable & CURL_CSELECT_IN));
1266 }
1267
cf_socket_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)1268 static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1269 const void *buf, size_t len, CURLcode *err)
1270 {
1271 struct cf_socket_ctx *ctx = cf->ctx;
1272 curl_socket_t fdsave;
1273 ssize_t nwritten;
1274 size_t orig_len = len;
1275
1276 *err = CURLE_OK;
1277 fdsave = cf->conn->sock[cf->sockindex];
1278 cf->conn->sock[cf->sockindex] = ctx->sock;
1279
1280 #ifdef DEBUGBUILD
1281 /* simulate network blocking/partial writes */
1282 if(ctx->wblock_percent > 0) {
1283 unsigned char c = 0;
1284 Curl_rand(data, &c, 1);
1285 if(c >= ((100-ctx->wblock_percent)*256/100)) {
1286 CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
1287 *err = CURLE_AGAIN;
1288 nwritten = -1;
1289 cf->conn->sock[cf->sockindex] = fdsave;
1290 return nwritten;
1291 }
1292 }
1293 if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
1294 len = len * ctx->wpartial_percent / 100;
1295 if(!len)
1296 len = 1;
1297 CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes",
1298 orig_len, len);
1299 }
1300 #endif
1301
1302 #if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
1303 if(cf->conn->bits.tcp_fastopen) {
1304 nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
1305 &cf->conn->remote_addr->sa_addr,
1306 cf->conn->remote_addr->addrlen);
1307 cf->conn->bits.tcp_fastopen = FALSE;
1308 }
1309 else
1310 #endif
1311 nwritten = swrite(ctx->sock, buf, len);
1312
1313 if(-1 == nwritten) {
1314 int sockerr = SOCKERRNO;
1315
1316 if(
1317 #ifdef WSAEWOULDBLOCK
1318 /* This is how Windows does it */
1319 (WSAEWOULDBLOCK == sockerr)
1320 #else
1321 /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
1322 due to its inability to send off data without blocking. We therefore
1323 treat both error codes the same here */
1324 (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
1325 (EINPROGRESS == sockerr)
1326 #endif
1327 ) {
1328 /* this is just a case of EWOULDBLOCK */
1329 *err = CURLE_AGAIN;
1330 }
1331 else {
1332 char buffer[STRERROR_LEN];
1333 failf(data, "Send failure: %s",
1334 Curl_strerror(sockerr, buffer, sizeof(buffer)));
1335 data->state.os_errno = sockerr;
1336 *err = CURLE_SEND_ERROR;
1337 }
1338 }
1339
1340 CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
1341 orig_len, (int)nwritten, *err);
1342 cf->conn->sock[cf->sockindex] = fdsave;
1343 return nwritten;
1344 }
1345
cf_socket_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)1346 static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1347 char *buf, size_t len, CURLcode *err)
1348 {
1349 struct cf_socket_ctx *ctx = cf->ctx;
1350 curl_socket_t fdsave;
1351 ssize_t nread;
1352
1353 *err = CURLE_OK;
1354
1355 fdsave = cf->conn->sock[cf->sockindex];
1356 cf->conn->sock[cf->sockindex] = ctx->sock;
1357
1358 #ifdef DEBUGBUILD
1359 /* simulate network blocking/partial reads */
1360 if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
1361 unsigned char c = 0;
1362 Curl_rand(data, &c, 1);
1363 if(c >= ((100-ctx->rblock_percent)*256/100)) {
1364 CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
1365 *err = CURLE_AGAIN;
1366 nread = -1;
1367 cf->conn->sock[cf->sockindex] = fdsave;
1368 return nread;
1369 }
1370 }
1371 if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
1372 size_t orig_len = len;
1373 len = ctx->recv_max;
1374 CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes",
1375 orig_len, len);
1376 }
1377 #endif
1378
1379 if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) {
1380 CURL_TRC_CF(data, cf, "recv from buffer");
1381 nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1382 }
1383 else {
1384 struct reader_ctx rctx;
1385
1386 rctx.cf = cf;
1387 rctx.data = data;
1388
1389 /* "small" reads may trigger filling our buffer, "large" reads
1390 * are probably not worth the additional copy */
1391 if(ctx->buffer_recv && len < NW_SMALL_READS) {
1392 ssize_t nwritten;
1393 nwritten = Curl_bufq_slurp(&ctx->recvbuf, nw_in_read, &rctx, err);
1394 if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) {
1395 /* we have a partial read with an error. need to deliver
1396 * what we got, return the error later. */
1397 CURL_TRC_CF(data, cf, "partial read: empty buffer first");
1398 nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1399 }
1400 else if(nwritten < 0) {
1401 nread = -1;
1402 goto out;
1403 }
1404 else if(nwritten == 0) {
1405 /* eof */
1406 *err = CURLE_OK;
1407 nread = 0;
1408 }
1409 else {
1410 CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten);
1411 nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1412 }
1413 }
1414 else {
1415 nread = nw_in_read(&rctx, (unsigned char *)buf, len, err);
1416 }
1417 }
1418
1419 out:
1420 CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
1421 *err);
1422 if(nread > 0 && !ctx->got_first_byte) {
1423 ctx->first_byte_at = Curl_now();
1424 ctx->got_first_byte = TRUE;
1425 }
1426 cf->conn->sock[cf->sockindex] = fdsave;
1427 return nread;
1428 }
1429
cf_socket_active(struct Curl_cfilter * cf,struct Curl_easy * data)1430 static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
1431 {
1432 struct cf_socket_ctx *ctx = cf->ctx;
1433
1434 /* use this socket from now on */
1435 cf->conn->sock[cf->sockindex] = ctx->sock;
1436 set_local_ip(cf, data);
1437 if(cf->sockindex == SECONDARYSOCKET)
1438 cf->conn->secondary = ctx->ip;
1439 else
1440 cf->conn->primary = ctx->ip;
1441 /* the first socket info gets some specials */
1442 if(cf->sockindex == FIRSTSOCKET) {
1443 cf->conn->remote_addr = &ctx->addr;
1444 #ifdef USE_IPV6
1445 cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
1446 #endif
1447 Curl_persistconninfo(data, cf->conn, &ctx->ip);
1448 /* buffering is currently disabled by default because we have stalls
1449 * in parallel transfers where not all buffered data is consumed and no
1450 * socket events happen.
1451 */
1452 ctx->buffer_recv = FALSE;
1453 }
1454 ctx->active = TRUE;
1455 }
1456
cf_socket_cntrl(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)1457 static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
1458 struct Curl_easy *data,
1459 int event, int arg1, void *arg2)
1460 {
1461 struct cf_socket_ctx *ctx = cf->ctx;
1462
1463 (void)arg1;
1464 (void)arg2;
1465 switch(event) {
1466 case CF_CTRL_CONN_INFO_UPDATE:
1467 cf_socket_active(cf, data);
1468 break;
1469 case CF_CTRL_DATA_SETUP:
1470 Curl_persistconninfo(data, cf->conn, &ctx->ip);
1471 break;
1472 case CF_CTRL_FORGET_SOCKET:
1473 ctx->sock = CURL_SOCKET_BAD;
1474 break;
1475 }
1476 return CURLE_OK;
1477 }
1478
cf_socket_conn_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)1479 static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
1480 struct Curl_easy *data,
1481 bool *input_pending)
1482 {
1483 struct cf_socket_ctx *ctx = cf->ctx;
1484 struct pollfd pfd[1];
1485 int r;
1486
1487 *input_pending = FALSE;
1488 (void)data;
1489 if(!ctx || ctx->sock == CURL_SOCKET_BAD)
1490 return FALSE;
1491
1492 /* Check with 0 timeout if there are any events pending on the socket */
1493 pfd[0].fd = ctx->sock;
1494 pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
1495 pfd[0].revents = 0;
1496
1497 r = Curl_poll(pfd, 1, 0);
1498 if(r < 0) {
1499 CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead");
1500 return FALSE;
1501 }
1502 else if(r == 0) {
1503 CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive");
1504 return TRUE;
1505 }
1506 else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) {
1507 CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead");
1508 return FALSE;
1509 }
1510
1511 CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive");
1512 *input_pending = TRUE;
1513 return TRUE;
1514 }
1515
cf_socket_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)1516 static CURLcode cf_socket_query(struct Curl_cfilter *cf,
1517 struct Curl_easy *data,
1518 int query, int *pres1, void *pres2)
1519 {
1520 struct cf_socket_ctx *ctx = cf->ctx;
1521
1522 switch(query) {
1523 case CF_QUERY_SOCKET:
1524 DEBUGASSERT(pres2);
1525 *((curl_socket_t *)pres2) = ctx->sock;
1526 return CURLE_OK;
1527 case CF_QUERY_CONNECT_REPLY_MS:
1528 if(ctx->got_first_byte) {
1529 timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
1530 *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
1531 }
1532 else
1533 *pres1 = -1;
1534 return CURLE_OK;
1535 case CF_QUERY_TIMER_CONNECT: {
1536 struct curltime *when = pres2;
1537 switch(ctx->transport) {
1538 case TRNSPRT_UDP:
1539 case TRNSPRT_QUIC:
1540 /* Since UDP connected sockets work different from TCP, we use the
1541 * time of the first byte from the peer as the "connect" time. */
1542 if(ctx->got_first_byte) {
1543 *when = ctx->first_byte_at;
1544 break;
1545 }
1546 FALLTHROUGH();
1547 default:
1548 *when = ctx->connected_at;
1549 break;
1550 }
1551 return CURLE_OK;
1552 }
1553 default:
1554 break;
1555 }
1556 return cf->next?
1557 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1558 CURLE_UNKNOWN_OPTION;
1559 }
1560
1561 struct Curl_cftype Curl_cft_tcp = {
1562 "TCP",
1563 CF_TYPE_IP_CONNECT,
1564 CURL_LOG_LVL_NONE,
1565 cf_socket_destroy,
1566 cf_tcp_connect,
1567 cf_socket_close,
1568 cf_socket_get_host,
1569 cf_socket_adjust_pollset,
1570 cf_socket_data_pending,
1571 cf_socket_send,
1572 cf_socket_recv,
1573 cf_socket_cntrl,
1574 cf_socket_conn_is_alive,
1575 Curl_cf_def_conn_keep_alive,
1576 cf_socket_query,
1577 };
1578
Curl_cf_tcp_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)1579 CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
1580 struct Curl_easy *data,
1581 struct connectdata *conn,
1582 const struct Curl_addrinfo *ai,
1583 int transport)
1584 {
1585 struct cf_socket_ctx *ctx = NULL;
1586 struct Curl_cfilter *cf = NULL;
1587 CURLcode result;
1588
1589 (void)data;
1590 (void)conn;
1591 DEBUGASSERT(transport == TRNSPRT_TCP);
1592 ctx = calloc(1, sizeof(*ctx));
1593 if(!ctx) {
1594 result = CURLE_OUT_OF_MEMORY;
1595 goto out;
1596 }
1597 cf_socket_ctx_init(ctx, ai, transport);
1598
1599 result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
1600
1601 out:
1602 *pcf = (!result)? cf : NULL;
1603 if(result) {
1604 Curl_safefree(cf);
1605 Curl_safefree(ctx);
1606 }
1607
1608 return result;
1609 }
1610
cf_udp_setup_quic(struct Curl_cfilter * cf,struct Curl_easy * data)1611 static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
1612 struct Curl_easy *data)
1613 {
1614 struct cf_socket_ctx *ctx = cf->ctx;
1615 int rc;
1616
1617 /* QUIC needs a connected socket, nonblocking */
1618 DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
1619
1620 #if defined(__APPLE__) && defined(USE_OPENSSL_QUIC)
1621 (void)rc;
1622 /* On macOS OpenSSL QUIC fails on connected sockets.
1623 * see: <https://github.com/openssl/openssl/issues/23251> */
1624 #else
1625 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1626 if(-1 == rc) {
1627 return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
1628 }
1629 ctx->sock_connected = TRUE;
1630 #endif
1631 set_local_ip(cf, data);
1632 CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
1633 " connected: [%s:%d] -> [%s:%d]",
1634 (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
1635 ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
1636 ctx->ip.remote_ip, ctx->ip.remote_port);
1637
1638 (void)curlx_nonblock(ctx->sock, TRUE);
1639 switch(ctx->addr.family) {
1640 #if defined(__linux__) && defined(IP_MTU_DISCOVER)
1641 case AF_INET: {
1642 int val = IP_PMTUDISC_DO;
1643 (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
1644 sizeof(val));
1645 break;
1646 }
1647 #endif
1648 #if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
1649 case AF_INET6: {
1650 int val = IPV6_PMTUDISC_DO;
1651 (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
1652 sizeof(val));
1653 break;
1654 }
1655 #endif
1656 }
1657 return CURLE_OK;
1658 }
1659
cf_udp_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1660 static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
1661 struct Curl_easy *data,
1662 bool blocking, bool *done)
1663 {
1664 struct cf_socket_ctx *ctx = cf->ctx;
1665 CURLcode result = CURLE_COULDNT_CONNECT;
1666
1667 (void)blocking;
1668 if(cf->connected) {
1669 *done = TRUE;
1670 return CURLE_OK;
1671 }
1672 *done = FALSE;
1673 if(ctx->sock == CURL_SOCKET_BAD) {
1674 result = cf_socket_open(cf, data);
1675 if(result) {
1676 CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result);
1677 goto out;
1678 }
1679
1680 if(ctx->transport == TRNSPRT_QUIC) {
1681 result = cf_udp_setup_quic(cf, data);
1682 if(result)
1683 goto out;
1684 CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1685 CURL_FORMAT_SOCKET_T " (%s:%d)",
1686 ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
1687 }
1688 else {
1689 CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1690 CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock);
1691 }
1692 *done = TRUE;
1693 cf->connected = TRUE;
1694 }
1695 out:
1696 return result;
1697 }
1698
1699 struct Curl_cftype Curl_cft_udp = {
1700 "UDP",
1701 CF_TYPE_IP_CONNECT,
1702 CURL_LOG_LVL_NONE,
1703 cf_socket_destroy,
1704 cf_udp_connect,
1705 cf_socket_close,
1706 cf_socket_get_host,
1707 cf_socket_adjust_pollset,
1708 cf_socket_data_pending,
1709 cf_socket_send,
1710 cf_socket_recv,
1711 cf_socket_cntrl,
1712 cf_socket_conn_is_alive,
1713 Curl_cf_def_conn_keep_alive,
1714 cf_socket_query,
1715 };
1716
Curl_cf_udp_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)1717 CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
1718 struct Curl_easy *data,
1719 struct connectdata *conn,
1720 const struct Curl_addrinfo *ai,
1721 int transport)
1722 {
1723 struct cf_socket_ctx *ctx = NULL;
1724 struct Curl_cfilter *cf = NULL;
1725 CURLcode result;
1726
1727 (void)data;
1728 (void)conn;
1729 DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
1730 ctx = calloc(1, sizeof(*ctx));
1731 if(!ctx) {
1732 result = CURLE_OUT_OF_MEMORY;
1733 goto out;
1734 }
1735 cf_socket_ctx_init(ctx, ai, transport);
1736
1737 result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
1738
1739 out:
1740 *pcf = (!result)? cf : NULL;
1741 if(result) {
1742 Curl_safefree(cf);
1743 Curl_safefree(ctx);
1744 }
1745
1746 return result;
1747 }
1748
1749 /* this is the TCP filter which can also handle this case */
1750 struct Curl_cftype Curl_cft_unix = {
1751 "UNIX",
1752 CF_TYPE_IP_CONNECT,
1753 CURL_LOG_LVL_NONE,
1754 cf_socket_destroy,
1755 cf_tcp_connect,
1756 cf_socket_close,
1757 cf_socket_get_host,
1758 cf_socket_adjust_pollset,
1759 cf_socket_data_pending,
1760 cf_socket_send,
1761 cf_socket_recv,
1762 cf_socket_cntrl,
1763 cf_socket_conn_is_alive,
1764 Curl_cf_def_conn_keep_alive,
1765 cf_socket_query,
1766 };
1767
Curl_cf_unix_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai,int transport)1768 CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
1769 struct Curl_easy *data,
1770 struct connectdata *conn,
1771 const struct Curl_addrinfo *ai,
1772 int transport)
1773 {
1774 struct cf_socket_ctx *ctx = NULL;
1775 struct Curl_cfilter *cf = NULL;
1776 CURLcode result;
1777
1778 (void)data;
1779 (void)conn;
1780 DEBUGASSERT(transport == TRNSPRT_UNIX);
1781 ctx = calloc(1, sizeof(*ctx));
1782 if(!ctx) {
1783 result = CURLE_OUT_OF_MEMORY;
1784 goto out;
1785 }
1786 cf_socket_ctx_init(ctx, ai, transport);
1787
1788 result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
1789
1790 out:
1791 *pcf = (!result)? cf : NULL;
1792 if(result) {
1793 Curl_safefree(cf);
1794 Curl_safefree(ctx);
1795 }
1796
1797 return result;
1798 }
1799
cf_tcp_accept_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)1800 static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
1801 struct Curl_easy *data,
1802 bool blocking, bool *done)
1803 {
1804 /* we start accepted, if we ever close, we cannot go on */
1805 (void)data;
1806 (void)blocking;
1807 if(cf->connected) {
1808 *done = TRUE;
1809 return CURLE_OK;
1810 }
1811 return CURLE_FAILED_INIT;
1812 }
1813
1814 struct Curl_cftype Curl_cft_tcp_accept = {
1815 "TCP-ACCEPT",
1816 CF_TYPE_IP_CONNECT,
1817 CURL_LOG_LVL_NONE,
1818 cf_socket_destroy,
1819 cf_tcp_accept_connect,
1820 cf_socket_close,
1821 cf_socket_get_host, /* TODO: not accurate */
1822 cf_socket_adjust_pollset,
1823 cf_socket_data_pending,
1824 cf_socket_send,
1825 cf_socket_recv,
1826 cf_socket_cntrl,
1827 cf_socket_conn_is_alive,
1828 Curl_cf_def_conn_keep_alive,
1829 cf_socket_query,
1830 };
1831
Curl_conn_tcp_listen_set(struct Curl_easy * data,struct connectdata * conn,int sockindex,curl_socket_t * s)1832 CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
1833 struct connectdata *conn,
1834 int sockindex, curl_socket_t *s)
1835 {
1836 CURLcode result;
1837 struct Curl_cfilter *cf = NULL;
1838 struct cf_socket_ctx *ctx = NULL;
1839
1840 /* replace any existing */
1841 Curl_conn_cf_discard_all(data, conn, sockindex);
1842 DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
1843
1844 ctx = calloc(1, sizeof(*ctx));
1845 if(!ctx) {
1846 result = CURLE_OUT_OF_MEMORY;
1847 goto out;
1848 }
1849 ctx->transport = conn->transport;
1850 ctx->sock = *s;
1851 ctx->accepted = FALSE;
1852 result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
1853 if(result)
1854 goto out;
1855 Curl_conn_cf_add(data, conn, sockindex, cf);
1856
1857 conn->sock[sockindex] = ctx->sock;
1858 set_local_ip(cf, data);
1859 ctx->active = TRUE;
1860 ctx->connected_at = Curl_now();
1861 cf->connected = TRUE;
1862 CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%"
1863 CURL_FORMAT_SOCKET_T ")", ctx->sock);
1864
1865 out:
1866 if(result) {
1867 Curl_safefree(cf);
1868 Curl_safefree(ctx);
1869 }
1870 return result;
1871 }
1872
set_accepted_remote_ip(struct Curl_cfilter * cf,struct Curl_easy * data)1873 static void set_accepted_remote_ip(struct Curl_cfilter *cf,
1874 struct Curl_easy *data)
1875 {
1876 struct cf_socket_ctx *ctx = cf->ctx;
1877 #ifdef HAVE_GETPEERNAME
1878 char buffer[STRERROR_LEN];
1879 struct Curl_sockaddr_storage ssrem;
1880 curl_socklen_t plen;
1881
1882 ctx->ip.remote_ip[0] = 0;
1883 ctx->ip.remote_port = 0;
1884 plen = sizeof(ssrem);
1885 memset(&ssrem, 0, plen);
1886 if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
1887 int error = SOCKERRNO;
1888 failf(data, "getpeername() failed with errno %d: %s",
1889 error, Curl_strerror(error, buffer, sizeof(buffer)));
1890 return;
1891 }
1892 if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
1893 ctx->ip.remote_ip, &ctx->ip.remote_port)) {
1894 failf(data, "ssrem inet_ntop() failed with errno %d: %s",
1895 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1896 return;
1897 }
1898 #else
1899 ctx->ip.remote_ip[0] = 0;
1900 ctx->ip.remote_port = 0;
1901 (void)data;
1902 #endif
1903 }
1904
Curl_conn_tcp_accepted_set(struct Curl_easy * data,struct connectdata * conn,int sockindex,curl_socket_t * s)1905 CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
1906 struct connectdata *conn,
1907 int sockindex, curl_socket_t *s)
1908 {
1909 struct Curl_cfilter *cf = NULL;
1910 struct cf_socket_ctx *ctx = NULL;
1911
1912 cf = conn->cfilter[sockindex];
1913 if(!cf || cf->cft != &Curl_cft_tcp_accept)
1914 return CURLE_FAILED_INIT;
1915
1916 ctx = cf->ctx;
1917 /* discard the listen socket */
1918 socket_close(data, conn, TRUE, ctx->sock);
1919 ctx->sock = *s;
1920 conn->sock[sockindex] = ctx->sock;
1921 set_accepted_remote_ip(cf, data);
1922 set_local_ip(cf, data);
1923 ctx->active = TRUE;
1924 ctx->accepted = TRUE;
1925 ctx->connected_at = Curl_now();
1926 cf->connected = TRUE;
1927 CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
1928 ", remote=%s port=%d)",
1929 ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port);
1930
1931 return CURLE_OK;
1932 }
1933
1934 /**
1935 * Return TRUE iff `cf` is a socket filter.
1936 */
cf_is_socket(struct Curl_cfilter * cf)1937 static bool cf_is_socket(struct Curl_cfilter *cf)
1938 {
1939 return cf && (cf->cft == &Curl_cft_tcp ||
1940 cf->cft == &Curl_cft_udp ||
1941 cf->cft == &Curl_cft_unix ||
1942 cf->cft == &Curl_cft_tcp_accept);
1943 }
1944
Curl_cf_socket_peek(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * psock,const struct Curl_sockaddr_ex ** paddr,struct ip_quadruple * pip)1945 CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
1946 struct Curl_easy *data,
1947 curl_socket_t *psock,
1948 const struct Curl_sockaddr_ex **paddr,
1949 struct ip_quadruple *pip)
1950 {
1951 (void)data;
1952 if(cf_is_socket(cf) && cf->ctx) {
1953 struct cf_socket_ctx *ctx = cf->ctx;
1954
1955 if(psock)
1956 *psock = ctx->sock;
1957 if(paddr)
1958 *paddr = &ctx->addr;
1959 if(pip)
1960 *pip = ctx->ip;
1961 return CURLE_OK;
1962 }
1963 return CURLE_FAILED_INIT;
1964 }
1965