xref: /freebsd/crypto/openssl/apps/lib/s_socket.c (revision 0d0c8621fd181e507f0fb50ffcca606faf66a8c2)
1b077aed3SPierre Pronchery /*
2*0d0c8621SEnji Cooper  * Copyright 1995-2025 The OpenSSL Project Authors. All Rights Reserved.
3b077aed3SPierre Pronchery  *
4b077aed3SPierre Pronchery  * Licensed under the Apache License 2.0 (the "License").  You may not use
5b077aed3SPierre Pronchery  * this file except in compliance with the License.  You can obtain a copy
6b077aed3SPierre Pronchery  * in the file LICENSE in the source distribution or at
7b077aed3SPierre Pronchery  * https://www.openssl.org/source/license.html
8b077aed3SPierre Pronchery  */
9b077aed3SPierre Pronchery 
10b077aed3SPierre Pronchery /* socket-related functions used by s_client and s_server */
11b077aed3SPierre Pronchery #include <stdio.h>
12b077aed3SPierre Pronchery #include <stdlib.h>
13b077aed3SPierre Pronchery #include <string.h>
14b077aed3SPierre Pronchery #include <errno.h>
15b077aed3SPierre Pronchery #include <signal.h>
16b077aed3SPierre Pronchery #include <openssl/opensslconf.h>
17b077aed3SPierre Pronchery 
18b077aed3SPierre Pronchery /*
19b077aed3SPierre Pronchery  * With IPv6, it looks like Digital has mixed up the proper order of
20b077aed3SPierre Pronchery  * recursive header file inclusion, resulting in the compiler complaining
21b077aed3SPierre Pronchery  * that u_int isn't defined, but only if _POSIX_C_SOURCE is defined, which is
22b077aed3SPierre Pronchery  * needed to have fileno() declared correctly...  So let's define u_int
23b077aed3SPierre Pronchery  */
24b077aed3SPierre Pronchery #if defined(OPENSSL_SYS_VMS_DECC) && !defined(__U_INT)
25b077aed3SPierre Pronchery # define __U_INT
26b077aed3SPierre Pronchery typedef unsigned int u_int;
27b077aed3SPierre Pronchery #endif
28b077aed3SPierre Pronchery 
29b077aed3SPierre Pronchery #ifdef _WIN32
30b077aed3SPierre Pronchery # include <process.h>
31b077aed3SPierre Pronchery 
32b077aed3SPierre Pronchery /* MSVC renamed some POSIX functions to have an underscore prefix. */
33b077aed3SPierre Pronchery # ifdef _MSC_VER
34b077aed3SPierre Pronchery #  define getpid _getpid
35b077aed3SPierre Pronchery # endif
36b077aed3SPierre Pronchery #endif
37b077aed3SPierre Pronchery 
38b077aed3SPierre Pronchery #ifndef OPENSSL_NO_SOCK
39b077aed3SPierre Pronchery 
40b077aed3SPierre Pronchery # include "apps.h"
41b077aed3SPierre Pronchery # include "s_apps.h"
42b077aed3SPierre Pronchery # include "internal/sockets.h"
43b077aed3SPierre Pronchery 
44b077aed3SPierre Pronchery # if defined(__TANDEM)
45b077aed3SPierre Pronchery #  if defined(OPENSSL_TANDEM_FLOSS)
46b077aed3SPierre Pronchery #   include <floss.h(floss_read)>
47b077aed3SPierre Pronchery #  endif
48b077aed3SPierre Pronchery # endif
49b077aed3SPierre Pronchery 
50b077aed3SPierre Pronchery # include <openssl/bio.h>
51b077aed3SPierre Pronchery # include <openssl/err.h>
52b077aed3SPierre Pronchery 
53b077aed3SPierre Pronchery /* Keep track of our peer's address for the cookie callback */
54b077aed3SPierre Pronchery BIO_ADDR *ourpeer = NULL;
55b077aed3SPierre Pronchery 
56b077aed3SPierre Pronchery /*
57b077aed3SPierre Pronchery  * init_client - helper routine to set up socket communication
58b077aed3SPierre Pronchery  * @sock: pointer to storage of resulting socket.
59b077aed3SPierre Pronchery  * @host: the host name or path (for AF_UNIX) to connect to.
60b077aed3SPierre Pronchery  * @port: the port to connect to (ignored for AF_UNIX).
61b077aed3SPierre Pronchery  * @bindhost: source host or path (for AF_UNIX).
62b077aed3SPierre Pronchery  * @bindport: source port (ignored for AF_UNIX).
63b077aed3SPierre Pronchery  * @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or
64b077aed3SPierre Pronchery  *  AF_UNSPEC
65b077aed3SPierre Pronchery  * @type: socket type, must be SOCK_STREAM or SOCK_DGRAM
66b077aed3SPierre Pronchery  * @protocol: socket protocol, e.g. IPPROTO_TCP or IPPROTO_UDP (or 0 for any)
67b077aed3SPierre Pronchery  *
68b077aed3SPierre Pronchery  * This will create a socket and use it to connect to a host:port, or if
69b077aed3SPierre Pronchery  * family == AF_UNIX, to the path found in host.
70b077aed3SPierre Pronchery  *
71b077aed3SPierre Pronchery  * If the host has more than one address, it will try them one by one until
72b077aed3SPierre Pronchery  * a successful connection is established.  The resulting socket will be
73b077aed3SPierre Pronchery  * found in *sock on success, it will be given INVALID_SOCKET otherwise.
74b077aed3SPierre Pronchery  *
75b077aed3SPierre Pronchery  * Returns 1 on success, 0 on failure.
76b077aed3SPierre Pronchery  */
init_client(int * sock,const char * host,const char * port,const char * bindhost,const char * bindport,int family,int type,int protocol)77b077aed3SPierre Pronchery int init_client(int *sock, const char *host, const char *port,
78b077aed3SPierre Pronchery                 const char *bindhost, const char *bindport,
79b077aed3SPierre Pronchery                 int family, int type, int protocol)
80b077aed3SPierre Pronchery {
81b077aed3SPierre Pronchery     BIO_ADDRINFO *res = NULL;
82b077aed3SPierre Pronchery     BIO_ADDRINFO *bindaddr = NULL;
83b077aed3SPierre Pronchery     const BIO_ADDRINFO *ai = NULL;
84b077aed3SPierre Pronchery     const BIO_ADDRINFO *bi = NULL;
85b077aed3SPierre Pronchery     int found = 0;
86b077aed3SPierre Pronchery     int ret;
87b077aed3SPierre Pronchery 
88b077aed3SPierre Pronchery     if (BIO_sock_init() != 1)
89b077aed3SPierre Pronchery         return 0;
90b077aed3SPierre Pronchery 
91b077aed3SPierre Pronchery     ret = BIO_lookup_ex(host, port, BIO_LOOKUP_CLIENT, family, type, protocol,
92b077aed3SPierre Pronchery                         &res);
93b077aed3SPierre Pronchery     if (ret == 0) {
94b077aed3SPierre Pronchery         ERR_print_errors(bio_err);
95b077aed3SPierre Pronchery         return 0;
96b077aed3SPierre Pronchery     }
97b077aed3SPierre Pronchery 
98b077aed3SPierre Pronchery     if (bindhost != NULL || bindport != NULL) {
99b077aed3SPierre Pronchery         ret = BIO_lookup_ex(bindhost, bindport, BIO_LOOKUP_CLIENT,
100b077aed3SPierre Pronchery                             family, type, protocol, &bindaddr);
101b077aed3SPierre Pronchery         if (ret == 0) {
102b077aed3SPierre Pronchery             ERR_print_errors (bio_err);
103b077aed3SPierre Pronchery             goto out;
104b077aed3SPierre Pronchery         }
105b077aed3SPierre Pronchery     }
106b077aed3SPierre Pronchery 
107b077aed3SPierre Pronchery     ret = 0;
108b077aed3SPierre Pronchery     for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
109b077aed3SPierre Pronchery         /* Admittedly, these checks are quite paranoid, we should not get
110b077aed3SPierre Pronchery          * anything in the BIO_ADDRINFO chain that we haven't
111b077aed3SPierre Pronchery          * asked for. */
112b077aed3SPierre Pronchery         OPENSSL_assert((family == AF_UNSPEC
113b077aed3SPierre Pronchery                         || family == BIO_ADDRINFO_family(ai))
114b077aed3SPierre Pronchery                        && (type == 0 || type == BIO_ADDRINFO_socktype(ai))
115b077aed3SPierre Pronchery                        && (protocol == 0
116b077aed3SPierre Pronchery                            || protocol == BIO_ADDRINFO_protocol(ai)));
117b077aed3SPierre Pronchery 
118b077aed3SPierre Pronchery         if (bindaddr != NULL) {
119b077aed3SPierre Pronchery             for (bi = bindaddr; bi != NULL; bi = BIO_ADDRINFO_next(bi)) {
120b077aed3SPierre Pronchery                 if (BIO_ADDRINFO_family(bi) == BIO_ADDRINFO_family(ai))
121b077aed3SPierre Pronchery                     break;
122b077aed3SPierre Pronchery             }
123b077aed3SPierre Pronchery             if (bi == NULL)
124b077aed3SPierre Pronchery                 continue;
125b077aed3SPierre Pronchery             ++found;
126b077aed3SPierre Pronchery         }
127b077aed3SPierre Pronchery 
128b077aed3SPierre Pronchery         *sock = BIO_socket(BIO_ADDRINFO_family(ai), BIO_ADDRINFO_socktype(ai),
129b077aed3SPierre Pronchery                            BIO_ADDRINFO_protocol(ai), 0);
130b077aed3SPierre Pronchery         if (*sock == INVALID_SOCKET) {
131b077aed3SPierre Pronchery             /* Maybe the kernel doesn't support the socket family, even if
132b077aed3SPierre Pronchery              * BIO_lookup() added it in the returned result...
133b077aed3SPierre Pronchery              */
134b077aed3SPierre Pronchery             continue;
135b077aed3SPierre Pronchery         }
136b077aed3SPierre Pronchery 
137b077aed3SPierre Pronchery         if (bi != NULL) {
138b077aed3SPierre Pronchery             if (!BIO_bind(*sock, BIO_ADDRINFO_address(bi),
139b077aed3SPierre Pronchery                           BIO_SOCK_REUSEADDR)) {
140b077aed3SPierre Pronchery                 BIO_closesocket(*sock);
141b077aed3SPierre Pronchery                 *sock = INVALID_SOCKET;
142b077aed3SPierre Pronchery                 break;
143b077aed3SPierre Pronchery             }
144b077aed3SPierre Pronchery         }
145b077aed3SPierre Pronchery 
146b077aed3SPierre Pronchery #ifndef OPENSSL_NO_SCTP
147b077aed3SPierre Pronchery         if (protocol == IPPROTO_SCTP) {
148b077aed3SPierre Pronchery             /*
149b077aed3SPierre Pronchery              * For SCTP we have to set various options on the socket prior to
150b077aed3SPierre Pronchery              * connecting. This is done automatically by BIO_new_dgram_sctp().
151b077aed3SPierre Pronchery              * We don't actually need the created BIO though so we free it again
152b077aed3SPierre Pronchery              * immediately.
153b077aed3SPierre Pronchery              */
154b077aed3SPierre Pronchery             BIO *tmpbio = BIO_new_dgram_sctp(*sock, BIO_NOCLOSE);
155b077aed3SPierre Pronchery 
156b077aed3SPierre Pronchery             if (tmpbio == NULL) {
157b077aed3SPierre Pronchery                 ERR_print_errors(bio_err);
158b077aed3SPierre Pronchery                 return 0;
159b077aed3SPierre Pronchery             }
160b077aed3SPierre Pronchery             BIO_free(tmpbio);
161b077aed3SPierre Pronchery         }
162b077aed3SPierre Pronchery #endif
163b077aed3SPierre Pronchery 
164b077aed3SPierre Pronchery         if (!BIO_connect(*sock, BIO_ADDRINFO_address(ai),
165b077aed3SPierre Pronchery                          BIO_ADDRINFO_protocol(ai) == IPPROTO_TCP ? BIO_SOCK_NODELAY : 0)) {
166b077aed3SPierre Pronchery             BIO_closesocket(*sock);
167b077aed3SPierre Pronchery             *sock = INVALID_SOCKET;
168b077aed3SPierre Pronchery             continue;
169b077aed3SPierre Pronchery         }
170b077aed3SPierre Pronchery 
171b077aed3SPierre Pronchery         /* Success, don't try any more addresses */
172b077aed3SPierre Pronchery         break;
173b077aed3SPierre Pronchery     }
174b077aed3SPierre Pronchery 
175b077aed3SPierre Pronchery     if (*sock == INVALID_SOCKET) {
176b077aed3SPierre Pronchery         if (bindaddr != NULL && !found) {
177b077aed3SPierre Pronchery             BIO_printf(bio_err, "Can't bind %saddress for %s%s%s\n",
178b077aed3SPierre Pronchery #ifdef AF_INET6
179b077aed3SPierre Pronchery                        BIO_ADDRINFO_family(res) == AF_INET6 ? "IPv6 " :
180b077aed3SPierre Pronchery #endif
181b077aed3SPierre Pronchery                        BIO_ADDRINFO_family(res) == AF_INET ? "IPv4 " :
182b077aed3SPierre Pronchery                        BIO_ADDRINFO_family(res) == AF_UNIX ? "unix " : "",
183b077aed3SPierre Pronchery                        bindhost != NULL ? bindhost : "",
184b077aed3SPierre Pronchery                        bindport != NULL ? ":" : "",
185b077aed3SPierre Pronchery                        bindport != NULL ? bindport : "");
186b077aed3SPierre Pronchery             ERR_clear_error();
187b077aed3SPierre Pronchery             ret = 0;
188b077aed3SPierre Pronchery         }
189b077aed3SPierre Pronchery         ERR_print_errors(bio_err);
190b077aed3SPierre Pronchery     } else {
191b077aed3SPierre Pronchery         /* Remove any stale errors from previous connection attempts */
192b077aed3SPierre Pronchery         ERR_clear_error();
193b077aed3SPierre Pronchery         ret = 1;
194b077aed3SPierre Pronchery     }
195b077aed3SPierre Pronchery out:
196b077aed3SPierre Pronchery     if (bindaddr != NULL) {
197b077aed3SPierre Pronchery         BIO_ADDRINFO_free (bindaddr);
198b077aed3SPierre Pronchery     }
199b077aed3SPierre Pronchery     BIO_ADDRINFO_free(res);
200b077aed3SPierre Pronchery     return ret;
201b077aed3SPierre Pronchery }
202b077aed3SPierre Pronchery 
report_server_accept(BIO * out,int asock,int with_address,int with_pid)203b077aed3SPierre Pronchery int report_server_accept(BIO *out, int asock, int with_address, int with_pid)
204b077aed3SPierre Pronchery {
205b077aed3SPierre Pronchery     int success = 1;
206b077aed3SPierre Pronchery 
207b077aed3SPierre Pronchery     if (BIO_printf(out, "ACCEPT") <= 0)
208b077aed3SPierre Pronchery         return 0;
209b077aed3SPierre Pronchery     if (with_address) {
210b077aed3SPierre Pronchery         union BIO_sock_info_u info;
211b077aed3SPierre Pronchery         char *hostname = NULL;
212b077aed3SPierre Pronchery         char *service = NULL;
213b077aed3SPierre Pronchery 
214b077aed3SPierre Pronchery         if ((info.addr = BIO_ADDR_new()) != NULL
215b077aed3SPierre Pronchery             && BIO_sock_info(asock, BIO_SOCK_INFO_ADDRESS, &info)
216b077aed3SPierre Pronchery             && (hostname = BIO_ADDR_hostname_string(info.addr, 1)) != NULL
217b077aed3SPierre Pronchery             && (service = BIO_ADDR_service_string(info.addr, 1)) != NULL) {
218b077aed3SPierre Pronchery             success = BIO_printf(out,
219b077aed3SPierre Pronchery                                  strchr(hostname, ':') == NULL
220b077aed3SPierre Pronchery                                  ? /* IPv4 */ " %s:%s"
221b077aed3SPierre Pronchery                                  : /* IPv6 */ " [%s]:%s",
222b077aed3SPierre Pronchery                                  hostname, service) > 0;
223b077aed3SPierre Pronchery         } else {
224b077aed3SPierre Pronchery             (void)BIO_printf(out, "unknown:error\n");
225b077aed3SPierre Pronchery             success = 0;
226b077aed3SPierre Pronchery         }
227b077aed3SPierre Pronchery         OPENSSL_free(hostname);
228b077aed3SPierre Pronchery         OPENSSL_free(service);
229b077aed3SPierre Pronchery         BIO_ADDR_free(info.addr);
230b077aed3SPierre Pronchery     }
231b077aed3SPierre Pronchery     if (with_pid)
232b077aed3SPierre Pronchery         success = success && BIO_printf(out, " PID=%d", getpid()) > 0;
233b077aed3SPierre Pronchery     success = success && BIO_printf(out, "\n") > 0;
234b077aed3SPierre Pronchery     (void)BIO_flush(out);
235b077aed3SPierre Pronchery 
236b077aed3SPierre Pronchery     return success;
237b077aed3SPierre Pronchery }
238b077aed3SPierre Pronchery 
239b077aed3SPierre Pronchery /*
240b077aed3SPierre Pronchery  * do_server - helper routine to perform a server operation
241b077aed3SPierre Pronchery  * @accept_sock: pointer to storage of resulting socket.
242b077aed3SPierre Pronchery  * @host: the host name or path (for AF_UNIX) to connect to.
243b077aed3SPierre Pronchery  * @port: the port to connect to (ignored for AF_UNIX).
244b077aed3SPierre Pronchery  * @family: desired socket family, may be AF_INET, AF_INET6, AF_UNIX or
245b077aed3SPierre Pronchery  *  AF_UNSPEC
246b077aed3SPierre Pronchery  * @type: socket type, must be SOCK_STREAM or SOCK_DGRAM
247b077aed3SPierre Pronchery  * @cb: pointer to a function that receives the accepted socket and
248b077aed3SPierre Pronchery  *  should perform the communication with the connecting client.
249b077aed3SPierre Pronchery  * @context: pointer to memory that's passed verbatim to the cb function.
250b077aed3SPierre Pronchery  * @naccept: number of times an incoming connect should be accepted.  If -1,
251b077aed3SPierre Pronchery  *  unlimited number.
252b077aed3SPierre Pronchery  *
253b077aed3SPierre Pronchery  * This will create a socket and use it to listen to a host:port, or if
254b077aed3SPierre Pronchery  * family == AF_UNIX, to the path found in host, then start accepting
255b077aed3SPierre Pronchery  * incoming connections and run cb on the resulting socket.
256b077aed3SPierre Pronchery  *
257b077aed3SPierre Pronchery  * 0 on failure, something other on success.
258b077aed3SPierre Pronchery  */
do_server(int * accept_sock,const char * host,const char * port,int family,int type,int protocol,do_server_cb cb,unsigned char * context,int naccept,BIO * bio_s_out)259b077aed3SPierre Pronchery int do_server(int *accept_sock, const char *host, const char *port,
260b077aed3SPierre Pronchery               int family, int type, int protocol, do_server_cb cb,
261b077aed3SPierre Pronchery               unsigned char *context, int naccept, BIO *bio_s_out)
262b077aed3SPierre Pronchery {
263b077aed3SPierre Pronchery     int asock = 0;
264b077aed3SPierre Pronchery     int sock;
265b077aed3SPierre Pronchery     int i;
266b077aed3SPierre Pronchery     BIO_ADDRINFO *res = NULL;
267b077aed3SPierre Pronchery     const BIO_ADDRINFO *next;
268b077aed3SPierre Pronchery     int sock_family, sock_type, sock_protocol, sock_port;
269b077aed3SPierre Pronchery     const BIO_ADDR *sock_address;
270b077aed3SPierre Pronchery     int sock_family_fallback = AF_UNSPEC;
271b077aed3SPierre Pronchery     const BIO_ADDR *sock_address_fallback = NULL;
272b077aed3SPierre Pronchery     int sock_options = BIO_SOCK_REUSEADDR;
273b077aed3SPierre Pronchery     int ret = 0;
274b077aed3SPierre Pronchery 
275b077aed3SPierre Pronchery     if (BIO_sock_init() != 1)
276b077aed3SPierre Pronchery         return 0;
277b077aed3SPierre Pronchery 
278b077aed3SPierre Pronchery     if (!BIO_lookup_ex(host, port, BIO_LOOKUP_SERVER, family, type, protocol,
279b077aed3SPierre Pronchery                        &res)) {
280b077aed3SPierre Pronchery         ERR_print_errors(bio_err);
281b077aed3SPierre Pronchery         return 0;
282b077aed3SPierre Pronchery     }
283b077aed3SPierre Pronchery 
284b077aed3SPierre Pronchery     /* Admittedly, these checks are quite paranoid, we should not get
285b077aed3SPierre Pronchery      * anything in the BIO_ADDRINFO chain that we haven't asked for */
286b077aed3SPierre Pronchery     OPENSSL_assert((family == AF_UNSPEC || family == BIO_ADDRINFO_family(res))
287b077aed3SPierre Pronchery                    && (type == 0 || type == BIO_ADDRINFO_socktype(res))
288b077aed3SPierre Pronchery                    && (protocol == 0 || protocol == BIO_ADDRINFO_protocol(res)));
289b077aed3SPierre Pronchery 
290b077aed3SPierre Pronchery     sock_family = BIO_ADDRINFO_family(res);
291b077aed3SPierre Pronchery     sock_type = BIO_ADDRINFO_socktype(res);
292b077aed3SPierre Pronchery     sock_protocol = BIO_ADDRINFO_protocol(res);
293b077aed3SPierre Pronchery     sock_address = BIO_ADDRINFO_address(res);
294b077aed3SPierre Pronchery     next = BIO_ADDRINFO_next(res);
295b077aed3SPierre Pronchery #ifdef AF_INET6
296b077aed3SPierre Pronchery     if (sock_family == AF_INET6)
297b077aed3SPierre Pronchery         sock_options |= BIO_SOCK_V6_ONLY;
298b077aed3SPierre Pronchery     if (next != NULL
299b077aed3SPierre Pronchery             && BIO_ADDRINFO_socktype(next) == sock_type
300b077aed3SPierre Pronchery             && BIO_ADDRINFO_protocol(next) == sock_protocol) {
301b077aed3SPierre Pronchery         if (sock_family == AF_INET
302b077aed3SPierre Pronchery                 && BIO_ADDRINFO_family(next) == AF_INET6) {
303b077aed3SPierre Pronchery             /* In case AF_INET6 is returned but not supported by the
304b077aed3SPierre Pronchery              * kernel, retry with the first detected address family */
305b077aed3SPierre Pronchery             sock_family_fallback = sock_family;
306b077aed3SPierre Pronchery             sock_address_fallback = sock_address;
307b077aed3SPierre Pronchery             sock_family = AF_INET6;
308b077aed3SPierre Pronchery             sock_address = BIO_ADDRINFO_address(next);
309b077aed3SPierre Pronchery         } else if (sock_family == AF_INET6
310b077aed3SPierre Pronchery                    && BIO_ADDRINFO_family(next) == AF_INET) {
311b077aed3SPierre Pronchery             sock_options &= ~BIO_SOCK_V6_ONLY;
312b077aed3SPierre Pronchery         }
313b077aed3SPierre Pronchery     }
314b077aed3SPierre Pronchery #endif
315b077aed3SPierre Pronchery 
316b077aed3SPierre Pronchery     asock = BIO_socket(sock_family, sock_type, sock_protocol, 0);
317b077aed3SPierre Pronchery     if (asock == INVALID_SOCKET && sock_family_fallback != AF_UNSPEC) {
318b077aed3SPierre Pronchery         asock = BIO_socket(sock_family_fallback, sock_type, sock_protocol, 0);
319b077aed3SPierre Pronchery         sock_address = sock_address_fallback;
320b077aed3SPierre Pronchery     }
321b077aed3SPierre Pronchery     if (asock == INVALID_SOCKET
322b077aed3SPierre Pronchery         || !BIO_listen(asock, sock_address, sock_options)) {
323b077aed3SPierre Pronchery         BIO_ADDRINFO_free(res);
324b077aed3SPierre Pronchery         ERR_print_errors(bio_err);
325b077aed3SPierre Pronchery         if (asock != INVALID_SOCKET)
326b077aed3SPierre Pronchery             BIO_closesocket(asock);
327b077aed3SPierre Pronchery         goto end;
328b077aed3SPierre Pronchery     }
329b077aed3SPierre Pronchery 
330b077aed3SPierre Pronchery #ifndef OPENSSL_NO_SCTP
331b077aed3SPierre Pronchery     if (protocol == IPPROTO_SCTP) {
332b077aed3SPierre Pronchery         /*
333b077aed3SPierre Pronchery          * For SCTP we have to set various options on the socket prior to
334b077aed3SPierre Pronchery          * accepting. This is done automatically by BIO_new_dgram_sctp().
335b077aed3SPierre Pronchery          * We don't actually need the created BIO though so we free it again
336b077aed3SPierre Pronchery          * immediately.
337b077aed3SPierre Pronchery          */
338b077aed3SPierre Pronchery         BIO *tmpbio = BIO_new_dgram_sctp(asock, BIO_NOCLOSE);
339b077aed3SPierre Pronchery 
340b077aed3SPierre Pronchery         if (tmpbio == NULL) {
341b077aed3SPierre Pronchery             BIO_closesocket(asock);
342b077aed3SPierre Pronchery             ERR_print_errors(bio_err);
343b077aed3SPierre Pronchery             goto end;
344b077aed3SPierre Pronchery         }
345b077aed3SPierre Pronchery         BIO_free(tmpbio);
346b077aed3SPierre Pronchery     }
347b077aed3SPierre Pronchery #endif
348b077aed3SPierre Pronchery 
349b077aed3SPierre Pronchery     sock_port = BIO_ADDR_rawport(sock_address);
350b077aed3SPierre Pronchery 
351b077aed3SPierre Pronchery     BIO_ADDRINFO_free(res);
352b077aed3SPierre Pronchery     res = NULL;
353b077aed3SPierre Pronchery 
354b077aed3SPierre Pronchery     if (!report_server_accept(bio_s_out, asock, sock_port == 0, 0)) {
355b077aed3SPierre Pronchery         BIO_closesocket(asock);
356b077aed3SPierre Pronchery         ERR_print_errors(bio_err);
357b077aed3SPierre Pronchery         goto end;
358b077aed3SPierre Pronchery     }
359b077aed3SPierre Pronchery 
360b077aed3SPierre Pronchery     if (accept_sock != NULL)
361b077aed3SPierre Pronchery         *accept_sock = asock;
362b077aed3SPierre Pronchery     for (;;) {
363b077aed3SPierre Pronchery         char sink[64];
364b077aed3SPierre Pronchery         struct timeval timeout;
365b077aed3SPierre Pronchery         fd_set readfds;
366b077aed3SPierre Pronchery 
367b077aed3SPierre Pronchery         if (type == SOCK_STREAM) {
368b077aed3SPierre Pronchery             BIO_ADDR_free(ourpeer);
369b077aed3SPierre Pronchery             ourpeer = BIO_ADDR_new();
370b077aed3SPierre Pronchery             if (ourpeer == NULL) {
371b077aed3SPierre Pronchery                 BIO_closesocket(asock);
372b077aed3SPierre Pronchery                 ERR_print_errors(bio_err);
373b077aed3SPierre Pronchery                 goto end;
374b077aed3SPierre Pronchery             }
375b077aed3SPierre Pronchery             do {
376b077aed3SPierre Pronchery                 sock = BIO_accept_ex(asock, ourpeer, 0);
377b077aed3SPierre Pronchery             } while (sock < 0 && BIO_sock_should_retry(sock));
378b077aed3SPierre Pronchery             if (sock < 0) {
379b077aed3SPierre Pronchery                 ERR_print_errors(bio_err);
380b077aed3SPierre Pronchery                 BIO_closesocket(asock);
381b077aed3SPierre Pronchery                 break;
382b077aed3SPierre Pronchery             }
383*0d0c8621SEnji Cooper 
384*0d0c8621SEnji Cooper             if (naccept != -1)
385*0d0c8621SEnji Cooper                 naccept--;
386*0d0c8621SEnji Cooper             if (naccept == 0)
387*0d0c8621SEnji Cooper                 BIO_closesocket(asock);
388*0d0c8621SEnji Cooper 
389b077aed3SPierre Pronchery             BIO_set_tcp_ndelay(sock, 1);
390b077aed3SPierre Pronchery             i = (*cb)(sock, type, protocol, context);
391b077aed3SPierre Pronchery 
392b077aed3SPierre Pronchery             /*
393b077aed3SPierre Pronchery              * If we ended with an alert being sent, but still with data in the
394b077aed3SPierre Pronchery              * network buffer to be read, then calling BIO_closesocket() will
395b077aed3SPierre Pronchery              * result in a TCP-RST being sent. On some platforms (notably
396b077aed3SPierre Pronchery              * Windows) then this will result in the peer immediately abandoning
397b077aed3SPierre Pronchery              * the connection including any buffered alert data before it has
398b077aed3SPierre Pronchery              * had a chance to be read. Shutting down the sending side first,
399b077aed3SPierre Pronchery              * and then closing the socket sends TCP-FIN first followed by
400b077aed3SPierre Pronchery              * TCP-RST. This seems to allow the peer to read the alert data.
401b077aed3SPierre Pronchery              */
402b077aed3SPierre Pronchery             shutdown(sock, 1); /* SHUT_WR */
403b077aed3SPierre Pronchery             /*
404b077aed3SPierre Pronchery              * We just said we have nothing else to say, but it doesn't mean
405b077aed3SPierre Pronchery              * that the other side has nothing. It's even recommended to
406b077aed3SPierre Pronchery              * consume incoming data. [In testing context this ensures that
407b077aed3SPierre Pronchery              * alerts are passed on...]
408b077aed3SPierre Pronchery              */
409b077aed3SPierre Pronchery             timeout.tv_sec = 0;
410b077aed3SPierre Pronchery             timeout.tv_usec = 500000;  /* some extreme round-trip */
411b077aed3SPierre Pronchery             do {
412b077aed3SPierre Pronchery                 FD_ZERO(&readfds);
413b077aed3SPierre Pronchery                 openssl_fdset(sock, &readfds);
414b077aed3SPierre Pronchery             } while (select(sock + 1, &readfds, NULL, NULL, &timeout) > 0
415b077aed3SPierre Pronchery                      && readsocket(sock, sink, sizeof(sink)) > 0);
416b077aed3SPierre Pronchery 
417b077aed3SPierre Pronchery             BIO_closesocket(sock);
418b077aed3SPierre Pronchery         } else {
419*0d0c8621SEnji Cooper             if (naccept != -1)
420*0d0c8621SEnji Cooper                 naccept--;
421*0d0c8621SEnji Cooper 
422b077aed3SPierre Pronchery             i = (*cb)(asock, type, protocol, context);
423b077aed3SPierre Pronchery         }
424b077aed3SPierre Pronchery 
425b077aed3SPierre Pronchery         if (i < 0 || naccept == 0) {
426b077aed3SPierre Pronchery             BIO_closesocket(asock);
427b077aed3SPierre Pronchery             ret = i;
428b077aed3SPierre Pronchery             break;
429b077aed3SPierre Pronchery         }
430b077aed3SPierre Pronchery     }
431b077aed3SPierre Pronchery  end:
432b077aed3SPierre Pronchery # ifdef AF_UNIX
433b077aed3SPierre Pronchery     if (family == AF_UNIX)
434b077aed3SPierre Pronchery         unlink(host);
435b077aed3SPierre Pronchery # endif
436b077aed3SPierre Pronchery     BIO_ADDR_free(ourpeer);
437b077aed3SPierre Pronchery     ourpeer = NULL;
438b077aed3SPierre Pronchery     return ret;
439b077aed3SPierre Pronchery }
440b077aed3SPierre Pronchery 
do_ssl_shutdown(SSL * ssl)441b077aed3SPierre Pronchery void do_ssl_shutdown(SSL *ssl)
442b077aed3SPierre Pronchery {
443b077aed3SPierre Pronchery     int ret;
444b077aed3SPierre Pronchery 
445b077aed3SPierre Pronchery     do {
446b077aed3SPierre Pronchery         /* We only do unidirectional shutdown */
447b077aed3SPierre Pronchery         ret = SSL_shutdown(ssl);
448b077aed3SPierre Pronchery         if (ret < 0) {
449b077aed3SPierre Pronchery             switch (SSL_get_error(ssl, ret)) {
450b077aed3SPierre Pronchery             case SSL_ERROR_WANT_READ:
451b077aed3SPierre Pronchery             case SSL_ERROR_WANT_WRITE:
452b077aed3SPierre Pronchery             case SSL_ERROR_WANT_ASYNC:
453b077aed3SPierre Pronchery             case SSL_ERROR_WANT_ASYNC_JOB:
454b077aed3SPierre Pronchery                 /* We just do busy waiting. Nothing clever */
455b077aed3SPierre Pronchery                 continue;
456b077aed3SPierre Pronchery             }
457b077aed3SPierre Pronchery             ret = 0;
458b077aed3SPierre Pronchery         }
459b077aed3SPierre Pronchery     } while (ret < 0);
460b077aed3SPierre Pronchery }
461b077aed3SPierre Pronchery 
462b077aed3SPierre Pronchery #endif  /* OPENSSL_NO_SOCK */
463