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