xref: /openbsd/lib/libcrypto/bio/bss_conn.c (revision 5067ae9f)
1 /* $OpenBSD: bss_conn.c,v 1.33 2017/01/29 17:49:22 beck 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 <netinet/in.h>
62 
63 #include <errno.h>
64 #include <netdb.h>
65 #include <stdio.h>
66 #include <string.h>
67 #include <unistd.h>
68 
69 #include <openssl/bio.h>
70 #include <openssl/buffer.h>
71 #include <openssl/err.h>
72 
73 #define SOCKET_PROTOCOL IPPROTO_TCP
74 
75 typedef struct bio_connect_st {
76 	int state;
77 
78 	char *param_hostname;
79 	char *param_port;
80 	int nbio;
81 
82 	unsigned char ip[4];
83 	unsigned short port;
84 
85 	struct sockaddr_in them;
86 
87 	/* int socket; this will be kept in bio->num so that it is
88 	 * compatible with the bss_sock bio */
89 
90 	/* called when the connection is initially made
91 	 *  callback(BIO,state,ret);  The callback should return
92 	 * 'ret'.  state is for compatibility with the ssl info_callback */
93 	int (*info_callback)(const BIO *bio, int state, int ret);
94 } BIO_CONNECT;
95 
96 static int conn_write(BIO *h, const char *buf, int num);
97 static int conn_read(BIO *h, char *buf, int size);
98 static int conn_puts(BIO *h, const char *str);
99 static long conn_ctrl(BIO *h, int cmd, long arg1, void *arg2);
100 static int conn_new(BIO *h);
101 static int conn_free(BIO *data);
102 static long conn_callback_ctrl(BIO *h, int cmd, bio_info_cb *);
103 
104 static int conn_state(BIO *b, BIO_CONNECT *c);
105 static void conn_close_socket(BIO *data);
106 BIO_CONNECT *BIO_CONNECT_new(void);
107 void BIO_CONNECT_free(BIO_CONNECT *a);
108 
109 static BIO_METHOD methods_connectp = {
110 	.type = BIO_TYPE_CONNECT,
111 	.name = "socket connect",
112 	.bwrite = conn_write,
113 	.bread = conn_read,
114 	.bputs = conn_puts,
115 	.ctrl = conn_ctrl,
116 	.create = conn_new,
117 	.destroy = conn_free,
118 	.callback_ctrl = conn_callback_ctrl
119 };
120 
121 static int
122 conn_state(BIO *b, BIO_CONNECT *c)
123 {
124 	int ret = -1, i;
125 	unsigned long l;
126 	char *p, *q;
127 	int (*cb)(const BIO *, int, int) = NULL;
128 
129 	if (c->info_callback != NULL)
130 		cb = c->info_callback;
131 
132 	for (;;) {
133 		switch (c->state) {
134 		case BIO_CONN_S_BEFORE:
135 			p = c->param_hostname;
136 			if (p == NULL) {
137 				BIOerror(BIO_R_NO_HOSTNAME_SPECIFIED);
138 				goto exit_loop;
139 			}
140 			for (; *p != '\0'; p++) {
141 				if ((*p == ':') || (*p == '/'))
142 				break;
143 			}
144 
145 			i= *p;
146 			if ((i == ':') || (i == '/')) {
147 				*(p++) = '\0';
148 				if (i == ':') {
149 					for (q = p; *q; q++)
150 						if (*q == '/') {
151 							*q = '\0';
152 							break;
153 						}
154 					free(c->param_port);
155 					c->param_port = strdup(p);
156 				}
157 			}
158 
159 			if (c->param_port == NULL) {
160 				BIOerror(BIO_R_NO_PORT_SPECIFIED);
161 				ERR_asprintf_error_data("host=%s",
162 				    c->param_hostname);
163 				goto exit_loop;
164 			}
165 			c->state = BIO_CONN_S_GET_IP;
166 			break;
167 
168 		case BIO_CONN_S_GET_IP:
169 			if (BIO_get_host_ip(c->param_hostname, &(c->ip[0])) <= 0)
170 				goto exit_loop;
171 			c->state = BIO_CONN_S_GET_PORT;
172 			break;
173 
174 		case BIO_CONN_S_GET_PORT:
175 			if (c->param_port == NULL) {
176 				/* abort(); */
177 				goto exit_loop;
178 			} else if (BIO_get_port(c->param_port, &c->port) <= 0)
179 				goto exit_loop;
180 			c->state = BIO_CONN_S_CREATE_SOCKET;
181 			break;
182 
183 		case BIO_CONN_S_CREATE_SOCKET:
184 			/* now setup address */
185 			memset((char *)&c->them, 0, sizeof(c->them));
186 			c->them.sin_family = AF_INET;
187 			c->them.sin_port = htons((unsigned short)c->port);
188 			l = (unsigned long)
189 			    ((unsigned long)c->ip[0] << 24L)|
190 			    ((unsigned long)c->ip[1] << 16L)|
191 			    ((unsigned long)c->ip[2] << 8L)|
192 			    ((unsigned long)c->ip[3]);
193 			c->them.sin_addr.s_addr = htonl(l);
194 			c->state = BIO_CONN_S_CREATE_SOCKET;
195 
196 			ret = socket(AF_INET, SOCK_STREAM, SOCKET_PROTOCOL);
197 			if (ret == -1) {
198 				SYSerror(errno);
199 				ERR_asprintf_error_data("host=%s:%s",
200 				    c->param_hostname, c->param_port);
201 				BIOerror(BIO_R_UNABLE_TO_CREATE_SOCKET);
202 				goto exit_loop;
203 			}
204 			b->num = ret;
205 			c->state = BIO_CONN_S_NBIO;
206 			break;
207 
208 		case BIO_CONN_S_NBIO:
209 			if (c->nbio) {
210 				if (!BIO_socket_nbio(b->num, 1)) {
211 					BIOerror(BIO_R_ERROR_SETTING_NBIO);
212 					ERR_asprintf_error_data("host=%s:%s",
213 					    c->param_hostname, c->param_port);
214 					goto exit_loop;
215 				}
216 			}
217 			c->state = BIO_CONN_S_CONNECT;
218 
219 #if defined(SO_KEEPALIVE)
220 			i = 1;
221 			i = setsockopt(b->num, SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i));
222 			if (i < 0) {
223 				SYSerror(errno);
224 				ERR_asprintf_error_data("host=%s:%s",
225 				    c->param_hostname, c->param_port);
226 				BIOerror(BIO_R_KEEPALIVE);
227 				goto exit_loop;
228 			}
229 #endif
230 			break;
231 
232 		case BIO_CONN_S_CONNECT:
233 			BIO_clear_retry_flags(b);
234 			ret = connect(b->num,
235 			(struct sockaddr *)&c->them,
236 			sizeof(c->them));
237 			b->retry_reason = 0;
238 			if (ret < 0) {
239 				if (BIO_sock_should_retry(ret)) {
240 					BIO_set_retry_special(b);
241 					c->state = BIO_CONN_S_BLOCKED_CONNECT;
242 					b->retry_reason = BIO_RR_CONNECT;
243 				} else {
244 					SYSerror(errno);
245 					ERR_asprintf_error_data("host=%s:%s",
246 					    c->param_hostname, c->param_port);
247 					BIOerror(BIO_R_CONNECT_ERROR);
248 				}
249 				goto exit_loop;
250 			} else
251 				c->state = BIO_CONN_S_OK;
252 			break;
253 
254 		case BIO_CONN_S_BLOCKED_CONNECT:
255 			i = BIO_sock_error(b->num);
256 			if (i) {
257 				BIO_clear_retry_flags(b);
258 				SYSerror(i);
259 				ERR_asprintf_error_data("host=%s:%s",
260 				    c->param_hostname, c->param_port);
261 				BIOerror(BIO_R_NBIO_CONNECT_ERROR);
262 				ret = 0;
263 				goto exit_loop;
264 			} else
265 				c->state = BIO_CONN_S_OK;
266 			break;
267 
268 		case BIO_CONN_S_OK:
269 			ret = 1;
270 			goto exit_loop;
271 		default:
272 			/* abort(); */
273 			goto exit_loop;
274 		}
275 
276 		if (cb != NULL) {
277 			if (!(ret = cb((BIO *)b, c->state, ret)))
278 				goto end;
279 		}
280 	}
281 
282 	/* Loop does not exit */
283 exit_loop:
284 	if (cb != NULL)
285 		ret = cb((BIO *)b, c->state, ret);
286 end:
287 	return (ret);
288 }
289 
290 BIO_CONNECT *
291 BIO_CONNECT_new(void)
292 {
293 	BIO_CONNECT *ret;
294 
295 	if ((ret = malloc(sizeof(BIO_CONNECT))) == NULL)
296 		return (NULL);
297 	ret->state = BIO_CONN_S_BEFORE;
298 	ret->param_hostname = NULL;
299 	ret->param_port = NULL;
300 	ret->info_callback = NULL;
301 	ret->nbio = 0;
302 	ret->ip[0] = 0;
303 	ret->ip[1] = 0;
304 	ret->ip[2] = 0;
305 	ret->ip[3] = 0;
306 	ret->port = 0;
307 	memset((char *)&ret->them, 0, sizeof(ret->them));
308 	return (ret);
309 }
310 
311 void
312 BIO_CONNECT_free(BIO_CONNECT *a)
313 {
314 	if (a == NULL)
315 		return;
316 
317 	free(a->param_hostname);
318 	free(a->param_port);
319 	free(a);
320 }
321 
322 BIO_METHOD *
323 BIO_s_connect(void)
324 {
325 	return (&methods_connectp);
326 }
327 
328 static int
329 conn_new(BIO *bi)
330 {
331 	bi->init = 0;
332 	bi->num = -1;
333 	bi->flags = 0;
334 	if ((bi->ptr = (char *)BIO_CONNECT_new()) == NULL)
335 		return (0);
336 	else
337 		return (1);
338 }
339 
340 static void
341 conn_close_socket(BIO *bio)
342 {
343 	BIO_CONNECT *c;
344 
345 	c = (BIO_CONNECT *)bio->ptr;
346 	if (bio->num != -1) {
347 		/* Only do a shutdown if things were established */
348 		if (c->state == BIO_CONN_S_OK)
349 			shutdown(bio->num, SHUT_RDWR);
350 		close(bio->num);
351 		bio->num = -1;
352 	}
353 }
354 
355 static int
356 conn_free(BIO *a)
357 {
358 	BIO_CONNECT *data;
359 
360 	if (a == NULL)
361 		return (0);
362 	data = (BIO_CONNECT *)a->ptr;
363 
364 	if (a->shutdown) {
365 		conn_close_socket(a);
366 		BIO_CONNECT_free(data);
367 		a->ptr = NULL;
368 		a->flags = 0;
369 		a->init = 0;
370 	}
371 	return (1);
372 }
373 
374 static int
375 conn_read(BIO *b, char *out, int outl)
376 {
377 	int ret = 0;
378 	BIO_CONNECT *data;
379 
380 	data = (BIO_CONNECT *)b->ptr;
381 	if (data->state != BIO_CONN_S_OK) {
382 		ret = conn_state(b, data);
383 		if (ret <= 0)
384 			return (ret);
385 	}
386 
387 	if (out != NULL) {
388 		errno = 0;
389 		ret = read(b->num, out, outl);
390 		BIO_clear_retry_flags(b);
391 		if (ret <= 0) {
392 			if (BIO_sock_should_retry(ret))
393 				BIO_set_retry_read(b);
394 		}
395 	}
396 	return (ret);
397 }
398 
399 static int
400 conn_write(BIO *b, const char *in, int inl)
401 {
402 	int ret;
403 	BIO_CONNECT *data;
404 
405 	data = (BIO_CONNECT *)b->ptr;
406 	if (data->state != BIO_CONN_S_OK) {
407 		ret = conn_state(b, data);
408 		if (ret <= 0)
409 			return (ret);
410 	}
411 
412 	errno = 0;
413 	ret = write(b->num, in, inl);
414 	BIO_clear_retry_flags(b);
415 	if (ret <= 0) {
416 		if (BIO_sock_should_retry(ret))
417 			BIO_set_retry_write(b);
418 	}
419 	return (ret);
420 }
421 
422 static long
423 conn_ctrl(BIO *b, int cmd, long num, void *ptr)
424 {
425 	BIO *dbio;
426 	int *ip;
427 	const char **pptr;
428 	long ret = 1;
429 	BIO_CONNECT *data;
430 
431 	data = (BIO_CONNECT *)b->ptr;
432 
433 	switch (cmd) {
434 	case BIO_CTRL_RESET:
435 		ret = 0;
436 		data->state = BIO_CONN_S_BEFORE;
437 		conn_close_socket(b);
438 		b->flags = 0;
439 		break;
440 	case BIO_C_DO_STATE_MACHINE:
441 		/* use this one to start the connection */
442 		if (data->state != BIO_CONN_S_OK)
443 			ret = (long)conn_state(b, data);
444 		else
445 			ret = 1;
446 		break;
447 	case BIO_C_GET_CONNECT:
448 		if (ptr != NULL) {
449 			pptr = (const char **)ptr;
450 			if (num == 0) {
451 				*pptr = data->param_hostname;
452 
453 			} else if (num == 1) {
454 				*pptr = data->param_port;
455 			} else if (num == 2) {
456 				*pptr = (char *)&(data->ip[0]);
457 			} else if (num == 3) {
458 				*((int *)ptr) = data->port;
459 			}
460 			if ((!b->init) || (ptr == NULL))
461 				*pptr = "not initialized";
462 			ret = 1;
463 		}
464 		break;
465 	case BIO_C_SET_CONNECT:
466 		if (ptr != NULL) {
467 			b->init = 1;
468 			if (num == 0) {
469 				free(data->param_hostname);
470 				data->param_hostname = strdup(ptr);
471 			} else if (num == 1) {
472 				free(data->param_port);
473 				data->param_port = strdup(ptr);
474 			} else if (num == 2) {
475 				unsigned char *p = ptr;
476 				free(data->param_hostname);
477 				if (asprintf(&data->param_hostname,
478 					"%u.%u.%u.%u", p[0], p[1],
479 					p[2], p[3]) == -1)
480 					data->param_hostname = NULL;
481 				memcpy(&(data->ip[0]), ptr, 4);
482 			} else if (num == 3) {
483 				free(data->param_port);
484 				data->port= *(int *)ptr;
485 				if (asprintf(&data->param_port, "%d",
486 					data->port) == -1)
487 					data->param_port = NULL;
488 			}
489 		}
490 		break;
491 	case BIO_C_SET_NBIO:
492 		data->nbio = (int)num;
493 		break;
494 	case BIO_C_GET_FD:
495 		if (b->init) {
496 			ip = (int *)ptr;
497 			if (ip != NULL)
498 				*ip = b->num;
499 			ret = b->num;
500 		} else
501 			ret = -1;
502 		break;
503 	case BIO_CTRL_GET_CLOSE:
504 		ret = b->shutdown;
505 		break;
506 	case BIO_CTRL_SET_CLOSE:
507 		b->shutdown = (int)num;
508 		break;
509 	case BIO_CTRL_PENDING:
510 	case BIO_CTRL_WPENDING:
511 		ret = 0;
512 		break;
513 	case BIO_CTRL_FLUSH:
514 		break;
515 	case BIO_CTRL_DUP:
516 		{
517 			dbio = (BIO *)ptr;
518 			if (data->param_port)
519 				BIO_set_conn_port(dbio, data->param_port);
520 			if (data->param_hostname)
521 				BIO_set_conn_hostname(dbio,
522 				    data->param_hostname);
523 			BIO_set_nbio(dbio, data->nbio);
524 			/* FIXME: the cast of the function seems unlikely to be a good idea */
525 			(void)BIO_set_info_callback(dbio,
526 			    (bio_info_cb *)data->info_callback);
527 		}
528 		break;
529 	case BIO_CTRL_SET_CALLBACK:
530 		{
531 #if 0 /* FIXME: Should this be used?  -- Richard Levitte */
532 			BIOerror(ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
533 			ret = -1;
534 #else
535 			ret = 0;
536 #endif
537 		}
538 		break;
539 	case BIO_CTRL_GET_CALLBACK:
540 		{
541 			int (**fptr)(const BIO *bio, int state, int xret);
542 
543 			fptr = (int (**)(const BIO *bio, int state, int xret))ptr;
544 			*fptr = data->info_callback;
545 		}
546 		break;
547 	default:
548 		ret = 0;
549 		break;
550 	}
551 	return (ret);
552 }
553 
554 static long
555 conn_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
556 {
557 	long ret = 1;
558 	BIO_CONNECT *data;
559 
560 	data = (BIO_CONNECT *)b->ptr;
561 
562 	switch (cmd) {
563 	case BIO_CTRL_SET_CALLBACK:
564 		{
565 			data->info_callback = (int (*)(const struct bio_st *, int, int))fp;
566 		}
567 		break;
568 	default:
569 		ret = 0;
570 		break;
571 	}
572 	return (ret);
573 }
574 
575 static int
576 conn_puts(BIO *bp, const char *str)
577 {
578 	int n, ret;
579 
580 	n = strlen(str);
581 	ret = conn_write(bp, str, n);
582 	return (ret);
583 }
584 
585 BIO *
586 BIO_new_connect(char *str)
587 {
588 	BIO *ret;
589 
590 	ret = BIO_new(BIO_s_connect());
591 	if (ret == NULL)
592 		return (NULL);
593 	if (BIO_set_conn_hostname(ret, str))
594 		return (ret);
595 	else {
596 		BIO_free(ret);
597 		return (NULL);
598 	}
599 }
600 
601