VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/cf-socket.c@ 99459

Last change on this file since 99459 was 99459, checked in by vboxsync, 20 months ago

setting missed svn:sync-process property on curl-8.0.1 files

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