xref: /openbsd/lib/libcrypto/bio/bss_conn.c (revision 0b1e3033)
1 /* crypto/bio/bss_conn.c */
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 <stdio.h>
60 #include <errno.h>
61 #include <unistd.h>
62 #include "cryptlib.h"
63 #include <openssl/bio.h>
64 #include <netdb.h>
65 #include <sys/socket.h>
66 #include <netinet/in.h>
67 
68 
69 #define SOCKET_PROTOCOL IPPROTO_TCP
70 
71 typedef struct bio_connect_st {
72 	int state;
73 
74 	char *param_hostname;
75 	char *param_port;
76 	int nbio;
77 
78 	unsigned char ip[4];
79 	unsigned short port;
80 
81 	struct sockaddr_in them;
82 
83 	/* int socket; this will be kept in bio->num so that it is
84 	 * compatible with the bss_sock bio */
85 
86 	/* called when the connection is initially made
87 	 *  callback(BIO,state,ret);  The callback should return
88 	 * 'ret'.  state is for compatibility with the ssl info_callback */
89 	int (*info_callback)(const BIO *bio, int state, int ret);
90 } BIO_CONNECT;
91 
92 static int conn_write(BIO *h, const char *buf, int num);
93 static int conn_read(BIO *h, char *buf, int size);
94 static int conn_puts(BIO *h, const char *str);
95 static long conn_ctrl(BIO *h, int cmd, long arg1, void *arg2);
96 static int conn_new(BIO *h);
97 static int conn_free(BIO *data);
98 static long conn_callback_ctrl(BIO *h, int cmd, bio_info_cb *);
99 
100 static int conn_state(BIO *b, BIO_CONNECT *c);
101 static void conn_close_socket(BIO *data);
102 BIO_CONNECT *BIO_CONNECT_new(void);
103 void BIO_CONNECT_free(BIO_CONNECT *a);
104 
105 static BIO_METHOD methods_connectp = {
106 	.type = BIO_TYPE_CONNECT,
107 	.name = "socket connect",
108 	.bwrite = conn_write,
109 	.bread = conn_read,
110 	.bputs = conn_puts,
111 	.ctrl = conn_ctrl,
112 	.create = conn_new,
113 	.destroy = conn_free,
114 	.callback_ctrl = conn_callback_ctrl
115 };
116 
117 static int
118 conn_state(BIO *b, BIO_CONNECT *c)
119 {
120 	int ret = -1, i;
121 	unsigned long l;
122 	char *p, *q;
123 	int (*cb)(const BIO *, int, int) = NULL;
124 
125 	if (c->info_callback != NULL)
126 		cb = c->info_callback;
127 
128 	for (;;) {
129 		switch (c->state) {
130 		case BIO_CONN_S_BEFORE:
131 			p = c->param_hostname;
132 			if (p == NULL) {
133 				BIOerr(BIO_F_CONN_STATE, BIO_R_NO_HOSTNAME_SPECIFIED);
134 				goto exit_loop;
135 			}
136 			for (; *p != '\0'; p++) {
137 				if ((*p == ':') || (*p == '/'))
138 				break;
139 			}
140 
141 			i= *p;
142 			if ((i == ':') || (i == '/')) {
143 				*(p++) = '\0';
144 				if (i == ':') {
145 					for (q = p; *q; q++)
146 						if (*q == '/') {
147 							*q = '\0';
148 							break;
149 						}
150 					free(c->param_port);
151 					c->param_port = BUF_strdup(p);
152 				}
153 			}
154 
155 			if (c->param_port == NULL) {
156 				BIOerr(BIO_F_CONN_STATE, BIO_R_NO_PORT_SPECIFIED);
157 				ERR_asprintf_error_data("host=%s",
158 				    c->param_hostname);
159 				goto exit_loop;
160 			}
161 			c->state = BIO_CONN_S_GET_IP;
162 			break;
163 
164 		case BIO_CONN_S_GET_IP:
165 			if (BIO_get_host_ip(c->param_hostname, &(c->ip[0])) <= 0)
166 				goto exit_loop;
167 			c->state = BIO_CONN_S_GET_PORT;
168 			break;
169 
170 		case BIO_CONN_S_GET_PORT:
171 			if (c->param_port == NULL) {
172 				/* abort(); */
173 				goto exit_loop;
174 			} else if (BIO_get_port(c->param_port, &c->port) <= 0)
175 				goto exit_loop;
176 			c->state = BIO_CONN_S_CREATE_SOCKET;
177 			break;
178 
179 		case BIO_CONN_S_CREATE_SOCKET:
180 			/* now setup address */
181 			memset((char *)&c->them, 0, sizeof(c->them));
182 			c->them.sin_family = AF_INET;
183 			c->them.sin_port = htons((unsigned short)c->port);
184 			l = (unsigned long)
185 			    ((unsigned long)c->ip[0] << 24L)|
186 			    ((unsigned long)c->ip[1] << 16L)|
187 			    ((unsigned long)c->ip[2] << 8L)|
188 			    ((unsigned long)c->ip[3]);
189 			c->them.sin_addr.s_addr = htonl(l);
190 			c->state = BIO_CONN_S_CREATE_SOCKET;
191 
192 			ret = socket(AF_INET, SOCK_STREAM, SOCKET_PROTOCOL);
193 			if (ret == -1) {
194 				SYSerr(SYS_F_SOCKET, errno);
195 				ERR_asprintf_error_data("host=%s:%s",
196 				    c->param_hostname, c->param_port);
197 				BIOerr(BIO_F_CONN_STATE,
198 				    BIO_R_UNABLE_TO_CREATE_SOCKET);
199 				goto exit_loop;
200 			}
201 			b->num = ret;
202 			c->state = BIO_CONN_S_NBIO;
203 			break;
204 
205 		case BIO_CONN_S_NBIO:
206 			if (c->nbio) {
207 				if (!BIO_socket_nbio(b->num, 1)) {
208 					BIOerr(BIO_F_CONN_STATE,
209 					    BIO_R_ERROR_SETTING_NBIO);
210 					ERR_asprintf_error_data("host=%s:%s",
211 					    c->param_hostname, c->param_port);
212 					goto exit_loop;
213 				}
214 			}
215 			c->state = BIO_CONN_S_CONNECT;
216 
217 #if defined(SO_KEEPALIVE) && !defined(OPENSSL_SYS_MPE)
218 			i = 1;
219 			i = setsockopt(b->num, SOL_SOCKET, SO_KEEPALIVE,(char *)&i, sizeof(i));
220 			if (i < 0) {
221 				SYSerr(SYS_F_SOCKET, errno);
222 				ERR_asprintf_error_data("host=%s:%s",
223 				    c->param_hostname, c->param_port);
224 				BIOerr(BIO_F_CONN_STATE, BIO_R_KEEPALIVE);
225 				goto exit_loop;
226 			}
227 #endif
228 			break;
229 
230 		case BIO_CONN_S_CONNECT:
231 			BIO_clear_retry_flags(b);
232 			ret = connect(b->num,
233 			(struct sockaddr *)&c->them,
234 			sizeof(c->them));
235 			b->retry_reason = 0;
236 			if (ret < 0) {
237 				if (BIO_sock_should_retry(ret)) {
238 					BIO_set_retry_special(b);
239 					c->state = BIO_CONN_S_BLOCKED_CONNECT;
240 					b->retry_reason = BIO_RR_CONNECT;
241 				} else {
242 					SYSerr(SYS_F_CONNECT, errno);
243 					ERR_asprintf_error_data("host=%s:%s",
244 					    c->param_hostname, c->param_port);
245 					BIOerr(BIO_F_CONN_STATE,
246 					    BIO_R_CONNECT_ERROR);
247 				}
248 				goto exit_loop;
249 			} else
250 				c->state = BIO_CONN_S_OK;
251 			break;
252 
253 		case BIO_CONN_S_BLOCKED_CONNECT:
254 			i = BIO_sock_error(b->num);
255 			if (i) {
256 				BIO_clear_retry_flags(b);
257 				SYSerr(SYS_F_CONNECT, i);
258 				ERR_asprintf_error_data("host=%s:%s",
259 				    c->param_hostname, c->param_port);
260 				BIOerr(BIO_F_CONN_STATE,
261 				    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 = BUF_strdup(ptr);
471 			} else if (num == 1) {
472 				free(data->param_port);
473 				data->param_port = BUF_strdup(ptr);
474 			} else if (num == 2) {
475 				char buf[16];
476 				unsigned char *p = ptr;
477 
478 				snprintf(buf, sizeof buf, "%d.%d.%d.%d",
479 				    p[0], p[1], p[2], p[3]);
480 				free(data->param_hostname);
481 				data->param_hostname = BUF_strdup(buf);
482 				memcpy(&(data->ip[0]), ptr, 4);
483 			} else if (num == 3) {
484 				char buf[DECIMAL_SIZE(int) + 1];
485 
486 				snprintf(buf, sizeof buf, "%d",
487 				    *(int *)ptr);
488 				free(data->param_port);
489 				data->param_port = BUF_strdup(buf);
490 				data->port= *(int *)ptr;
491 			}
492 		}
493 		break;
494 	case BIO_C_SET_NBIO:
495 		data->nbio = (int)num;
496 		break;
497 	case BIO_C_GET_FD:
498 		if (b->init) {
499 			ip = (int *)ptr;
500 			if (ip != NULL)
501 				*ip = b->num;
502 			ret = b->num;
503 		} else
504 			ret = -1;
505 		break;
506 	case BIO_CTRL_GET_CLOSE:
507 		ret = b->shutdown;
508 		break;
509 	case BIO_CTRL_SET_CLOSE:
510 		b->shutdown = (int)num;
511 		break;
512 	case BIO_CTRL_PENDING:
513 	case BIO_CTRL_WPENDING:
514 		ret = 0;
515 		break;
516 	case BIO_CTRL_FLUSH:
517 		break;
518 	case BIO_CTRL_DUP:
519 		{
520 			dbio = (BIO *)ptr;
521 			if (data->param_port)
522 				BIO_set_conn_port(dbio, data->param_port);
523 			if (data->param_hostname)
524 				BIO_set_conn_hostname(dbio,
525 				    data->param_hostname);
526 			BIO_set_nbio(dbio, data->nbio);
527 			/* FIXME: the cast of the function seems unlikely to be a good idea */
528 			(void)BIO_set_info_callback(dbio,
529 			    (bio_info_cb *)data->info_callback);
530 		}
531 		break;
532 	case BIO_CTRL_SET_CALLBACK:
533 		{
534 #if 0 /* FIXME: Should this be used?  -- Richard Levitte */
535 			BIOerr(BIO_F_CONN_CTRL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
536 			ret = -1;
537 #else
538 			ret = 0;
539 #endif
540 		}
541 		break;
542 	case BIO_CTRL_GET_CALLBACK:
543 		{
544 			int (**fptr)(const BIO *bio, int state, int xret);
545 
546 			fptr = (int (**)(const BIO *bio, int state, int xret))ptr;
547 			*fptr = data->info_callback;
548 		}
549 		break;
550 	default:
551 		ret = 0;
552 		break;
553 	}
554 	return (ret);
555 }
556 
557 static long
558 conn_callback_ctrl(BIO *b, int cmd, bio_info_cb *fp)
559 {
560 	long ret = 1;
561 	BIO_CONNECT *data;
562 
563 	data = (BIO_CONNECT *)b->ptr;
564 
565 	switch (cmd) {
566 	case BIO_CTRL_SET_CALLBACK:
567 		{
568 			data->info_callback = (int (*)(const struct bio_st *, int, int))fp;
569 		}
570 		break;
571 	default:
572 		ret = 0;
573 		break;
574 	}
575 	return (ret);
576 }
577 
578 static int
579 conn_puts(BIO *bp, const char *str)
580 {
581 	int n, ret;
582 
583 	n = strlen(str);
584 	ret = conn_write(bp, str, n);
585 	return (ret);
586 }
587 
588 BIO *
589 BIO_new_connect(char *str)
590 {
591 	BIO *ret;
592 
593 	ret = BIO_new(BIO_s_connect());
594 	if (ret == NULL)
595 		return (NULL);
596 	if (BIO_set_conn_hostname(ret, str))
597 		return (ret);
598 	else {
599 		BIO_free(ret);
600 		return (NULL);
601 	}
602 }
603 
604