VirtualBox

source: vbox/trunk/src/libs/curl-8.3.0/lib/cf-socket.c@ 101259

Last change on this file since 101259 was 101127, checked in by vboxsync, 19 months ago

curl-8.3.0: Applied and adjusted our curl changes to 8.0.1. bugref:10526

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

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