1 /* $OpenBSD: bss_dgram.c,v 1.43 2022/01/07 09:02:17 tb Exp $ */
2 /*
3  * DTLS implementation written by Nagendra Modadugu
4  * (nagendra@cs.stanford.edu) for the OpenSSL project 2005.
5  */
6 /* ====================================================================
7  * Copyright (c) 1999-2005 The OpenSSL Project.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in
18  *    the documentation and/or other materials provided with the
19  *    distribution.
20  *
21  * 3. All advertising materials mentioning features or use of this
22  *    software must display the following acknowledgment:
23  *    "This product includes software developed by the OpenSSL Project
24  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
25  *
26  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27  *    endorse or promote products derived from this software without
28  *    prior written permission. For written permission, please contact
29  *    openssl-core@OpenSSL.org.
30  *
31  * 5. Products derived from this software may not be called "OpenSSL"
32  *    nor may "OpenSSL" appear in their names without prior written
33  *    permission of the OpenSSL Project.
34  *
35  * 6. Redistributions of any form whatsoever must retain the following
36  *    acknowledgment:
37  *    "This product includes software developed by the OpenSSL Project
38  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
39  *
40  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
41  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
44  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51  * OF THE POSSIBILITY OF SUCH DAMAGE.
52  * ====================================================================
53  *
54  * This product includes cryptographic software written by Eric Young
55  * (eay@cryptsoft.com).  This product includes software written by Tim
56  * Hudson (tjh@cryptsoft.com).
57  *
58  */
59 
60 #include <sys/socket.h>
61 #include <sys/time.h>
62 
63 #include <netinet/in.h>
64 
65 #include <errno.h>
66 #include <netdb.h>
67 #include <stdio.h>
68 #include <string.h>
69 #include <unistd.h>
70 
71 #include <openssl/opensslconf.h>
72 
73 #include <openssl/bio.h>
74 
75 #include "bio_local.h"
76 
77 #ifndef OPENSSL_NO_DGRAM
78 
79 
80 static int dgram_write(BIO *h, const char *buf, int num);
81 static int dgram_read(BIO *h, char *buf, int size);
82 static int dgram_puts(BIO *h, const char *str);
83 static long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2);
84 static int dgram_new(BIO *h);
85 static int dgram_free(BIO *data);
86 static int dgram_clear(BIO *bio);
87 
88 
89 static int BIO_dgram_should_retry(int s);
90 
91 static const BIO_METHOD methods_dgramp = {
92 	.type = BIO_TYPE_DGRAM,
93 	.name = "datagram socket",
94 	.bwrite = dgram_write,
95 	.bread = dgram_read,
96 	.bputs = dgram_puts,
97 	.ctrl = dgram_ctrl,
98 	.create = dgram_new,
99 	.destroy = dgram_free
100 };
101 
102 
103 typedef struct bio_dgram_data_st {
104 	union {
105 		struct sockaddr sa;
106 		struct sockaddr_in sa_in;
107 		struct sockaddr_in6 sa_in6;
108 	} peer;
109 	unsigned int connected;
110 	unsigned int _errno;
111 	unsigned int mtu;
112 	struct timeval next_timeout;
113 	struct timeval socket_timeout;
114 } bio_dgram_data;
115 
116 
117 const BIO_METHOD *
118 BIO_s_datagram(void)
119 {
120 	return (&methods_dgramp);
121 }
122 
123 BIO *
124 BIO_new_dgram(int fd, int close_flag)
125 {
126 	BIO *ret;
127 
128 	ret = BIO_new(BIO_s_datagram());
129 	if (ret == NULL)
130 		return (NULL);
131 	BIO_set_fd(ret, fd, close_flag);
132 	return (ret);
133 }
134 
135 static int
136 dgram_new(BIO *bi)
137 {
138 	bio_dgram_data *data = NULL;
139 
140 	bi->init = 0;
141 	bi->num = 0;
142 	data = calloc(1, sizeof(bio_dgram_data));
143 	if (data == NULL)
144 		return 0;
145 	bi->ptr = data;
146 
147 	bi->flags = 0;
148 	return (1);
149 }
150 
151 static int
152 dgram_free(BIO *a)
153 {
154 	bio_dgram_data *data;
155 
156 	if (a == NULL)
157 		return (0);
158 	if (!dgram_clear(a))
159 		return 0;
160 
161 	data = (bio_dgram_data *)a->ptr;
162 	free(data);
163 
164 	return (1);
165 }
166 
167 static int
168 dgram_clear(BIO *a)
169 {
170 	if (a == NULL)
171 		return (0);
172 	if (a->shutdown) {
173 		if (a->init) {
174 			shutdown(a->num, SHUT_RDWR);
175 			close(a->num);
176 		}
177 		a->init = 0;
178 		a->flags = 0;
179 	}
180 	return (1);
181 }
182 
183 static void
184 dgram_adjust_rcv_timeout(BIO *b)
185 {
186 #if defined(SO_RCVTIMEO)
187 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
188 
189 	/* Is a timer active? */
190 	if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
191 		struct timeval timenow, timeleft;
192 
193 		/* Read current socket timeout */
194 		socklen_t sz = sizeof(data->socket_timeout);
195 		if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
196 		    &(data->socket_timeout), &sz) < 0) {
197 			perror("getsockopt");
198 		}
199 
200 		/* Get current time */
201 		gettimeofday(&timenow, NULL);
202 
203 		/* Calculate time left until timer expires */
204 		memcpy(&timeleft, &(data->next_timeout), sizeof(struct timeval));
205 		timeleft.tv_sec -= timenow.tv_sec;
206 		timeleft.tv_usec -= timenow.tv_usec;
207 		if (timeleft.tv_usec < 0) {
208 			timeleft.tv_sec--;
209 			timeleft.tv_usec += 1000000;
210 		}
211 
212 		if (timeleft.tv_sec < 0) {
213 			timeleft.tv_sec = 0;
214 			timeleft.tv_usec = 1;
215 		}
216 
217 		/* Adjust socket timeout if next handhake message timer
218 		 * will expire earlier.
219 		 */
220 		if ((data->socket_timeout.tv_sec == 0 &&
221 		    data->socket_timeout.tv_usec == 0) ||
222 		    (data->socket_timeout.tv_sec > timeleft.tv_sec) ||
223 		    (data->socket_timeout.tv_sec == timeleft.tv_sec &&
224 		    data->socket_timeout.tv_usec >= timeleft.tv_usec)) {
225 			if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
226 			    &timeleft, sizeof(struct timeval)) < 0) {
227 				perror("setsockopt");
228 			}
229 		}
230 	}
231 #endif
232 }
233 
234 static void
235 dgram_reset_rcv_timeout(BIO *b)
236 {
237 #if defined(SO_RCVTIMEO)
238 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
239 
240 	/* Is a timer active? */
241 	if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
242 		if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
243 		    &(data->socket_timeout), sizeof(struct timeval)) < 0) {
244 			perror("setsockopt");
245 		}
246 	}
247 #endif
248 }
249 
250 static int
251 dgram_read(BIO *b, char *out, int outl)
252 {
253 	int ret = 0;
254 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
255 
256 	struct	{
257 		socklen_t len;
258 		union	{
259 			struct sockaddr sa;
260 			struct sockaddr_in sa_in;
261 			struct sockaddr_in6 sa_in6;
262 		} peer;
263 	} sa;
264 
265 	sa.len = sizeof(sa.peer);
266 
267 	if (out != NULL) {
268 		errno = 0;
269 		memset(&sa.peer, 0, sizeof(sa.peer));
270 		dgram_adjust_rcv_timeout(b);
271 		ret = recvfrom(b->num, out, outl, 0, &sa.peer.sa, &sa.len);
272 
273 		if (! data->connected  && ret >= 0)
274 			BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &sa.peer);
275 
276 		BIO_clear_retry_flags(b);
277 		if (ret < 0) {
278 			if (BIO_dgram_should_retry(ret)) {
279 				BIO_set_retry_read(b);
280 				data->_errno = errno;
281 			}
282 		}
283 
284 		dgram_reset_rcv_timeout(b);
285 	}
286 	return (ret);
287 }
288 
289 static int
290 dgram_write(BIO *b, const char *in, int inl)
291 {
292 	int ret;
293 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
294 	errno = 0;
295 
296 	if (data->connected)
297 		ret = write(b->num, in, inl);
298 	else {
299 		int peerlen = sizeof(data->peer);
300 
301 		if (data->peer.sa.sa_family == AF_INET)
302 			peerlen = sizeof(data->peer.sa_in);
303 		else if (data->peer.sa.sa_family == AF_INET6)
304 			peerlen = sizeof(data->peer.sa_in6);
305 		ret = sendto(b->num, in, inl, 0, &data->peer.sa, peerlen);
306 	}
307 
308 	BIO_clear_retry_flags(b);
309 	if (ret <= 0) {
310 		if (BIO_dgram_should_retry(ret)) {
311 			BIO_set_retry_write(b);
312 
313 			data->_errno = errno;
314 			/*
315 			 * higher layers are responsible for querying MTU,
316 			 * if necessary
317 			 */
318 		}
319 	}
320 	return (ret);
321 }
322 
323 static long
324 dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
325 {
326 	long ret = 1;
327 	int *ip;
328 	struct sockaddr *to = NULL;
329 	bio_dgram_data *data = NULL;
330 #if (defined(IP_MTU_DISCOVER) || defined(IP_MTU))
331 	int sockopt_val = 0;
332 	socklen_t sockopt_len;	/* assume that system supporting IP_MTU is
333 				 * modern enough to define socklen_t */
334 	socklen_t addr_len;
335 	union	{
336 		struct sockaddr	sa;
337 		struct sockaddr_in s4;
338 		struct sockaddr_in6 s6;
339 	} addr;
340 #endif
341 
342 	data = (bio_dgram_data *)b->ptr;
343 
344 	switch (cmd) {
345 	case BIO_CTRL_RESET:
346 		num = 0;
347 	case BIO_C_FILE_SEEK:
348 		ret = 0;
349 		break;
350 	case BIO_C_FILE_TELL:
351 	case BIO_CTRL_INFO:
352 		ret = 0;
353 		break;
354 	case BIO_C_SET_FD:
355 		dgram_clear(b);
356 		b->num= *((int *)ptr);
357 		b->shutdown = (int)num;
358 		b->init = 1;
359 		break;
360 	case BIO_C_GET_FD:
361 		if (b->init) {
362 			ip = (int *)ptr;
363 			if (ip != NULL)
364 				*ip = b->num;
365 			ret = b->num;
366 		} else
367 			ret = -1;
368 		break;
369 	case BIO_CTRL_GET_CLOSE:
370 		ret = b->shutdown;
371 		break;
372 	case BIO_CTRL_SET_CLOSE:
373 		b->shutdown = (int)num;
374 		break;
375 	case BIO_CTRL_PENDING:
376 	case BIO_CTRL_WPENDING:
377 		ret = 0;
378 		break;
379 	case BIO_CTRL_DUP:
380 	case BIO_CTRL_FLUSH:
381 		ret = 1;
382 		break;
383 	case BIO_CTRL_DGRAM_CONNECT:
384 		to = (struct sockaddr *)ptr;
385 		switch (to->sa_family) {
386 		case AF_INET:
387 			memcpy(&data->peer, to, sizeof(data->peer.sa_in));
388 			break;
389 		case AF_INET6:
390 			memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
391 			break;
392 		default:
393 			memcpy(&data->peer, to, sizeof(data->peer.sa));
394 			break;
395 		}
396 		break;
397 		/* (Linux)kernel sets DF bit on outgoing IP packets */
398 	case BIO_CTRL_DGRAM_MTU_DISCOVER:
399 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
400 		addr_len = (socklen_t)sizeof(addr);
401 		memset((void *)&addr, 0, sizeof(addr));
402 		if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
403 			ret = 0;
404 			break;
405 		}
406 		switch (addr.sa.sa_family) {
407 		case AF_INET:
408 			sockopt_val = IP_PMTUDISC_DO;
409 			ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
410 			    &sockopt_val, sizeof(sockopt_val));
411 			if (ret < 0)
412 				perror("setsockopt");
413 			break;
414 #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
415 		case AF_INET6:
416 			sockopt_val = IPV6_PMTUDISC_DO;
417 			ret = setsockopt(b->num, IPPROTO_IPV6,
418 			    IPV6_MTU_DISCOVER, &sockopt_val,
419 			    sizeof(sockopt_val));
420 			if (ret < 0)
421 				perror("setsockopt");
422 			break;
423 #endif
424 		default:
425 			ret = -1;
426 			break;
427 		}
428 #else
429 		ret = -1;
430 #endif
431 		break;
432 	case BIO_CTRL_DGRAM_QUERY_MTU:
433 #if defined(IP_MTU)
434 		addr_len = (socklen_t)sizeof(addr);
435 		memset((void *)&addr, 0, sizeof(addr));
436 		if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
437 			ret = 0;
438 			break;
439 		}
440 		sockopt_len = sizeof(sockopt_val);
441 		switch (addr.sa.sa_family) {
442 		case AF_INET:
443 			ret = getsockopt(b->num, IPPROTO_IP, IP_MTU,
444 			    &sockopt_val, &sockopt_len);
445 			if (ret < 0 || sockopt_val < 0) {
446 				ret = 0;
447 			} else {
448 				/* we assume that the transport protocol is UDP and no
449 				 * IP options are used.
450 				 */
451 				data->mtu = sockopt_val - 8 - 20;
452 				ret = data->mtu;
453 			}
454 			break;
455 #if defined(IPV6_MTU)
456 		case AF_INET6:
457 			ret = getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU,
458 			    &sockopt_val, &sockopt_len);
459 			if (ret < 0 || sockopt_val < 0) {
460 				ret = 0;
461 			} else {
462 				/* we assume that the transport protocol is UDP and no
463 				 * IPV6 options are used.
464 				 */
465 				data->mtu = sockopt_val - 8 - 40;
466 				ret = data->mtu;
467 			}
468 			break;
469 #endif
470 default:
471 			ret = 0;
472 			break;
473 		}
474 #else
475 		ret = 0;
476 #endif
477 		break;
478 	case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
479 		switch (data->peer.sa.sa_family) {
480 		case AF_INET:
481 			ret = 576 - 20 - 8;
482 			break;
483 		case AF_INET6:
484 #ifdef IN6_IS_ADDR_V4MAPPED
485 			if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr))
486 				ret = 576 - 20 - 8;
487 			else
488 #endif
489 				ret = 1280 - 40 - 8;
490 			break;
491 		default:
492 			ret = 576 - 20 - 8;
493 			break;
494 		}
495 		break;
496 	case BIO_CTRL_DGRAM_GET_MTU:
497 		return data->mtu;
498 		break;
499 	case BIO_CTRL_DGRAM_SET_MTU:
500 		data->mtu = num;
501 		ret = num;
502 		break;
503 	case BIO_CTRL_DGRAM_SET_CONNECTED:
504 		to = (struct sockaddr *)ptr;
505 
506 		if (to != NULL) {
507 			data->connected = 1;
508 			switch (to->sa_family) {
509 			case AF_INET:
510 				memcpy(&data->peer, to, sizeof(data->peer.sa_in));
511 				break;
512 			case AF_INET6:
513 				memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
514 				break;
515 			default:
516 				memcpy(&data->peer, to, sizeof(data->peer.sa));
517 				break;
518 			}
519 		} else {
520 			data->connected = 0;
521 			memset(&(data->peer), 0, sizeof(data->peer));
522 		}
523 		break;
524 	case BIO_CTRL_DGRAM_GET_PEER:
525 		switch (data->peer.sa.sa_family) {
526 		case AF_INET:
527 			ret = sizeof(data->peer.sa_in);
528 			break;
529 		case AF_INET6:
530 			ret = sizeof(data->peer.sa_in6);
531 			break;
532 		default:
533 			ret = sizeof(data->peer.sa);
534 			break;
535 		}
536 		if (num == 0 || num > ret)
537 			num = ret;
538 		memcpy(ptr, &data->peer, (ret = num));
539 		break;
540 	case BIO_CTRL_DGRAM_SET_PEER:
541 		to = (struct sockaddr *) ptr;
542 		switch (to->sa_family) {
543 		case AF_INET:
544 			memcpy(&data->peer, to, sizeof(data->peer.sa_in));
545 			break;
546 		case AF_INET6:
547 			memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
548 			break;
549 		default:
550 			memcpy(&data->peer, to, sizeof(data->peer.sa));
551 			break;
552 		}
553 		break;
554 	case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
555 		memcpy(&(data->next_timeout), ptr, sizeof(struct timeval));
556 		break;
557 #if defined(SO_RCVTIMEO)
558 	case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
559 		if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr,
560 		    sizeof(struct timeval)) < 0) {
561 			perror("setsockopt");
562 			ret = -1;
563 		}
564 		break;
565 	case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
566 		{
567 			socklen_t sz = sizeof(struct timeval);
568 			if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
569 			    ptr, &sz) < 0) {
570 				perror("getsockopt");
571 				ret = -1;
572 			} else
573 				ret = sz;
574 		}
575 		break;
576 #endif
577 #if defined(SO_SNDTIMEO)
578 	case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
579 		if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr,
580 		    sizeof(struct timeval)) < 0) {
581 			perror("setsockopt");
582 			ret = -1;
583 		}
584 		break;
585 	case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
586 		{
587 			socklen_t sz = sizeof(struct timeval);
588 			if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
589 			    ptr, &sz) < 0) {
590 				perror("getsockopt");
591 				ret = -1;
592 			} else
593 				ret = sz;
594 		}
595 		break;
596 #endif
597 	case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
598 		/* fall-through */
599 	case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
600 		if (data->_errno == EAGAIN) {
601 			ret = 1;
602 			data->_errno = 0;
603 		} else
604 			ret = 0;
605 		break;
606 #ifdef EMSGSIZE
607 	case BIO_CTRL_DGRAM_MTU_EXCEEDED:
608 		if (data->_errno == EMSGSIZE) {
609 			ret = 1;
610 			data->_errno = 0;
611 		} else
612 			ret = 0;
613 		break;
614 #endif
615 	default:
616 		ret = 0;
617 		break;
618 	}
619 	return (ret);
620 }
621 
622 static int
623 dgram_puts(BIO *bp, const char *str)
624 {
625 	int n, ret;
626 
627 	n = strlen(str);
628 	ret = dgram_write(bp, str, n);
629 	return (ret);
630 }
631 
632 
633 static int
634 BIO_dgram_should_retry(int i)
635 {
636 	int err;
637 
638 	if ((i == 0) || (i == -1)) {
639 		err = errno;
640 		return (BIO_dgram_non_fatal_error(err));
641 	}
642 	return (0);
643 }
644 
645 int
646 BIO_dgram_non_fatal_error(int err)
647 {
648 	switch (err) {
649 	case EINTR:
650 	case EAGAIN:
651 	case EINPROGRESS:
652 	case EALREADY:
653 		return (1);
654 	default:
655 		break;
656 	}
657 	return (0);
658 }
659 
660 #endif
661