GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libcrypto/bio/bss_dgram.c Lines: 0 200 0.0 %
Date: 2017-11-07 Branches: 0 140 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: bss_dgram.c,v 1.41 2015/07/20 23:15:28 doug 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
#ifndef OPENSSL_NO_DGRAM
76
77
78
static int dgram_write(BIO *h, const char *buf, int num);
79
static int dgram_read(BIO *h, char *buf, int size);
80
static int dgram_puts(BIO *h, const char *str);
81
static long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2);
82
static int dgram_new(BIO *h);
83
static int dgram_free(BIO *data);
84
static int dgram_clear(BIO *bio);
85
86
87
static int BIO_dgram_should_retry(int s);
88
89
static BIO_METHOD methods_dgramp = {
90
	.type = BIO_TYPE_DGRAM,
91
	.name = "datagram socket",
92
	.bwrite = dgram_write,
93
	.bread = dgram_read,
94
	.bputs = dgram_puts,
95
	.ctrl = dgram_ctrl,
96
	.create = dgram_new,
97
	.destroy = dgram_free
98
};
99
100
101
typedef struct bio_dgram_data_st {
102
	union {
103
		struct sockaddr sa;
104
		struct sockaddr_in sa_in;
105
		struct sockaddr_in6 sa_in6;
106
	} peer;
107
	unsigned int connected;
108
	unsigned int _errno;
109
	unsigned int mtu;
110
	struct timeval next_timeout;
111
	struct timeval socket_timeout;
112
} bio_dgram_data;
113
114
115
BIO_METHOD *
116
BIO_s_datagram(void)
117
{
118
	return (&methods_dgramp);
119
}
120
121
BIO *
122
BIO_new_dgram(int fd, int close_flag)
123
{
124
	BIO *ret;
125
126
	ret = BIO_new(BIO_s_datagram());
127
	if (ret == NULL)
128
		return (NULL);
129
	BIO_set_fd(ret, fd, close_flag);
130
	return (ret);
131
}
132
133
static int
134
dgram_new(BIO *bi)
135
{
136
	bio_dgram_data *data = NULL;
137
138
	bi->init = 0;
139
	bi->num = 0;
140
	data = calloc(1, sizeof(bio_dgram_data));
141
	if (data == NULL)
142
		return 0;
143
	bi->ptr = data;
144
145
	bi->flags = 0;
146
	return (1);
147
}
148
149
static int
150
dgram_free(BIO *a)
151
{
152
	bio_dgram_data *data;
153
154
	if (a == NULL)
155
		return (0);
156
	if (!dgram_clear(a))
157
		return 0;
158
159
	data = (bio_dgram_data *)a->ptr;
160
	free(data);
161
162
	return (1);
163
}
164
165
static int
166
dgram_clear(BIO *a)
167
{
168
	if (a == NULL)
169
		return (0);
170
	if (a->shutdown) {
171
		if (a->init) {
172
			shutdown(a->num, SHUT_RDWR);
173
			close(a->num);
174
		}
175
		a->init = 0;
176
		a->flags = 0;
177
	}
178
	return (1);
179
}
180
181
static void
182
dgram_adjust_rcv_timeout(BIO *b)
183
{
184
#if defined(SO_RCVTIMEO)
185
	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
186
187
	/* Is a timer active? */
188
	if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
189
		struct timeval timenow, timeleft;
190
191
		/* Read current socket timeout */
192
		socklen_t sz = sizeof(data->socket_timeout);
193
		if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
194
		    &(data->socket_timeout), &sz) < 0) {
195
			perror("getsockopt");
196
		}
197
198
		/* Get current time */
199
		gettimeofday(&timenow, NULL);
200
201
		/* Calculate time left until timer expires */
202
		memcpy(&timeleft, &(data->next_timeout), sizeof(struct timeval));
203
		timeleft.tv_sec -= timenow.tv_sec;
204
		timeleft.tv_usec -= timenow.tv_usec;
205
		if (timeleft.tv_usec < 0) {
206
			timeleft.tv_sec--;
207
			timeleft.tv_usec += 1000000;
208
		}
209
210
		if (timeleft.tv_sec < 0) {
211
			timeleft.tv_sec = 0;
212
			timeleft.tv_usec = 1;
213
		}
214
215
		/* Adjust socket timeout if next handhake message timer
216
		 * will expire earlier.
217
		 */
218
		if ((data->socket_timeout.tv_sec == 0 &&
219
		    data->socket_timeout.tv_usec == 0) ||
220
		    (data->socket_timeout.tv_sec > timeleft.tv_sec) ||
221
		    (data->socket_timeout.tv_sec == timeleft.tv_sec &&
222
		    data->socket_timeout.tv_usec >= timeleft.tv_usec)) {
223
			if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
224
			    &timeleft, sizeof(struct timeval)) < 0) {
225
				perror("setsockopt");
226
			}
227
		}
228
	}
229
#endif
230
}
231
232
static void
233
dgram_reset_rcv_timeout(BIO *b)
234
{
235
#if defined(SO_RCVTIMEO)
236
	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
237
238
	/* Is a timer active? */
239
	if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
240
		if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
241
		    &(data->socket_timeout), sizeof(struct timeval)) < 0) {
242
			perror("setsockopt");
243
		}
244
	}
245
#endif
246
}
247
248
static int
249
dgram_read(BIO *b, char *out, int outl)
250
{
251
	int ret = 0;
252
	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
253
254
	struct	{
255
		socklen_t len;
256
		union	{
257
			struct sockaddr sa;
258
			struct sockaddr_in sa_in;
259
			struct sockaddr_in6 sa_in6;
260
		} peer;
261
	} sa;
262
263
	sa.len = sizeof(sa.peer);
264
265
	if (out != NULL) {
266
		errno = 0;
267
		memset(&sa.peer, 0, sizeof(sa.peer));
268
		dgram_adjust_rcv_timeout(b);
269
		ret = recvfrom(b->num, out, outl, 0, &sa.peer.sa, &sa.len);
270
271
		if (! data->connected  && ret >= 0)
272
			BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &sa.peer);
273
274
		BIO_clear_retry_flags(b);
275
		if (ret < 0) {
276
			if (BIO_dgram_should_retry(ret)) {
277
				BIO_set_retry_read(b);
278
				data->_errno = errno;
279
			}
280
		}
281
282
		dgram_reset_rcv_timeout(b);
283
	}
284
	return (ret);
285
}
286
287
static int
288
dgram_write(BIO *b, const char *in, int inl)
289
{
290
	int ret;
291
	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
292
	errno = 0;
293
294
	if (data->connected)
295
		ret = write(b->num, in, inl);
296
	else {
297
		int peerlen = sizeof(data->peer);
298
299
		if (data->peer.sa.sa_family == AF_INET)
300
			peerlen = sizeof(data->peer.sa_in);
301
		else if (data->peer.sa.sa_family == AF_INET6)
302
			peerlen = sizeof(data->peer.sa_in6);
303
		ret = sendto(b->num, in, inl, 0, &data->peer.sa, peerlen);
304
	}
305
306
	BIO_clear_retry_flags(b);
307
	if (ret <= 0) {
308
		if (BIO_dgram_should_retry(ret)) {
309
			BIO_set_retry_write(b);
310
311
			data->_errno = errno;
312
			/*
313
			 * higher layers are responsible for querying MTU,
314
			 * if necessary
315
			 */
316
		}
317
	}
318
	return (ret);
319
}
320
321
static long
322
dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
323
{
324
	long ret = 1;
325
	int *ip;
326
	struct sockaddr *to = NULL;
327
	bio_dgram_data *data = NULL;
328
#if (defined(IP_MTU_DISCOVER) || defined(IP_MTU))
329
	int sockopt_val = 0;
330
	socklen_t sockopt_len;	/* assume that system supporting IP_MTU is
331
				 * modern enough to define socklen_t */
332
	socklen_t addr_len;
333
	union	{
334
		struct sockaddr	sa;
335
		struct sockaddr_in s4;
336
		struct sockaddr_in6 s6;
337
	} addr;
338
#endif
339
340
	data = (bio_dgram_data *)b->ptr;
341
342
	switch (cmd) {
343
	case BIO_CTRL_RESET:
344
		num = 0;
345
	case BIO_C_FILE_SEEK:
346
		ret = 0;
347
		break;
348
	case BIO_C_FILE_TELL:
349
	case BIO_CTRL_INFO:
350
		ret = 0;
351
		break;
352
	case BIO_C_SET_FD:
353
		dgram_clear(b);
354
		b->num= *((int *)ptr);
355
		b->shutdown = (int)num;
356
		b->init = 1;
357
		break;
358
	case BIO_C_GET_FD:
359
		if (b->init) {
360
			ip = (int *)ptr;
361
			if (ip != NULL)
362
				*ip = b->num;
363
			ret = b->num;
364
		} else
365
			ret = -1;
366
		break;
367
	case BIO_CTRL_GET_CLOSE:
368
		ret = b->shutdown;
369
		break;
370
	case BIO_CTRL_SET_CLOSE:
371
		b->shutdown = (int)num;
372
		break;
373
	case BIO_CTRL_PENDING:
374
	case BIO_CTRL_WPENDING:
375
		ret = 0;
376
		break;
377
	case BIO_CTRL_DUP:
378
	case BIO_CTRL_FLUSH:
379
		ret = 1;
380
		break;
381
	case BIO_CTRL_DGRAM_CONNECT:
382
		to = (struct sockaddr *)ptr;
383
		switch (to->sa_family) {
384
		case AF_INET:
385
			memcpy(&data->peer, to, sizeof(data->peer.sa_in));
386
			break;
387
		case AF_INET6:
388
			memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
389
			break;
390
		default:
391
			memcpy(&data->peer, to, sizeof(data->peer.sa));
392
			break;
393
		}
394
		break;
395
		/* (Linux)kernel sets DF bit on outgoing IP packets */
396
	case BIO_CTRL_DGRAM_MTU_DISCOVER:
397
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
398
		addr_len = (socklen_t)sizeof(addr);
399
		memset((void *)&addr, 0, sizeof(addr));
400
		if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
401
			ret = 0;
402
			break;
403
		}
404
		switch (addr.sa.sa_family) {
405
		case AF_INET:
406
			sockopt_val = IP_PMTUDISC_DO;
407
			ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
408
			    &sockopt_val, sizeof(sockopt_val));
409
			if (ret < 0)
410
				perror("setsockopt");
411
			break;
412
#if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
413
		case AF_INET6:
414
			sockopt_val = IPV6_PMTUDISC_DO;
415
			ret = setsockopt(b->num, IPPROTO_IPV6,
416
			    IPV6_MTU_DISCOVER, &sockopt_val,
417
			    sizeof(sockopt_val));
418
			if (ret < 0)
419
				perror("setsockopt");
420
			break;
421
#endif
422
		default:
423
			ret = -1;
424
			break;
425
		}
426
#else
427
		ret = -1;
428
#endif
429
		break;
430
	case BIO_CTRL_DGRAM_QUERY_MTU:
431
#if defined(IP_MTU)
432
		addr_len = (socklen_t)sizeof(addr);
433
		memset((void *)&addr, 0, sizeof(addr));
434
		if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
435
			ret = 0;
436
			break;
437
		}
438
		sockopt_len = sizeof(sockopt_val);
439
		switch (addr.sa.sa_family) {
440
		case AF_INET:
441
			ret = getsockopt(b->num, IPPROTO_IP, IP_MTU,
442
			    &sockopt_val, &sockopt_len);
443
			if (ret < 0 || sockopt_val < 0) {
444
				ret = 0;
445
			} else {
446
				/* we assume that the transport protocol is UDP and no
447
				 * IP options are used.
448
				 */
449
				data->mtu = sockopt_val - 8 - 20;
450
				ret = data->mtu;
451
			}
452
			break;
453
#if defined(IPV6_MTU)
454
		case AF_INET6:
455
			ret = getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU,
456
			    &sockopt_val, &sockopt_len);
457
			if (ret < 0 || sockopt_val < 0) {
458
				ret = 0;
459
			} else {
460
				/* we assume that the transport protocol is UDP and no
461
				 * IPV6 options are used.
462
				 */
463
				data->mtu = sockopt_val - 8 - 40;
464
				ret = data->mtu;
465
			}
466
			break;
467
#endif
468
default:
469
			ret = 0;
470
			break;
471
		}
472
#else
473
		ret = 0;
474
#endif
475
		break;
476
	case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
477
		switch (data->peer.sa.sa_family) {
478
		case AF_INET:
479
			ret = 576 - 20 - 8;
480
			break;
481
		case AF_INET6:
482
#ifdef IN6_IS_ADDR_V4MAPPED
483
			if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr))
484
				ret = 576 - 20 - 8;
485
			else
486
#endif
487
				ret = 1280 - 40 - 8;
488
			break;
489
		default:
490
			ret = 576 - 20 - 8;
491
			break;
492
		}
493
		break;
494
	case BIO_CTRL_DGRAM_GET_MTU:
495
		return data->mtu;
496
		break;
497
	case BIO_CTRL_DGRAM_SET_MTU:
498
		data->mtu = num;
499
		ret = num;
500
		break;
501
	case BIO_CTRL_DGRAM_SET_CONNECTED:
502
		to = (struct sockaddr *)ptr;
503
504
		if (to != NULL) {
505
			data->connected = 1;
506
			switch (to->sa_family) {
507
			case AF_INET:
508
				memcpy(&data->peer, to, sizeof(data->peer.sa_in));
509
				break;
510
			case AF_INET6:
511
				memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
512
				break;
513
			default:
514
				memcpy(&data->peer, to, sizeof(data->peer.sa));
515
				break;
516
			}
517
		} else {
518
			data->connected = 0;
519
			memset(&(data->peer), 0, sizeof(data->peer));
520
		}
521
		break;
522
	case BIO_CTRL_DGRAM_GET_PEER:
523
		switch (data->peer.sa.sa_family) {
524
		case AF_INET:
525
			ret = sizeof(data->peer.sa_in);
526
			break;
527
		case AF_INET6:
528
			ret = sizeof(data->peer.sa_in6);
529
			break;
530
		default:
531
			ret = sizeof(data->peer.sa);
532
			break;
533
		}
534
		if (num == 0 || num > ret)
535
			num = ret;
536
		memcpy(ptr, &data->peer, (ret = num));
537
		break;
538
	case BIO_CTRL_DGRAM_SET_PEER:
539
		to = (struct sockaddr *) ptr;
540
		switch (to->sa_family) {
541
		case AF_INET:
542
			memcpy(&data->peer, to, sizeof(data->peer.sa_in));
543
			break;
544
		case AF_INET6:
545
			memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
546
			break;
547
		default:
548
			memcpy(&data->peer, to, sizeof(data->peer.sa));
549
			break;
550
		}
551
		break;
552
	case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
553
		memcpy(&(data->next_timeout), ptr, sizeof(struct timeval));
554
		break;
555
#if defined(SO_RCVTIMEO)
556
	case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
557
		if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr,
558
		    sizeof(struct timeval)) < 0) {
559
			perror("setsockopt");
560
			ret = -1;
561
		}
562
		break;
563
	case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
564
		{
565
			socklen_t sz = sizeof(struct timeval);
566
			if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
567
			    ptr, &sz) < 0) {
568
				perror("getsockopt");
569
				ret = -1;
570
			} else
571
				ret = sz;
572
		}
573
		break;
574
#endif
575
#if defined(SO_SNDTIMEO)
576
	case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
577
		if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr,
578
		    sizeof(struct timeval)) < 0) {
579
			perror("setsockopt");
580
			ret = -1;
581
		}
582
		break;
583
	case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
584
		{
585
			socklen_t sz = sizeof(struct timeval);
586
			if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
587
			    ptr, &sz) < 0) {
588
				perror("getsockopt");
589
				ret = -1;
590
			} else
591
				ret = sz;
592
		}
593
		break;
594
#endif
595
	case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
596
		/* fall-through */
597
	case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
598
		if (data->_errno == EAGAIN) {
599
			ret = 1;
600
			data->_errno = 0;
601
		} else
602
			ret = 0;
603
		break;
604
#ifdef EMSGSIZE
605
	case BIO_CTRL_DGRAM_MTU_EXCEEDED:
606
		if (data->_errno == EMSGSIZE) {
607
			ret = 1;
608
			data->_errno = 0;
609
		} else
610
			ret = 0;
611
		break;
612
#endif
613
	default:
614
		ret = 0;
615
		break;
616
	}
617
	return (ret);
618
}
619
620
static int
621
dgram_puts(BIO *bp, const char *str)
622
{
623
	int n, ret;
624
625
	n = strlen(str);
626
	ret = dgram_write(bp, str, n);
627
	return (ret);
628
}
629
630
631
static int
632
BIO_dgram_should_retry(int i)
633
{
634
	int err;
635
636
	if ((i == 0) || (i == -1)) {
637
		err = errno;
638
		return (BIO_dgram_non_fatal_error(err));
639
	}
640
	return (0);
641
}
642
643
int
644
BIO_dgram_non_fatal_error(int err)
645
{
646
	switch (err) {
647
	case EINTR:
648
	case EAGAIN:
649
	case EINPROGRESS:
650
	case EALREADY:
651
		return (1);
652
	default:
653
		break;
654
	}
655
	return (0);
656
}
657
658
#endif