VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/cf-socket.c@ 107713

Last change on this file since 107713 was 104085, checked in by vboxsync, 11 months ago

curl-8.7.1: Make it build. bugref:10639

File size: 57.0 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#ifdef HAVE_NETINET_IN_H
28#include <netinet/in.h> /* <netinet/tcp.h> may need it */
29#endif
30#ifdef HAVE_SYS_UN_H
31#include <sys/un.h> /* for sockaddr_un */
32#endif
33#ifdef HAVE_LINUX_TCP_H
34#include <linux/tcp.h>
35#elif defined(HAVE_NETINET_TCP_H)
36#include <netinet/tcp.h>
37#endif
38#ifdef HAVE_SYS_IOCTL_H
39#include <sys/ioctl.h>
40#endif
41#ifdef HAVE_NETDB_H
42#include <netdb.h>
43#endif
44#ifdef HAVE_FCNTL_H
45#include <fcntl.h>
46#endif
47#ifdef HAVE_ARPA_INET_H
48#include <arpa/inet.h>
49#endif
50
51#ifdef __VMS
52#include <in.h>
53#include <inet.h>
54#endif
55
56#include "urldata.h"
57#include "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(ENABLE_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 */
92static 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
100static 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? */
122static 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
150struct tcp_keepalive {
151 u_long onoff;
152 u_long keepalivetime;
153 u_long keepaliveinterval;
154};
155#endif
156
157static void
158tcpkeepalive(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 */
226void 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
259static 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(ENABLE_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 */
308CURLcode 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
324static 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 */
350int 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
373void 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
401static 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 ENABLE_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 ENABLE_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 ENABLE_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 ENABLE_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 ENABLE_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 ENABLE_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 struct Curl_sockaddr_storage add;
620 curl_socklen_t size = sizeof(add);
621 memset(&add, 0, sizeof(struct Curl_sockaddr_storage));
622 if(getsockname(sockfd, (struct sockaddr *) &add, &size) < 0) {
623 char buffer[STRERROR_LEN];
624 data->state.os_errno = error = SOCKERRNO;
625 failf(data, "getsockname() failed with errno %d: %s",
626 error, Curl_strerror(error, buffer, sizeof(buffer)));
627 return CURLE_INTERFACE_FAILED;
628 }
629 infof(data, "Local port: %hu", port);
630 conn->bits.bound = TRUE;
631 return CURLE_OK;
632 }
633
634 if(--portnum > 0) {
635 port++; /* try next port */
636 if(port == 0)
637 break;
638 infof(data, "Bind to local port %d failed, trying next", port - 1);
639 /* We reuse/clobber the port variable here below */
640 if(sock->sa_family == AF_INET)
641 si4->sin_port = ntohs(port);
642#ifdef ENABLE_IPV6
643 else
644 si6->sin6_port = ntohs(port);
645#endif
646 }
647 else
648 break;
649 }
650 {
651 char buffer[STRERROR_LEN];
652 data->state.os_errno = error = SOCKERRNO;
653 failf(data, "bind failed with errno %d: %s",
654 error, Curl_strerror(error, buffer, sizeof(buffer)));
655 }
656
657 return CURLE_INTERFACE_FAILED;
658}
659#endif
660
661/*
662 * verifyconnect() returns TRUE if the connect really has happened.
663 */
664static bool verifyconnect(curl_socket_t sockfd, int *error)
665{
666 bool rc = TRUE;
667#ifdef SO_ERROR
668 int err = 0;
669 curl_socklen_t errSize = sizeof(err);
670
671#ifdef _WIN32
672 /*
673 * In October 2003 we effectively nullified this function on Windows due to
674 * problems with it using all CPU in multi-threaded cases.
675 *
676 * In May 2004, we bring it back to offer more info back on connect failures.
677 * Gisle Vanem could reproduce the former problems with this function, but
678 * could avoid them by adding this SleepEx() call below:
679 *
680 * "I don't have Rational Quantify, but the hint from his post was
681 * ntdll::NtRemoveIoCompletion(). So I'd assume the SleepEx (or maybe
682 * just Sleep(0) would be enough?) would release whatever
683 * mutex/critical-section the ntdll call is waiting on.
684 *
685 * Someone got to verify this on Win-NT 4.0, 2000."
686 */
687
688#ifdef _WIN32_WCE
689 Sleep(0);
690#else
691 SleepEx(0, FALSE);
692#endif
693
694#endif
695
696 if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
697 err = SOCKERRNO;
698#ifdef _WIN32_WCE
699 /* Old WinCE versions don't support SO_ERROR */
700 if(WSAENOPROTOOPT == err) {
701 SET_SOCKERRNO(0);
702 err = 0;
703 }
704#endif
705#if defined(EBADIOCTL) && defined(__minix)
706 /* Minix 3.1.x doesn't support getsockopt on UDP sockets */
707 if(EBADIOCTL == err) {
708 SET_SOCKERRNO(0);
709 err = 0;
710 }
711#endif
712 if((0 == err) || (EISCONN == err))
713 /* we are connected, awesome! */
714 rc = TRUE;
715 else
716 /* This wasn't a successful connect */
717 rc = FALSE;
718 if(error)
719 *error = err;
720#else
721 (void)sockfd;
722 if(error)
723 *error = SOCKERRNO;
724#endif
725 return rc;
726}
727
728/**
729 * Determine the curl code for a socket connect() == -1 with errno.
730 */
731static CURLcode socket_connect_result(struct Curl_easy *data,
732 const char *ipaddress, int error)
733{
734 switch(error) {
735 case EINPROGRESS:
736 case EWOULDBLOCK:
737#if defined(EAGAIN)
738#if (EAGAIN) != (EWOULDBLOCK)
739 /* On some platforms EAGAIN and EWOULDBLOCK are the
740 * same value, and on others they are different, hence
741 * the odd #if
742 */
743 case EAGAIN:
744#endif
745#endif
746 return CURLE_OK;
747
748 default:
749 /* unknown error, fallthrough and try another address! */
750#ifdef CURL_DISABLE_VERBOSE_STRINGS
751 (void)ipaddress;
752#else
753 {
754 char buffer[STRERROR_LEN];
755 infof(data, "Immediate connect fail for %s: %s",
756 ipaddress, Curl_strerror(error, buffer, sizeof(buffer)));
757 }
758#endif
759 data->state.os_errno = error;
760 /* connect failed */
761 return CURLE_COULDNT_CONNECT;
762 }
763}
764
765/* We have a recv buffer to enhance reads with len < NW_SMALL_READS.
766 * This happens often on TLS connections where the TLS implementation
767 * tries to read the head of a TLS record, determine the length of the
768 * full record and then make a subsequent read for that.
769 * On large reads, we will not fill the buffer to avoid the double copy. */
770#define NW_RECV_CHUNK_SIZE (64 * 1024)
771#define NW_RECV_CHUNKS 1
772#define NW_SMALL_READS (1024)
773
774struct cf_socket_ctx {
775 int transport;
776 struct Curl_sockaddr_ex addr; /* address to connect to */
777 curl_socket_t sock; /* current attempt socket */
778 struct bufq recvbuf; /* used when `buffer_recv` is set */
779 struct ip_quadruple ip; /* The IP quadruple 2x(addr+port) */
780 struct curltime started_at; /* when socket was created */
781 struct curltime connected_at; /* when socket connected/got first byte */
782 struct curltime first_byte_at; /* when first byte was recvd */
783 int error; /* errno of last failure or 0 */
784#ifdef DEBUGBUILD
785 int wblock_percent; /* percent of writes doing EAGAIN */
786 int wpartial_percent; /* percent of bytes written in send */
787 int rblock_percent; /* percent of reads doing EAGAIN */
788 size_t recv_max; /* max enforced read size */
789#endif
790 BIT(got_first_byte); /* if first byte was received */
791 BIT(accepted); /* socket was accepted, not connected */
792 BIT(sock_connected); /* socket is "connected", e.g. in UDP */
793 BIT(active);
794 BIT(buffer_recv);
795};
796
797static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
798 const struct Curl_addrinfo *ai,
799 int transport)
800{
801 memset(ctx, 0, sizeof(*ctx));
802 ctx->sock = CURL_SOCKET_BAD;
803 ctx->transport = transport;
804 Curl_sock_assign_addr(&ctx->addr, ai, transport);
805 Curl_bufq_init(&ctx->recvbuf, NW_RECV_CHUNK_SIZE, NW_RECV_CHUNKS);
806#ifdef DEBUGBUILD
807 {
808 char *p = getenv("CURL_DBG_SOCK_WBLOCK");
809 if(p) {
810 long l = strtol(p, NULL, 10);
811 if(l >= 0 && l <= 100)
812 ctx->wblock_percent = (int)l;
813 }
814 p = getenv("CURL_DBG_SOCK_WPARTIAL");
815 if(p) {
816 long l = strtol(p, NULL, 10);
817 if(l >= 0 && l <= 100)
818 ctx->wpartial_percent = (int)l;
819 }
820 p = getenv("CURL_DBG_SOCK_RBLOCK");
821 if(p) {
822 long l = strtol(p, NULL, 10);
823 if(l >= 0 && l <= 100)
824 ctx->rblock_percent = (int)l;
825 }
826 p = getenv("CURL_DBG_SOCK_RMAX");
827 if(p) {
828 long l = strtol(p, NULL, 10);
829 if(l >= 0)
830 ctx->recv_max = (size_t)l;
831 }
832 }
833#endif
834}
835
836struct reader_ctx {
837 struct Curl_cfilter *cf;
838 struct Curl_easy *data;
839};
840
841static ssize_t nw_in_read(void *reader_ctx,
842 unsigned char *buf, size_t len,
843 CURLcode *err)
844{
845 struct reader_ctx *rctx = reader_ctx;
846 struct cf_socket_ctx *ctx = rctx->cf->ctx;
847 ssize_t nread;
848
849 *err = CURLE_OK;
850 nread = sread(ctx->sock, buf, len);
851
852 if(-1 == nread) {
853 int sockerr = SOCKERRNO;
854
855 if(
856#ifdef WSAEWOULDBLOCK
857 /* This is how Windows does it */
858 (WSAEWOULDBLOCK == sockerr)
859#else
860 /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
861 due to its inability to send off data without blocking. We therefore
862 treat both error codes the same here */
863 (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
864#endif
865 ) {
866 /* this is just a case of EWOULDBLOCK */
867 *err = CURLE_AGAIN;
868 nread = -1;
869 }
870 else {
871 char buffer[STRERROR_LEN];
872
873 failf(rctx->data, "Recv failure: %s",
874 Curl_strerror(sockerr, buffer, sizeof(buffer)));
875 rctx->data->state.os_errno = sockerr;
876 *err = CURLE_RECV_ERROR;
877 nread = -1;
878 }
879 }
880 CURL_TRC_CF(rctx->data, rctx->cf, "nw_in_read(len=%zu, fd=%"
881 CURL_FORMAT_SOCKET_T ") -> %d, err=%d",
882 len, ctx->sock, (int)nread, *err);
883 return nread;
884}
885
886static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
887{
888 struct cf_socket_ctx *ctx = cf->ctx;
889
890 if(ctx && CURL_SOCKET_BAD != ctx->sock) {
891 CURL_TRC_CF(data, cf, "cf_socket_close(%" CURL_FORMAT_SOCKET_T
892 ")", ctx->sock);
893 if(ctx->sock == cf->conn->sock[cf->sockindex])
894 cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
895 socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
896 ctx->sock = CURL_SOCKET_BAD;
897 if(ctx->active && cf->sockindex == FIRSTSOCKET)
898 cf->conn->remote_addr = NULL;
899 Curl_bufq_reset(&ctx->recvbuf);
900 ctx->active = FALSE;
901 ctx->buffer_recv = FALSE;
902 memset(&ctx->started_at, 0, sizeof(ctx->started_at));
903 memset(&ctx->connected_at, 0, sizeof(ctx->connected_at));
904 }
905
906 cf->connected = FALSE;
907}
908
909static void cf_socket_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
910{
911 struct cf_socket_ctx *ctx = cf->ctx;
912
913 cf_socket_close(cf, data);
914 CURL_TRC_CF(data, cf, "destroy");
915 Curl_bufq_free(&ctx->recvbuf);
916 free(ctx);
917 cf->ctx = NULL;
918}
919
920static CURLcode set_local_ip(struct Curl_cfilter *cf,
921 struct Curl_easy *data)
922{
923 struct cf_socket_ctx *ctx = cf->ctx;
924
925#ifdef HAVE_GETSOCKNAME
926 if(!(data->conn->handler->protocol & CURLPROTO_TFTP)) {
927 /* TFTP does not connect, so it cannot get the IP like this */
928
929 char buffer[STRERROR_LEN];
930 struct Curl_sockaddr_storage ssloc;
931 curl_socklen_t slen = sizeof(struct Curl_sockaddr_storage);
932
933 memset(&ssloc, 0, sizeof(ssloc));
934 if(getsockname(ctx->sock, (struct sockaddr*) &ssloc, &slen)) {
935 int error = SOCKERRNO;
936 failf(data, "getsockname() failed with errno %d: %s",
937 error, Curl_strerror(error, buffer, sizeof(buffer)));
938 return CURLE_FAILED_INIT;
939 }
940 if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
941 ctx->ip.local_ip, &ctx->ip.local_port)) {
942 failf(data, "ssloc inet_ntop() failed with errno %d: %s",
943 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
944 return CURLE_FAILED_INIT;
945 }
946 }
947#else
948 (void)data;
949 ctx->ip.local_ip[0] = 0;
950 ctx->ip.local_port = -1;
951#endif
952 return CURLE_OK;
953}
954
955static CURLcode set_remote_ip(struct Curl_cfilter *cf,
956 struct Curl_easy *data)
957{
958 struct cf_socket_ctx *ctx = cf->ctx;
959
960 /* store remote address and port used in this connection attempt */
961 if(!Curl_addr2string(&ctx->addr.sa_addr, ctx->addr.addrlen,
962 ctx->ip.remote_ip, &ctx->ip.remote_port)) {
963 char buffer[STRERROR_LEN];
964
965 ctx->error = errno;
966 /* malformed address or bug in inet_ntop, try next address */
967 failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
968 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
969 return CURLE_FAILED_INIT;
970 }
971 return CURLE_OK;
972}
973
974static CURLcode cf_socket_open(struct Curl_cfilter *cf,
975 struct Curl_easy *data)
976{
977 struct cf_socket_ctx *ctx = cf->ctx;
978 int error = 0;
979 bool isconnected = FALSE;
980 CURLcode result = CURLE_COULDNT_CONNECT;
981 bool is_tcp;
982
983 (void)data;
984 DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
985 ctx->started_at = Curl_now();
986 result = socket_open(data, &ctx->addr, &ctx->sock);
987 if(result)
988 goto out;
989
990 result = set_remote_ip(cf, data);
991 if(result)
992 goto out;
993
994#ifdef ENABLE_IPV6
995 if(ctx->addr.family == AF_INET6) {
996 set_ipv6_v6only(ctx->sock, 0);
997 infof(data, " Trying [%s]:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
998 }
999 else
1000#endif
1001 infof(data, " Trying %s:%d...", ctx->ip.remote_ip, ctx->ip.remote_port);
1002
1003#ifdef ENABLE_IPV6
1004 is_tcp = (ctx->addr.family == AF_INET
1005 || ctx->addr.family == AF_INET6) &&
1006 ctx->addr.socktype == SOCK_STREAM;
1007#else
1008 is_tcp = (ctx->addr.family == AF_INET) &&
1009 ctx->addr.socktype == SOCK_STREAM;
1010#endif
1011 if(is_tcp && data->set.tcp_nodelay)
1012 tcpnodelay(data, ctx->sock);
1013
1014 nosigpipe(data, ctx->sock);
1015
1016 Curl_sndbufset(ctx->sock);
1017
1018 if(is_tcp && data->set.tcp_keepalive)
1019 tcpkeepalive(data, ctx->sock);
1020
1021 if(data->set.fsockopt) {
1022 /* activate callback for setting socket options */
1023 Curl_set_in_callback(data, true);
1024 error = data->set.fsockopt(data->set.sockopt_client,
1025 ctx->sock,
1026 CURLSOCKTYPE_IPCXN);
1027 Curl_set_in_callback(data, false);
1028
1029 if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
1030 isconnected = TRUE;
1031 else if(error) {
1032 result = CURLE_ABORTED_BY_CALLBACK;
1033 goto out;
1034 }
1035 }
1036
1037#ifndef CURL_DISABLE_BINDLOCAL
1038 /* possibly bind the local end to an IP, interface or port */
1039 if(ctx->addr.family == AF_INET
1040#ifdef ENABLE_IPV6
1041 || ctx->addr.family == AF_INET6
1042#endif
1043 ) {
1044 result = bindlocal(data, cf->conn, ctx->sock, ctx->addr.family,
1045 Curl_ipv6_scope(&ctx->addr.sa_addr));
1046 if(result) {
1047 if(result == CURLE_UNSUPPORTED_PROTOCOL) {
1048 /* The address family is not supported on this interface.
1049 We can continue trying addresses */
1050 result = CURLE_COULDNT_CONNECT;
1051 }
1052 goto out;
1053 }
1054 }
1055#endif
1056
1057 /* set socket non-blocking */
1058 (void)curlx_nonblock(ctx->sock, TRUE);
1059 ctx->sock_connected = (ctx->addr.socktype != SOCK_DGRAM);
1060out:
1061 if(result) {
1062 if(ctx->sock != CURL_SOCKET_BAD) {
1063 socket_close(data, cf->conn, TRUE, ctx->sock);
1064 ctx->sock = CURL_SOCKET_BAD;
1065 }
1066 }
1067 else if(isconnected) {
1068 set_local_ip(cf, data);
1069 ctx->connected_at = Curl_now();
1070 cf->connected = TRUE;
1071 }
1072 CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" CURL_FORMAT_SOCKET_T,
1073 result, ctx->sock);
1074 return result;
1075}
1076
1077static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
1078 bool is_tcp_fastopen)
1079{
1080 struct cf_socket_ctx *ctx = cf->ctx;
1081#ifdef TCP_FASTOPEN_CONNECT
1082 int optval = 1;
1083#endif
1084 int rc = -1;
1085
1086 (void)data;
1087 if(is_tcp_fastopen) {
1088#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
1089# if defined(HAVE_BUILTIN_AVAILABLE)
1090 /* while connectx function is available since macOS 10.11 / iOS 9,
1091 it did not have the interface declared correctly until
1092 Xcode 9 / macOS SDK 10.13 */
1093 if(__builtin_available(macOS 10.11, iOS 9.0, tvOS 9.0, watchOS 2.0, *)) {
1094 sa_endpoints_t endpoints;
1095 endpoints.sae_srcif = 0;
1096 endpoints.sae_srcaddr = NULL;
1097 endpoints.sae_srcaddrlen = 0;
1098 endpoints.sae_dstaddr = &ctx->addr.sa_addr;
1099 endpoints.sae_dstaddrlen = ctx->addr.addrlen;
1100
1101 rc = connectx(ctx->sock, &endpoints, SAE_ASSOCID_ANY,
1102 CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT,
1103 NULL, 0, NULL, NULL);
1104 }
1105 else {
1106 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1107 }
1108# else
1109 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1110# endif /* HAVE_BUILTIN_AVAILABLE */
1111#elif defined(TCP_FASTOPEN_CONNECT) /* Linux >= 4.11 */
1112 if(setsockopt(ctx->sock, IPPROTO_TCP, TCP_FASTOPEN_CONNECT,
1113 (void *)&optval, sizeof(optval)) < 0)
1114 infof(data, "Failed to enable TCP Fast Open on fd %"
1115 CURL_FORMAT_SOCKET_T, ctx->sock);
1116
1117 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1118#elif defined(MSG_FASTOPEN) /* old Linux */
1119 if(cf->conn->given->flags & PROTOPT_SSL)
1120 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1121 else
1122 rc = 0; /* Do nothing */
1123#endif
1124 }
1125 else {
1126 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1127 }
1128 return rc;
1129}
1130
1131static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
1132 struct Curl_easy *data,
1133 bool blocking, bool *done)
1134{
1135 struct cf_socket_ctx *ctx = cf->ctx;
1136 CURLcode result = CURLE_COULDNT_CONNECT;
1137 int rc = 0;
1138
1139 (void)data;
1140 if(cf->connected) {
1141 *done = TRUE;
1142 return CURLE_OK;
1143 }
1144
1145 /* TODO: need to support blocking connect? */
1146 if(blocking)
1147 return CURLE_UNSUPPORTED_PROTOCOL;
1148
1149 *done = FALSE; /* a very negative world view is best */
1150 if(ctx->sock == CURL_SOCKET_BAD) {
1151 int error;
1152
1153 result = cf_socket_open(cf, data);
1154 if(result)
1155 goto out;
1156
1157 if(cf->connected) {
1158 *done = TRUE;
1159 return CURLE_OK;
1160 }
1161
1162 /* Connect TCP socket */
1163 rc = do_connect(cf, data, cf->conn->bits.tcp_fastopen);
1164 error = SOCKERRNO;
1165 set_local_ip(cf, data);
1166 CURL_TRC_CF(data, cf, "local address %s port %d...",
1167 ctx->ip.local_ip, ctx->ip.local_port);
1168 if(-1 == rc) {
1169 result = socket_connect_result(data, ctx->ip.remote_ip, error);
1170 goto out;
1171 }
1172 }
1173
1174#ifdef mpeix
1175 /* Call this function once now, and ignore the results. We do this to
1176 "clear" the error state on the socket so that we can later read it
1177 reliably. This is reported necessary on the MPE/iX operating
1178 system. */
1179 (void)verifyconnect(ctx->sock, NULL);
1180#endif
1181 /* check socket for connect */
1182 rc = SOCKET_WRITABLE(ctx->sock, 0);
1183
1184 if(rc == 0) { /* no connection yet */
1185 CURL_TRC_CF(data, cf, "not connected yet");
1186 return CURLE_OK;
1187 }
1188 else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
1189 if(verifyconnect(ctx->sock, &ctx->error)) {
1190 /* we are connected with TCP, awesome! */
1191 ctx->connected_at = Curl_now();
1192 set_local_ip(cf, data);
1193 *done = TRUE;
1194 cf->connected = TRUE;
1195 CURL_TRC_CF(data, cf, "connected");
1196 return CURLE_OK;
1197 }
1198 }
1199 else if(rc & CURL_CSELECT_ERR) {
1200 (void)verifyconnect(ctx->sock, &ctx->error);
1201 result = CURLE_COULDNT_CONNECT;
1202 }
1203
1204out:
1205 if(result) {
1206 if(ctx->error) {
1207 set_local_ip(cf, data);
1208 data->state.os_errno = ctx->error;
1209 SET_SOCKERRNO(ctx->error);
1210#ifndef CURL_DISABLE_VERBOSE_STRINGS
1211 {
1212 char buffer[STRERROR_LEN];
1213 infof(data, "connect to %s port %u from %s port %d failed: %s",
1214 ctx->ip.remote_ip, ctx->ip.remote_port,
1215 ctx->ip.local_ip, ctx->ip.local_port,
1216 Curl_strerror(ctx->error, buffer, sizeof(buffer)));
1217 }
1218#endif
1219 }
1220 if(ctx->sock != CURL_SOCKET_BAD) {
1221 socket_close(data, cf->conn, TRUE, ctx->sock);
1222 ctx->sock = CURL_SOCKET_BAD;
1223 }
1224 *done = FALSE;
1225 }
1226 return result;
1227}
1228
1229static void cf_socket_get_host(struct Curl_cfilter *cf,
1230 struct Curl_easy *data,
1231 const char **phost,
1232 const char **pdisplay_host,
1233 int *pport)
1234{
1235 struct cf_socket_ctx *ctx = cf->ctx;
1236 (void)data;
1237 *phost = cf->conn->host.name;
1238 *pdisplay_host = cf->conn->host.dispname;
1239 *pport = ctx->ip.remote_port;
1240}
1241
1242static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
1243 struct Curl_easy *data,
1244 struct easy_pollset *ps)
1245{
1246 struct cf_socket_ctx *ctx = cf->ctx;
1247
1248 if(ctx->sock != CURL_SOCKET_BAD) {
1249 if(!cf->connected) {
1250 Curl_pollset_set_out_only(data, ps, ctx->sock);
1251 CURL_TRC_CF(data, cf, "adjust_pollset, !connected, POLLOUT fd=%"
1252 CURL_FORMAT_SOCKET_T, ctx->sock);
1253 }
1254 else if(!ctx->active) {
1255 Curl_pollset_add_in(data, ps, ctx->sock);
1256 CURL_TRC_CF(data, cf, "adjust_pollset, !active, POLLIN fd=%"
1257 CURL_FORMAT_SOCKET_T, ctx->sock);
1258 }
1259 }
1260}
1261
1262static bool cf_socket_data_pending(struct Curl_cfilter *cf,
1263 const struct Curl_easy *data)
1264{
1265 struct cf_socket_ctx *ctx = cf->ctx;
1266 int readable;
1267
1268 (void)data;
1269 if(!Curl_bufq_is_empty(&ctx->recvbuf))
1270 return TRUE;
1271
1272 readable = SOCKET_READABLE(ctx->sock, 0);
1273 return (readable > 0 && (readable & CURL_CSELECT_IN));
1274}
1275
1276static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1277 const void *buf, size_t len, CURLcode *err)
1278{
1279 struct cf_socket_ctx *ctx = cf->ctx;
1280 curl_socket_t fdsave;
1281 ssize_t nwritten;
1282 size_t orig_len = len;
1283
1284 *err = CURLE_OK;
1285 fdsave = cf->conn->sock[cf->sockindex];
1286 cf->conn->sock[cf->sockindex] = ctx->sock;
1287
1288#ifdef DEBUGBUILD
1289 /* simulate network blocking/partial writes */
1290 if(ctx->wblock_percent > 0) {
1291 unsigned char c;
1292 Curl_rand(data, &c, 1);
1293 if(c >= ((100-ctx->wblock_percent)*256/100)) {
1294 CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE EWOULDBLOCK", orig_len);
1295 *err = CURLE_AGAIN;
1296 nwritten = -1;
1297 cf->conn->sock[cf->sockindex] = fdsave;
1298 return nwritten;
1299 }
1300 }
1301 if(cf->cft != &Curl_cft_udp && ctx->wpartial_percent > 0 && len > 8) {
1302 len = len * ctx->wpartial_percent / 100;
1303 if(!len)
1304 len = 1;
1305 CURL_TRC_CF(data, cf, "send(len=%zu) SIMULATE partial write of %zu bytes",
1306 orig_len, len);
1307 }
1308#endif
1309
1310#if defined(MSG_FASTOPEN) && !defined(TCP_FASTOPEN_CONNECT) /* Linux */
1311 if(cf->conn->bits.tcp_fastopen) {
1312 nwritten = sendto(ctx->sock, buf, len, MSG_FASTOPEN,
1313 &cf->conn->remote_addr->sa_addr,
1314 cf->conn->remote_addr->addrlen);
1315 cf->conn->bits.tcp_fastopen = FALSE;
1316 }
1317 else
1318#endif
1319 nwritten = swrite(ctx->sock, buf, len);
1320
1321 if(-1 == nwritten) {
1322 int sockerr = SOCKERRNO;
1323
1324 if(
1325#ifdef WSAEWOULDBLOCK
1326 /* This is how Windows does it */
1327 (WSAEWOULDBLOCK == sockerr)
1328#else
1329 /* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
1330 due to its inability to send off data without blocking. We therefore
1331 treat both error codes the same here */
1332 (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
1333 (EINPROGRESS == sockerr)
1334#endif
1335 ) {
1336 /* this is just a case of EWOULDBLOCK */
1337 *err = CURLE_AGAIN;
1338 }
1339 else {
1340 char buffer[STRERROR_LEN];
1341 failf(data, "Send failure: %s",
1342 Curl_strerror(sockerr, buffer, sizeof(buffer)));
1343 data->state.os_errno = sockerr;
1344 *err = CURLE_SEND_ERROR;
1345 }
1346 }
1347
1348 CURL_TRC_CF(data, cf, "send(len=%zu) -> %d, err=%d",
1349 orig_len, (int)nwritten, *err);
1350 cf->conn->sock[cf->sockindex] = fdsave;
1351 return nwritten;
1352}
1353
1354static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1355 char *buf, size_t len, CURLcode *err)
1356{
1357 struct cf_socket_ctx *ctx = cf->ctx;
1358 curl_socket_t fdsave;
1359 ssize_t nread;
1360
1361 *err = CURLE_OK;
1362
1363 fdsave = cf->conn->sock[cf->sockindex];
1364 cf->conn->sock[cf->sockindex] = ctx->sock;
1365
1366#ifdef DEBUGBUILD
1367 /* simulate network blocking/partial reads */
1368 if(cf->cft != &Curl_cft_udp && ctx->rblock_percent > 0) {
1369 unsigned char c;
1370 Curl_rand(data, &c, 1);
1371 if(c >= ((100-ctx->rblock_percent)*256/100)) {
1372 CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE EWOULDBLOCK", len);
1373 *err = CURLE_AGAIN;
1374 nread = -1;
1375 cf->conn->sock[cf->sockindex] = fdsave;
1376 return nread;
1377 }
1378 }
1379 if(cf->cft != &Curl_cft_udp && ctx->recv_max && ctx->recv_max < len) {
1380 size_t orig_len = len;
1381 len = ctx->recv_max;
1382 CURL_TRC_CF(data, cf, "recv(len=%zu) SIMULATE max read of %zu bytes",
1383 orig_len, len);
1384 }
1385#endif
1386
1387 if(ctx->buffer_recv && !Curl_bufq_is_empty(&ctx->recvbuf)) {
1388 CURL_TRC_CF(data, cf, "recv from buffer");
1389 nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1390 }
1391 else {
1392 struct reader_ctx rctx;
1393
1394 rctx.cf = cf;
1395 rctx.data = data;
1396
1397 /* "small" reads may trigger filling our buffer, "large" reads
1398 * are probably not worth the additional copy */
1399 if(ctx->buffer_recv && len < NW_SMALL_READS) {
1400 ssize_t nwritten;
1401 nwritten = Curl_bufq_slurp(&ctx->recvbuf, nw_in_read, &rctx, err);
1402 if(nwritten < 0 && !Curl_bufq_is_empty(&ctx->recvbuf)) {
1403 /* we have a partial read with an error. need to deliver
1404 * what we got, return the error later. */
1405 CURL_TRC_CF(data, cf, "partial read: empty buffer first");
1406 nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1407 }
1408 else if(nwritten < 0) {
1409 nread = -1;
1410 goto out;
1411 }
1412 else if(nwritten == 0) {
1413 /* eof */
1414 *err = CURLE_OK;
1415 nread = 0;
1416 }
1417 else {
1418 CURL_TRC_CF(data, cf, "buffered %zd additional bytes", nwritten);
1419 nread = Curl_bufq_read(&ctx->recvbuf, (unsigned char *)buf, len, err);
1420 }
1421 }
1422 else {
1423 nread = nw_in_read(&rctx, (unsigned char *)buf, len, err);
1424 }
1425 }
1426
1427out:
1428 CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
1429 *err);
1430 if(nread > 0 && !ctx->got_first_byte) {
1431 ctx->first_byte_at = Curl_now();
1432 ctx->got_first_byte = TRUE;
1433 }
1434 cf->conn->sock[cf->sockindex] = fdsave;
1435 return nread;
1436}
1437
1438static void cf_socket_active(struct Curl_cfilter *cf, struct Curl_easy *data)
1439{
1440 struct cf_socket_ctx *ctx = cf->ctx;
1441
1442 /* use this socket from now on */
1443 cf->conn->sock[cf->sockindex] = ctx->sock;
1444 set_local_ip(cf, data);
1445 if(cf->sockindex == SECONDARYSOCKET)
1446 cf->conn->secondary = ctx->ip;
1447 else
1448 cf->conn->primary = ctx->ip;
1449 /* the first socket info gets some specials */
1450 if(cf->sockindex == FIRSTSOCKET) {
1451 cf->conn->remote_addr = &ctx->addr;
1452 #ifdef ENABLE_IPV6
1453 cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
1454 #endif
1455 Curl_persistconninfo(data, cf->conn, &ctx->ip);
1456 /* buffering is currently disabled by default because we have stalls
1457 * in parallel transfers where not all buffered data is consumed and no
1458 * socket events happen.
1459 */
1460 ctx->buffer_recv = FALSE;
1461 }
1462 ctx->active = TRUE;
1463}
1464
1465static CURLcode cf_socket_cntrl(struct Curl_cfilter *cf,
1466 struct Curl_easy *data,
1467 int event, int arg1, void *arg2)
1468{
1469 struct cf_socket_ctx *ctx = cf->ctx;
1470
1471 (void)arg1;
1472 (void)arg2;
1473 switch(event) {
1474 case CF_CTRL_CONN_INFO_UPDATE:
1475 cf_socket_active(cf, data);
1476 break;
1477 case CF_CTRL_DATA_SETUP:
1478 Curl_persistconninfo(data, cf->conn, &ctx->ip);
1479 break;
1480 case CF_CTRL_FORGET_SOCKET:
1481 ctx->sock = CURL_SOCKET_BAD;
1482 break;
1483 }
1484 return CURLE_OK;
1485}
1486
1487static bool cf_socket_conn_is_alive(struct Curl_cfilter *cf,
1488 struct Curl_easy *data,
1489 bool *input_pending)
1490{
1491 struct cf_socket_ctx *ctx = cf->ctx;
1492 struct pollfd pfd[1];
1493 int r;
1494
1495 *input_pending = FALSE;
1496 (void)data;
1497 if(!ctx || ctx->sock == CURL_SOCKET_BAD)
1498 return FALSE;
1499
1500 /* Check with 0 timeout if there are any events pending on the socket */
1501 pfd[0].fd = ctx->sock;
1502 pfd[0].events = POLLRDNORM|POLLIN|POLLRDBAND|POLLPRI;
1503 pfd[0].revents = 0;
1504
1505 r = Curl_poll(pfd, 1, 0);
1506 if(r < 0) {
1507 CURL_TRC_CF(data, cf, "is_alive: poll error, assume dead");
1508 return FALSE;
1509 }
1510 else if(r == 0) {
1511 CURL_TRC_CF(data, cf, "is_alive: poll timeout, assume alive");
1512 return TRUE;
1513 }
1514 else if(pfd[0].revents & (POLLERR|POLLHUP|POLLPRI|POLLNVAL)) {
1515 CURL_TRC_CF(data, cf, "is_alive: err/hup/etc events, assume dead");
1516 return FALSE;
1517 }
1518
1519 CURL_TRC_CF(data, cf, "is_alive: valid events, looks alive");
1520 *input_pending = TRUE;
1521 return TRUE;
1522}
1523
1524static CURLcode cf_socket_query(struct Curl_cfilter *cf,
1525 struct Curl_easy *data,
1526 int query, int *pres1, void *pres2)
1527{
1528 struct cf_socket_ctx *ctx = cf->ctx;
1529
1530 switch(query) {
1531 case CF_QUERY_SOCKET:
1532 DEBUGASSERT(pres2);
1533 *((curl_socket_t *)pres2) = ctx->sock;
1534 return CURLE_OK;
1535 case CF_QUERY_CONNECT_REPLY_MS:
1536 if(ctx->got_first_byte) {
1537 timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
1538 *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
1539 }
1540 else
1541 *pres1 = -1;
1542 return CURLE_OK;
1543 case CF_QUERY_TIMER_CONNECT: {
1544 struct curltime *when = pres2;
1545 switch(ctx->transport) {
1546 case TRNSPRT_UDP:
1547 case TRNSPRT_QUIC:
1548 /* Since UDP connected sockets work different from TCP, we use the
1549 * time of the first byte from the peer as the "connect" time. */
1550 if(ctx->got_first_byte) {
1551 *when = ctx->first_byte_at;
1552 break;
1553 }
1554 FALLTHROUGH();
1555 default:
1556 *when = ctx->connected_at;
1557 break;
1558 }
1559 return CURLE_OK;
1560 }
1561 default:
1562 break;
1563 }
1564 return cf->next?
1565 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1566 CURLE_UNKNOWN_OPTION;
1567}
1568
1569struct Curl_cftype Curl_cft_tcp = {
1570 "TCP",
1571 CF_TYPE_IP_CONNECT,
1572 CURL_LOG_LVL_NONE,
1573 cf_socket_destroy,
1574 cf_tcp_connect,
1575 cf_socket_close,
1576 cf_socket_get_host,
1577 cf_socket_adjust_pollset,
1578 cf_socket_data_pending,
1579 cf_socket_send,
1580 cf_socket_recv,
1581 cf_socket_cntrl,
1582 cf_socket_conn_is_alive,
1583 Curl_cf_def_conn_keep_alive,
1584 cf_socket_query,
1585};
1586
1587CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
1588 struct Curl_easy *data,
1589 struct connectdata *conn,
1590 const struct Curl_addrinfo *ai,
1591 int transport)
1592{
1593 struct cf_socket_ctx *ctx = NULL;
1594 struct Curl_cfilter *cf = NULL;
1595 CURLcode result;
1596
1597 (void)data;
1598 (void)conn;
1599 DEBUGASSERT(transport == TRNSPRT_TCP);
1600 ctx = calloc(1, sizeof(*ctx));
1601 if(!ctx) {
1602 result = CURLE_OUT_OF_MEMORY;
1603 goto out;
1604 }
1605 cf_socket_ctx_init(ctx, ai, transport);
1606
1607 result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
1608
1609out:
1610 *pcf = (!result)? cf : NULL;
1611 if(result) {
1612 Curl_safefree(cf);
1613 Curl_safefree(ctx);
1614 }
1615
1616 return result;
1617}
1618
1619static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
1620 struct Curl_easy *data)
1621{
1622 struct cf_socket_ctx *ctx = cf->ctx;
1623 int rc;
1624
1625 /* QUIC needs a connected socket, nonblocking */
1626 DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
1627
1628#if defined(__APPLE__) && defined(USE_OPENSSL_QUIC)
1629 (void)rc;
1630 /* On macOS OpenSSL QUIC fails on connected sockets.
1631 * see: <https://github.com/openssl/openssl/issues/23251> */
1632#else
1633 rc = connect(ctx->sock, &ctx->addr.sa_addr, ctx->addr.addrlen);
1634 if(-1 == rc) {
1635 return socket_connect_result(data, ctx->ip.remote_ip, SOCKERRNO);
1636 }
1637 ctx->sock_connected = TRUE;
1638#endif
1639 set_local_ip(cf, data);
1640 CURL_TRC_CF(data, cf, "%s socket %" CURL_FORMAT_SOCKET_T
1641 " connected: [%s:%d] -> [%s:%d]",
1642 (ctx->transport == TRNSPRT_QUIC)? "QUIC" : "UDP",
1643 ctx->sock, ctx->ip.local_ip, ctx->ip.local_port,
1644 ctx->ip.remote_ip, ctx->ip.remote_port);
1645
1646 (void)curlx_nonblock(ctx->sock, TRUE);
1647 switch(ctx->addr.family) {
1648#if defined(__linux__) && defined(IP_MTU_DISCOVER)
1649 case AF_INET: {
1650 int val = IP_PMTUDISC_DO;
1651 (void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
1652 sizeof(val));
1653 break;
1654 }
1655#endif
1656#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
1657 case AF_INET6: {
1658 int val = IPV6_PMTUDISC_DO;
1659 (void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
1660 sizeof(val));
1661 break;
1662 }
1663#endif
1664 }
1665 return CURLE_OK;
1666}
1667
1668static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
1669 struct Curl_easy *data,
1670 bool blocking, bool *done)
1671{
1672 struct cf_socket_ctx *ctx = cf->ctx;
1673 CURLcode result = CURLE_COULDNT_CONNECT;
1674
1675 (void)blocking;
1676 if(cf->connected) {
1677 *done = TRUE;
1678 return CURLE_OK;
1679 }
1680 *done = FALSE;
1681 if(ctx->sock == CURL_SOCKET_BAD) {
1682 result = cf_socket_open(cf, data);
1683 if(result) {
1684 CURL_TRC_CF(data, cf, "cf_udp_connect(), open failed -> %d", result);
1685 goto out;
1686 }
1687
1688 if(ctx->transport == TRNSPRT_QUIC) {
1689 result = cf_udp_setup_quic(cf, data);
1690 if(result)
1691 goto out;
1692 CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1693 CURL_FORMAT_SOCKET_T " (%s:%d)",
1694 ctx->sock, ctx->ip.local_ip, ctx->ip.local_port);
1695 }
1696 else {
1697 CURL_TRC_CF(data, cf, "cf_udp_connect(), opened socket=%"
1698 CURL_FORMAT_SOCKET_T " (unconnected)", ctx->sock);
1699 }
1700 *done = TRUE;
1701 cf->connected = TRUE;
1702 }
1703out:
1704 return result;
1705}
1706
1707struct Curl_cftype Curl_cft_udp = {
1708 "UDP",
1709 CF_TYPE_IP_CONNECT,
1710 CURL_LOG_LVL_NONE,
1711 cf_socket_destroy,
1712 cf_udp_connect,
1713 cf_socket_close,
1714 cf_socket_get_host,
1715 cf_socket_adjust_pollset,
1716 cf_socket_data_pending,
1717 cf_socket_send,
1718 cf_socket_recv,
1719 cf_socket_cntrl,
1720 cf_socket_conn_is_alive,
1721 Curl_cf_def_conn_keep_alive,
1722 cf_socket_query,
1723};
1724
1725CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
1726 struct Curl_easy *data,
1727 struct connectdata *conn,
1728 const struct Curl_addrinfo *ai,
1729 int transport)
1730{
1731 struct cf_socket_ctx *ctx = NULL;
1732 struct Curl_cfilter *cf = NULL;
1733 CURLcode result;
1734
1735 (void)data;
1736 (void)conn;
1737 DEBUGASSERT(transport == TRNSPRT_UDP || transport == TRNSPRT_QUIC);
1738 ctx = calloc(1, sizeof(*ctx));
1739 if(!ctx) {
1740 result = CURLE_OUT_OF_MEMORY;
1741 goto out;
1742 }
1743 cf_socket_ctx_init(ctx, ai, transport);
1744
1745 result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
1746
1747out:
1748 *pcf = (!result)? cf : NULL;
1749 if(result) {
1750 Curl_safefree(cf);
1751 Curl_safefree(ctx);
1752 }
1753
1754 return result;
1755}
1756
1757/* this is the TCP filter which can also handle this case */
1758struct Curl_cftype Curl_cft_unix = {
1759 "UNIX",
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_get_host,
1766 cf_socket_adjust_pollset,
1767 cf_socket_data_pending,
1768 cf_socket_send,
1769 cf_socket_recv,
1770 cf_socket_cntrl,
1771 cf_socket_conn_is_alive,
1772 Curl_cf_def_conn_keep_alive,
1773 cf_socket_query,
1774};
1775
1776CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
1777 struct Curl_easy *data,
1778 struct connectdata *conn,
1779 const struct Curl_addrinfo *ai,
1780 int transport)
1781{
1782 struct cf_socket_ctx *ctx = NULL;
1783 struct Curl_cfilter *cf = NULL;
1784 CURLcode result;
1785
1786 (void)data;
1787 (void)conn;
1788 DEBUGASSERT(transport == TRNSPRT_UNIX);
1789 ctx = calloc(1, sizeof(*ctx));
1790 if(!ctx) {
1791 result = CURLE_OUT_OF_MEMORY;
1792 goto out;
1793 }
1794 cf_socket_ctx_init(ctx, ai, transport);
1795
1796 result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
1797
1798out:
1799 *pcf = (!result)? cf : NULL;
1800 if(result) {
1801 Curl_safefree(cf);
1802 Curl_safefree(ctx);
1803 }
1804
1805 return result;
1806}
1807
1808static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
1809 struct Curl_easy *data,
1810 bool blocking, bool *done)
1811{
1812 /* we start accepted, if we ever close, we cannot go on */
1813 (void)data;
1814 (void)blocking;
1815 if(cf->connected) {
1816 *done = TRUE;
1817 return CURLE_OK;
1818 }
1819 return CURLE_FAILED_INIT;
1820}
1821
1822struct Curl_cftype Curl_cft_tcp_accept = {
1823 "TCP-ACCEPT",
1824 CF_TYPE_IP_CONNECT,
1825 CURL_LOG_LVL_NONE,
1826 cf_socket_destroy,
1827 cf_tcp_accept_connect,
1828 cf_socket_close,
1829 cf_socket_get_host, /* TODO: not accurate */
1830 cf_socket_adjust_pollset,
1831 cf_socket_data_pending,
1832 cf_socket_send,
1833 cf_socket_recv,
1834 cf_socket_cntrl,
1835 cf_socket_conn_is_alive,
1836 Curl_cf_def_conn_keep_alive,
1837 cf_socket_query,
1838};
1839
1840CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
1841 struct connectdata *conn,
1842 int sockindex, curl_socket_t *s)
1843{
1844 CURLcode result;
1845 struct Curl_cfilter *cf = NULL;
1846 struct cf_socket_ctx *ctx = NULL;
1847
1848 /* replace any existing */
1849 Curl_conn_cf_discard_all(data, conn, sockindex);
1850 DEBUGASSERT(conn->sock[sockindex] == CURL_SOCKET_BAD);
1851
1852 ctx = calloc(1, sizeof(*ctx));
1853 if(!ctx) {
1854 result = CURLE_OUT_OF_MEMORY;
1855 goto out;
1856 }
1857 ctx->transport = conn->transport;
1858 ctx->sock = *s;
1859 ctx->accepted = FALSE;
1860 result = Curl_cf_create(&cf, &Curl_cft_tcp_accept, ctx);
1861 if(result)
1862 goto out;
1863 Curl_conn_cf_add(data, conn, sockindex, cf);
1864
1865 conn->sock[sockindex] = ctx->sock;
1866 set_local_ip(cf, data);
1867 ctx->active = TRUE;
1868 ctx->connected_at = Curl_now();
1869 cf->connected = TRUE;
1870 CURL_TRC_CF(data, cf, "Curl_conn_tcp_listen_set(%"
1871 CURL_FORMAT_SOCKET_T ")", ctx->sock);
1872
1873out:
1874 if(result) {
1875 Curl_safefree(cf);
1876 Curl_safefree(ctx);
1877 }
1878 return result;
1879}
1880
1881static void set_accepted_remote_ip(struct Curl_cfilter *cf,
1882 struct Curl_easy *data)
1883{
1884 struct cf_socket_ctx *ctx = cf->ctx;
1885#ifdef HAVE_GETPEERNAME
1886 char buffer[STRERROR_LEN];
1887 struct Curl_sockaddr_storage ssrem;
1888 curl_socklen_t plen;
1889
1890 ctx->ip.remote_ip[0] = 0;
1891 ctx->ip.remote_port = 0;
1892 plen = sizeof(ssrem);
1893 memset(&ssrem, 0, plen);
1894 if(getpeername(ctx->sock, (struct sockaddr*) &ssrem, &plen)) {
1895 int error = SOCKERRNO;
1896 failf(data, "getpeername() failed with errno %d: %s",
1897 error, Curl_strerror(error, buffer, sizeof(buffer)));
1898 return;
1899 }
1900 if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
1901 ctx->ip.remote_ip, &ctx->ip.remote_port)) {
1902 failf(data, "ssrem inet_ntop() failed with errno %d: %s",
1903 errno, Curl_strerror(errno, buffer, sizeof(buffer)));
1904 return;
1905 }
1906#else
1907 ctx->ip.remote_ip[0] = 0;
1908 ctx->ip.remote_port = 0;
1909 (void)data;
1910#endif
1911}
1912
1913CURLcode Curl_conn_tcp_accepted_set(struct Curl_easy *data,
1914 struct connectdata *conn,
1915 int sockindex, curl_socket_t *s)
1916{
1917 struct Curl_cfilter *cf = NULL;
1918 struct cf_socket_ctx *ctx = NULL;
1919
1920 cf = conn->cfilter[sockindex];
1921 if(!cf || cf->cft != &Curl_cft_tcp_accept)
1922 return CURLE_FAILED_INIT;
1923
1924 ctx = cf->ctx;
1925 /* discard the listen socket */
1926 socket_close(data, conn, TRUE, ctx->sock);
1927 ctx->sock = *s;
1928 conn->sock[sockindex] = ctx->sock;
1929 set_accepted_remote_ip(cf, data);
1930 set_local_ip(cf, data);
1931 ctx->active = TRUE;
1932 ctx->accepted = TRUE;
1933 ctx->connected_at = Curl_now();
1934 cf->connected = TRUE;
1935 CURL_TRC_CF(data, cf, "accepted_set(sock=%" CURL_FORMAT_SOCKET_T
1936 ", remote=%s port=%d)",
1937 ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port);
1938
1939 return CURLE_OK;
1940}
1941
1942/**
1943 * Return TRUE iff `cf` is a socket filter.
1944 */
1945static bool cf_is_socket(struct Curl_cfilter *cf)
1946{
1947 return cf && (cf->cft == &Curl_cft_tcp ||
1948 cf->cft == &Curl_cft_udp ||
1949 cf->cft == &Curl_cft_unix ||
1950 cf->cft == &Curl_cft_tcp_accept);
1951}
1952
1953CURLcode Curl_cf_socket_peek(struct Curl_cfilter *cf,
1954 struct Curl_easy *data,
1955 curl_socket_t *psock,
1956 const struct Curl_sockaddr_ex **paddr,
1957 struct ip_quadruple *pip)
1958{
1959 (void)data;
1960 if(cf_is_socket(cf) && cf->ctx) {
1961 struct cf_socket_ctx *ctx = cf->ctx;
1962
1963 if(psock)
1964 *psock = ctx->sock;
1965 if(paddr)
1966 *paddr = &ctx->addr;
1967 if(pip)
1968 *pip = ctx->ip;
1969 return CURLE_OK;
1970 }
1971 return CURLE_FAILED_INIT;
1972}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette