1 /* $OpenBSD: bss_acpt.c,v 1.30 2022/01/07 09:02:17 tb Exp $ */
2 /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3  * All rights reserved.
4  *
5  * This package is an SSL implementation written
6  * by Eric Young (eay@cryptsoft.com).
7  * The implementation was written so as to conform with Netscapes SSL.
8  *
9  * This library is free for commercial and non-commercial use as long as
10  * the following conditions are aheared to.  The following conditions
11  * apply to all code found in this distribution, be it the RC4, RSA,
12  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13  * included with this distribution is covered by the same copyright terms
14  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15  *
16  * Copyright remains Eric Young's, and as such any Copyright notices in
17  * the code are not to be removed.
18  * If this package is used in a product, Eric Young should be given attribution
19  * as the author of the parts of the library used.
20  * This can be in the form of a textual message at program startup or
21  * in documentation (online or textual) provided with the package.
22  *
23  * Redistribution and use in source and binary forms, with or without
24  * modification, are permitted provided that the following conditions
25  * are met:
26  * 1. Redistributions of source code must retain the copyright
27  *    notice, this list of conditions and the following disclaimer.
28  * 2. Redistributions in binary form must reproduce the above copyright
29  *    notice, this list of conditions and the following disclaimer in the
30  *    documentation and/or other materials provided with the distribution.
31  * 3. All advertising materials mentioning features or use of this software
32  *    must display the following acknowledgement:
33  *    "This product includes cryptographic software written by
34  *     Eric Young (eay@cryptsoft.com)"
35  *    The word 'cryptographic' can be left out if the rouines from the library
36  *    being used are not cryptographic related :-).
37  * 4. If you include any Windows specific code (or a derivative thereof) from
38  *    the apps directory (application code) you must include an acknowledgement:
39  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40  *
41  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51  * SUCH DAMAGE.
52  *
53  * The licence and distribution terms for any publically available version or
54  * derivative of this code cannot be changed.  i.e. this code cannot simply be
55  * copied and put under another distribution licence
56  * [including the GNU Public Licence.]
57  */
58 
59 #include <sys/socket.h>
60 
61 #include <errno.h>
62 #include <stdio.h>
63 #include <string.h>
64 #include <unistd.h>
65 
66 #include <openssl/bio.h>
67 #include <openssl/buffer.h>
68 #include <openssl/err.h>
69 
70 #include "bio_local.h"
71 
72 #define SOCKET_PROTOCOL IPPROTO_TCP
73 
74 typedef struct bio_accept_st {
75 	int state;
76 	char *param_addr;
77 
78 	int accept_sock;
79 	int accept_nbio;
80 
81 	char *addr;
82 	int nbio;
83 	/* If 0, it means normal, if 1, do a connect on bind failure,
84 	 * and if there is no-one listening, bind with SO_REUSEADDR.
85 	 * If 2, always use SO_REUSEADDR. */
86 	int bind_mode;
87 	BIO *bio_chain;
88 } BIO_ACCEPT;
89 
90 static int acpt_write(BIO *h, const char *buf, int num);
91 static int acpt_read(BIO *h, char *buf, int size);
92 static int acpt_puts(BIO *h, const char *str);
93 static long acpt_ctrl(BIO *h, int cmd, long arg1, void *arg2);
94 static int acpt_new(BIO *h);
95 static int acpt_free(BIO *data);
96 static int acpt_state(BIO *b, BIO_ACCEPT *c);
97 static void acpt_close_socket(BIO *data);
98 static BIO_ACCEPT *BIO_ACCEPT_new(void );
99 static void BIO_ACCEPT_free(BIO_ACCEPT *a);
100 
101 #define ACPT_S_BEFORE			1
102 #define ACPT_S_GET_ACCEPT_SOCKET	2
103 #define ACPT_S_OK			3
104 
105 static const BIO_METHOD methods_acceptp = {
106 	.type = BIO_TYPE_ACCEPT,
107 	.name = "socket accept",
108 	.bwrite = acpt_write,
109 	.bread = acpt_read,
110 	.bputs = acpt_puts,
111 	.ctrl = acpt_ctrl,
112 	.create = acpt_new,
113 	.destroy = acpt_free
114 };
115 
116 const BIO_METHOD *
BIO_s_accept(void)117 BIO_s_accept(void)
118 {
119 	return (&methods_acceptp);
120 }
121 
122 static int
acpt_new(BIO * bi)123 acpt_new(BIO *bi)
124 {
125 	BIO_ACCEPT *ba;
126 
127 	bi->init = 0;
128 	bi->num = -1;
129 	bi->flags = 0;
130 	if ((ba = BIO_ACCEPT_new()) == NULL)
131 		return (0);
132 	bi->ptr = (char *)ba;
133 	ba->state = ACPT_S_BEFORE;
134 	bi->shutdown = 1;
135 	return (1);
136 }
137 
138 static BIO_ACCEPT *
BIO_ACCEPT_new(void)139 BIO_ACCEPT_new(void)
140 {
141 	BIO_ACCEPT *ret;
142 
143 	if ((ret = calloc(1, sizeof(BIO_ACCEPT))) == NULL)
144 		return (NULL);
145 	ret->accept_sock = -1;
146 	ret->bind_mode = BIO_BIND_NORMAL;
147 	return (ret);
148 }
149 
150 static void
BIO_ACCEPT_free(BIO_ACCEPT * a)151 BIO_ACCEPT_free(BIO_ACCEPT *a)
152 {
153 	if (a == NULL)
154 		return;
155 
156 	free(a->param_addr);
157 	free(a->addr);
158 	BIO_free(a->bio_chain);
159 	free(a);
160 }
161 
162 static void
acpt_close_socket(BIO * bio)163 acpt_close_socket(BIO *bio)
164 {
165 	BIO_ACCEPT *c;
166 
167 	c = (BIO_ACCEPT *)bio->ptr;
168 	if (c->accept_sock != -1) {
169 		shutdown(c->accept_sock, SHUT_RDWR);
170 		close(c->accept_sock);
171 		c->accept_sock = -1;
172 		bio->num = -1;
173 	}
174 }
175 
176 static int
acpt_free(BIO * a)177 acpt_free(BIO *a)
178 {
179 	BIO_ACCEPT *data;
180 
181 	if (a == NULL)
182 		return (0);
183 	data = (BIO_ACCEPT *)a->ptr;
184 
185 	if (a->shutdown) {
186 		acpt_close_socket(a);
187 		BIO_ACCEPT_free(data);
188 		a->ptr = NULL;
189 		a->flags = 0;
190 		a->init = 0;
191 	}
192 	return (1);
193 }
194 
195 static int
acpt_state(BIO * b,BIO_ACCEPT * c)196 acpt_state(BIO *b, BIO_ACCEPT *c)
197 {
198 	BIO *bio = NULL, *dbio;
199 	int s = -1;
200 	int i;
201 
202 again:
203 	switch (c->state) {
204 	case ACPT_S_BEFORE:
205 		if (c->param_addr == NULL) {
206 			BIOerror(BIO_R_NO_ACCEPT_PORT_SPECIFIED);
207 			return (-1);
208 		}
209 		s = BIO_get_accept_socket(c->param_addr, c->bind_mode);
210 		if (s == -1)
211 			return (-1);
212 
213 		if (c->accept_nbio) {
214 			if (!BIO_socket_nbio(s, 1)) {
215 				close(s);
216 				BIOerror(BIO_R_ERROR_SETTING_NBIO_ON_ACCEPT_SOCKET);
217 				return (-1);
218 			}
219 		}
220 		c->accept_sock = s;
221 		b->num = s;
222 		c->state = ACPT_S_GET_ACCEPT_SOCKET;
223 		return (1);
224 		/* break; */
225 	case ACPT_S_GET_ACCEPT_SOCKET:
226 		if (b->next_bio != NULL) {
227 			c->state = ACPT_S_OK;
228 			goto again;
229 		}
230 		BIO_clear_retry_flags(b);
231 		b->retry_reason = 0;
232 		i = BIO_accept(c->accept_sock, &(c->addr));
233 
234 		/* -2 return means we should retry */
235 		if (i == -2) {
236 			BIO_set_retry_special(b);
237 			b->retry_reason = BIO_RR_ACCEPT;
238 			return -1;
239 		}
240 
241 		if (i < 0)
242 			return (i);
243 
244 		bio = BIO_new_socket(i, BIO_CLOSE);
245 		if (bio == NULL)
246 			goto err;
247 
248 		BIO_set_callback(bio, BIO_get_callback(b));
249 		BIO_set_callback_arg(bio, BIO_get_callback_arg(b));
250 
251 		if (c->nbio) {
252 			if (!BIO_socket_nbio(i, 1)) {
253 				BIOerror(BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET);
254 				goto err;
255 			}
256 		}
257 
258 		/* If the accept BIO has an bio_chain, we dup it and
259 		 * put the new socket at the end. */
260 		if (c->bio_chain != NULL) {
261 			if ((dbio = BIO_dup_chain(c->bio_chain)) == NULL)
262 				goto err;
263 			if (!BIO_push(dbio, bio)) goto err;
264 				bio = dbio;
265 		}
266 		if (BIO_push(b, bio)
267 			== NULL) goto err;
268 
269 		c->state = ACPT_S_OK;
270 		return (1);
271 
272 err:
273 		if (bio != NULL)
274 			BIO_free(bio);
275 		return (0);
276 		/* break; */
277 	case ACPT_S_OK:
278 		if (b->next_bio == NULL) {
279 			c->state = ACPT_S_GET_ACCEPT_SOCKET;
280 			goto again;
281 		}
282 		return (1);
283 		/* break; */
284 	default:
285 		return (0);
286 		/* break; */
287 	}
288 }
289 
290 static int
acpt_read(BIO * b,char * out,int outl)291 acpt_read(BIO *b, char *out, int outl)
292 {
293 	int ret = 0;
294 	BIO_ACCEPT *data;
295 
296 	BIO_clear_retry_flags(b);
297 	data = (BIO_ACCEPT *)b->ptr;
298 
299 	while (b->next_bio == NULL) {
300 		ret = acpt_state(b, data);
301 		if (ret <= 0)
302 			return (ret);
303 	}
304 
305 	ret = BIO_read(b->next_bio, out, outl);
306 	BIO_copy_next_retry(b);
307 	return (ret);
308 }
309 
310 static int
acpt_write(BIO * b,const char * in,int inl)311 acpt_write(BIO *b, const char *in, int inl)
312 {
313 	int ret;
314 	BIO_ACCEPT *data;
315 
316 	BIO_clear_retry_flags(b);
317 	data = (BIO_ACCEPT *)b->ptr;
318 
319 	while (b->next_bio == NULL) {
320 		ret = acpt_state(b, data);
321 		if (ret <= 0)
322 			return (ret);
323 	}
324 
325 	ret = BIO_write(b->next_bio, in, inl);
326 	BIO_copy_next_retry(b);
327 	return (ret);
328 }
329 
330 static long
acpt_ctrl(BIO * b,int cmd,long num,void * ptr)331 acpt_ctrl(BIO *b, int cmd, long num, void *ptr)
332 {
333 	int *ip;
334 	long ret = 1;
335 	BIO_ACCEPT *data;
336 	char **pp;
337 
338 	data = (BIO_ACCEPT *)b->ptr;
339 
340 	switch (cmd) {
341 	case BIO_CTRL_RESET:
342 		ret = 0;
343 		data->state = ACPT_S_BEFORE;
344 		acpt_close_socket(b);
345 		b->flags = 0;
346 		break;
347 	case BIO_C_DO_STATE_MACHINE:
348 		/* use this one to start the connection */
349 		ret = (long)acpt_state(b, data);
350 		break;
351 	case BIO_C_SET_ACCEPT:
352 		if (ptr != NULL) {
353 			if (num == 0) {
354 				b->init = 1;
355 				free(data->param_addr);
356 				data->param_addr = strdup(ptr);
357 			} else if (num == 1) {
358 				data->accept_nbio = (ptr != NULL);
359 			} else if (num == 2) {
360 				BIO_free(data->bio_chain);
361 				data->bio_chain = (BIO *)ptr;
362 			}
363 		}
364 		break;
365 	case BIO_C_SET_NBIO:
366 		data->nbio = (int)num;
367 		break;
368 	case BIO_C_SET_FD:
369 		b->init = 1;
370 		b->num= *((int *)ptr);
371 		data->accept_sock = b->num;
372 		data->state = ACPT_S_GET_ACCEPT_SOCKET;
373 		b->shutdown = (int)num;
374 		b->init = 1;
375 		break;
376 	case BIO_C_GET_FD:
377 		if (b->init) {
378 			ip = (int *)ptr;
379 			if (ip != NULL)
380 				*ip = data->accept_sock;
381 			ret = data->accept_sock;
382 		} else
383 			ret = -1;
384 		break;
385 	case BIO_C_GET_ACCEPT:
386 		if (b->init) {
387 			if (ptr != NULL) {
388 				pp = (char **)ptr;
389 				*pp = data->param_addr;
390 			} else
391 				ret = -1;
392 		} else
393 			ret = -1;
394 		break;
395 	case BIO_CTRL_GET_CLOSE:
396 		ret = b->shutdown;
397 		break;
398 	case BIO_CTRL_SET_CLOSE:
399 		b->shutdown = (int)num;
400 		break;
401 	case BIO_CTRL_PENDING:
402 	case BIO_CTRL_WPENDING:
403 		ret = 0;
404 		break;
405 	case BIO_CTRL_FLUSH:
406 		break;
407 	case BIO_C_SET_BIND_MODE:
408 		data->bind_mode = (int)num;
409 		break;
410 	case BIO_C_GET_BIND_MODE:
411 		ret = (long)data->bind_mode;
412 		break;
413 	case BIO_CTRL_DUP:
414 /*		dbio=(BIO *)ptr;
415 		if (data->param_port) EAY EAY
416 			BIO_set_port(dbio,data->param_port);
417 		if (data->param_hostname)
418 			BIO_set_hostname(dbio,data->param_hostname);
419 		BIO_set_nbio(dbio,data->nbio);
420 */
421 		break;
422 
423 	default:
424 		ret = 0;
425 		break;
426 	}
427 	return (ret);
428 }
429 
430 static int
acpt_puts(BIO * bp,const char * str)431 acpt_puts(BIO *bp, const char *str)
432 {
433 	int n, ret;
434 
435 	n = strlen(str);
436 	ret = acpt_write(bp, str, n);
437 	return (ret);
438 }
439 
440 BIO *
BIO_new_accept(const char * str)441 BIO_new_accept(const char *str)
442 {
443 	BIO *ret;
444 
445 	ret = BIO_new(BIO_s_accept());
446 	if (ret == NULL)
447 		return (NULL);
448 	if (BIO_set_accept_port(ret, str))
449 		return (ret);
450 	else {
451 		BIO_free(ret);
452 		return (NULL);
453 	}
454 }
455 
456