xref: /openbsd-src/lib/libcrypto/bio/bss_dgram.c (revision acf644016ec1190723fc541ba590471e90a9ef53)
1*acf64401Sbeck /* $OpenBSD: bss_dgram.c,v 1.45 2023/07/05 21:23:37 beck Exp $ */
25650a0e1Sdjm /*
35650a0e1Sdjm  * DTLS implementation written by Nagendra Modadugu
45650a0e1Sdjm  * (nagendra@cs.stanford.edu) for the OpenSSL project 2005.
55650a0e1Sdjm  */
65650a0e1Sdjm /* ====================================================================
75650a0e1Sdjm  * Copyright (c) 1999-2005 The OpenSSL Project.  All rights reserved.
85650a0e1Sdjm  *
95650a0e1Sdjm  * Redistribution and use in source and binary forms, with or without
105650a0e1Sdjm  * modification, are permitted provided that the following conditions
115650a0e1Sdjm  * are met:
125650a0e1Sdjm  *
135650a0e1Sdjm  * 1. Redistributions of source code must retain the above copyright
145650a0e1Sdjm  *    notice, this list of conditions and the following disclaimer.
155650a0e1Sdjm  *
165650a0e1Sdjm  * 2. Redistributions in binary form must reproduce the above copyright
175650a0e1Sdjm  *    notice, this list of conditions and the following disclaimer in
185650a0e1Sdjm  *    the documentation and/or other materials provided with the
195650a0e1Sdjm  *    distribution.
205650a0e1Sdjm  *
215650a0e1Sdjm  * 3. All advertising materials mentioning features or use of this
225650a0e1Sdjm  *    software must display the following acknowledgment:
235650a0e1Sdjm  *    "This product includes software developed by the OpenSSL Project
245650a0e1Sdjm  *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
255650a0e1Sdjm  *
265650a0e1Sdjm  * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
275650a0e1Sdjm  *    endorse or promote products derived from this software without
285650a0e1Sdjm  *    prior written permission. For written permission, please contact
295650a0e1Sdjm  *    openssl-core@OpenSSL.org.
305650a0e1Sdjm  *
315650a0e1Sdjm  * 5. Products derived from this software may not be called "OpenSSL"
325650a0e1Sdjm  *    nor may "OpenSSL" appear in their names without prior written
335650a0e1Sdjm  *    permission of the OpenSSL Project.
345650a0e1Sdjm  *
355650a0e1Sdjm  * 6. Redistributions of any form whatsoever must retain the following
365650a0e1Sdjm  *    acknowledgment:
375650a0e1Sdjm  *    "This product includes software developed by the OpenSSL Project
385650a0e1Sdjm  *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
395650a0e1Sdjm  *
405650a0e1Sdjm  * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
415650a0e1Sdjm  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
425650a0e1Sdjm  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
435650a0e1Sdjm  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
445650a0e1Sdjm  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
455650a0e1Sdjm  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
465650a0e1Sdjm  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
475650a0e1Sdjm  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
485650a0e1Sdjm  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
495650a0e1Sdjm  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
505650a0e1Sdjm  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
515650a0e1Sdjm  * OF THE POSSIBILITY OF SUCH DAMAGE.
525650a0e1Sdjm  * ====================================================================
535650a0e1Sdjm  *
545650a0e1Sdjm  * This product includes cryptographic software written by Eric Young
555650a0e1Sdjm  * (eay@cryptsoft.com).  This product includes software written by Tim
565650a0e1Sdjm  * Hudson (tjh@cryptsoft.com).
575650a0e1Sdjm  *
585650a0e1Sdjm  */
595650a0e1Sdjm 
60a8913c44Sjsing #include <sys/socket.h>
6170eb4da3Sbcook #include <sys/time.h>
625650a0e1Sdjm 
63a8913c44Sjsing #include <netinet/in.h>
64a8913c44Sjsing 
655650a0e1Sdjm #include <errno.h>
66a8913c44Sjsing #include <netdb.h>
67a8913c44Sjsing #include <stdio.h>
68a8913c44Sjsing #include <string.h>
690b65c6b9Sderaadt #include <unistd.h>
70a8913c44Sjsing 
718cf4d6a6Sjsing #include <openssl/opensslconf.h>
728cf4d6a6Sjsing 
735650a0e1Sdjm #include <openssl/bio.h>
74edd82e4aSderaadt 
7594b1984eStb #include "bio_local.h"
7694b1984eStb 
77c32db552Sdjm #ifndef OPENSSL_NO_DGRAM
785650a0e1Sdjm 
795cdd308eSdjm 
805650a0e1Sdjm static int dgram_write(BIO *h, const char *buf, int num);
815650a0e1Sdjm static int dgram_read(BIO *h, char *buf, int size);
825650a0e1Sdjm static int dgram_puts(BIO *h, const char *str);
835650a0e1Sdjm static long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2);
845650a0e1Sdjm static int dgram_new(BIO *h);
855650a0e1Sdjm static int dgram_free(BIO *data);
865650a0e1Sdjm static int dgram_clear(BIO *bio);
875650a0e1Sdjm 
885cdd308eSdjm 
898214bb00Sdjm static int BIO_dgram_should_retry(int s);
905650a0e1Sdjm 
918341b5ddStb static const BIO_METHOD methods_dgramp = {
92e402ce74Smiod 	.type = BIO_TYPE_DGRAM,
93e402ce74Smiod 	.name = "datagram socket",
94e402ce74Smiod 	.bwrite = dgram_write,
95e402ce74Smiod 	.bread = dgram_read,
96e402ce74Smiod 	.bputs = dgram_puts,
97e402ce74Smiod 	.ctrl = dgram_ctrl,
98e402ce74Smiod 	.create = dgram_new,
99e402ce74Smiod 	.destroy = dgram_free
1005650a0e1Sdjm };
1015650a0e1Sdjm 
1025cdd308eSdjm 
103c3d505beSjsing typedef struct bio_dgram_data_st {
1040a5d6edeSdjm 	union {
1050a5d6edeSdjm 		struct sockaddr sa;
1060a5d6edeSdjm 		struct sockaddr_in sa_in;
1070a5d6edeSdjm 		struct sockaddr_in6 sa_in6;
1080a5d6edeSdjm 	} peer;
1095650a0e1Sdjm 	unsigned int connected;
1105650a0e1Sdjm 	unsigned int _errno;
1115650a0e1Sdjm 	unsigned int mtu;
1120a5d6edeSdjm 	struct timeval next_timeout;
1130a5d6edeSdjm 	struct timeval socket_timeout;
1145650a0e1Sdjm } bio_dgram_data;
1155650a0e1Sdjm 
1165cdd308eSdjm 
1178341b5ddStb const BIO_METHOD *
BIO_s_datagram(void)118ae7f143bSderaadt BIO_s_datagram(void)
1195650a0e1Sdjm {
1205650a0e1Sdjm 	return (&methods_dgramp);
1215650a0e1Sdjm }
122*acf64401Sbeck LCRYPTO_ALIAS(BIO_s_datagram);
1235650a0e1Sdjm 
124ae7f143bSderaadt BIO *
BIO_new_dgram(int fd,int close_flag)125ae7f143bSderaadt BIO_new_dgram(int fd, int close_flag)
1265650a0e1Sdjm {
1275650a0e1Sdjm 	BIO *ret;
1285650a0e1Sdjm 
1295650a0e1Sdjm 	ret = BIO_new(BIO_s_datagram());
130c3d505beSjsing 	if (ret == NULL)
131c3d505beSjsing 		return (NULL);
1325650a0e1Sdjm 	BIO_set_fd(ret, fd, close_flag);
1335650a0e1Sdjm 	return (ret);
1345650a0e1Sdjm }
135*acf64401Sbeck LCRYPTO_ALIAS(BIO_new_dgram);
1365650a0e1Sdjm 
137c3d505beSjsing static int
dgram_new(BIO * bi)138c3d505beSjsing dgram_new(BIO *bi)
1395650a0e1Sdjm {
1405650a0e1Sdjm 	bio_dgram_data *data = NULL;
1415650a0e1Sdjm 
1425650a0e1Sdjm 	bi->init = 0;
1435650a0e1Sdjm 	bi->num = 0;
1443c6fe066Sderaadt 	data = calloc(1, sizeof(bio_dgram_data));
1455650a0e1Sdjm 	if (data == NULL)
1465650a0e1Sdjm 		return 0;
1475650a0e1Sdjm 	bi->ptr = data;
1485650a0e1Sdjm 
1495650a0e1Sdjm 	bi->flags = 0;
1505650a0e1Sdjm 	return (1);
1515650a0e1Sdjm }
1525650a0e1Sdjm 
153c3d505beSjsing static int
dgram_free(BIO * a)154c3d505beSjsing dgram_free(BIO *a)
1555650a0e1Sdjm {
1565650a0e1Sdjm 	bio_dgram_data *data;
1575650a0e1Sdjm 
158c3d505beSjsing 	if (a == NULL)
159c3d505beSjsing 		return (0);
1605650a0e1Sdjm 	if (!dgram_clear(a))
1615650a0e1Sdjm 		return 0;
1625650a0e1Sdjm 
1635650a0e1Sdjm 	data = (bio_dgram_data *)a->ptr;
1646f3a6cb1Sbeck 	free(data);
1655650a0e1Sdjm 
1665650a0e1Sdjm 	return (1);
1675650a0e1Sdjm }
1685650a0e1Sdjm 
169c3d505beSjsing static int
dgram_clear(BIO * a)170c3d505beSjsing dgram_clear(BIO *a)
1715650a0e1Sdjm {
172c3d505beSjsing 	if (a == NULL)
173c3d505beSjsing 		return (0);
174c3d505beSjsing 	if (a->shutdown) {
175c3d505beSjsing 		if (a->init) {
176244f535cSderaadt 			shutdown(a->num, SHUT_RDWR);
177244f535cSderaadt 			close(a->num);
1785650a0e1Sdjm 		}
1795650a0e1Sdjm 		a->init = 0;
1805650a0e1Sdjm 		a->flags = 0;
1815650a0e1Sdjm 	}
1825650a0e1Sdjm 	return (1);
1835650a0e1Sdjm }
1845650a0e1Sdjm 
185c3d505beSjsing static void
dgram_adjust_rcv_timeout(BIO * b)186c3d505beSjsing dgram_adjust_rcv_timeout(BIO *b)
1870a5d6edeSdjm {
1880a5d6edeSdjm #if defined(SO_RCVTIMEO)
1890a5d6edeSdjm 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
1900a5d6edeSdjm 
1910a5d6edeSdjm 	/* Is a timer active? */
192c3d505beSjsing 	if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
1930a5d6edeSdjm 		struct timeval timenow, timeleft;
1940a5d6edeSdjm 
1950a5d6edeSdjm 		/* Read current socket timeout */
196c0f961d4Sbcook 		socklen_t sz = sizeof(data->socket_timeout);
1970a5d6edeSdjm 		if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
198c0f961d4Sbcook 		    &(data->socket_timeout), &sz) < 0) {
199c3d505beSjsing 			perror("getsockopt");
200c0f961d4Sbcook 		}
2010a5d6edeSdjm 
2020a5d6edeSdjm 		/* Get current time */
2030f5f5d2dSbcook 		gettimeofday(&timenow, NULL);
2040a5d6edeSdjm 
2050a5d6edeSdjm 		/* Calculate time left until timer expires */
2060a5d6edeSdjm 		memcpy(&timeleft, &(data->next_timeout), sizeof(struct timeval));
2070a5d6edeSdjm 		timeleft.tv_sec -= timenow.tv_sec;
2080a5d6edeSdjm 		timeleft.tv_usec -= timenow.tv_usec;
209c3d505beSjsing 		if (timeleft.tv_usec < 0) {
2100a5d6edeSdjm 			timeleft.tv_sec--;
2110a5d6edeSdjm 			timeleft.tv_usec += 1000000;
2120a5d6edeSdjm 		}
2130a5d6edeSdjm 
214c3d505beSjsing 		if (timeleft.tv_sec < 0) {
2150a5d6edeSdjm 			timeleft.tv_sec = 0;
2160a5d6edeSdjm 			timeleft.tv_usec = 1;
2170a5d6edeSdjm 		}
2180a5d6edeSdjm 
21971743258Sjmc 		/* Adjust socket timeout if next handshake message timer
2200a5d6edeSdjm 		 * will expire earlier.
2210a5d6edeSdjm 		 */
222c3d505beSjsing 		if ((data->socket_timeout.tv_sec == 0 &&
223c3d505beSjsing 		    data->socket_timeout.tv_usec == 0) ||
2240a5d6edeSdjm 		    (data->socket_timeout.tv_sec > timeleft.tv_sec) ||
2250a5d6edeSdjm 		    (data->socket_timeout.tv_sec == timeleft.tv_sec &&
226c3d505beSjsing 		    data->socket_timeout.tv_usec >= timeleft.tv_usec)) {
227c3d505beSjsing 			if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
228c3d505beSjsing 			    &timeleft, sizeof(struct timeval)) < 0) {
229c3d505beSjsing 				perror("setsockopt");
230c3d505beSjsing 			}
2310a5d6edeSdjm 		}
2320a5d6edeSdjm 	}
2330a5d6edeSdjm #endif
2340a5d6edeSdjm }
2350a5d6edeSdjm 
236c3d505beSjsing static void
dgram_reset_rcv_timeout(BIO * b)237c3d505beSjsing dgram_reset_rcv_timeout(BIO *b)
2380a5d6edeSdjm {
2390a5d6edeSdjm #if defined(SO_RCVTIMEO)
2400a5d6edeSdjm 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
2410a5d6edeSdjm 
2420a5d6edeSdjm 	/* Is a timer active? */
243c3d505beSjsing 	if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
244c3d505beSjsing 		if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
245c3d505beSjsing 		    &(data->socket_timeout), sizeof(struct timeval)) < 0) {
246c3d505beSjsing 			perror("setsockopt");
247c3d505beSjsing 		}
2480a5d6edeSdjm 	}
2490a5d6edeSdjm #endif
2500a5d6edeSdjm }
2510a5d6edeSdjm 
252c3d505beSjsing static int
dgram_read(BIO * b,char * out,int outl)253c3d505beSjsing dgram_read(BIO *b, char *out, int outl)
2545650a0e1Sdjm {
2555650a0e1Sdjm 	int ret = 0;
2565650a0e1Sdjm 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
2575650a0e1Sdjm 
2580a5d6edeSdjm 	struct	{
259de05ecf1Smiod 		socklen_t len;
2600a5d6edeSdjm 		union	{
2610a5d6edeSdjm 			struct sockaddr sa;
2620a5d6edeSdjm 			struct sockaddr_in sa_in;
2630a5d6edeSdjm 			struct sockaddr_in6 sa_in6;
2640a5d6edeSdjm 		} peer;
2650a5d6edeSdjm 	} sa;
2660a5d6edeSdjm 
267de05ecf1Smiod 	sa.len = sizeof(sa.peer);
2685650a0e1Sdjm 
269c3d505beSjsing 	if (out != NULL) {
2709ee38ff1Sderaadt 		errno = 0;
2713d7ceb20Sbcook 		memset(&sa.peer, 0, sizeof(sa.peer));
2720a5d6edeSdjm 		dgram_adjust_rcv_timeout(b);
273de05ecf1Smiod 		ret = recvfrom(b->num, out, outl, 0, &sa.peer.sa, &sa.len);
2745650a0e1Sdjm 
2750a5d6edeSdjm 		if (! data->connected  && ret >= 0)
2760a5d6edeSdjm 			BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &sa.peer);
2775650a0e1Sdjm 
2785650a0e1Sdjm 		BIO_clear_retry_flags(b);
279c3d505beSjsing 		if (ret < 0) {
280c3d505beSjsing 			if (BIO_dgram_should_retry(ret)) {
2815650a0e1Sdjm 				BIO_set_retry_read(b);
2829ee38ff1Sderaadt 				data->_errno = errno;
2835650a0e1Sdjm 			}
2845650a0e1Sdjm 		}
285c32db552Sdjm 
286c32db552Sdjm 		dgram_reset_rcv_timeout(b);
2875650a0e1Sdjm 	}
2885650a0e1Sdjm 	return (ret);
2895650a0e1Sdjm }
2905650a0e1Sdjm 
291c3d505beSjsing static int
dgram_write(BIO * b,const char * in,int inl)292c3d505beSjsing dgram_write(BIO *b, const char *in, int inl)
2935650a0e1Sdjm {
2945650a0e1Sdjm 	int ret;
2955650a0e1Sdjm 	bio_dgram_data *data = (bio_dgram_data *)b->ptr;
2969ee38ff1Sderaadt 	errno = 0;
2975650a0e1Sdjm 
2985650a0e1Sdjm 	if (data->connected)
2999ee38ff1Sderaadt 		ret = write(b->num, in, inl);
300c3d505beSjsing 	else {
3010a5d6edeSdjm 		int peerlen = sizeof(data->peer);
3020a5d6edeSdjm 
3030a5d6edeSdjm 		if (data->peer.sa.sa_family == AF_INET)
3040a5d6edeSdjm 			peerlen = sizeof(data->peer.sa_in);
3050a5d6edeSdjm 		else if (data->peer.sa.sa_family == AF_INET6)
3060a5d6edeSdjm 			peerlen = sizeof(data->peer.sa_in6);
3070a5d6edeSdjm 		ret = sendto(b->num, in, inl, 0, &data->peer.sa, peerlen);
3080a5d6edeSdjm 	}
3095650a0e1Sdjm 
3105650a0e1Sdjm 	BIO_clear_retry_flags(b);
311c3d505beSjsing 	if (ret <= 0) {
312c3d505beSjsing 		if (BIO_dgram_should_retry(ret)) {
3135650a0e1Sdjm 			BIO_set_retry_write(b);
314c3d505beSjsing 
3159ee38ff1Sderaadt 			data->_errno = errno;
31669f1b125Stedu 			/*
31769f1b125Stedu 			 * higher layers are responsible for querying MTU,
31869f1b125Stedu 			 * if necessary
31969f1b125Stedu 			 */
3205650a0e1Sdjm 		}
3215650a0e1Sdjm 	}
3225650a0e1Sdjm 	return (ret);
3235650a0e1Sdjm }
3245650a0e1Sdjm 
325c3d505beSjsing static long
dgram_ctrl(BIO * b,int cmd,long num,void * ptr)326c3d505beSjsing dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
3275650a0e1Sdjm {
3285650a0e1Sdjm 	long ret = 1;
3295650a0e1Sdjm 	int *ip;
3305650a0e1Sdjm 	struct sockaddr *to = NULL;
3315650a0e1Sdjm 	bio_dgram_data *data = NULL;
3322bfe11d6Sbcook #if (defined(IP_MTU_DISCOVER) || defined(IP_MTU))
33397222eddSmiod 	int sockopt_val = 0;
33497222eddSmiod 	socklen_t sockopt_len;	/* assume that system supporting IP_MTU is
33597222eddSmiod 				 * modern enough to define socklen_t */
3360a5d6edeSdjm 	socklen_t addr_len;
3370a5d6edeSdjm 	union	{
3380a5d6edeSdjm 		struct sockaddr	sa;
3390a5d6edeSdjm 		struct sockaddr_in s4;
3400a5d6edeSdjm 		struct sockaddr_in6 s6;
3410a5d6edeSdjm 	} addr;
3420a5d6edeSdjm #endif
3435650a0e1Sdjm 
3445650a0e1Sdjm 	data = (bio_dgram_data *)b->ptr;
3455650a0e1Sdjm 
346c3d505beSjsing 	switch (cmd) {
3475650a0e1Sdjm 	case BIO_CTRL_RESET:
3485650a0e1Sdjm 		num = 0;
3495650a0e1Sdjm 	case BIO_C_FILE_SEEK:
3505650a0e1Sdjm 		ret = 0;
3515650a0e1Sdjm 		break;
3525650a0e1Sdjm 	case BIO_C_FILE_TELL:
3535650a0e1Sdjm 	case BIO_CTRL_INFO:
3545650a0e1Sdjm 		ret = 0;
3555650a0e1Sdjm 		break;
3565650a0e1Sdjm 	case BIO_C_SET_FD:
3575650a0e1Sdjm 		dgram_clear(b);
3585650a0e1Sdjm 		b->num= *((int *)ptr);
3595650a0e1Sdjm 		b->shutdown = (int)num;
3605650a0e1Sdjm 		b->init = 1;
3615650a0e1Sdjm 		break;
3625650a0e1Sdjm 	case BIO_C_GET_FD:
363c3d505beSjsing 		if (b->init) {
3645650a0e1Sdjm 			ip = (int *)ptr;
365c3d505beSjsing 			if (ip != NULL)
366c3d505beSjsing 				*ip = b->num;
3675650a0e1Sdjm 			ret = b->num;
368c3d505beSjsing 		} else
3695650a0e1Sdjm 			ret = -1;
3705650a0e1Sdjm 		break;
3715650a0e1Sdjm 	case BIO_CTRL_GET_CLOSE:
3725650a0e1Sdjm 		ret = b->shutdown;
3735650a0e1Sdjm 		break;
3745650a0e1Sdjm 	case BIO_CTRL_SET_CLOSE:
3755650a0e1Sdjm 		b->shutdown = (int)num;
3765650a0e1Sdjm 		break;
3775650a0e1Sdjm 	case BIO_CTRL_PENDING:
3785650a0e1Sdjm 	case BIO_CTRL_WPENDING:
3795650a0e1Sdjm 		ret = 0;
3805650a0e1Sdjm 		break;
3815650a0e1Sdjm 	case BIO_CTRL_DUP:
3825650a0e1Sdjm 	case BIO_CTRL_FLUSH:
3835650a0e1Sdjm 		ret = 1;
3845650a0e1Sdjm 		break;
3855650a0e1Sdjm 	case BIO_CTRL_DGRAM_CONNECT:
3865650a0e1Sdjm 		to = (struct sockaddr *)ptr;
387c3d505beSjsing 		switch (to->sa_family) {
3880a5d6edeSdjm 		case AF_INET:
3890a5d6edeSdjm 			memcpy(&data->peer, to, sizeof(data->peer.sa_in));
3900a5d6edeSdjm 			break;
3910a5d6edeSdjm 		case AF_INET6:
3920a5d6edeSdjm 			memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
3930a5d6edeSdjm 			break;
3940a5d6edeSdjm 		default:
3950a5d6edeSdjm 			memcpy(&data->peer, to, sizeof(data->peer.sa));
3960a5d6edeSdjm 			break;
3970a5d6edeSdjm 		}
3985650a0e1Sdjm 		break;
3995650a0e1Sdjm 		/* (Linux)kernel sets DF bit on outgoing IP packets */
4005650a0e1Sdjm 	case BIO_CTRL_DGRAM_MTU_DISCOVER:
4012bfe11d6Sbcook #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
4020a5d6edeSdjm 		addr_len = (socklen_t)sizeof(addr);
4030a5d6edeSdjm 		memset((void *)&addr, 0, sizeof(addr));
404c3d505beSjsing 		if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
4050a5d6edeSdjm 			ret = 0;
4060a5d6edeSdjm 			break;
4070a5d6edeSdjm 		}
408c3d505beSjsing 		switch (addr.sa.sa_family) {
4090a5d6edeSdjm 		case AF_INET:
4105650a0e1Sdjm 			sockopt_val = IP_PMTUDISC_DO;
411c0f961d4Sbcook 			ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
412c0f961d4Sbcook 			    &sockopt_val, sizeof(sockopt_val));
413c0f961d4Sbcook 			if (ret < 0)
4145650a0e1Sdjm 				perror("setsockopt");
4155650a0e1Sdjm 			break;
41640fdcb44Sderaadt #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
4170a5d6edeSdjm 		case AF_INET6:
4180a5d6edeSdjm 			sockopt_val = IPV6_PMTUDISC_DO;
419c0f961d4Sbcook 			ret = setsockopt(b->num, IPPROTO_IPV6,
420c3d505beSjsing 			    IPV6_MTU_DISCOVER, &sockopt_val,
421c0f961d4Sbcook 			    sizeof(sockopt_val));
422c0f961d4Sbcook 			if (ret < 0)
4230a5d6edeSdjm 				perror("setsockopt");
4240a5d6edeSdjm 			break;
4250a5d6edeSdjm #endif
4260a5d6edeSdjm 		default:
4270a5d6edeSdjm 			ret = -1;
4280a5d6edeSdjm 			break;
4290a5d6edeSdjm 		}
4300a5d6edeSdjm #else
4311ce6571fSdoug 		ret = -1;
4325650a0e1Sdjm #endif
4331ce6571fSdoug 		break;
4345650a0e1Sdjm 	case BIO_CTRL_DGRAM_QUERY_MTU:
4352bfe11d6Sbcook #if defined(IP_MTU)
4360a5d6edeSdjm 		addr_len = (socklen_t)sizeof(addr);
4370a5d6edeSdjm 		memset((void *)&addr, 0, sizeof(addr));
438c3d505beSjsing 		if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
4390a5d6edeSdjm 			ret = 0;
4400a5d6edeSdjm 			break;
4410a5d6edeSdjm 		}
4425650a0e1Sdjm 		sockopt_len = sizeof(sockopt_val);
443c3d505beSjsing 		switch (addr.sa.sa_family) {
4440a5d6edeSdjm 		case AF_INET:
445c0f961d4Sbcook 			ret = getsockopt(b->num, IPPROTO_IP, IP_MTU,
446c0f961d4Sbcook 			    &sockopt_val, &sockopt_len);
447c0f961d4Sbcook 			if (ret < 0 || sockopt_val < 0) {
4480a5d6edeSdjm 				ret = 0;
449c3d505beSjsing 			} else {
4500a5d6edeSdjm 				/* we assume that the transport protocol is UDP and no
4510a5d6edeSdjm 				 * IP options are used.
4520a5d6edeSdjm 				 */
4530a5d6edeSdjm 				data->mtu = sockopt_val - 8 - 20;
4545650a0e1Sdjm 				ret = data->mtu;
4555650a0e1Sdjm 			}
4565650a0e1Sdjm 			break;
45740fdcb44Sderaadt #if defined(IPV6_MTU)
4580a5d6edeSdjm 		case AF_INET6:
459c0f961d4Sbcook 			ret = getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU,
460c0f961d4Sbcook 			    &sockopt_val, &sockopt_len);
461c0f961d4Sbcook 			if (ret < 0 || sockopt_val < 0) {
4620a5d6edeSdjm 				ret = 0;
463c3d505beSjsing 			} else {
4640a5d6edeSdjm 				/* we assume that the transport protocol is UDP and no
4650a5d6edeSdjm 				 * IPV6 options are used.
4660a5d6edeSdjm 				 */
4670a5d6edeSdjm 				data->mtu = sockopt_val - 8 - 40;
4680a5d6edeSdjm 				ret = data->mtu;
4690a5d6edeSdjm 			}
4700a5d6edeSdjm 			break;
4710a5d6edeSdjm #endif
4720a5d6edeSdjm default:
4730a5d6edeSdjm 			ret = 0;
4740a5d6edeSdjm 			break;
4750a5d6edeSdjm 		}
4760a5d6edeSdjm #else
4770a5d6edeSdjm 		ret = 0;
4780a5d6edeSdjm #endif
4790a5d6edeSdjm 		break;
4805cdd308eSdjm 	case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
481c3d505beSjsing 		switch (data->peer.sa.sa_family) {
4825cdd308eSdjm 		case AF_INET:
4835cdd308eSdjm 			ret = 576 - 20 - 8;
4845cdd308eSdjm 			break;
4855cdd308eSdjm 		case AF_INET6:
4865cdd308eSdjm #ifdef IN6_IS_ADDR_V4MAPPED
4875cdd308eSdjm 			if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr))
4885cdd308eSdjm 				ret = 576 - 20 - 8;
4895cdd308eSdjm 			else
4905cdd308eSdjm #endif
4915cdd308eSdjm 				ret = 1280 - 40 - 8;
4925cdd308eSdjm 			break;
4935cdd308eSdjm 		default:
4945cdd308eSdjm 			ret = 576 - 20 - 8;
4955cdd308eSdjm 			break;
4965cdd308eSdjm 		}
4975cdd308eSdjm 		break;
4985650a0e1Sdjm 	case BIO_CTRL_DGRAM_GET_MTU:
4995650a0e1Sdjm 		return data->mtu;
5005650a0e1Sdjm 		break;
5015650a0e1Sdjm 	case BIO_CTRL_DGRAM_SET_MTU:
5025650a0e1Sdjm 		data->mtu = num;
5035650a0e1Sdjm 		ret = num;
5045650a0e1Sdjm 		break;
5055650a0e1Sdjm 	case BIO_CTRL_DGRAM_SET_CONNECTED:
5065650a0e1Sdjm 		to = (struct sockaddr *)ptr;
5075650a0e1Sdjm 
508c3d505beSjsing 		if (to != NULL) {
5095650a0e1Sdjm 			data->connected = 1;
510c3d505beSjsing 			switch (to->sa_family) {
5110a5d6edeSdjm 			case AF_INET:
5120a5d6edeSdjm 				memcpy(&data->peer, to, sizeof(data->peer.sa_in));
5130a5d6edeSdjm 				break;
5140a5d6edeSdjm 			case AF_INET6:
5150a5d6edeSdjm 				memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
5160a5d6edeSdjm 				break;
5170a5d6edeSdjm 			default:
5180a5d6edeSdjm 				memcpy(&data->peer, to, sizeof(data->peer.sa));
5190a5d6edeSdjm 				break;
5200a5d6edeSdjm 			}
521c3d505beSjsing 		} else {
5225650a0e1Sdjm 			data->connected = 0;
5233d7ceb20Sbcook 			memset(&(data->peer), 0, sizeof(data->peer));
5245650a0e1Sdjm 		}
5255650a0e1Sdjm 		break;
5260a5d6edeSdjm 	case BIO_CTRL_DGRAM_GET_PEER:
527c3d505beSjsing 		switch (data->peer.sa.sa_family) {
5280a5d6edeSdjm 		case AF_INET:
5290a5d6edeSdjm 			ret = sizeof(data->peer.sa_in);
5300a5d6edeSdjm 			break;
5310a5d6edeSdjm 		case AF_INET6:
5320a5d6edeSdjm 			ret = sizeof(data->peer.sa_in6);
5330a5d6edeSdjm 			break;
5340a5d6edeSdjm 		default:
5350a5d6edeSdjm 			ret = sizeof(data->peer.sa);
5360a5d6edeSdjm 			break;
5370a5d6edeSdjm 		}
5380a5d6edeSdjm 		if (num == 0 || num > ret)
5390a5d6edeSdjm 			num = ret;
5400a5d6edeSdjm 		memcpy(ptr, &data->peer, (ret = num));
5410a5d6edeSdjm 		break;
5425650a0e1Sdjm 	case BIO_CTRL_DGRAM_SET_PEER:
5435650a0e1Sdjm 		to = (struct sockaddr *) ptr;
544c3d505beSjsing 		switch (to->sa_family) {
5450a5d6edeSdjm 		case AF_INET:
5460a5d6edeSdjm 			memcpy(&data->peer, to, sizeof(data->peer.sa_in));
5470a5d6edeSdjm 			break;
5480a5d6edeSdjm 		case AF_INET6:
5490a5d6edeSdjm 			memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
5500a5d6edeSdjm 			break;
5510a5d6edeSdjm 		default:
5520a5d6edeSdjm 			memcpy(&data->peer, to, sizeof(data->peer.sa));
5530a5d6edeSdjm 			break;
5540a5d6edeSdjm 		}
5550a5d6edeSdjm 		break;
5560a5d6edeSdjm 	case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
5570a5d6edeSdjm 		memcpy(&(data->next_timeout), ptr, sizeof(struct timeval));
5585650a0e1Sdjm 		break;
5598214bb00Sdjm #if defined(SO_RCVTIMEO)
5605650a0e1Sdjm 	case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
5615650a0e1Sdjm 		if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr,
562c3d505beSjsing 		    sizeof(struct timeval)) < 0) {
563c3d505beSjsing 			perror("setsockopt");
564c3d505beSjsing 			ret = -1;
565c3d505beSjsing 		}
5665650a0e1Sdjm 		break;
5675650a0e1Sdjm 	case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
5688214bb00Sdjm 		{
569c0f961d4Sbcook 			socklen_t sz = sizeof(struct timeval);
5705650a0e1Sdjm 			if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
571c0f961d4Sbcook 			    ptr, &sz) < 0) {
572c3d505beSjsing 				perror("getsockopt");
573c3d505beSjsing 				ret = -1;
574c3d505beSjsing 			} else
575c0f961d4Sbcook 				ret = sz;
57697222eddSmiod 		}
5775650a0e1Sdjm 		break;
5788214bb00Sdjm #endif
5798214bb00Sdjm #if defined(SO_SNDTIMEO)
5805650a0e1Sdjm 	case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
5815650a0e1Sdjm 		if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr,
582c3d505beSjsing 		    sizeof(struct timeval)) < 0) {
583c3d505beSjsing 			perror("setsockopt");
584c3d505beSjsing 			ret = -1;
585c3d505beSjsing 		}
5865650a0e1Sdjm 		break;
5875650a0e1Sdjm 	case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
5888214bb00Sdjm 		{
589c0f961d4Sbcook 			socklen_t sz = sizeof(struct timeval);
5905650a0e1Sdjm 			if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
591c0f961d4Sbcook 			    ptr, &sz) < 0) {
592c3d505beSjsing 				perror("getsockopt");
593c3d505beSjsing 				ret = -1;
594c3d505beSjsing 			} else
595c0f961d4Sbcook 				ret = sz;
59697222eddSmiod 		}
5975650a0e1Sdjm 		break;
5988214bb00Sdjm #endif
5995650a0e1Sdjm 	case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
6005650a0e1Sdjm 		/* fall-through */
6015650a0e1Sdjm 	case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
6022ccb125dSjsing 		if (data->_errno == EAGAIN) {
6035650a0e1Sdjm 			ret = 1;
6045650a0e1Sdjm 			data->_errno = 0;
605c3d505beSjsing 		} else
6065650a0e1Sdjm 			ret = 0;
6075650a0e1Sdjm 		break;
6085650a0e1Sdjm #ifdef EMSGSIZE
6095650a0e1Sdjm 	case BIO_CTRL_DGRAM_MTU_EXCEEDED:
610c3d505beSjsing 		if (data->_errno == EMSGSIZE) {
6115650a0e1Sdjm 			ret = 1;
6125650a0e1Sdjm 			data->_errno = 0;
613c3d505beSjsing 		} else
6145650a0e1Sdjm 			ret = 0;
6155650a0e1Sdjm 		break;
6165650a0e1Sdjm #endif
6175650a0e1Sdjm 	default:
6185650a0e1Sdjm 		ret = 0;
6195650a0e1Sdjm 		break;
6205650a0e1Sdjm 	}
6215650a0e1Sdjm 	return (ret);
6225650a0e1Sdjm }
6235650a0e1Sdjm 
624c3d505beSjsing static int
dgram_puts(BIO * bp,const char * str)625c3d505beSjsing dgram_puts(BIO *bp, const char *str)
6265650a0e1Sdjm {
6275650a0e1Sdjm 	int n, ret;
6285650a0e1Sdjm 
6295650a0e1Sdjm 	n = strlen(str);
6305650a0e1Sdjm 	ret = dgram_write(bp, str, n);
6315650a0e1Sdjm 	return (ret);
6325650a0e1Sdjm }
6335650a0e1Sdjm 
6345cdd308eSdjm 
635c3d505beSjsing static int
BIO_dgram_should_retry(int i)636c3d505beSjsing BIO_dgram_should_retry(int i)
6375650a0e1Sdjm {
6385650a0e1Sdjm 	int err;
6395650a0e1Sdjm 
640c3d505beSjsing 	if ((i == 0) || (i == -1)) {
6419ee38ff1Sderaadt 		err = errno;
6425650a0e1Sdjm 		return (BIO_dgram_non_fatal_error(err));
6435650a0e1Sdjm 	}
6445650a0e1Sdjm 	return (0);
6455650a0e1Sdjm }
6465650a0e1Sdjm 
647c3d505beSjsing int
BIO_dgram_non_fatal_error(int err)648c3d505beSjsing BIO_dgram_non_fatal_error(int err)
6495650a0e1Sdjm {
650c3d505beSjsing 	switch (err) {
6515650a0e1Sdjm 	case EINTR:
6525650a0e1Sdjm 	case EAGAIN:
6535650a0e1Sdjm 	case EINPROGRESS:
6545650a0e1Sdjm 	case EALREADY:
6555650a0e1Sdjm 		return (1);
6565650a0e1Sdjm 	default:
6575650a0e1Sdjm 		break;
6585650a0e1Sdjm 	}
6595650a0e1Sdjm 	return (0);
6605650a0e1Sdjm }
661*acf64401Sbeck LCRYPTO_ALIAS(BIO_dgram_non_fatal_error);
6620a5d6edeSdjm 
663c32db552Sdjm #endif
664