1 /*
2 * Copyright (c) 2012 Tim Ruehsen
3 * Copyright (c) 2015-2021 Free Software Foundation, Inc.
4 *
5 * This file is part of libwget.
6 *
7 * Libwget is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Libwget is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with libwget. If not, see <https://www.gnu.org/licenses/>.
19 *
20 *
21 * network routines
22 *
23 * Changelog
24 * 25.04.2012 Tim Ruehsen created
25 * 16.11.2012 new functions tcp_set_family() and tcp_set_preferred_family()
26 *
27 * RFC 7413: TCP Fast Open
28 */
29
30 #include <config.h>
31
32 #include <sys/types.h>
33 #include <stddef.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <stdarg.h>
38 #include <c-ctype.h>
39 #include <time.h>
40 #include <errno.h>
41 #include <sys/socket.h>
42 #include <netdb.h>
43 #include <netinet/in.h>
44
45 #ifdef HAVE_NETINET_TCP_H
46 # include <netinet/tcp.h>
47 #endif
48
49 #if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
50 # include <sys/ioctl.h>
51 #else
52 # include <fcntl.h>
53 #endif
54
55 #if defined __APPLE__ && defined __MACH__ && defined CONNECT_DATA_IDEMPOTENT && defined CONNECT_RESUME_ON_READ_WRITE
56 # define TCP_FASTOPEN_OSX
57 #elif defined TCP_FASTOPEN_CONNECT // since Linux 4.11
58 # define TCP_FASTOPEN_LINUX_411
59 #elif defined TCP_FASTOPEN && defined MSG_FASTOPEN
60 # define TCP_FASTOPEN_LINUX
61 #endif
62
63 #include <wget.h>
64 #include "private.h"
65 #include "net.h"
66
67 /**
68 * \file
69 * \brief Functions to work with TCP sockets and SSL/TLS
70 * \defgroup libwget-net TCP sockets
71 *
72 * @{
73 *
74 * TCP sockets and DNS cache management functions.
75 *
76 * The following features are supported:
77 *
78 * - TCP Fast Open ([RFC 7413](https://tools.ietf.org/html/rfc7413))
79 * - SSL/TLS
80 *
81 * Most functions here take a `wget_tcp` structure as argument.
82 *
83 * The `wget_tcp` structure represents a TCP connection. You create it with wget_tcp_init()
84 * and destroy it with wget_tcp_deinit(). You can connect to a remote host with wget_tcp_connect(),
85 * or listen for incoming connections (and accept them) with wget_tcp_listen() and wget_tcp_accept().
86 * You end a connection with wget_tcp_close().
87 *
88 * There are several knobs you can use to customize the behavior of most functions here.
89 * The list that follows describes the most important parameters, although you can look at the getter and setter
90 * functions here to see them all (`wget_tcp_get_xxx`, `wget_tcp_set_xxx`).
91 *
92 * - Timeout: maximum time to wait for an operation to complete. For example, for wget_tcp_read(), it sets the maximum time
93 * to wait until some data is available to read. Most functions here can be non-blocking (with timeout = 0) returning immediately
94 * or they can block indefinitely until something happens (with timeout = -1). For any value greater than zero,
95 * the timeout is taken as milliseconds.
96 * - Family and preferred family: these are used to determine which address family should be used when resolving a host name or
97 * IP address. You probably use `AF_INET` or `AF_INET6` most of the time. The first one forces the library to use that family,
98 * failing if it cannot find any IP address with it. The second one is just a hint, about which family you would prefer; it will try
99 * to get an address of that family if possible, and will get another one if not.
100 * - SSL/TLS: do you want to use TLS?
101 *
102 * When you create a new `wget_tcp` with wget_tcp_init(), it is initialized with the following parameters:
103 *
104 * - Timeout: -1
105 * - Connection timeout (max. time to wait for a connection to be accepted by the remote host): -1
106 * - DNS timeout (max. time to wait for a DNS query to return): -1
107 * - Family: `AF_UNSPEC` (basically means "I don't care, pick the first one available").
108 */
109
110 static struct wget_tcp_st global_tcp = {
111 .sockfd = -1,
112 .dns_timeout = -1,
113 .connect_timeout = -1,
114 .timeout = -1,
115 .family = AF_UNSPEC,
116 #if defined TCP_FASTOPEN_OSX
117 .tcp_fastopen = 1,
118 #elif defined TCP_FASTOPEN_LINUX_411
119 .tcp_fastopen = 1,
120 #elif defined TCP_FASTOPEN_LINUX
121 .tcp_fastopen = 1,
122 .first_send = 1,
123 #endif
124 };
125
126 /* for Windows compatibility */
127 #include "sockets.h"
128 /**
129 * \return 0 for success, else failure
130 *
131 * Initialize the resources needed for network operations.
132 */
wget_net_init(void)133 int wget_net_init(void)
134 {
135 int rc = gl_sockets_startup(SOCKETS_2_2);
136
137 return rc ? -1 : 0;
138 }
139
140 /**
141 * \return 0 for success, else failure
142 *
143 * Free the resources allocated by wget_net_init().
144 */
wget_net_deinit(void)145 int wget_net_deinit(void)
146 {
147 int rc = gl_sockets_cleanup();
148
149 return rc ? -1 : 0;
150 }
151
value_to_family(int value)152 static int WGET_GCC_CONST value_to_family(int value)
153 {
154 switch (value) {
155 case WGET_NET_FAMILY_IPV4:
156 return AF_INET;
157 case WGET_NET_FAMILY_IPV6:
158 return AF_INET6;
159 default:
160 return AF_UNSPEC;
161 }
162 }
163
family_to_value(int family)164 static int WGET_GCC_CONST family_to_value(int family)
165 {
166 switch (family) {
167 case AF_INET:
168 return WGET_NET_FAMILY_IPV4;
169 case AF_INET6:
170 return WGET_NET_FAMILY_IPV6;
171 default:
172 return WGET_NET_FAMILY_ANY;
173 }
174 }
175
176 /**
177 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init().
178 * \param[in] protocol The protocol, either WGET_PROTOCOL_HTTP_2_0 or WGET_PROTOCOL_HTTP_1_1.
179 *
180 * Set the protocol for the connection provided, or globally.
181 *
182 * If \p tcp is NULL, theprotocol will be set globally (for all connections). Otherwise,
183 * only for the provided connection (\p tcp).
184 */
wget_tcp_set_dns(wget_tcp * tcp,wget_dns * dns)185 void wget_tcp_set_dns(wget_tcp *tcp, wget_dns *dns)
186 {
187 (tcp ? tcp : &global_tcp)->dns = dns;
188 }
189
190 /**
191 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
192 * \param[in] tcp_fastopen 1 or 0, whether to enable or disable TCP Fast Open.
193 *
194 * Enable or disable TCP Fast Open ([RFC 7413](https://tools.ietf.org/html/rfc7413)), if available.
195 *
196 * This function is a no-op on systems where TCP Fast Open is not supported.
197 *
198 * If \p tcp is NULL, TCP Fast Open is enabled or disabled globally.
199 */
wget_tcp_set_tcp_fastopen(wget_tcp * tcp,bool tcp_fastopen)200 void wget_tcp_set_tcp_fastopen(wget_tcp *tcp, bool tcp_fastopen)
201 {
202 #if defined TCP_FASTOPEN_OSX || defined TCP_FASTOPEN_LINUX || defined TCP_FASTOPEN_LINUX_411
203 (tcp ? tcp : &global_tcp)->tcp_fastopen = tcp_fastopen;
204 #else
205 (void) tcp; (void) tcp_fastopen;
206 #endif
207 }
208
209 /**
210 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
211 * \return 1 if TCP Fast Open is enabled, 0 otherwise.
212 *
213 * Tells whether TCP Fast Open is enabled or not.
214 *
215 * You can enable and disable it with wget_tcp_set_tcp_fastopen().
216 */
wget_tcp_get_tcp_fastopen(wget_tcp * tcp)217 bool wget_tcp_get_tcp_fastopen(wget_tcp *tcp)
218 {
219 return (tcp ? tcp : &global_tcp)->tcp_fastopen;
220 }
221
222 /**
223 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
224 * \param[in] false_start 1 or 0, whether to enable or disable TLS False Start.
225 *
226 * Enable or disable TLS False Start ([RFC 7918](https://tools.ietf.org/html/rfc7413)).
227 *
228 * If \p tcp is NULL, TLS False Start is enabled or disabled globally.
229 */
wget_tcp_set_tls_false_start(wget_tcp * tcp,bool false_start)230 void wget_tcp_set_tls_false_start(wget_tcp *tcp, bool false_start)
231 {
232 (tcp ? tcp : &global_tcp)->tls_false_start = false_start;
233 }
234
235 /**
236 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
237 * \return 1 if TLS False Start is enabled, 0 otherwise.
238 *
239 * Tells whether TLS False Start is enabled or not.
240 *
241 * You can enable and disable it with wget_tcp_set_tls_false_start().
242 */
wget_tcp_get_tls_false_start(wget_tcp * tcp)243 bool wget_tcp_get_tls_false_start(wget_tcp *tcp)
244 {
245 return (tcp ? tcp : &global_tcp)->tls_false_start;
246 }
247
248 /**
249 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init().
250 * \param[in] protocol The protocol, either WGET_PROTOCOL_HTTP_2_0 or WGET_PROTOCOL_HTTP_1_1.
251 *
252 * Set the protocol for the connection provided, or globally.
253 *
254 * If \p tcp is NULL, theprotocol will be set globally (for all connections). Otherwise,
255 * only for the provided connection (\p tcp).
256 */
wget_tcp_set_protocol(wget_tcp * tcp,int protocol)257 void wget_tcp_set_protocol(wget_tcp *tcp, int protocol)
258 {
259 (tcp ? tcp : &global_tcp)->protocol = protocol;
260 }
261
262 /**
263 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init().
264 * \return The protocol with this connection, currently WGET_PROTOCOL_HTTP_2_0 or WGET_PROTOCOL_HTTP_1_1.
265 *
266 * Get protocol used with the provided connection, or globally (if \p tcp is NULL).
267 */
wget_tcp_get_protocol(wget_tcp * tcp)268 int wget_tcp_get_protocol(wget_tcp *tcp)
269 {
270 return (tcp ? tcp : &global_tcp)->protocol;
271 }
272
273 /**
274 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
275 * \param[in] family One of the socket families defined in `<socket.h>`, such as `AF_INET` or `AF_INET6`.
276 *
277 * Tells the preferred address family that should be used when establishing a TCP connection.
278 *
279 * wget_tcp_resolve() will favor that and pick an address of that family if possible.
280 *
281 * If \p tcp is NULL, the preferred address family will be set globally.
282 */
wget_tcp_set_preferred_family(wget_tcp * tcp,int family)283 void wget_tcp_set_preferred_family(wget_tcp *tcp, int family)
284 {
285 (tcp ? tcp : &global_tcp)->preferred_family = value_to_family(family);
286 }
287
288 /**
289 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
290 * \return One of the socket families defined in `<socket.h>`, such as `AF_INET` or `AF_INET6`.
291 *
292 * Get the preferred address family that was previously set with wget_tcp_set_preferred_family().
293 */
wget_tcp_get_preferred_family(wget_tcp * tcp)294 int wget_tcp_get_preferred_family(wget_tcp *tcp)
295 {
296 return family_to_value((tcp ? tcp : &global_tcp)->preferred_family);
297 }
298
299 /**
300 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
301 * \param[in] family One of the socket families defined in `<socket.h>`, such as `AF_INET` or `AF_INET6`.
302 *
303 * Tell the address family that will be used when establishing a TCP connection.
304 *
305 * wget_tcp_resolve() will pick an address of that family, or fail if it cannot find one.
306 *
307 * If \p tcp is NULL, the address family will be set globally.
308 */
wget_tcp_set_family(wget_tcp * tcp,int family)309 void wget_tcp_set_family(wget_tcp *tcp, int family)
310 {
311 (tcp ? tcp : &global_tcp)->family = value_to_family(family);
312 }
313
314 /**
315 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
316 * \return One of the socket families defined in `<socket.h>`, such as `AF_INET` or `AF_INET6`.
317 *
318 * Get the address family that was previously set with wget_tcp_set_family().
319 */
wget_tcp_get_family(wget_tcp * tcp)320 int wget_tcp_get_family(wget_tcp *tcp)
321 {
322 return family_to_value((tcp ? tcp : &global_tcp)->family);
323 }
324
325 /**
326 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
327 * \return The local port.
328 *
329 * Get the port number the TCP connection \p tcp is bound to on the local machine.
330 */
wget_tcp_get_local_port(wget_tcp * tcp)331 int wget_tcp_get_local_port(wget_tcp *tcp)
332 {
333 if (unlikely(!tcp))
334 return 0;
335
336 struct sockaddr_storage addr_store;
337 struct sockaddr *addr = (struct sockaddr *)&addr_store;
338 socklen_t addr_len = sizeof(addr_store);
339
340 /* Get automatically retrieved port number */
341 if (getsockname(tcp->sockfd, addr, &addr_len) == 0) {
342 char s_port[NI_MAXSERV];
343
344 if (getnameinfo(addr, addr_len, NULL, 0, s_port, sizeof(s_port), NI_NUMERICSERV) == 0)
345 return atoi(s_port);
346 }
347
348 return 0;
349 }
350
351 /**
352 * \param[in] tcp A TCP connection.
353 * \param[in] timeout The timeout value.
354 *
355 * Set the timeout for the TCP connection.
356 *
357 * This is the maximum time to wait until the remote host accepts our connection.
358 *
359 * The following two values are special:
360 *
361 * - `0`: No timeout, immediate.
362 * - `-1`: Infinite timeout. Wait indefinitely.
363 */
wget_tcp_set_connect_timeout(wget_tcp * tcp,int timeout)364 void wget_tcp_set_connect_timeout(wget_tcp *tcp, int timeout)
365 {
366 (tcp ? tcp : &global_tcp)->connect_timeout = timeout;
367 }
368
369 /**
370 * \param[in] tcp A TCP connection.
371 * \param[in] timeout The timeout value.
372 *
373 * Set the timeout (in milliseconds) for wget_tcp_read(), wget_tcp_write() and wget_tcp_accept().
374 *
375 * The following two values are special:
376 *
377 * - `0`: No timeout, immediate.
378 * - `-1`: Infinite timeout. Wait indefinitely.
379 */
wget_tcp_set_timeout(wget_tcp * tcp,int timeout)380 void wget_tcp_set_timeout(wget_tcp *tcp, int timeout)
381 {
382 (tcp ? tcp : &global_tcp)->timeout = timeout;
383 }
384
385 /**
386 * \param[in] tcp A TCP connection.
387 * \return The timeout value that was set with wget_tcp_set_timeout().
388 *
389 * Get the timeout value that was set with wget_tcp_set_timeout().
390 */
wget_tcp_get_timeout(wget_tcp * tcp)391 int wget_tcp_get_timeout(wget_tcp *tcp)
392 {
393 return (tcp ? tcp : &global_tcp)->timeout;
394 }
395
396 /**
397 * \param[in] tcp A TCP connection. Might be NULL.
398 * \param[in] bind_address An IP address or host name.
399 *
400 * Set the IP address/hostname the socket \p tcp will bind to on the local machine
401 * when connecting to a remote host.
402 *
403 * The hostname can explicitly set the port after a colon (':').
404 *
405 * This is mainly relevant to wget_tcp_connect().
406 */
wget_tcp_set_bind_address(wget_tcp * tcp,const char * bind_address)407 void wget_tcp_set_bind_address(wget_tcp *tcp, const char *bind_address)
408 {
409 if (!tcp)
410 tcp = &global_tcp;
411
412 wget_dns_freeaddrinfo(tcp->dns, &tcp->bind_addrinfo);
413
414 if (bind_address) {
415 const char *host, *s = bind_address;
416
417 if (*s == '[') {
418 /* IPv6 address within brackets */
419 char *p = strrchr(s, ']');
420 if (p) {
421 host = s + 1;
422 s = p + 1;
423 } else {
424 /* Something is broken */
425 host = s + 1;
426 while (*s)
427 s++;
428 }
429 } else {
430 host = s;
431 while (*s && *s != ':')
432 s++;
433 }
434
435 if (*s == ':') {
436 char port[6];
437
438 wget_strscpy(port, s + 1, sizeof(port));
439
440 if (c_isdigit(*port))
441 tcp->bind_addrinfo = wget_dns_resolve(tcp->dns, host, (uint16_t) atoi(port), tcp->family, tcp->preferred_family);
442 } else {
443 tcp->bind_addrinfo = wget_dns_resolve(tcp->dns, host, 0, tcp->family, tcp->preferred_family);
444 }
445 }
446 }
447
448 /**
449 * \param[in] tcp A TCP connection. Might be NULL.
450 * \param[in] bind_interface A network interface name.
451 *
452 * Set the Network Interface the socket \p tcp will bind to on the local machine
453 * when connecting to a remote host.
454 *
455 * This is mainly relevant to wget_tcp_connect().
456 */
wget_tcp_set_bind_interface(wget_tcp * tcp,const char * bind_interface)457 void wget_tcp_set_bind_interface(wget_tcp *tcp, const char *bind_interface)
458 {
459 if (!tcp)
460 tcp = &global_tcp;
461
462 tcp->bind_interface = bind_interface;
463 }
464
465 /**
466 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init().
467 * \param[in] ssl Flag to enable or disable SSL/TLS on the given connection.
468 *
469 * Enable or disable SSL/TLS.
470 *
471 * If \p tcp is NULL, TLS will be enabled globally. Otherwise, TLS will be enabled only for the provided connection.
472 */
wget_tcp_set_ssl(wget_tcp * tcp,bool ssl)473 void wget_tcp_set_ssl(wget_tcp *tcp, bool ssl)
474 {
475 (tcp ? tcp : &global_tcp)->ssl = ssl;
476 }
477
478 /**
479 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init().
480 * \return 1 if TLs is enabled, 0 otherwise.
481 *
482 * Tells whether TLS is enabled or not.
483 */
wget_tcp_get_ssl(wget_tcp * tcp)484 bool wget_tcp_get_ssl(wget_tcp *tcp)
485 {
486 return (tcp ? tcp : &global_tcp)->ssl;
487 }
488
489 /**
490 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init().
491 * \return IP address as string, NULL if not available.
492 *
493 * Returns the IP address of a `wget_tcp` instance.
494 */
wget_tcp_get_ip(wget_tcp * tcp)495 const char *wget_tcp_get_ip(wget_tcp *tcp)
496 {
497 return tcp ? tcp->ip : NULL;
498 }
499
500 /**
501 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
502 * \param[in] hostname A hostname. The value of the SNI field.
503 *
504 * Sets the TLS Server Name Indication (SNI). For more info see [RFC 6066, sect. 3](https://tools.ietf.org/html/rfc6066#section-3).
505 *
506 * SNI basically does at the TLS layer what the `Host:` header field does at the application (HTTP) layer.
507 * The server might use this information to locate an appropriate X.509 certificate from a pool of certificates, or to direct
508 * the request to a specific virtual host, for instance.
509 */
wget_tcp_set_ssl_hostname(wget_tcp * tcp,const char * hostname)510 void wget_tcp_set_ssl_hostname(wget_tcp *tcp, const char *hostname)
511 {
512 if (!tcp)
513 tcp = &global_tcp;
514
515 xfree(tcp->ssl_hostname);
516 tcp->ssl_hostname = wget_strdup(hostname);
517 }
518
519 /**
520 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
521 * \return A hostname. The value of the SNI field.
522 *
523 * Returns the value that was set to SNI with a previous call to wget_tcp_set_ssl_hostname().
524 */
wget_tcp_get_ssl_hostname(wget_tcp * tcp)525 const char *wget_tcp_get_ssl_hostname(wget_tcp *tcp)
526 {
527 return (tcp ? tcp : &global_tcp)->ssl_hostname;
528 }
529
530 /**
531 * \return A new `wget_tcp` structure, with pre-defined parameters.
532 *
533 * Create a new `wget_tcp` structure, that represents a TCP connection.
534 * It can be destroyed with wget_tcp_deinit().
535 *
536 * This function does not establish or modify a TCP connection in any way.
537 * That can be done with the other functions in this file, such as
538 * wget_tcp_connect() or wget_tcp_listen() and wget_tcp_accept().
539 */
wget_tcp_init(void)540 wget_tcp *wget_tcp_init(void)
541 {
542 wget_tcp *tcp = wget_malloc(sizeof(wget_tcp));
543
544 if (tcp) {
545 *tcp = global_tcp;
546 tcp->ssl_hostname = wget_strdup(global_tcp.ssl_hostname);
547 }
548
549 return tcp;
550 }
551
552 /**
553 * \param[in] _tcp A **pointer** to a `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init(). Might be NULL.
554 *
555 * Release a TCP connection (created with wget_tcp_init()).
556 *
557 * The `wget_tcp` structure will be freed and \p _tcp will be set to NULL.
558 *
559 * If \p _tcp is NULL, the SNI field will be cleared.
560 *
561 * Does not free the internal DNS cache, so that other connections can re-use it.
562 * Call wget_dns_cache_free() if you want to free it.
563 */
wget_tcp_deinit(wget_tcp ** _tcp)564 void wget_tcp_deinit(wget_tcp **_tcp)
565 {
566 wget_tcp *tcp;
567
568 if (!_tcp) {
569 xfree(global_tcp.ssl_hostname);
570 return;
571 }
572
573 if ((tcp = *_tcp)) {
574 wget_tcp_close(tcp);
575
576 wget_dns_freeaddrinfo(tcp->dns, &tcp->bind_addrinfo);
577
578 xfree(tcp->ssl_hostname);
579 xfree(tcp->ip);
580 xfree(tcp);
581
582 *_tcp = NULL;
583 }
584 }
585
_set_async(int fd)586 static void _set_async(int fd)
587 {
588 #if ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
589 unsigned long blocking = 0;
590
591 if (ioctl(fd, FIONBIO, &blocking))
592 error_printf_exit(_("Failed to set socket to non-blocking\n"));
593 #else
594 int flags;
595
596 if ((flags = fcntl(fd, F_GETFL)) < 0)
597 error_printf_exit(_("Failed to get socket flags\n"));
598
599 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
600 error_printf_exit(_("Failed to set socket to non-blocking\n"));
601 #endif
602 }
603
set_socket_options(const wget_tcp * tcp,int fd)604 static void set_socket_options(const wget_tcp *tcp, int fd)
605 {
606 int on = 1;
607
608 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)) == -1)
609 error_printf(_("Failed to set socket option REUSEADDR\n"));
610
611 on = 1;
612 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)) == -1)
613 error_printf(_("Failed to set socket option NODELAY\n"));
614
615 #ifdef SO_BINDTODEVICE
616 if (tcp->bind_interface) {
617 if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, tcp->bind_interface, (socklen_t)strlen(tcp->bind_interface)) == -1)
618 error_printf(_("Failed to set socket option BINDTODEVICE\n"));
619 }
620 #else
621 // Let's exit here instead of using a wrong interface (privacy concerns)
622 if (tcp->bind_interface)
623 error_printf_exit(_("Unsupported socket option BINDTODEVICE\n"));
624 #endif
625
626 #ifdef TCP_FASTOPEN_LINUX_411
627 on = 1;
628 if (setsockopt(fd, IPPROTO_TCP, TCP_FASTOPEN_CONNECT, (void *)&on, sizeof(on)) == -1)
629 debug_printf("Failed to set socket option TCP_FASTOPEN_CONNECT\n");
630 #endif
631 }
632
633 /**
634 * Test whether the given connection (\p tcp) is ready to read or write.
635 *
636 * The parameter \p flags can have one or both (with bitwise OR) of the following values:
637 *
638 * - `WGET_IO_READABLE`: Is data available for reading?
639 * - `WGET_IO_WRITABLE`: Can we write immediately (without having to wait until the TCP buffer frees)?
640 */
wget_tcp_ready_2_transfer(wget_tcp * tcp,int flags)641 int wget_tcp_ready_2_transfer(wget_tcp *tcp, int flags)
642 {
643 if (likely(tcp))
644 return wget_ready_2_transfer(tcp->sockfd, tcp->timeout, flags);
645 else
646 return -1;
647 }
648
649 /**
650 * \param[in] tcp A `wget_tcp` structure representing a TCP connection, returned by wget_tcp_init().
651 * \param[in] host Hostname or IP address to connect to.
652 * \param[in] port port number
653 * \return WGET_E_SUCCESS (0) on success, or a negative integer on error (some of WGET_E_XXX defined in `<wget.h>`).
654 *
655 * Open a TCP connection with a remote host.
656 *
657 * This function will use TLS if it has been enabled for this `wget_tcp`. You can enable it
658 * with wget_tcp_set_ssl(). Additionally, you can also use wget_tcp_set_ssl_hostname() to set the
659 * Server Name Indication (SNI).
660 *
661 * You can set which IP address and port on the local machine will the socket be bound to
662 * with wget_tcp_set_bind_address(). Otherwise the socket will bind to any address and port
663 * chosen by the operating system.
664 *
665 * You can also set which Network Interface on the local machine will the socket be bound to
666 * with wget_tcp_bind_interface().
667 *
668 * This function will try to use TCP Fast Open if enabled and available. If TCP Fast Open fails,
669 * it will fall back to the normal TCP handshake, without raising an error. You can enable TCP Fast Open
670 * with wget_tcp_set_tcp_fastopen().
671 *
672 * If the connection fails, `WGET_E_CONNECT` is returned.
673 */
wget_tcp_connect(wget_tcp * tcp,const char * host,uint16_t port)674 int wget_tcp_connect(wget_tcp *tcp, const char *host, uint16_t port)
675 {
676 struct addrinfo *ai;
677 int rc, ret = WGET_E_UNKNOWN;
678 char adr[NI_MAXHOST], s_port[NI_MAXSERV];
679 int debug = wget_logger_is_active(wget_get_logger(WGET_LOGGER_DEBUG));
680
681 if (unlikely(!tcp))
682 return WGET_E_INVALID;
683
684 wget_dns_freeaddrinfo(tcp->dns, &tcp->addrinfo);
685
686 tcp->addrinfo = wget_dns_resolve(tcp->dns, host, port, tcp->family, tcp->preferred_family);
687
688 for (ai = tcp->addrinfo; ai; ai = ai->ai_next) {
689 if (debug) {
690 rc = getnameinfo(ai->ai_addr, ai->ai_addrlen,
691 adr, sizeof(adr),
692 s_port, sizeof(s_port),
693 NI_NUMERICHOST | NI_NUMERICSERV);
694 if (rc == 0)
695 debug_printf("trying %s:%s...\n", adr, s_port);
696 else
697 debug_printf("trying ???:%s (%s)...\n", s_port, gai_strerror(rc));
698 }
699
700 int sockfd;
701 if ((sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) != -1) {
702 _set_async(sockfd);
703 set_socket_options(tcp, sockfd);
704
705 if (tcp->bind_addrinfo) {
706 if (debug) {
707 rc = getnameinfo(tcp->bind_addrinfo->ai_addr,
708 tcp->bind_addrinfo->ai_addrlen,
709 adr, sizeof(adr),
710 s_port, sizeof(s_port),
711 NI_NUMERICHOST | NI_NUMERICSERV);
712 if (rc == 0)
713 debug_printf("binding to %s:%s...\n", adr, s_port);
714 else
715 debug_printf("binding to ???:%s (%s)...\n", s_port, gai_strerror(rc));
716 }
717
718 if (bind(sockfd, tcp->bind_addrinfo->ai_addr, tcp->bind_addrinfo->ai_addrlen) != 0) {
719 error_printf(_("Failed to bind (%d)\n"), errno);
720 close(sockfd);
721
722 return WGET_E_UNKNOWN;
723 }
724 }
725
726 /* Enable TCP Fast Open, if required by the user and available */
727 #ifdef TCP_FASTOPEN_OSX
728 if (tcp->tcp_fastopen) {
729 sa_endpoints_t endpoints = { .sae_dstaddr = ai->ai_addr, .sae_dstaddrlen = ai->ai_addrlen };
730 rc = connectx(sockfd, &endpoints, SAE_ASSOCID_ANY, CONNECT_RESUME_ON_READ_WRITE | CONNECT_DATA_IDEMPOTENT, NULL, 0, NULL, NULL);
731 tcp->first_send = 0;
732 #elif defined TCP_FASTOPEN_LINUX
733 if (tcp->tcp_fastopen) {
734 errno = 0;
735 tcp->connect_addrinfo = ai;
736 rc = 0;
737 tcp->first_send = 1;
738 #elif defined TCP_FASTOPEN_LINUX_411
739 if (tcp->tcp_fastopen) {
740 tcp->connect_addrinfo = ai;
741 rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
742 tcp->first_send = 0;
743 #else
744 if (0) {
745 #endif
746 } else {
747 rc = connect(sockfd, ai->ai_addr, ai->ai_addrlen);
748 tcp->first_send = 0;
749 }
750
751 if (rc < 0
752 && errno != EAGAIN
753 && errno != EINPROGRESS
754 ) {
755 error_printf(_("Failed to connect (%d)\n"), errno);
756 ret = WGET_E_CONNECT;
757 close(sockfd);
758 } else {
759 tcp->sockfd = sockfd;
760 if (tcp->ssl) {
761 if ((ret = wget_ssl_open(tcp))) {
762 if (ret == WGET_E_CERTIFICATE) {
763 wget_tcp_close(tcp);
764 break; /* stop here - the server cert couldn't be validated */
765 }
766
767 /* do not free tcp->addrinfo when calling wget_tcp_close() */
768 struct addrinfo *ai_tmp = tcp->addrinfo;
769
770 tcp->addrinfo = NULL;
771 wget_tcp_close(tcp);
772 tcp->addrinfo = ai_tmp;
773
774 continue;
775 }
776 }
777
778 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, adr, sizeof(adr), s_port, sizeof(s_port), NI_NUMERICHOST | NI_NUMERICSERV) == 0)
779 tcp->ip = wget_strdup(adr);
780 else
781 tcp->ip = NULL;
782
783 return WGET_E_SUCCESS;
784 }
785 } else
786 error_printf(_("Failed to create socket (%d)\n"), errno);
787 }
788
789 return ret;
790 }
791
792 /**
793 * \param[in] tcp An active connection.
794 * \return WGET_E_SUCCESS (0) on success, or a negative integer on error (one of WGET_E_XXX, defined in `<wget.h>`).
795 * Start TLS for this connection.
796 *
797 * This will typically be called by wget_tcp_accept().
798 *
799 * If the socket is listening (e.g. wget_tcp_listen(), wget_tcp_accept()), it will expect the client to perform a TLS handshake,
800 * and fail if it doesn't.
801 *
802 * If this is a client connection (e.g. wget_tcp_connect()), it will try perform a TLS handshake with the server.
803 */
804 int wget_tcp_tls_start(wget_tcp *tcp)
805 {
806 return wget_ssl_open(tcp);
807 }
808
809 /**
810 * \param[in] tcp An active connection.
811 *
812 * Stops TLS, but does not close the connection. Data will be transmitted in the clear from now on.
813 */
814 void wget_tcp_tls_stop(wget_tcp *tcp)
815 {
816 if (tcp)
817 wget_ssl_close(&tcp->ssl_session);
818 }
819
820 /**
821 * \param[in] tcp An active TCP connection.
822 * \param[in] buf Destination buffer, at least \p count bytes long.
823 * \param[in] count Length of the buffer \p buf.
824 * \return Number of bytes read
825 *
826 * Read \p count bytes of data from the TCP connection represented by \p tcp
827 * and store them in the buffer \p buf.
828 *
829 * This function knows whether the provided connection is over TLS or not
830 * and it will do the right thing.
831 *
832 * The `tcp->timeout` parameter is taken into account by this function as well.
833 * It specifies how long should this function wait until there's data available
834 * to read (in milliseconds). The default timeout is -1, which means to wait indefinitely.
835 *
836 * The following two values are special:
837 *
838 * - `0`: No timeout, immediate.
839 * - `-1`: Infinite timeout. Wait indefinitely until a new connection comes.
840 *
841 * You can set the timeout with wget_tcp_set_timeout().
842 *
843 * In particular, the returned value will be zero if no data was available for reading
844 * before the timeout elapsed.
845 */
846 ssize_t wget_tcp_read(wget_tcp *tcp, char *buf, size_t count)
847 {
848 ssize_t rc;
849
850 if (unlikely(!tcp || !buf))
851 return 0;
852
853 if (tcp->ssl_session) {
854 rc = wget_ssl_read_timeout(tcp->ssl_session, buf, count, tcp->timeout);
855 } else {
856 if (tcp->timeout) {
857 if ((rc = wget_ready_2_read(tcp->sockfd, tcp->timeout)) <= 0)
858 return rc;
859 }
860
861 rc = recvfrom(tcp->sockfd, buf, count, 0, NULL, NULL);
862 }
863
864 if (rc < 0)
865 error_printf(_("Failed to read %zu bytes (%d)\n"), count, errno);
866
867 return rc;
868 }
869
870 /**
871 * \param[in] tcp An active TCP connection.
872 * \param[in] buf A buffer, at least \p count bytes long.
873 * \param[in] count Number of bytes from \p buf to send through \p tcp.
874 * \return The number of bytes written, or -1 on error.
875 *
876 * Write \p count bytes of data from the buffer \p buf to the TCP connection
877 * represented by \p tcp.
878 *
879 * This function knows whether the provided connection is over TLS or not
880 * and it will do the right thing.
881 *
882 * TCP Fast Open will be used if it's available and enabled. You can enable TCP Fast Open
883 * with wget_tcp_set_tcp_fastopen().
884 *
885 * This function honors the `timeout` parameter. If the write operation fails because the socket buffer is full,
886 * then it will wait at most that amount of milliseconds. If after the timeout the socket is still unavailable
887 * for writing, this function returns zero.
888 *
889 * The following two values are special:
890 *
891 * - `0`: No timeout. The socket must be available immediately.
892 * - `-1`: Infinite timeout. Wait indefinitely until the socket becomes available.
893 *
894 * You can set the timeout with wget_tcp_set_timeout().
895 */
896 ssize_t wget_tcp_write(wget_tcp *tcp, const char *buf, size_t count)
897 {
898 ssize_t nwritten = 0;
899
900 if (unlikely(!tcp || !buf))
901 return -1;
902
903 if (tcp->ssl_session)
904 return wget_ssl_write_timeout(tcp->ssl_session, buf, count, tcp->timeout);
905
906 while (count) {
907 ssize_t n;
908
909 #ifdef TCP_FASTOPEN_LINUX
910 if (tcp->tcp_fastopen && tcp->first_send) {
911 n = sendto(tcp->sockfd, buf, count, MSG_FASTOPEN,
912 tcp->connect_addrinfo->ai_addr, tcp->connect_addrinfo->ai_addrlen);
913 tcp->first_send = 0;
914
915 if (n < 0 && errno == EOPNOTSUPP) {
916 /* fallback from fastopen, e.g. when fastopen is disabled in system */
917 tcp->tcp_fastopen = 0;
918
919 int rc = connect(tcp->sockfd, tcp->connect_addrinfo->ai_addr, tcp->connect_addrinfo->ai_addrlen);
920 if (rc < 0
921 && errno != EAGAIN
922 && errno != ENOTCONN
923 && errno != EINPROGRESS)
924 {
925 error_printf(_("Failed to connect (%d)\n"), errno);
926 return -1;
927 }
928 errno = EAGAIN;
929 }
930 } else
931 #endif
932 n = send(tcp->sockfd, buf, count, 0);
933
934 if (n >= 0) {
935 nwritten += n;
936
937 if ((size_t)n >= count)
938 return nwritten;
939
940 count -= n;
941 buf += n;
942 } else {
943 if (errno != EAGAIN
944 && errno != ENOTCONN
945 && errno != EINPROGRESS)
946 {
947 error_printf(_("Failed to write %zu bytes (%d)\n"), count, errno);
948 return -1;
949 }
950
951 if (tcp->timeout) {
952 int rc = wget_ready_2_write(tcp->sockfd, tcp->timeout);
953 if (rc <= 0)
954 return rc;
955 }
956 }
957 }
958
959 return 0;
960 }
961
962 /**
963 * \param[in] tcp An active TCP connection.
964 * \param[in] fmt Format string (like in `printf(3)`).
965 * \param[in] args `va_args` argument list (like in `vprintf(3)`)
966 *
967 * Write data in vprintf-style format, to the connection \p tcp.
968 *
969 * It uses wget_tcp_write().
970 */
971 ssize_t wget_tcp_vprintf(wget_tcp *tcp, const char *fmt, va_list args)
972 {
973 char sbuf[4096];
974 wget_buffer buf;
975 ssize_t len2;
976
977 wget_buffer_init(&buf, sbuf, sizeof(sbuf));
978 wget_buffer_vprintf(&buf, fmt, args);
979
980 len2 = wget_tcp_write(tcp, buf.data, buf.length);
981
982 wget_buffer_deinit(&buf);
983
984 if (len2 > 0)
985 debug_write(buf.data, len2);
986
987 if (len2 > 0 && (ssize_t) buf.length != len2)
988 error_printf(_("%s: internal error: length mismatch %zu != %zd\n"), __func__, buf.length, len2);
989
990 return len2;
991 }
992
993 /**
994 * \param[in] tcp An active TCP connection.
995 * \param[in] fmt Format string (like in `printf(3)`).
996 *
997 * Write data in printf-style format, to the connection \p tcp.
998 *
999 * It uses wget_tcp_vprintf(), which in turn uses wget_tcp_write().
1000 */
1001 ssize_t wget_tcp_printf(wget_tcp *tcp, const char *fmt, ...)
1002 {
1003 va_list args;
1004
1005 va_start(args, fmt);
1006 ssize_t len = wget_tcp_vprintf(tcp, fmt, args);
1007 va_end(args);
1008
1009 return len;
1010 }
1011
1012 /**
1013 * \param[in] tcp An active TCP connection
1014 *
1015 * Close a TCP connection.
1016 */
1017 void wget_tcp_close(wget_tcp *tcp)
1018 {
1019 if (likely(tcp)) {
1020 wget_tcp_tls_stop(tcp);
1021 if (tcp->sockfd != -1) {
1022 close(tcp->sockfd);
1023 tcp->sockfd = -1;
1024 }
1025 wget_dns_freeaddrinfo(tcp->dns, &tcp->addrinfo);
1026 }
1027 }
1028 /** @} */
1029