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