1160814Ssimon/* crypto/bio/bio_dgram.c */
2280304Sjkim/*
3160814Ssimon * DTLS implementation written by Nagendra Modadugu
4280304Sjkim * (nagendra@cs.stanford.edu) for the OpenSSL project 2005.
5160814Ssimon */
6160814Ssimon/* ====================================================================
7160814Ssimon * Copyright (c) 1999-2005 The OpenSSL Project.  All rights reserved.
8160814Ssimon *
9160814Ssimon * Redistribution and use in source and binary forms, with or without
10160814Ssimon * modification, are permitted provided that the following conditions
11160814Ssimon * are met:
12160814Ssimon *
13160814Ssimon * 1. Redistributions of source code must retain the above copyright
14280304Sjkim *    notice, this list of conditions and the following disclaimer.
15160814Ssimon *
16160814Ssimon * 2. Redistributions in binary form must reproduce the above copyright
17160814Ssimon *    notice, this list of conditions and the following disclaimer in
18160814Ssimon *    the documentation and/or other materials provided with the
19160814Ssimon *    distribution.
20160814Ssimon *
21160814Ssimon * 3. All advertising materials mentioning features or use of this
22160814Ssimon *    software must display the following acknowledgment:
23160814Ssimon *    "This product includes software developed by the OpenSSL Project
24160814Ssimon *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
25160814Ssimon *
26160814Ssimon * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27160814Ssimon *    endorse or promote products derived from this software without
28160814Ssimon *    prior written permission. For written permission, please contact
29160814Ssimon *    openssl-core@OpenSSL.org.
30160814Ssimon *
31160814Ssimon * 5. Products derived from this software may not be called "OpenSSL"
32160814Ssimon *    nor may "OpenSSL" appear in their names without prior written
33160814Ssimon *    permission of the OpenSSL Project.
34160814Ssimon *
35160814Ssimon * 6. Redistributions of any form whatsoever must retain the following
36160814Ssimon *    acknowledgment:
37160814Ssimon *    "This product includes software developed by the OpenSSL Project
38160814Ssimon *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
39160814Ssimon *
40160814Ssimon * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
41160814Ssimon * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42160814Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43160814Ssimon * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
44160814Ssimon * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45160814Ssimon * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46160814Ssimon * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47160814Ssimon * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48160814Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49160814Ssimon * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50160814Ssimon * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51160814Ssimon * OF THE POSSIBILITY OF SUCH DAMAGE.
52160814Ssimon * ====================================================================
53160814Ssimon *
54160814Ssimon * This product includes cryptographic software written by Eric Young
55160814Ssimon * (eay@cryptsoft.com).  This product includes software written by Tim
56160814Ssimon * Hudson (tjh@cryptsoft.com).
57160814Ssimon *
58160814Ssimon */
59160814Ssimon
60160814Ssimon#include <stdio.h>
61160814Ssimon#include <errno.h>
62160814Ssimon#define USE_SOCKETS
63160814Ssimon#include "cryptlib.h"
64160814Ssimon
65160814Ssimon#include <openssl/bio.h>
66237657Sjkim#ifndef OPENSSL_NO_DGRAM
67160814Ssimon
68280304Sjkim# if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_VMS)
69280304Sjkim#  include <sys/timeb.h>
70280304Sjkim# endif
71205128Ssimon
72280304Sjkim# ifndef OPENSSL_NO_SCTP
73280304Sjkim#  include <netinet/sctp.h>
74280304Sjkim#  include <fcntl.h>
75280304Sjkim#  define OPENSSL_SCTP_DATA_CHUNK_TYPE            0x00
76280304Sjkim#  define OPENSSL_SCTP_FORWARD_CUM_TSN_CHUNK_TYPE 0xc0
77280304Sjkim# endif
78238405Sjkim
79280304Sjkim# if defined(OPENSSL_SYS_LINUX) && !defined(IP_MTU)
80280304Sjkim#  define IP_MTU      14        /* linux is lame */
81280304Sjkim# endif
82160814Ssimon
83280304Sjkim# if defined(__FreeBSD__) && defined(IN6_IS_ADDR_V4MAPPED)
84246772Sjkim/* Standard definition causes type-punning problems. */
85280304Sjkim#  undef IN6_IS_ADDR_V4MAPPED
86280304Sjkim#  define s6_addr32 __u6_addr.__u6_addr32
87280304Sjkim#  define IN6_IS_ADDR_V4MAPPED(a)               \
88246772Sjkim        (((a)->s6_addr32[0] == 0) &&          \
89246772Sjkim         ((a)->s6_addr32[1] == 0) &&          \
90246772Sjkim         ((a)->s6_addr32[2] == htonl(0x0000ffff)))
91280304Sjkim# endif
92246772Sjkim
93280304Sjkim# ifdef WATT32
94280304Sjkim#  define sock_write SockWrite  /* Watt-32 uses same names */
95280304Sjkim#  define sock_read  SockRead
96280304Sjkim#  define sock_puts  SockPuts
97280304Sjkim# endif
98160814Ssimon
99160814Ssimonstatic int dgram_write(BIO *h, const char *buf, int num);
100160814Ssimonstatic int dgram_read(BIO *h, char *buf, int size);
101160814Ssimonstatic int dgram_puts(BIO *h, const char *str);
102160814Ssimonstatic long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2);
103160814Ssimonstatic int dgram_new(BIO *h);
104160814Ssimonstatic int dgram_free(BIO *data);
105160814Ssimonstatic int dgram_clear(BIO *bio);
106160814Ssimon
107280304Sjkim# ifndef OPENSSL_NO_SCTP
108238405Sjkimstatic int dgram_sctp_write(BIO *h, const char *buf, int num);
109238405Sjkimstatic int dgram_sctp_read(BIO *h, char *buf, int size);
110238405Sjkimstatic int dgram_sctp_puts(BIO *h, const char *str);
111238405Sjkimstatic long dgram_sctp_ctrl(BIO *h, int cmd, long arg1, void *arg2);
112238405Sjkimstatic int dgram_sctp_new(BIO *h);
113238405Sjkimstatic int dgram_sctp_free(BIO *data);
114280304Sjkim#  ifdef SCTP_AUTHENTICATION_EVENT
115280304Sjkimstatic void dgram_sctp_handle_auth_free_key_event(BIO *b, union sctp_notification
116280304Sjkim                                                  *snp);
117280304Sjkim#  endif
118280304Sjkim# endif
119238405Sjkim
120194206Ssimonstatic int BIO_dgram_should_retry(int s);
121160814Ssimon
122205128Ssimonstatic void get_current_time(struct timeval *t);
123205128Ssimon
124280304Sjkimstatic BIO_METHOD methods_dgramp = {
125280304Sjkim    BIO_TYPE_DGRAM,
126280304Sjkim    "datagram socket",
127280304Sjkim    dgram_write,
128280304Sjkim    dgram_read,
129280304Sjkim    dgram_puts,
130280304Sjkim    NULL,                       /* dgram_gets, */
131280304Sjkim    dgram_ctrl,
132280304Sjkim    dgram_new,
133280304Sjkim    dgram_free,
134280304Sjkim    NULL,
135280304Sjkim};
136160814Ssimon
137280304Sjkim# ifndef OPENSSL_NO_SCTP
138280304Sjkimstatic BIO_METHOD methods_dgramp_sctp = {
139280304Sjkim    BIO_TYPE_DGRAM_SCTP,
140280304Sjkim    "datagram sctp socket",
141280304Sjkim    dgram_sctp_write,
142280304Sjkim    dgram_sctp_read,
143280304Sjkim    dgram_sctp_puts,
144280304Sjkim    NULL,                       /* dgram_gets, */
145280304Sjkim    dgram_sctp_ctrl,
146280304Sjkim    dgram_sctp_new,
147280304Sjkim    dgram_sctp_free,
148280304Sjkim    NULL,
149280304Sjkim};
150280304Sjkim# endif
151238405Sjkim
152280304Sjkimtypedef struct bio_dgram_data_st {
153280304Sjkim    union {
154280304Sjkim        struct sockaddr sa;
155280304Sjkim        struct sockaddr_in sa_in;
156280304Sjkim# if OPENSSL_USE_IPV6
157280304Sjkim        struct sockaddr_in6 sa_in6;
158280304Sjkim# endif
159280304Sjkim    } peer;
160280304Sjkim    unsigned int connected;
161280304Sjkim    unsigned int _errno;
162280304Sjkim    unsigned int mtu;
163280304Sjkim    struct timeval next_timeout;
164280304Sjkim    struct timeval socket_timeout;
165280304Sjkim} bio_dgram_data;
166160814Ssimon
167280304Sjkim# ifndef OPENSSL_NO_SCTP
168280304Sjkimtypedef struct bio_dgram_sctp_save_message_st {
169280304Sjkim    BIO *bio;
170280304Sjkim    char *data;
171280304Sjkim    int length;
172280304Sjkim} bio_dgram_sctp_save_message;
173238405Sjkim
174280304Sjkimtypedef struct bio_dgram_sctp_data_st {
175280304Sjkim    union {
176280304Sjkim        struct sockaddr sa;
177280304Sjkim        struct sockaddr_in sa_in;
178280304Sjkim#  if OPENSSL_USE_IPV6
179280304Sjkim        struct sockaddr_in6 sa_in6;
180280304Sjkim#  endif
181280304Sjkim    } peer;
182280304Sjkim    unsigned int connected;
183280304Sjkim    unsigned int _errno;
184280304Sjkim    unsigned int mtu;
185280304Sjkim    struct bio_dgram_sctp_sndinfo sndinfo;
186280304Sjkim    struct bio_dgram_sctp_rcvinfo rcvinfo;
187280304Sjkim    struct bio_dgram_sctp_prinfo prinfo;
188280304Sjkim    void (*handle_notifications) (BIO *bio, void *context, void *buf);
189280304Sjkim    void *notification_context;
190280304Sjkim    int in_handshake;
191280304Sjkim    int ccs_rcvd;
192280304Sjkim    int ccs_sent;
193280304Sjkim    int save_shutdown;
194280304Sjkim    int peer_auth_tested;
195280304Sjkim    bio_dgram_sctp_save_message saved_message;
196280304Sjkim} bio_dgram_sctp_data;
197280304Sjkim# endif
198238405Sjkim
199160814SsimonBIO_METHOD *BIO_s_datagram(void)
200280304Sjkim{
201280304Sjkim    return (&methods_dgramp);
202280304Sjkim}
203160814Ssimon
204160814SsimonBIO *BIO_new_dgram(int fd, int close_flag)
205280304Sjkim{
206280304Sjkim    BIO *ret;
207160814Ssimon
208280304Sjkim    ret = BIO_new(BIO_s_datagram());
209280304Sjkim    if (ret == NULL)
210280304Sjkim        return (NULL);
211280304Sjkim    BIO_set_fd(ret, fd, close_flag);
212280304Sjkim    return (ret);
213280304Sjkim}
214160814Ssimon
215160814Ssimonstatic int dgram_new(BIO *bi)
216280304Sjkim{
217280304Sjkim    bio_dgram_data *data = NULL;
218160814Ssimon
219280304Sjkim    bi->init = 0;
220280304Sjkim    bi->num = 0;
221280304Sjkim    data = OPENSSL_malloc(sizeof(bio_dgram_data));
222280304Sjkim    if (data == NULL)
223280304Sjkim        return 0;
224280304Sjkim    memset(data, 0x00, sizeof(bio_dgram_data));
225160814Ssimon    bi->ptr = data;
226160814Ssimon
227280304Sjkim    bi->flags = 0;
228280304Sjkim    return (1);
229280304Sjkim}
230160814Ssimon
231160814Ssimonstatic int dgram_free(BIO *a)
232280304Sjkim{
233280304Sjkim    bio_dgram_data *data;
234160814Ssimon
235280304Sjkim    if (a == NULL)
236280304Sjkim        return (0);
237280304Sjkim    if (!dgram_clear(a))
238280304Sjkim        return 0;
239160814Ssimon
240280304Sjkim    data = (bio_dgram_data *)a->ptr;
241280304Sjkim    if (data != NULL)
242280304Sjkim        OPENSSL_free(data);
243160814Ssimon
244280304Sjkim    return (1);
245280304Sjkim}
246160814Ssimon
247160814Ssimonstatic int dgram_clear(BIO *a)
248280304Sjkim{
249280304Sjkim    if (a == NULL)
250280304Sjkim        return (0);
251280304Sjkim    if (a->shutdown) {
252280304Sjkim        if (a->init) {
253280304Sjkim            SHUTDOWN2(a->num);
254280304Sjkim        }
255280304Sjkim        a->init = 0;
256280304Sjkim        a->flags = 0;
257280304Sjkim    }
258280304Sjkim    return (1);
259280304Sjkim}
260205128Ssimon
261205128Ssimonstatic void dgram_adjust_rcv_timeout(BIO *b)
262280304Sjkim{
263280304Sjkim# if defined(SO_RCVTIMEO)
264280304Sjkim    bio_dgram_data *data = (bio_dgram_data *)b->ptr;
265280304Sjkim    union {
266280304Sjkim        size_t s;
267280304Sjkim        int i;
268280304Sjkim    } sz = {
269280304Sjkim        0
270280304Sjkim    };
271205128Ssimon
272280304Sjkim    /* Is a timer active? */
273280304Sjkim    if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
274280304Sjkim        struct timeval timenow, timeleft;
275205128Ssimon
276280304Sjkim        /* Read current socket timeout */
277280304Sjkim#  ifdef OPENSSL_SYS_WINDOWS
278280304Sjkim        int timeout;
279246772Sjkim
280280304Sjkim        sz.i = sizeof(timeout);
281280304Sjkim        if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
282280304Sjkim                       (void *)&timeout, &sz.i) < 0) {
283280304Sjkim            perror("getsockopt");
284280304Sjkim        } else {
285280304Sjkim            data->socket_timeout.tv_sec = timeout / 1000;
286280304Sjkim            data->socket_timeout.tv_usec = (timeout % 1000) * 1000;
287280304Sjkim        }
288280304Sjkim#  else
289280304Sjkim        sz.i = sizeof(data->socket_timeout);
290280304Sjkim        if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
291280304Sjkim                       &(data->socket_timeout), (void *)&sz) < 0) {
292280304Sjkim            perror("getsockopt");
293280304Sjkim        } else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0)
294280304Sjkim            OPENSSL_assert(sz.s <= sizeof(data->socket_timeout));
295280304Sjkim#  endif
296205128Ssimon
297280304Sjkim        /* Get current time */
298280304Sjkim        get_current_time(&timenow);
299205128Ssimon
300280304Sjkim        /* Calculate time left until timer expires */
301280304Sjkim        memcpy(&timeleft, &(data->next_timeout), sizeof(struct timeval));
302284285Sjkim        if (timeleft.tv_usec < timenow.tv_usec) {
303284285Sjkim            timeleft.tv_usec = 1000000 - timenow.tv_usec + timeleft.tv_usec;
304280304Sjkim            timeleft.tv_sec--;
305284285Sjkim        } else {
306284285Sjkim            timeleft.tv_usec -= timenow.tv_usec;
307280304Sjkim        }
308284285Sjkim        if (timeleft.tv_sec < timenow.tv_sec) {
309280304Sjkim            timeleft.tv_sec = 0;
310280304Sjkim            timeleft.tv_usec = 1;
311284285Sjkim        } else {
312284285Sjkim            timeleft.tv_sec -= timenow.tv_sec;
313280304Sjkim        }
314205128Ssimon
315280304Sjkim        /*
316280304Sjkim         * Adjust socket timeout if next handhake message timer will expire
317280304Sjkim         * earlier.
318280304Sjkim         */
319280304Sjkim        if ((data->socket_timeout.tv_sec == 0
320280304Sjkim             && data->socket_timeout.tv_usec == 0)
321280304Sjkim            || (data->socket_timeout.tv_sec > timeleft.tv_sec)
322280304Sjkim            || (data->socket_timeout.tv_sec == timeleft.tv_sec
323280304Sjkim                && data->socket_timeout.tv_usec >= timeleft.tv_usec)) {
324280304Sjkim#  ifdef OPENSSL_SYS_WINDOWS
325280304Sjkim            timeout = timeleft.tv_sec * 1000 + timeleft.tv_usec / 1000;
326280304Sjkim            if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
327280304Sjkim                           (void *)&timeout, sizeof(timeout)) < 0) {
328280304Sjkim                perror("setsockopt");
329280304Sjkim            }
330280304Sjkim#  else
331280304Sjkim            if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, &timeleft,
332280304Sjkim                           sizeof(struct timeval)) < 0) {
333280304Sjkim                perror("setsockopt");
334280304Sjkim            }
335280304Sjkim#  endif
336280304Sjkim        }
337280304Sjkim    }
338280304Sjkim# endif
339280304Sjkim}
340205128Ssimon
341205128Ssimonstatic void dgram_reset_rcv_timeout(BIO *b)
342280304Sjkim{
343280304Sjkim# if defined(SO_RCVTIMEO)
344280304Sjkim    bio_dgram_data *data = (bio_dgram_data *)b->ptr;
345205128Ssimon
346280304Sjkim    /* Is a timer active? */
347280304Sjkim    if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
348280304Sjkim#  ifdef OPENSSL_SYS_WINDOWS
349280304Sjkim        int timeout = data->socket_timeout.tv_sec * 1000 +
350280304Sjkim            data->socket_timeout.tv_usec / 1000;
351280304Sjkim        if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
352280304Sjkim                       (void *)&timeout, sizeof(timeout)) < 0) {
353280304Sjkim            perror("setsockopt");
354280304Sjkim        }
355280304Sjkim#  else
356280304Sjkim        if (setsockopt
357280304Sjkim            (b->num, SOL_SOCKET, SO_RCVTIMEO, &(data->socket_timeout),
358280304Sjkim             sizeof(struct timeval)) < 0) {
359280304Sjkim            perror("setsockopt");
360280304Sjkim        }
361280304Sjkim#  endif
362280304Sjkim    }
363280304Sjkim# endif
364280304Sjkim}
365205128Ssimon
366160814Ssimonstatic int dgram_read(BIO *b, char *out, int outl)
367280304Sjkim{
368280304Sjkim    int ret = 0;
369280304Sjkim    bio_dgram_data *data = (bio_dgram_data *)b->ptr;
370160814Ssimon
371280304Sjkim    struct {
372280304Sjkim        /*
373280304Sjkim         * See commentary in b_sock.c. <appro>
374280304Sjkim         */
375280304Sjkim        union {
376280304Sjkim            size_t s;
377280304Sjkim            int i;
378280304Sjkim        } len;
379280304Sjkim        union {
380280304Sjkim            struct sockaddr sa;
381280304Sjkim            struct sockaddr_in sa_in;
382280304Sjkim# if OPENSSL_USE_IPV6
383280304Sjkim            struct sockaddr_in6 sa_in6;
384280304Sjkim# endif
385280304Sjkim        } peer;
386280304Sjkim    } sa;
387160814Ssimon
388280304Sjkim    sa.len.s = 0;
389280304Sjkim    sa.len.i = sizeof(sa.peer);
390238405Sjkim
391280304Sjkim    if (out != NULL) {
392280304Sjkim        clear_socket_error();
393280304Sjkim        memset(&sa.peer, 0x00, sizeof(sa.peer));
394280304Sjkim        dgram_adjust_rcv_timeout(b);
395280304Sjkim        ret = recvfrom(b->num, out, outl, 0, &sa.peer.sa, (void *)&sa.len);
396280304Sjkim        if (sizeof(sa.len.i) != sizeof(sa.len.s) && sa.len.i == 0) {
397280304Sjkim            OPENSSL_assert(sa.len.s <= sizeof(sa.peer));
398280304Sjkim            sa.len.i = (int)sa.len.s;
399280304Sjkim        }
400160814Ssimon
401280304Sjkim        if (!data->connected && ret >= 0)
402280304Sjkim            BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &sa.peer);
403160814Ssimon
404280304Sjkim        BIO_clear_retry_flags(b);
405280304Sjkim        if (ret < 0) {
406280304Sjkim            if (BIO_dgram_should_retry(ret)) {
407280304Sjkim                BIO_set_retry_read(b);
408280304Sjkim                data->_errno = get_last_socket_error();
409280304Sjkim            }
410280304Sjkim        }
411237657Sjkim
412280304Sjkim        dgram_reset_rcv_timeout(b);
413280304Sjkim    }
414280304Sjkim    return (ret);
415280304Sjkim}
416160814Ssimon
417160814Ssimonstatic int dgram_write(BIO *b, const char *in, int inl)
418280304Sjkim{
419280304Sjkim    int ret;
420280304Sjkim    bio_dgram_data *data = (bio_dgram_data *)b->ptr;
421280304Sjkim    clear_socket_error();
422160814Ssimon
423280304Sjkim    if (data->connected)
424280304Sjkim        ret = writesocket(b->num, in, inl);
425280304Sjkim    else {
426280304Sjkim        int peerlen = sizeof(data->peer);
427238405Sjkim
428280304Sjkim        if (data->peer.sa.sa_family == AF_INET)
429280304Sjkim            peerlen = sizeof(data->peer.sa_in);
430280304Sjkim# if OPENSSL_USE_IPV6
431280304Sjkim        else if (data->peer.sa.sa_family == AF_INET6)
432280304Sjkim            peerlen = sizeof(data->peer.sa_in6);
433280304Sjkim# endif
434280304Sjkim# if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK)
435280304Sjkim        ret = sendto(b->num, (char *)in, inl, 0, &data->peer.sa, peerlen);
436280304Sjkim# else
437280304Sjkim        ret = sendto(b->num, in, inl, 0, &data->peer.sa, peerlen);
438280304Sjkim# endif
439280304Sjkim    }
440160814Ssimon
441280304Sjkim    BIO_clear_retry_flags(b);
442280304Sjkim    if (ret <= 0) {
443280304Sjkim        if (BIO_dgram_should_retry(ret)) {
444280304Sjkim            BIO_set_retry_write(b);
445280304Sjkim            data->_errno = get_last_socket_error();
446160814Ssimon
447280304Sjkim# if 0                          /* higher layers are responsible for querying
448280304Sjkim                                 * MTU, if necessary */
449280304Sjkim            if (data->_errno == EMSGSIZE)
450280304Sjkim                /* retrieve the new MTU */
451280304Sjkim                BIO_ctrl(b, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
452280304Sjkim# endif
453280304Sjkim        }
454280304Sjkim    }
455280304Sjkim    return (ret);
456280304Sjkim}
457160814Ssimon
458276864Sjkimstatic long dgram_get_mtu_overhead(bio_dgram_data *data)
459280304Sjkim{
460280304Sjkim    long ret;
461276864Sjkim
462280304Sjkim    switch (data->peer.sa.sa_family) {
463280304Sjkim    case AF_INET:
464280304Sjkim        /*
465280304Sjkim         * Assume this is UDP - 20 bytes for IP, 8 bytes for UDP
466280304Sjkim         */
467280304Sjkim        ret = 28;
468280304Sjkim        break;
469280304Sjkim# if OPENSSL_USE_IPV6
470280304Sjkim    case AF_INET6:
471280304Sjkim#  ifdef IN6_IS_ADDR_V4MAPPED
472280304Sjkim        if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr))
473280304Sjkim            /*
474280304Sjkim             * Assume this is UDP - 20 bytes for IP, 8 bytes for UDP
475280304Sjkim             */
476280304Sjkim            ret = 28;
477280304Sjkim        else
478280304Sjkim#  endif
479280304Sjkim            /*
480280304Sjkim             * Assume this is UDP - 40 bytes for IP, 8 bytes for UDP
481280304Sjkim             */
482280304Sjkim            ret = 48;
483280304Sjkim        break;
484280304Sjkim# endif
485280304Sjkim    default:
486280304Sjkim        /* We don't know. Go with the historical default */
487280304Sjkim        ret = 28;
488280304Sjkim        break;
489280304Sjkim    }
490280304Sjkim    return ret;
491280304Sjkim}
492276864Sjkim
493160814Ssimonstatic long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
494280304Sjkim{
495280304Sjkim    long ret = 1;
496280304Sjkim    int *ip;
497280304Sjkim    struct sockaddr *to = NULL;
498280304Sjkim    bio_dgram_data *data = NULL;
499280304Sjkim# if defined(OPENSSL_SYS_LINUX) && (defined(IP_MTU_DISCOVER) || defined(IP_MTU))
500280304Sjkim    int sockopt_val = 0;
501280304Sjkim    socklen_t sockopt_len;      /* assume that system supporting IP_MTU is
502280304Sjkim                                 * modern enough to define socklen_t */
503280304Sjkim    socklen_t addr_len;
504280304Sjkim    union {
505280304Sjkim        struct sockaddr sa;
506280304Sjkim        struct sockaddr_in s4;
507280304Sjkim#  if OPENSSL_USE_IPV6
508280304Sjkim        struct sockaddr_in6 s6;
509280304Sjkim#  endif
510280304Sjkim    } addr;
511280304Sjkim# endif
512160814Ssimon
513280304Sjkim    data = (bio_dgram_data *)b->ptr;
514160814Ssimon
515280304Sjkim    switch (cmd) {
516280304Sjkim    case BIO_CTRL_RESET:
517280304Sjkim        num = 0;
518280304Sjkim        ret = 0;
519280304Sjkim        break;
520280304Sjkim    case BIO_CTRL_INFO:
521280304Sjkim        ret = 0;
522280304Sjkim        break;
523280304Sjkim    case BIO_C_SET_FD:
524280304Sjkim        dgram_clear(b);
525280304Sjkim        b->num = *((int *)ptr);
526280304Sjkim        b->shutdown = (int)num;
527280304Sjkim        b->init = 1;
528280304Sjkim        break;
529280304Sjkim    case BIO_C_GET_FD:
530280304Sjkim        if (b->init) {
531280304Sjkim            ip = (int *)ptr;
532280304Sjkim            if (ip != NULL)
533280304Sjkim                *ip = b->num;
534280304Sjkim            ret = b->num;
535280304Sjkim        } else
536280304Sjkim            ret = -1;
537280304Sjkim        break;
538280304Sjkim    case BIO_CTRL_GET_CLOSE:
539280304Sjkim        ret = b->shutdown;
540280304Sjkim        break;
541280304Sjkim    case BIO_CTRL_SET_CLOSE:
542280304Sjkim        b->shutdown = (int)num;
543280304Sjkim        break;
544280304Sjkim    case BIO_CTRL_PENDING:
545280304Sjkim    case BIO_CTRL_WPENDING:
546280304Sjkim        ret = 0;
547280304Sjkim        break;
548280304Sjkim    case BIO_CTRL_DUP:
549280304Sjkim    case BIO_CTRL_FLUSH:
550280304Sjkim        ret = 1;
551280304Sjkim        break;
552280304Sjkim    case BIO_CTRL_DGRAM_CONNECT:
553280304Sjkim        to = (struct sockaddr *)ptr;
554280304Sjkim# if 0
555280304Sjkim        if (connect(b->num, to, sizeof(struct sockaddr)) < 0) {
556280304Sjkim            perror("connect");
557280304Sjkim            ret = 0;
558280304Sjkim        } else {
559280304Sjkim# endif
560280304Sjkim            switch (to->sa_family) {
561280304Sjkim            case AF_INET:
562280304Sjkim                memcpy(&data->peer, to, sizeof(data->peer.sa_in));
563280304Sjkim                break;
564280304Sjkim# if OPENSSL_USE_IPV6
565280304Sjkim            case AF_INET6:
566280304Sjkim                memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
567280304Sjkim                break;
568280304Sjkim# endif
569280304Sjkim            default:
570280304Sjkim                memcpy(&data->peer, to, sizeof(data->peer.sa));
571280304Sjkim                break;
572280304Sjkim            }
573280304Sjkim# if 0
574280304Sjkim        }
575280304Sjkim# endif
576280304Sjkim        break;
577280304Sjkim        /* (Linux)kernel sets DF bit on outgoing IP packets */
578280304Sjkim    case BIO_CTRL_DGRAM_MTU_DISCOVER:
579280304Sjkim# if defined(OPENSSL_SYS_LINUX) && defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
580280304Sjkim        addr_len = (socklen_t) sizeof(addr);
581280304Sjkim        memset((void *)&addr, 0, sizeof(addr));
582280304Sjkim        if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
583280304Sjkim            ret = 0;
584280304Sjkim            break;
585280304Sjkim        }
586280304Sjkim        switch (addr.sa.sa_family) {
587280304Sjkim        case AF_INET:
588280304Sjkim            sockopt_val = IP_PMTUDISC_DO;
589280304Sjkim            if ((ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
590280304Sjkim                                  &sockopt_val, sizeof(sockopt_val))) < 0)
591280304Sjkim                perror("setsockopt");
592280304Sjkim            break;
593280304Sjkim#  if OPENSSL_USE_IPV6 && defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
594280304Sjkim        case AF_INET6:
595280304Sjkim            sockopt_val = IPV6_PMTUDISC_DO;
596280304Sjkim            if ((ret = setsockopt(b->num, IPPROTO_IPV6, IPV6_MTU_DISCOVER,
597280304Sjkim                                  &sockopt_val, sizeof(sockopt_val))) < 0)
598280304Sjkim                perror("setsockopt");
599280304Sjkim            break;
600280304Sjkim#  endif
601280304Sjkim        default:
602280304Sjkim            ret = -1;
603280304Sjkim            break;
604280304Sjkim        }
605280304Sjkim        ret = -1;
606280304Sjkim# else
607280304Sjkim        break;
608280304Sjkim# endif
609280304Sjkim    case BIO_CTRL_DGRAM_QUERY_MTU:
610280304Sjkim# if defined(OPENSSL_SYS_LINUX) && defined(IP_MTU)
611280304Sjkim        addr_len = (socklen_t) sizeof(addr);
612280304Sjkim        memset((void *)&addr, 0, sizeof(addr));
613280304Sjkim        if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
614280304Sjkim            ret = 0;
615280304Sjkim            break;
616280304Sjkim        }
617280304Sjkim        sockopt_len = sizeof(sockopt_val);
618280304Sjkim        switch (addr.sa.sa_family) {
619280304Sjkim        case AF_INET:
620280304Sjkim            if ((ret =
621280304Sjkim                 getsockopt(b->num, IPPROTO_IP, IP_MTU, (void *)&sockopt_val,
622280304Sjkim                            &sockopt_len)) < 0 || sockopt_val < 0) {
623280304Sjkim                ret = 0;
624280304Sjkim            } else {
625280304Sjkim                /*
626280304Sjkim                 * we assume that the transport protocol is UDP and no IP
627280304Sjkim                 * options are used.
628280304Sjkim                 */
629280304Sjkim                data->mtu = sockopt_val - 8 - 20;
630280304Sjkim                ret = data->mtu;
631280304Sjkim            }
632280304Sjkim            break;
633280304Sjkim#  if OPENSSL_USE_IPV6 && defined(IPV6_MTU)
634280304Sjkim        case AF_INET6:
635280304Sjkim            if ((ret =
636280304Sjkim                 getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU,
637280304Sjkim                            (void *)&sockopt_val, &sockopt_len)) < 0
638280304Sjkim                || sockopt_val < 0) {
639280304Sjkim                ret = 0;
640280304Sjkim            } else {
641280304Sjkim                /*
642280304Sjkim                 * we assume that the transport protocol is UDP and no IPV6
643280304Sjkim                 * options are used.
644280304Sjkim                 */
645280304Sjkim                data->mtu = sockopt_val - 8 - 40;
646280304Sjkim                ret = data->mtu;
647280304Sjkim            }
648280304Sjkim            break;
649280304Sjkim#  endif
650280304Sjkim        default:
651280304Sjkim            ret = 0;
652280304Sjkim            break;
653280304Sjkim        }
654280304Sjkim# else
655280304Sjkim        ret = 0;
656280304Sjkim# endif
657280304Sjkim        break;
658280304Sjkim    case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
659280304Sjkim        ret = -dgram_get_mtu_overhead(data);
660280304Sjkim        switch (data->peer.sa.sa_family) {
661280304Sjkim        case AF_INET:
662280304Sjkim            ret += 576;
663280304Sjkim            break;
664280304Sjkim# if OPENSSL_USE_IPV6
665280304Sjkim        case AF_INET6:
666280304Sjkim#  ifdef IN6_IS_ADDR_V4MAPPED
667280304Sjkim            if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr))
668280304Sjkim                ret += 576;
669280304Sjkim            else
670280304Sjkim#  endif
671280304Sjkim                ret += 1280;
672280304Sjkim            break;
673280304Sjkim# endif
674280304Sjkim        default:
675280304Sjkim            ret += 576;
676280304Sjkim            break;
677280304Sjkim        }
678280304Sjkim        break;
679280304Sjkim    case BIO_CTRL_DGRAM_GET_MTU:
680280304Sjkim        return data->mtu;
681280304Sjkim        break;
682280304Sjkim    case BIO_CTRL_DGRAM_SET_MTU:
683280304Sjkim        data->mtu = num;
684280304Sjkim        ret = num;
685280304Sjkim        break;
686280304Sjkim    case BIO_CTRL_DGRAM_SET_CONNECTED:
687280304Sjkim        to = (struct sockaddr *)ptr;
688160814Ssimon
689280304Sjkim        if (to != NULL) {
690280304Sjkim            data->connected = 1;
691280304Sjkim            switch (to->sa_family) {
692280304Sjkim            case AF_INET:
693280304Sjkim                memcpy(&data->peer, to, sizeof(data->peer.sa_in));
694280304Sjkim                break;
695280304Sjkim# if OPENSSL_USE_IPV6
696280304Sjkim            case AF_INET6:
697280304Sjkim                memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
698280304Sjkim                break;
699280304Sjkim# endif
700280304Sjkim            default:
701280304Sjkim                memcpy(&data->peer, to, sizeof(data->peer.sa));
702280304Sjkim                break;
703280304Sjkim            }
704280304Sjkim        } else {
705280304Sjkim            data->connected = 0;
706280304Sjkim            memset(&(data->peer), 0x00, sizeof(data->peer));
707280304Sjkim        }
708280304Sjkim        break;
709280304Sjkim    case BIO_CTRL_DGRAM_GET_PEER:
710280304Sjkim        switch (data->peer.sa.sa_family) {
711280304Sjkim        case AF_INET:
712280304Sjkim            ret = sizeof(data->peer.sa_in);
713280304Sjkim            break;
714280304Sjkim# if OPENSSL_USE_IPV6
715280304Sjkim        case AF_INET6:
716280304Sjkim            ret = sizeof(data->peer.sa_in6);
717280304Sjkim            break;
718280304Sjkim# endif
719280304Sjkim        default:
720280304Sjkim            ret = sizeof(data->peer.sa);
721280304Sjkim            break;
722280304Sjkim        }
723280304Sjkim        if (num == 0 || num > ret)
724280304Sjkim            num = ret;
725280304Sjkim        memcpy(ptr, &data->peer, (ret = num));
726280304Sjkim        break;
727280304Sjkim    case BIO_CTRL_DGRAM_SET_PEER:
728280304Sjkim        to = (struct sockaddr *)ptr;
729280304Sjkim        switch (to->sa_family) {
730280304Sjkim        case AF_INET:
731280304Sjkim            memcpy(&data->peer, to, sizeof(data->peer.sa_in));
732280304Sjkim            break;
733280304Sjkim# if OPENSSL_USE_IPV6
734280304Sjkim        case AF_INET6:
735280304Sjkim            memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
736280304Sjkim            break;
737280304Sjkim# endif
738280304Sjkim        default:
739280304Sjkim            memcpy(&data->peer, to, sizeof(data->peer.sa));
740280304Sjkim            break;
741280304Sjkim        }
742280304Sjkim        break;
743280304Sjkim    case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
744280304Sjkim        memcpy(&(data->next_timeout), ptr, sizeof(struct timeval));
745280304Sjkim        break;
746280304Sjkim# if defined(SO_RCVTIMEO)
747280304Sjkim    case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
748280304Sjkim#  ifdef OPENSSL_SYS_WINDOWS
749280304Sjkim        {
750280304Sjkim            struct timeval *tv = (struct timeval *)ptr;
751280304Sjkim            int timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
752280304Sjkim            if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
753280304Sjkim                           (void *)&timeout, sizeof(timeout)) < 0) {
754280304Sjkim                perror("setsockopt");
755280304Sjkim                ret = -1;
756280304Sjkim            }
757280304Sjkim        }
758280304Sjkim#  else
759280304Sjkim        if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr,
760280304Sjkim                       sizeof(struct timeval)) < 0) {
761280304Sjkim            perror("setsockopt");
762280304Sjkim            ret = -1;
763280304Sjkim        }
764280304Sjkim#  endif
765280304Sjkim        break;
766280304Sjkim    case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
767280304Sjkim        {
768280304Sjkim            union {
769280304Sjkim                size_t s;
770280304Sjkim                int i;
771280304Sjkim            } sz = {
772280304Sjkim                0
773280304Sjkim            };
774280304Sjkim#  ifdef OPENSSL_SYS_WINDOWS
775280304Sjkim            int timeout;
776280304Sjkim            struct timeval *tv = (struct timeval *)ptr;
777246772Sjkim
778280304Sjkim            sz.i = sizeof(timeout);
779280304Sjkim            if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
780280304Sjkim                           (void *)&timeout, &sz.i) < 0) {
781280304Sjkim                perror("getsockopt");
782280304Sjkim                ret = -1;
783280304Sjkim            } else {
784280304Sjkim                tv->tv_sec = timeout / 1000;
785280304Sjkim                tv->tv_usec = (timeout % 1000) * 1000;
786280304Sjkim                ret = sizeof(*tv);
787280304Sjkim            }
788280304Sjkim#  else
789280304Sjkim            sz.i = sizeof(struct timeval);
790280304Sjkim            if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
791280304Sjkim                           ptr, (void *)&sz) < 0) {
792280304Sjkim                perror("getsockopt");
793280304Sjkim                ret = -1;
794280304Sjkim            } else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0) {
795280304Sjkim                OPENSSL_assert(sz.s <= sizeof(struct timeval));
796280304Sjkim                ret = (int)sz.s;
797280304Sjkim            } else
798280304Sjkim                ret = sz.i;
799280304Sjkim#  endif
800280304Sjkim        }
801280304Sjkim        break;
802280304Sjkim# endif
803280304Sjkim# if defined(SO_SNDTIMEO)
804280304Sjkim    case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
805280304Sjkim#  ifdef OPENSSL_SYS_WINDOWS
806280304Sjkim        {
807280304Sjkim            struct timeval *tv = (struct timeval *)ptr;
808280304Sjkim            int timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
809280304Sjkim            if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
810280304Sjkim                           (void *)&timeout, sizeof(timeout)) < 0) {
811280304Sjkim                perror("setsockopt");
812280304Sjkim                ret = -1;
813280304Sjkim            }
814280304Sjkim        }
815280304Sjkim#  else
816280304Sjkim        if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr,
817280304Sjkim                       sizeof(struct timeval)) < 0) {
818280304Sjkim            perror("setsockopt");
819280304Sjkim            ret = -1;
820280304Sjkim        }
821280304Sjkim#  endif
822280304Sjkim        break;
823280304Sjkim    case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
824280304Sjkim        {
825280304Sjkim            union {
826280304Sjkim                size_t s;
827280304Sjkim                int i;
828280304Sjkim            } sz = {
829280304Sjkim                0
830280304Sjkim            };
831280304Sjkim#  ifdef OPENSSL_SYS_WINDOWS
832280304Sjkim            int timeout;
833280304Sjkim            struct timeval *tv = (struct timeval *)ptr;
834246772Sjkim
835280304Sjkim            sz.i = sizeof(timeout);
836280304Sjkim            if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
837280304Sjkim                           (void *)&timeout, &sz.i) < 0) {
838280304Sjkim                perror("getsockopt");
839280304Sjkim                ret = -1;
840280304Sjkim            } else {
841280304Sjkim                tv->tv_sec = timeout / 1000;
842280304Sjkim                tv->tv_usec = (timeout % 1000) * 1000;
843280304Sjkim                ret = sizeof(*tv);
844280304Sjkim            }
845280304Sjkim#  else
846280304Sjkim            sz.i = sizeof(struct timeval);
847280304Sjkim            if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
848280304Sjkim                           ptr, (void *)&sz) < 0) {
849280304Sjkim                perror("getsockopt");
850280304Sjkim                ret = -1;
851280304Sjkim            } else if (sizeof(sz.s) != sizeof(sz.i) && sz.i == 0) {
852280304Sjkim                OPENSSL_assert(sz.s <= sizeof(struct timeval));
853280304Sjkim                ret = (int)sz.s;
854280304Sjkim            } else
855280304Sjkim                ret = sz.i;
856280304Sjkim#  endif
857280304Sjkim        }
858280304Sjkim        break;
859280304Sjkim# endif
860280304Sjkim    case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
861280304Sjkim        /* fall-through */
862280304Sjkim    case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
863280304Sjkim# ifdef OPENSSL_SYS_WINDOWS
864280304Sjkim        if (data->_errno == WSAETIMEDOUT)
865280304Sjkim# else
866280304Sjkim        if (data->_errno == EAGAIN)
867280304Sjkim# endif
868280304Sjkim        {
869280304Sjkim            ret = 1;
870280304Sjkim            data->_errno = 0;
871280304Sjkim        } else
872280304Sjkim            ret = 0;
873280304Sjkim        break;
874280304Sjkim# ifdef EMSGSIZE
875280304Sjkim    case BIO_CTRL_DGRAM_MTU_EXCEEDED:
876280304Sjkim        if (data->_errno == EMSGSIZE) {
877280304Sjkim            ret = 1;
878280304Sjkim            data->_errno = 0;
879280304Sjkim        } else
880280304Sjkim            ret = 0;
881280304Sjkim        break;
882280304Sjkim# endif
883280304Sjkim    case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
884280304Sjkim        ret = dgram_get_mtu_overhead(data);
885280304Sjkim        break;
886280304Sjkim    default:
887280304Sjkim        ret = 0;
888280304Sjkim        break;
889280304Sjkim    }
890280304Sjkim    return (ret);
891280304Sjkim}
892160814Ssimon
893160814Ssimonstatic int dgram_puts(BIO *bp, const char *str)
894280304Sjkim{
895280304Sjkim    int n, ret;
896160814Ssimon
897280304Sjkim    n = strlen(str);
898280304Sjkim    ret = dgram_write(bp, str, n);
899280304Sjkim    return (ret);
900280304Sjkim}
901160814Ssimon
902280304Sjkim# ifndef OPENSSL_NO_SCTP
903238405SjkimBIO_METHOD *BIO_s_datagram_sctp(void)
904280304Sjkim{
905280304Sjkim    return (&methods_dgramp_sctp);
906280304Sjkim}
907238405Sjkim
908238405SjkimBIO *BIO_new_dgram_sctp(int fd, int close_flag)
909280304Sjkim{
910280304Sjkim    BIO *bio;
911280304Sjkim    int ret, optval = 20000;
912280304Sjkim    int auth_data = 0, auth_forward = 0;
913280304Sjkim    unsigned char *p;
914280304Sjkim    struct sctp_authchunk auth;
915280304Sjkim    struct sctp_authchunks *authchunks;
916280304Sjkim    socklen_t sockopt_len;
917280304Sjkim#  ifdef SCTP_AUTHENTICATION_EVENT
918280304Sjkim#   ifdef SCTP_EVENT
919280304Sjkim    struct sctp_event event;
920280304Sjkim#   else
921280304Sjkim    struct sctp_event_subscribe event;
922280304Sjkim#   endif
923280304Sjkim#  endif
924238405Sjkim
925280304Sjkim    bio = BIO_new(BIO_s_datagram_sctp());
926280304Sjkim    if (bio == NULL)
927280304Sjkim        return (NULL);
928280304Sjkim    BIO_set_fd(bio, fd, close_flag);
929238405Sjkim
930280304Sjkim    /* Activate SCTP-AUTH for DATA and FORWARD-TSN chunks */
931280304Sjkim    auth.sauth_chunk = OPENSSL_SCTP_DATA_CHUNK_TYPE;
932280304Sjkim    ret =
933280304Sjkim        setsockopt(fd, IPPROTO_SCTP, SCTP_AUTH_CHUNK, &auth,
934280304Sjkim                   sizeof(struct sctp_authchunk));
935280304Sjkim    if (ret < 0) {
936280304Sjkim        BIO_vfree(bio);
937280304Sjkim        return (NULL);
938280304Sjkim    }
939280304Sjkim    auth.sauth_chunk = OPENSSL_SCTP_FORWARD_CUM_TSN_CHUNK_TYPE;
940280304Sjkim    ret =
941280304Sjkim        setsockopt(fd, IPPROTO_SCTP, SCTP_AUTH_CHUNK, &auth,
942280304Sjkim                   sizeof(struct sctp_authchunk));
943280304Sjkim    if (ret < 0) {
944280304Sjkim        BIO_vfree(bio);
945280304Sjkim        return (NULL);
946280304Sjkim    }
947238405Sjkim
948280304Sjkim    /*
949280304Sjkim     * Test if activation was successful. When using accept(), SCTP-AUTH has
950280304Sjkim     * to be activated for the listening socket already, otherwise the
951280304Sjkim     * connected socket won't use it.
952280304Sjkim     */
953280304Sjkim    sockopt_len = (socklen_t) (sizeof(sctp_assoc_t) + 256 * sizeof(uint8_t));
954280304Sjkim    authchunks = OPENSSL_malloc(sockopt_len);
955284285Sjkim    if (!authchunks) {
956280304Sjkim        BIO_vfree(bio);
957280304Sjkim        return (NULL);
958280304Sjkim    }
959280304Sjkim    memset(authchunks, 0, sizeof(sockopt_len));
960280304Sjkim    ret =
961280304Sjkim        getsockopt(fd, IPPROTO_SCTP, SCTP_LOCAL_AUTH_CHUNKS, authchunks,
962280304Sjkim                   &sockopt_len);
963264331Sjkim
964280304Sjkim    if (ret < 0) {
965280304Sjkim        OPENSSL_free(authchunks);
966280304Sjkim        BIO_vfree(bio);
967280304Sjkim        return (NULL);
968280304Sjkim    }
969276864Sjkim
970280304Sjkim    for (p = (unsigned char *)authchunks->gauth_chunks;
971280304Sjkim         p < (unsigned char *)authchunks + sockopt_len;
972280304Sjkim         p += sizeof(uint8_t)) {
973280304Sjkim        if (*p == OPENSSL_SCTP_DATA_CHUNK_TYPE)
974280304Sjkim            auth_data = 1;
975280304Sjkim        if (*p == OPENSSL_SCTP_FORWARD_CUM_TSN_CHUNK_TYPE)
976280304Sjkim            auth_forward = 1;
977280304Sjkim    }
978238405Sjkim
979280304Sjkim    OPENSSL_free(authchunks);
980238405Sjkim
981280304Sjkim    OPENSSL_assert(auth_data);
982280304Sjkim    OPENSSL_assert(auth_forward);
983238405Sjkim
984280304Sjkim#  ifdef SCTP_AUTHENTICATION_EVENT
985280304Sjkim#   ifdef SCTP_EVENT
986280304Sjkim    memset(&event, 0, sizeof(struct sctp_event));
987280304Sjkim    event.se_assoc_id = 0;
988280304Sjkim    event.se_type = SCTP_AUTHENTICATION_EVENT;
989280304Sjkim    event.se_on = 1;
990280304Sjkim    ret =
991280304Sjkim        setsockopt(fd, IPPROTO_SCTP, SCTP_EVENT, &event,
992280304Sjkim                   sizeof(struct sctp_event));
993280304Sjkim    if (ret < 0) {
994280304Sjkim        BIO_vfree(bio);
995280304Sjkim        return (NULL);
996280304Sjkim    }
997280304Sjkim#   else
998280304Sjkim    sockopt_len = (socklen_t) sizeof(struct sctp_event_subscribe);
999280304Sjkim    ret = getsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event, &sockopt_len);
1000280304Sjkim    if (ret < 0) {
1001280304Sjkim        BIO_vfree(bio);
1002280304Sjkim        return (NULL);
1003280304Sjkim    }
1004238405Sjkim
1005280304Sjkim    event.sctp_authentication_event = 1;
1006238405Sjkim
1007280304Sjkim    ret =
1008280304Sjkim        setsockopt(fd, IPPROTO_SCTP, SCTP_EVENTS, &event,
1009280304Sjkim                   sizeof(struct sctp_event_subscribe));
1010280304Sjkim    if (ret < 0) {
1011280304Sjkim        BIO_vfree(bio);
1012280304Sjkim        return (NULL);
1013280304Sjkim    }
1014280304Sjkim#   endif
1015280304Sjkim#  endif
1016238405Sjkim
1017280304Sjkim    /*
1018280304Sjkim     * Disable partial delivery by setting the min size larger than the max
1019280304Sjkim     * record size of 2^14 + 2048 + 13
1020280304Sjkim     */
1021280304Sjkim    ret =
1022280304Sjkim        setsockopt(fd, IPPROTO_SCTP, SCTP_PARTIAL_DELIVERY_POINT, &optval,
1023280304Sjkim                   sizeof(optval));
1024280304Sjkim    if (ret < 0) {
1025280304Sjkim        BIO_vfree(bio);
1026280304Sjkim        return (NULL);
1027280304Sjkim    }
1028238405Sjkim
1029280304Sjkim    return (bio);
1030280304Sjkim}
1031280304Sjkim
1032238405Sjkimint BIO_dgram_is_sctp(BIO *bio)
1033280304Sjkim{
1034280304Sjkim    return (BIO_method_type(bio) == BIO_TYPE_DGRAM_SCTP);
1035280304Sjkim}
1036238405Sjkim
1037238405Sjkimstatic int dgram_sctp_new(BIO *bi)
1038280304Sjkim{
1039280304Sjkim    bio_dgram_sctp_data *data = NULL;
1040238405Sjkim
1041280304Sjkim    bi->init = 0;
1042280304Sjkim    bi->num = 0;
1043280304Sjkim    data = OPENSSL_malloc(sizeof(bio_dgram_sctp_data));
1044280304Sjkim    if (data == NULL)
1045280304Sjkim        return 0;
1046280304Sjkim    memset(data, 0x00, sizeof(bio_dgram_sctp_data));
1047280304Sjkim#  ifdef SCTP_PR_SCTP_NONE
1048280304Sjkim    data->prinfo.pr_policy = SCTP_PR_SCTP_NONE;
1049280304Sjkim#  endif
1050238405Sjkim    bi->ptr = data;
1051238405Sjkim
1052280304Sjkim    bi->flags = 0;
1053280304Sjkim    return (1);
1054280304Sjkim}
1055238405Sjkim
1056238405Sjkimstatic int dgram_sctp_free(BIO *a)
1057280304Sjkim{
1058280304Sjkim    bio_dgram_sctp_data *data;
1059238405Sjkim
1060280304Sjkim    if (a == NULL)
1061280304Sjkim        return (0);
1062280304Sjkim    if (!dgram_clear(a))
1063280304Sjkim        return 0;
1064238405Sjkim
1065280304Sjkim    data = (bio_dgram_sctp_data *) a->ptr;
1066280304Sjkim    if (data != NULL) {
1067280304Sjkim        if (data->saved_message.data != NULL)
1068280304Sjkim            OPENSSL_free(data->saved_message.data);
1069280304Sjkim        OPENSSL_free(data);
1070280304Sjkim    }
1071238405Sjkim
1072280304Sjkim    return (1);
1073280304Sjkim}
1074238405Sjkim
1075280304Sjkim#  ifdef SCTP_AUTHENTICATION_EVENT
1076280304Sjkimvoid dgram_sctp_handle_auth_free_key_event(BIO *b,
1077280304Sjkim                                           union sctp_notification *snp)
1078280304Sjkim{
1079280304Sjkim    int ret;
1080280304Sjkim    struct sctp_authkey_event *authkeyevent = &snp->sn_auth_event;
1081238405Sjkim
1082280304Sjkim    if (authkeyevent->auth_indication == SCTP_AUTH_FREE_KEY) {
1083280304Sjkim        struct sctp_authkeyid authkeyid;
1084238405Sjkim
1085280304Sjkim        /* delete key */
1086280304Sjkim        authkeyid.scact_keynumber = authkeyevent->auth_keynumber;
1087280304Sjkim        ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DELETE_KEY,
1088280304Sjkim                         &authkeyid, sizeof(struct sctp_authkeyid));
1089280304Sjkim    }
1090280304Sjkim}
1091280304Sjkim#  endif
1092238405Sjkim
1093238405Sjkimstatic int dgram_sctp_read(BIO *b, char *out, int outl)
1094280304Sjkim{
1095280304Sjkim    int ret = 0, n = 0, i, optval;
1096280304Sjkim    socklen_t optlen;
1097280304Sjkim    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
1098280304Sjkim    union sctp_notification *snp;
1099280304Sjkim    struct msghdr msg;
1100280304Sjkim    struct iovec iov;
1101280304Sjkim    struct cmsghdr *cmsg;
1102280304Sjkim    char cmsgbuf[512];
1103238405Sjkim
1104280304Sjkim    if (out != NULL) {
1105280304Sjkim        clear_socket_error();
1106238405Sjkim
1107280304Sjkim        do {
1108280304Sjkim            memset(&data->rcvinfo, 0x00,
1109280304Sjkim                   sizeof(struct bio_dgram_sctp_rcvinfo));
1110280304Sjkim            iov.iov_base = out;
1111280304Sjkim            iov.iov_len = outl;
1112280304Sjkim            msg.msg_name = NULL;
1113280304Sjkim            msg.msg_namelen = 0;
1114280304Sjkim            msg.msg_iov = &iov;
1115280304Sjkim            msg.msg_iovlen = 1;
1116280304Sjkim            msg.msg_control = cmsgbuf;
1117280304Sjkim            msg.msg_controllen = 512;
1118280304Sjkim            msg.msg_flags = 0;
1119280304Sjkim            n = recvmsg(b->num, &msg, 0);
1120238405Sjkim
1121280304Sjkim            if (n <= 0) {
1122280304Sjkim                if (n < 0)
1123280304Sjkim                    ret = n;
1124280304Sjkim                break;
1125280304Sjkim            }
1126276864Sjkim
1127280304Sjkim            if (msg.msg_controllen > 0) {
1128280304Sjkim                for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
1129280304Sjkim                     cmsg = CMSG_NXTHDR(&msg, cmsg)) {
1130280304Sjkim                    if (cmsg->cmsg_level != IPPROTO_SCTP)
1131280304Sjkim                        continue;
1132280304Sjkim#  ifdef SCTP_RCVINFO
1133280304Sjkim                    if (cmsg->cmsg_type == SCTP_RCVINFO) {
1134280304Sjkim                        struct sctp_rcvinfo *rcvinfo;
1135238405Sjkim
1136280304Sjkim                        rcvinfo = (struct sctp_rcvinfo *)CMSG_DATA(cmsg);
1137280304Sjkim                        data->rcvinfo.rcv_sid = rcvinfo->rcv_sid;
1138280304Sjkim                        data->rcvinfo.rcv_ssn = rcvinfo->rcv_ssn;
1139280304Sjkim                        data->rcvinfo.rcv_flags = rcvinfo->rcv_flags;
1140280304Sjkim                        data->rcvinfo.rcv_ppid = rcvinfo->rcv_ppid;
1141280304Sjkim                        data->rcvinfo.rcv_tsn = rcvinfo->rcv_tsn;
1142280304Sjkim                        data->rcvinfo.rcv_cumtsn = rcvinfo->rcv_cumtsn;
1143280304Sjkim                        data->rcvinfo.rcv_context = rcvinfo->rcv_context;
1144280304Sjkim                    }
1145280304Sjkim#  endif
1146280304Sjkim#  ifdef SCTP_SNDRCV
1147280304Sjkim                    if (cmsg->cmsg_type == SCTP_SNDRCV) {
1148280304Sjkim                        struct sctp_sndrcvinfo *sndrcvinfo;
1149238405Sjkim
1150280304Sjkim                        sndrcvinfo =
1151280304Sjkim                            (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
1152280304Sjkim                        data->rcvinfo.rcv_sid = sndrcvinfo->sinfo_stream;
1153280304Sjkim                        data->rcvinfo.rcv_ssn = sndrcvinfo->sinfo_ssn;
1154280304Sjkim                        data->rcvinfo.rcv_flags = sndrcvinfo->sinfo_flags;
1155280304Sjkim                        data->rcvinfo.rcv_ppid = sndrcvinfo->sinfo_ppid;
1156280304Sjkim                        data->rcvinfo.rcv_tsn = sndrcvinfo->sinfo_tsn;
1157280304Sjkim                        data->rcvinfo.rcv_cumtsn = sndrcvinfo->sinfo_cumtsn;
1158280304Sjkim                        data->rcvinfo.rcv_context = sndrcvinfo->sinfo_context;
1159280304Sjkim                    }
1160280304Sjkim#  endif
1161280304Sjkim                }
1162280304Sjkim            }
1163238405Sjkim
1164280304Sjkim            if (msg.msg_flags & MSG_NOTIFICATION) {
1165280304Sjkim                snp = (union sctp_notification *)out;
1166280304Sjkim                if (snp->sn_header.sn_type == SCTP_SENDER_DRY_EVENT) {
1167280304Sjkim#  ifdef SCTP_EVENT
1168280304Sjkim                    struct sctp_event event;
1169280304Sjkim#  else
1170280304Sjkim                    struct sctp_event_subscribe event;
1171280304Sjkim                    socklen_t eventsize;
1172280304Sjkim#  endif
1173280304Sjkim                    /*
1174280304Sjkim                     * If a message has been delayed until the socket is dry,
1175280304Sjkim                     * it can be sent now.
1176280304Sjkim                     */
1177280304Sjkim                    if (data->saved_message.length > 0) {
1178280304Sjkim                        dgram_sctp_write(data->saved_message.bio,
1179280304Sjkim                                         data->saved_message.data,
1180280304Sjkim                                         data->saved_message.length);
1181280304Sjkim                        OPENSSL_free(data->saved_message.data);
1182280304Sjkim                        data->saved_message.data = NULL;
1183280304Sjkim                        data->saved_message.length = 0;
1184280304Sjkim                    }
1185238405Sjkim
1186280304Sjkim                    /* disable sender dry event */
1187280304Sjkim#  ifdef SCTP_EVENT
1188280304Sjkim                    memset(&event, 0, sizeof(struct sctp_event));
1189280304Sjkim                    event.se_assoc_id = 0;
1190280304Sjkim                    event.se_type = SCTP_SENDER_DRY_EVENT;
1191280304Sjkim                    event.se_on = 0;
1192280304Sjkim                    i = setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENT, &event,
1193280304Sjkim                                   sizeof(struct sctp_event));
1194280304Sjkim                    if (i < 0) {
1195280304Sjkim                        ret = i;
1196280304Sjkim                        break;
1197280304Sjkim                    }
1198280304Sjkim#  else
1199280304Sjkim                    eventsize = sizeof(struct sctp_event_subscribe);
1200280304Sjkim                    i = getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
1201280304Sjkim                                   &eventsize);
1202280304Sjkim                    if (i < 0) {
1203280304Sjkim                        ret = i;
1204280304Sjkim                        break;
1205280304Sjkim                    }
1206238405Sjkim
1207280304Sjkim                    event.sctp_sender_dry_event = 0;
1208238405Sjkim
1209280304Sjkim                    i = setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
1210280304Sjkim                                   sizeof(struct sctp_event_subscribe));
1211280304Sjkim                    if (i < 0) {
1212280304Sjkim                        ret = i;
1213280304Sjkim                        break;
1214280304Sjkim                    }
1215280304Sjkim#  endif
1216280304Sjkim                }
1217280304Sjkim#  ifdef SCTP_AUTHENTICATION_EVENT
1218280304Sjkim                if (snp->sn_header.sn_type == SCTP_AUTHENTICATION_EVENT)
1219280304Sjkim                    dgram_sctp_handle_auth_free_key_event(b, snp);
1220280304Sjkim#  endif
1221238405Sjkim
1222280304Sjkim                if (data->handle_notifications != NULL)
1223280304Sjkim                    data->handle_notifications(b, data->notification_context,
1224280304Sjkim                                               (void *)out);
1225238405Sjkim
1226280304Sjkim                memset(out, 0, outl);
1227280304Sjkim            } else
1228280304Sjkim                ret += n;
1229280304Sjkim        }
1230280304Sjkim        while ((msg.msg_flags & MSG_NOTIFICATION) && (msg.msg_flags & MSG_EOR)
1231280304Sjkim               && (ret < outl));
1232238405Sjkim
1233280304Sjkim        if (ret > 0 && !(msg.msg_flags & MSG_EOR)) {
1234280304Sjkim            /* Partial message read, this should never happen! */
1235238405Sjkim
1236280304Sjkim            /*
1237280304Sjkim             * The buffer was too small, this means the peer sent a message
1238280304Sjkim             * that was larger than allowed.
1239280304Sjkim             */
1240280304Sjkim            if (ret == outl)
1241280304Sjkim                return -1;
1242238405Sjkim
1243280304Sjkim            /*
1244280304Sjkim             * Test if socket buffer can handle max record size (2^14 + 2048
1245280304Sjkim             * + 13)
1246280304Sjkim             */
1247280304Sjkim            optlen = (socklen_t) sizeof(int);
1248280304Sjkim            ret = getsockopt(b->num, SOL_SOCKET, SO_RCVBUF, &optval, &optlen);
1249280304Sjkim            if (ret >= 0)
1250280304Sjkim                OPENSSL_assert(optval >= 18445);
1251238405Sjkim
1252280304Sjkim            /*
1253280304Sjkim             * Test if SCTP doesn't partially deliver below max record size
1254280304Sjkim             * (2^14 + 2048 + 13)
1255280304Sjkim             */
1256280304Sjkim            optlen = (socklen_t) sizeof(int);
1257280304Sjkim            ret =
1258280304Sjkim                getsockopt(b->num, IPPROTO_SCTP, SCTP_PARTIAL_DELIVERY_POINT,
1259280304Sjkim                           &optval, &optlen);
1260280304Sjkim            if (ret >= 0)
1261280304Sjkim                OPENSSL_assert(optval >= 18445);
1262238405Sjkim
1263280304Sjkim            /*
1264280304Sjkim             * Partially delivered notification??? Probably a bug....
1265280304Sjkim             */
1266280304Sjkim            OPENSSL_assert(!(msg.msg_flags & MSG_NOTIFICATION));
1267238405Sjkim
1268280304Sjkim            /*
1269280304Sjkim             * Everything seems ok till now, so it's most likely a message
1270280304Sjkim             * dropped by PR-SCTP.
1271280304Sjkim             */
1272280304Sjkim            memset(out, 0, outl);
1273280304Sjkim            BIO_set_retry_read(b);
1274280304Sjkim            return -1;
1275280304Sjkim        }
1276238405Sjkim
1277280304Sjkim        BIO_clear_retry_flags(b);
1278280304Sjkim        if (ret < 0) {
1279280304Sjkim            if (BIO_dgram_should_retry(ret)) {
1280280304Sjkim                BIO_set_retry_read(b);
1281280304Sjkim                data->_errno = get_last_socket_error();
1282280304Sjkim            }
1283280304Sjkim        }
1284238405Sjkim
1285280304Sjkim        /* Test if peer uses SCTP-AUTH before continuing */
1286280304Sjkim        if (!data->peer_auth_tested) {
1287280304Sjkim            int ii, auth_data = 0, auth_forward = 0;
1288280304Sjkim            unsigned char *p;
1289280304Sjkim            struct sctp_authchunks *authchunks;
1290238405Sjkim
1291280304Sjkim            optlen =
1292280304Sjkim                (socklen_t) (sizeof(sctp_assoc_t) + 256 * sizeof(uint8_t));
1293280304Sjkim            authchunks = OPENSSL_malloc(optlen);
1294280304Sjkim            if (!authchunks) {
1295284285Sjkim                BIOerr(BIO_F_DGRAM_SCTP_READ, ERR_R_MALLOC_FAILURE);
1296280304Sjkim                return -1;
1297280304Sjkim            }
1298280304Sjkim            memset(authchunks, 0, sizeof(optlen));
1299280304Sjkim            ii = getsockopt(b->num, IPPROTO_SCTP, SCTP_PEER_AUTH_CHUNKS,
1300280304Sjkim                            authchunks, &optlen);
1301238405Sjkim
1302280304Sjkim            if (ii >= 0)
1303280304Sjkim                for (p = (unsigned char *)authchunks->gauth_chunks;
1304280304Sjkim                     p < (unsigned char *)authchunks + optlen;
1305280304Sjkim                     p += sizeof(uint8_t)) {
1306280304Sjkim                    if (*p == OPENSSL_SCTP_DATA_CHUNK_TYPE)
1307280304Sjkim                        auth_data = 1;
1308280304Sjkim                    if (*p == OPENSSL_SCTP_FORWARD_CUM_TSN_CHUNK_TYPE)
1309280304Sjkim                        auth_forward = 1;
1310280304Sjkim                }
1311238405Sjkim
1312280304Sjkim            OPENSSL_free(authchunks);
1313238405Sjkim
1314280304Sjkim            if (!auth_data || !auth_forward) {
1315280304Sjkim                BIOerr(BIO_F_DGRAM_SCTP_READ, BIO_R_CONNECT_ERROR);
1316280304Sjkim                return -1;
1317280304Sjkim            }
1318238405Sjkim
1319280304Sjkim            data->peer_auth_tested = 1;
1320280304Sjkim        }
1321280304Sjkim    }
1322280304Sjkim    return (ret);
1323280304Sjkim}
1324238405Sjkim
1325238405Sjkimstatic int dgram_sctp_write(BIO *b, const char *in, int inl)
1326280304Sjkim{
1327280304Sjkim    int ret;
1328280304Sjkim    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
1329280304Sjkim    struct bio_dgram_sctp_sndinfo *sinfo = &(data->sndinfo);
1330280304Sjkim    struct bio_dgram_sctp_prinfo *pinfo = &(data->prinfo);
1331280304Sjkim    struct bio_dgram_sctp_sndinfo handshake_sinfo;
1332280304Sjkim    struct iovec iov[1];
1333280304Sjkim    struct msghdr msg;
1334280304Sjkim    struct cmsghdr *cmsg;
1335280304Sjkim#  if defined(SCTP_SNDINFO) && defined(SCTP_PRINFO)
1336280304Sjkim    char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndinfo)) +
1337280304Sjkim                 CMSG_SPACE(sizeof(struct sctp_prinfo))];
1338280304Sjkim    struct sctp_sndinfo *sndinfo;
1339280304Sjkim    struct sctp_prinfo *prinfo;
1340280304Sjkim#  else
1341280304Sjkim    char cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))];
1342280304Sjkim    struct sctp_sndrcvinfo *sndrcvinfo;
1343280304Sjkim#  endif
1344238405Sjkim
1345280304Sjkim    clear_socket_error();
1346238405Sjkim
1347280304Sjkim    /*
1348280304Sjkim     * If we're send anything else than application data, disable all user
1349280304Sjkim     * parameters and flags.
1350280304Sjkim     */
1351280304Sjkim    if (in[0] != 23) {
1352280304Sjkim        memset(&handshake_sinfo, 0x00, sizeof(struct bio_dgram_sctp_sndinfo));
1353280304Sjkim#  ifdef SCTP_SACK_IMMEDIATELY
1354280304Sjkim        handshake_sinfo.snd_flags = SCTP_SACK_IMMEDIATELY;
1355280304Sjkim#  endif
1356280304Sjkim        sinfo = &handshake_sinfo;
1357280304Sjkim    }
1358238405Sjkim
1359280304Sjkim    /*
1360280304Sjkim     * If we have to send a shutdown alert message and the socket is not dry
1361280304Sjkim     * yet, we have to save it and send it as soon as the socket gets dry.
1362280304Sjkim     */
1363280304Sjkim    if (data->save_shutdown && !BIO_dgram_sctp_wait_for_dry(b)) {
1364280304Sjkim        char *tmp;
1365280304Sjkim        data->saved_message.bio = b;
1366284285Sjkim        if (!(tmp = OPENSSL_malloc(inl))) {
1367284285Sjkim            BIOerr(BIO_F_DGRAM_SCTP_WRITE, ERR_R_MALLOC_FAILURE);
1368280304Sjkim            return -1;
1369280304Sjkim        }
1370280304Sjkim        if (data->saved_message.data)
1371280304Sjkim            OPENSSL_free(data->saved_message.data);
1372280304Sjkim        data->saved_message.data = tmp;
1373280304Sjkim        memcpy(data->saved_message.data, in, inl);
1374280304Sjkim        data->saved_message.length = inl;
1375280304Sjkim        return inl;
1376280304Sjkim    }
1377238405Sjkim
1378280304Sjkim    iov[0].iov_base = (char *)in;
1379280304Sjkim    iov[0].iov_len = inl;
1380280304Sjkim    msg.msg_name = NULL;
1381280304Sjkim    msg.msg_namelen = 0;
1382280304Sjkim    msg.msg_iov = iov;
1383280304Sjkim    msg.msg_iovlen = 1;
1384280304Sjkim    msg.msg_control = (caddr_t) cmsgbuf;
1385280304Sjkim    msg.msg_controllen = 0;
1386280304Sjkim    msg.msg_flags = 0;
1387280304Sjkim#  if defined(SCTP_SNDINFO) && defined(SCTP_PRINFO)
1388280304Sjkim    cmsg = (struct cmsghdr *)cmsgbuf;
1389280304Sjkim    cmsg->cmsg_level = IPPROTO_SCTP;
1390280304Sjkim    cmsg->cmsg_type = SCTP_SNDINFO;
1391280304Sjkim    cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndinfo));
1392280304Sjkim    sndinfo = (struct sctp_sndinfo *)CMSG_DATA(cmsg);
1393280304Sjkim    memset(sndinfo, 0, sizeof(struct sctp_sndinfo));
1394280304Sjkim    sndinfo->snd_sid = sinfo->snd_sid;
1395280304Sjkim    sndinfo->snd_flags = sinfo->snd_flags;
1396280304Sjkim    sndinfo->snd_ppid = sinfo->snd_ppid;
1397280304Sjkim    sndinfo->snd_context = sinfo->snd_context;
1398280304Sjkim    msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndinfo));
1399238405Sjkim
1400280304Sjkim    cmsg =
1401280304Sjkim        (struct cmsghdr *)&cmsgbuf[CMSG_SPACE(sizeof(struct sctp_sndinfo))];
1402280304Sjkim    cmsg->cmsg_level = IPPROTO_SCTP;
1403280304Sjkim    cmsg->cmsg_type = SCTP_PRINFO;
1404280304Sjkim    cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_prinfo));
1405280304Sjkim    prinfo = (struct sctp_prinfo *)CMSG_DATA(cmsg);
1406280304Sjkim    memset(prinfo, 0, sizeof(struct sctp_prinfo));
1407280304Sjkim    prinfo->pr_policy = pinfo->pr_policy;
1408280304Sjkim    prinfo->pr_value = pinfo->pr_value;
1409280304Sjkim    msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_prinfo));
1410280304Sjkim#  else
1411280304Sjkim    cmsg = (struct cmsghdr *)cmsgbuf;
1412280304Sjkim    cmsg->cmsg_level = IPPROTO_SCTP;
1413280304Sjkim    cmsg->cmsg_type = SCTP_SNDRCV;
1414280304Sjkim    cmsg->cmsg_len = CMSG_LEN(sizeof(struct sctp_sndrcvinfo));
1415280304Sjkim    sndrcvinfo = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
1416280304Sjkim    memset(sndrcvinfo, 0, sizeof(struct sctp_sndrcvinfo));
1417280304Sjkim    sndrcvinfo->sinfo_stream = sinfo->snd_sid;
1418280304Sjkim    sndrcvinfo->sinfo_flags = sinfo->snd_flags;
1419280304Sjkim#   ifdef __FreeBSD__
1420280304Sjkim    sndrcvinfo->sinfo_flags |= pinfo->pr_policy;
1421280304Sjkim#   endif
1422280304Sjkim    sndrcvinfo->sinfo_ppid = sinfo->snd_ppid;
1423280304Sjkim    sndrcvinfo->sinfo_context = sinfo->snd_context;
1424280304Sjkim    sndrcvinfo->sinfo_timetolive = pinfo->pr_value;
1425280304Sjkim    msg.msg_controllen += CMSG_SPACE(sizeof(struct sctp_sndrcvinfo));
1426280304Sjkim#  endif
1427238405Sjkim
1428280304Sjkim    ret = sendmsg(b->num, &msg, 0);
1429238405Sjkim
1430280304Sjkim    BIO_clear_retry_flags(b);
1431280304Sjkim    if (ret <= 0) {
1432280304Sjkim        if (BIO_dgram_should_retry(ret)) {
1433280304Sjkim            BIO_set_retry_write(b);
1434280304Sjkim            data->_errno = get_last_socket_error();
1435280304Sjkim        }
1436280304Sjkim    }
1437280304Sjkim    return (ret);
1438280304Sjkim}
1439238405Sjkim
1440238405Sjkimstatic long dgram_sctp_ctrl(BIO *b, int cmd, long num, void *ptr)
1441280304Sjkim{
1442280304Sjkim    long ret = 1;
1443280304Sjkim    bio_dgram_sctp_data *data = NULL;
1444280304Sjkim    socklen_t sockopt_len = 0;
1445280304Sjkim    struct sctp_authkeyid authkeyid;
1446280304Sjkim    struct sctp_authkey *authkey = NULL;
1447238405Sjkim
1448280304Sjkim    data = (bio_dgram_sctp_data *) b->ptr;
1449238405Sjkim
1450280304Sjkim    switch (cmd) {
1451280304Sjkim    case BIO_CTRL_DGRAM_QUERY_MTU:
1452280304Sjkim        /*
1453280304Sjkim         * Set to maximum (2^14) and ignore user input to enable transport
1454280304Sjkim         * protocol fragmentation. Returns always 2^14.
1455280304Sjkim         */
1456280304Sjkim        data->mtu = 16384;
1457280304Sjkim        ret = data->mtu;
1458280304Sjkim        break;
1459280304Sjkim    case BIO_CTRL_DGRAM_SET_MTU:
1460280304Sjkim        /*
1461280304Sjkim         * Set to maximum (2^14) and ignore input to enable transport
1462280304Sjkim         * protocol fragmentation. Returns always 2^14.
1463280304Sjkim         */
1464280304Sjkim        data->mtu = 16384;
1465280304Sjkim        ret = data->mtu;
1466280304Sjkim        break;
1467280304Sjkim    case BIO_CTRL_DGRAM_SET_CONNECTED:
1468280304Sjkim    case BIO_CTRL_DGRAM_CONNECT:
1469280304Sjkim        /* Returns always -1. */
1470280304Sjkim        ret = -1;
1471280304Sjkim        break;
1472280304Sjkim    case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
1473280304Sjkim        /*
1474280304Sjkim         * SCTP doesn't need the DTLS timer Returns always 1.
1475280304Sjkim         */
1476280304Sjkim        break;
1477280304Sjkim    case BIO_CTRL_DGRAM_GET_MTU_OVERHEAD:
1478280304Sjkim        /*
1479280304Sjkim         * We allow transport protocol fragmentation so this is irrelevant
1480280304Sjkim         */
1481280304Sjkim        ret = 0;
1482280304Sjkim        break;
1483280304Sjkim    case BIO_CTRL_DGRAM_SCTP_SET_IN_HANDSHAKE:
1484280304Sjkim        if (num > 0)
1485280304Sjkim            data->in_handshake = 1;
1486280304Sjkim        else
1487280304Sjkim            data->in_handshake = 0;
1488238405Sjkim
1489280304Sjkim        ret =
1490280304Sjkim            setsockopt(b->num, IPPROTO_SCTP, SCTP_NODELAY,
1491280304Sjkim                       &data->in_handshake, sizeof(int));
1492280304Sjkim        break;
1493280304Sjkim    case BIO_CTRL_DGRAM_SCTP_ADD_AUTH_KEY:
1494280304Sjkim        /*
1495280304Sjkim         * New shared key for SCTP AUTH. Returns 0 on success, -1 otherwise.
1496280304Sjkim         */
1497238405Sjkim
1498280304Sjkim        /* Get active key */
1499280304Sjkim        sockopt_len = sizeof(struct sctp_authkeyid);
1500280304Sjkim        ret =
1501280304Sjkim            getsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY, &authkeyid,
1502280304Sjkim                       &sockopt_len);
1503280304Sjkim        if (ret < 0)
1504280304Sjkim            break;
1505238405Sjkim
1506280304Sjkim        /* Add new key */
1507280304Sjkim        sockopt_len = sizeof(struct sctp_authkey) + 64 * sizeof(uint8_t);
1508280304Sjkim        authkey = OPENSSL_malloc(sockopt_len);
1509280304Sjkim        if (authkey == NULL) {
1510280304Sjkim            ret = -1;
1511280304Sjkim            break;
1512280304Sjkim        }
1513280304Sjkim        memset(authkey, 0x00, sockopt_len);
1514280304Sjkim        authkey->sca_keynumber = authkeyid.scact_keynumber + 1;
1515280304Sjkim#  ifndef __FreeBSD__
1516280304Sjkim        /*
1517280304Sjkim         * This field is missing in FreeBSD 8.2 and earlier, and FreeBSD 8.3
1518280304Sjkim         * and higher work without it.
1519280304Sjkim         */
1520280304Sjkim        authkey->sca_keylength = 64;
1521280304Sjkim#  endif
1522280304Sjkim        memcpy(&authkey->sca_key[0], ptr, 64 * sizeof(uint8_t));
1523238405Sjkim
1524280304Sjkim        ret =
1525280304Sjkim            setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_KEY, authkey,
1526280304Sjkim                       sockopt_len);
1527280304Sjkim        OPENSSL_free(authkey);
1528280304Sjkim        authkey = NULL;
1529280304Sjkim        if (ret < 0)
1530280304Sjkim            break;
1531238405Sjkim
1532280304Sjkim        /* Reset active key */
1533280304Sjkim        ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY,
1534280304Sjkim                         &authkeyid, sizeof(struct sctp_authkeyid));
1535280304Sjkim        if (ret < 0)
1536280304Sjkim            break;
1537238405Sjkim
1538280304Sjkim        break;
1539280304Sjkim    case BIO_CTRL_DGRAM_SCTP_NEXT_AUTH_KEY:
1540280304Sjkim        /* Returns 0 on success, -1 otherwise. */
1541238405Sjkim
1542280304Sjkim        /* Get active key */
1543280304Sjkim        sockopt_len = sizeof(struct sctp_authkeyid);
1544280304Sjkim        ret =
1545280304Sjkim            getsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY, &authkeyid,
1546280304Sjkim                       &sockopt_len);
1547280304Sjkim        if (ret < 0)
1548280304Sjkim            break;
1549238405Sjkim
1550280304Sjkim        /* Set active key */
1551280304Sjkim        authkeyid.scact_keynumber = authkeyid.scact_keynumber + 1;
1552280304Sjkim        ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY,
1553280304Sjkim                         &authkeyid, sizeof(struct sctp_authkeyid));
1554280304Sjkim        if (ret < 0)
1555280304Sjkim            break;
1556238405Sjkim
1557280304Sjkim        /*
1558280304Sjkim         * CCS has been sent, so remember that and fall through to check if
1559280304Sjkim         * we need to deactivate an old key
1560280304Sjkim         */
1561280304Sjkim        data->ccs_sent = 1;
1562238405Sjkim
1563280304Sjkim    case BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD:
1564280304Sjkim        /* Returns 0 on success, -1 otherwise. */
1565238405Sjkim
1566280304Sjkim        /*
1567280304Sjkim         * Has this command really been called or is this just a
1568280304Sjkim         * fall-through?
1569280304Sjkim         */
1570280304Sjkim        if (cmd == BIO_CTRL_DGRAM_SCTP_AUTH_CCS_RCVD)
1571280304Sjkim            data->ccs_rcvd = 1;
1572238405Sjkim
1573280304Sjkim        /*
1574280304Sjkim         * CSS has been both, received and sent, so deactivate an old key
1575280304Sjkim         */
1576280304Sjkim        if (data->ccs_rcvd == 1 && data->ccs_sent == 1) {
1577280304Sjkim            /* Get active key */
1578280304Sjkim            sockopt_len = sizeof(struct sctp_authkeyid);
1579280304Sjkim            ret =
1580280304Sjkim                getsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_ACTIVE_KEY,
1581280304Sjkim                           &authkeyid, &sockopt_len);
1582280304Sjkim            if (ret < 0)
1583280304Sjkim                break;
1584238405Sjkim
1585280304Sjkim            /*
1586280304Sjkim             * Deactivate key or delete second last key if
1587280304Sjkim             * SCTP_AUTHENTICATION_EVENT is not available.
1588280304Sjkim             */
1589280304Sjkim            authkeyid.scact_keynumber = authkeyid.scact_keynumber - 1;
1590280304Sjkim#  ifdef SCTP_AUTH_DEACTIVATE_KEY
1591280304Sjkim            sockopt_len = sizeof(struct sctp_authkeyid);
1592280304Sjkim            ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DEACTIVATE_KEY,
1593280304Sjkim                             &authkeyid, sockopt_len);
1594280304Sjkim            if (ret < 0)
1595280304Sjkim                break;
1596280304Sjkim#  endif
1597280304Sjkim#  ifndef SCTP_AUTHENTICATION_EVENT
1598280304Sjkim            if (authkeyid.scact_keynumber > 0) {
1599280304Sjkim                authkeyid.scact_keynumber = authkeyid.scact_keynumber - 1;
1600280304Sjkim                ret = setsockopt(b->num, IPPROTO_SCTP, SCTP_AUTH_DELETE_KEY,
1601280304Sjkim                                 &authkeyid, sizeof(struct sctp_authkeyid));
1602280304Sjkim                if (ret < 0)
1603280304Sjkim                    break;
1604280304Sjkim            }
1605280304Sjkim#  endif
1606238405Sjkim
1607280304Sjkim            data->ccs_rcvd = 0;
1608280304Sjkim            data->ccs_sent = 0;
1609280304Sjkim        }
1610280304Sjkim        break;
1611280304Sjkim    case BIO_CTRL_DGRAM_SCTP_GET_SNDINFO:
1612280304Sjkim        /* Returns the size of the copied struct. */
1613280304Sjkim        if (num > (long)sizeof(struct bio_dgram_sctp_sndinfo))
1614280304Sjkim            num = sizeof(struct bio_dgram_sctp_sndinfo);
1615238405Sjkim
1616280304Sjkim        memcpy(ptr, &(data->sndinfo), num);
1617280304Sjkim        ret = num;
1618280304Sjkim        break;
1619280304Sjkim    case BIO_CTRL_DGRAM_SCTP_SET_SNDINFO:
1620280304Sjkim        /* Returns the size of the copied struct. */
1621280304Sjkim        if (num > (long)sizeof(struct bio_dgram_sctp_sndinfo))
1622280304Sjkim            num = sizeof(struct bio_dgram_sctp_sndinfo);
1623238405Sjkim
1624280304Sjkim        memcpy(&(data->sndinfo), ptr, num);
1625280304Sjkim        break;
1626280304Sjkim    case BIO_CTRL_DGRAM_SCTP_GET_RCVINFO:
1627280304Sjkim        /* Returns the size of the copied struct. */
1628280304Sjkim        if (num > (long)sizeof(struct bio_dgram_sctp_rcvinfo))
1629280304Sjkim            num = sizeof(struct bio_dgram_sctp_rcvinfo);
1630238405Sjkim
1631280304Sjkim        memcpy(ptr, &data->rcvinfo, num);
1632238405Sjkim
1633280304Sjkim        ret = num;
1634280304Sjkim        break;
1635280304Sjkim    case BIO_CTRL_DGRAM_SCTP_SET_RCVINFO:
1636280304Sjkim        /* Returns the size of the copied struct. */
1637280304Sjkim        if (num > (long)sizeof(struct bio_dgram_sctp_rcvinfo))
1638280304Sjkim            num = sizeof(struct bio_dgram_sctp_rcvinfo);
1639238405Sjkim
1640280304Sjkim        memcpy(&(data->rcvinfo), ptr, num);
1641280304Sjkim        break;
1642280304Sjkim    case BIO_CTRL_DGRAM_SCTP_GET_PRINFO:
1643280304Sjkim        /* Returns the size of the copied struct. */
1644280304Sjkim        if (num > (long)sizeof(struct bio_dgram_sctp_prinfo))
1645280304Sjkim            num = sizeof(struct bio_dgram_sctp_prinfo);
1646238405Sjkim
1647280304Sjkim        memcpy(ptr, &(data->prinfo), num);
1648280304Sjkim        ret = num;
1649280304Sjkim        break;
1650280304Sjkim    case BIO_CTRL_DGRAM_SCTP_SET_PRINFO:
1651280304Sjkim        /* Returns the size of the copied struct. */
1652280304Sjkim        if (num > (long)sizeof(struct bio_dgram_sctp_prinfo))
1653280304Sjkim            num = sizeof(struct bio_dgram_sctp_prinfo);
1654238405Sjkim
1655280304Sjkim        memcpy(&(data->prinfo), ptr, num);
1656280304Sjkim        break;
1657280304Sjkim    case BIO_CTRL_DGRAM_SCTP_SAVE_SHUTDOWN:
1658280304Sjkim        /* Returns always 1. */
1659280304Sjkim        if (num > 0)
1660280304Sjkim            data->save_shutdown = 1;
1661280304Sjkim        else
1662280304Sjkim            data->save_shutdown = 0;
1663280304Sjkim        break;
1664238405Sjkim
1665280304Sjkim    default:
1666280304Sjkim        /*
1667280304Sjkim         * Pass to default ctrl function to process SCTP unspecific commands
1668280304Sjkim         */
1669280304Sjkim        ret = dgram_ctrl(b, cmd, num, ptr);
1670280304Sjkim        break;
1671280304Sjkim    }
1672280304Sjkim    return (ret);
1673280304Sjkim}
1674238405Sjkim
1675238405Sjkimint BIO_dgram_sctp_notification_cb(BIO *b,
1676280304Sjkim                                   void (*handle_notifications) (BIO *bio,
1677280304Sjkim                                                                 void
1678280304Sjkim                                                                 *context,
1679280304Sjkim                                                                 void *buf),
1680238405Sjkim                                   void *context)
1681280304Sjkim{
1682280304Sjkim    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
1683238405Sjkim
1684280304Sjkim    if (handle_notifications != NULL) {
1685280304Sjkim        data->handle_notifications = handle_notifications;
1686280304Sjkim        data->notification_context = context;
1687280304Sjkim    } else
1688280304Sjkim        return -1;
1689238405Sjkim
1690280304Sjkim    return 0;
1691280304Sjkim}
1692238405Sjkim
1693238405Sjkimint BIO_dgram_sctp_wait_for_dry(BIO *b)
1694238405Sjkim{
1695280304Sjkim    int is_dry = 0;
1696280304Sjkim    int n, sockflags, ret;
1697280304Sjkim    union sctp_notification snp;
1698280304Sjkim    struct msghdr msg;
1699280304Sjkim    struct iovec iov;
1700280304Sjkim#  ifdef SCTP_EVENT
1701280304Sjkim    struct sctp_event event;
1702280304Sjkim#  else
1703280304Sjkim    struct sctp_event_subscribe event;
1704280304Sjkim    socklen_t eventsize;
1705280304Sjkim#  endif
1706280304Sjkim    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
1707238405Sjkim
1708280304Sjkim    /* set sender dry event */
1709280304Sjkim#  ifdef SCTP_EVENT
1710280304Sjkim    memset(&event, 0, sizeof(struct sctp_event));
1711280304Sjkim    event.se_assoc_id = 0;
1712280304Sjkim    event.se_type = SCTP_SENDER_DRY_EVENT;
1713280304Sjkim    event.se_on = 1;
1714280304Sjkim    ret =
1715280304Sjkim        setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENT, &event,
1716280304Sjkim                   sizeof(struct sctp_event));
1717280304Sjkim#  else
1718280304Sjkim    eventsize = sizeof(struct sctp_event_subscribe);
1719280304Sjkim    ret = getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event, &eventsize);
1720280304Sjkim    if (ret < 0)
1721280304Sjkim        return -1;
1722238405Sjkim
1723280304Sjkim    event.sctp_sender_dry_event = 1;
1724238405Sjkim
1725280304Sjkim    ret =
1726280304Sjkim        setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
1727280304Sjkim                   sizeof(struct sctp_event_subscribe));
1728280304Sjkim#  endif
1729280304Sjkim    if (ret < 0)
1730280304Sjkim        return -1;
1731238405Sjkim
1732280304Sjkim    /* peek for notification */
1733280304Sjkim    memset(&snp, 0x00, sizeof(union sctp_notification));
1734280304Sjkim    iov.iov_base = (char *)&snp;
1735280304Sjkim    iov.iov_len = sizeof(union sctp_notification);
1736280304Sjkim    msg.msg_name = NULL;
1737280304Sjkim    msg.msg_namelen = 0;
1738280304Sjkim    msg.msg_iov = &iov;
1739280304Sjkim    msg.msg_iovlen = 1;
1740280304Sjkim    msg.msg_control = NULL;
1741280304Sjkim    msg.msg_controllen = 0;
1742280304Sjkim    msg.msg_flags = 0;
1743238405Sjkim
1744280304Sjkim    n = recvmsg(b->num, &msg, MSG_PEEK);
1745280304Sjkim    if (n <= 0) {
1746280304Sjkim        if ((n < 0) && (get_last_socket_error() != EAGAIN)
1747280304Sjkim            && (get_last_socket_error() != EWOULDBLOCK))
1748280304Sjkim            return -1;
1749280304Sjkim        else
1750280304Sjkim            return 0;
1751280304Sjkim    }
1752238405Sjkim
1753280304Sjkim    /* if we find a notification, process it and try again if necessary */
1754280304Sjkim    while (msg.msg_flags & MSG_NOTIFICATION) {
1755280304Sjkim        memset(&snp, 0x00, sizeof(union sctp_notification));
1756280304Sjkim        iov.iov_base = (char *)&snp;
1757280304Sjkim        iov.iov_len = sizeof(union sctp_notification);
1758280304Sjkim        msg.msg_name = NULL;
1759280304Sjkim        msg.msg_namelen = 0;
1760280304Sjkim        msg.msg_iov = &iov;
1761280304Sjkim        msg.msg_iovlen = 1;
1762280304Sjkim        msg.msg_control = NULL;
1763280304Sjkim        msg.msg_controllen = 0;
1764280304Sjkim        msg.msg_flags = 0;
1765238405Sjkim
1766280304Sjkim        n = recvmsg(b->num, &msg, 0);
1767280304Sjkim        if (n <= 0) {
1768280304Sjkim            if ((n < 0) && (get_last_socket_error() != EAGAIN)
1769280304Sjkim                && (get_last_socket_error() != EWOULDBLOCK))
1770280304Sjkim                return -1;
1771280304Sjkim            else
1772280304Sjkim                return is_dry;
1773280304Sjkim        }
1774238405Sjkim
1775280304Sjkim        if (snp.sn_header.sn_type == SCTP_SENDER_DRY_EVENT) {
1776280304Sjkim            is_dry = 1;
1777238405Sjkim
1778280304Sjkim            /* disable sender dry event */
1779280304Sjkim#  ifdef SCTP_EVENT
1780280304Sjkim            memset(&event, 0, sizeof(struct sctp_event));
1781280304Sjkim            event.se_assoc_id = 0;
1782280304Sjkim            event.se_type = SCTP_SENDER_DRY_EVENT;
1783280304Sjkim            event.se_on = 0;
1784280304Sjkim            ret =
1785280304Sjkim                setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENT, &event,
1786280304Sjkim                           sizeof(struct sctp_event));
1787280304Sjkim#  else
1788280304Sjkim            eventsize = (socklen_t) sizeof(struct sctp_event_subscribe);
1789280304Sjkim            ret =
1790280304Sjkim                getsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
1791280304Sjkim                           &eventsize);
1792280304Sjkim            if (ret < 0)
1793280304Sjkim                return -1;
1794238405Sjkim
1795280304Sjkim            event.sctp_sender_dry_event = 0;
1796238405Sjkim
1797280304Sjkim            ret =
1798280304Sjkim                setsockopt(b->num, IPPROTO_SCTP, SCTP_EVENTS, &event,
1799280304Sjkim                           sizeof(struct sctp_event_subscribe));
1800280304Sjkim#  endif
1801280304Sjkim            if (ret < 0)
1802280304Sjkim                return -1;
1803280304Sjkim        }
1804280304Sjkim#  ifdef SCTP_AUTHENTICATION_EVENT
1805280304Sjkim        if (snp.sn_header.sn_type == SCTP_AUTHENTICATION_EVENT)
1806280304Sjkim            dgram_sctp_handle_auth_free_key_event(b, &snp);
1807280304Sjkim#  endif
1808238405Sjkim
1809280304Sjkim        if (data->handle_notifications != NULL)
1810280304Sjkim            data->handle_notifications(b, data->notification_context,
1811280304Sjkim                                       (void *)&snp);
1812238405Sjkim
1813280304Sjkim        /* found notification, peek again */
1814280304Sjkim        memset(&snp, 0x00, sizeof(union sctp_notification));
1815280304Sjkim        iov.iov_base = (char *)&snp;
1816280304Sjkim        iov.iov_len = sizeof(union sctp_notification);
1817280304Sjkim        msg.msg_name = NULL;
1818280304Sjkim        msg.msg_namelen = 0;
1819280304Sjkim        msg.msg_iov = &iov;
1820280304Sjkim        msg.msg_iovlen = 1;
1821280304Sjkim        msg.msg_control = NULL;
1822280304Sjkim        msg.msg_controllen = 0;
1823280304Sjkim        msg.msg_flags = 0;
1824238405Sjkim
1825280304Sjkim        /* if we have seen the dry already, don't wait */
1826280304Sjkim        if (is_dry) {
1827280304Sjkim            sockflags = fcntl(b->num, F_GETFL, 0);
1828280304Sjkim            fcntl(b->num, F_SETFL, O_NONBLOCK);
1829280304Sjkim        }
1830238405Sjkim
1831280304Sjkim        n = recvmsg(b->num, &msg, MSG_PEEK);
1832238405Sjkim
1833280304Sjkim        if (is_dry) {
1834280304Sjkim            fcntl(b->num, F_SETFL, sockflags);
1835280304Sjkim        }
1836280304Sjkim
1837280304Sjkim        if (n <= 0) {
1838280304Sjkim            if ((n < 0) && (get_last_socket_error() != EAGAIN)
1839280304Sjkim                && (get_last_socket_error() != EWOULDBLOCK))
1840280304Sjkim                return -1;
1841280304Sjkim            else
1842280304Sjkim                return is_dry;
1843280304Sjkim        }
1844280304Sjkim    }
1845280304Sjkim
1846280304Sjkim    /* read anything else */
1847280304Sjkim    return is_dry;
1848238405Sjkim}
1849238405Sjkim
1850238405Sjkimint BIO_dgram_sctp_msg_waiting(BIO *b)
1851280304Sjkim{
1852280304Sjkim    int n, sockflags;
1853280304Sjkim    union sctp_notification snp;
1854280304Sjkim    struct msghdr msg;
1855280304Sjkim    struct iovec iov;
1856280304Sjkim    bio_dgram_sctp_data *data = (bio_dgram_sctp_data *) b->ptr;
1857238405Sjkim
1858280304Sjkim    /* Check if there are any messages waiting to be read */
1859280304Sjkim    do {
1860280304Sjkim        memset(&snp, 0x00, sizeof(union sctp_notification));
1861280304Sjkim        iov.iov_base = (char *)&snp;
1862280304Sjkim        iov.iov_len = sizeof(union sctp_notification);
1863280304Sjkim        msg.msg_name = NULL;
1864280304Sjkim        msg.msg_namelen = 0;
1865280304Sjkim        msg.msg_iov = &iov;
1866280304Sjkim        msg.msg_iovlen = 1;
1867280304Sjkim        msg.msg_control = NULL;
1868280304Sjkim        msg.msg_controllen = 0;
1869280304Sjkim        msg.msg_flags = 0;
1870238405Sjkim
1871280304Sjkim        sockflags = fcntl(b->num, F_GETFL, 0);
1872280304Sjkim        fcntl(b->num, F_SETFL, O_NONBLOCK);
1873280304Sjkim        n = recvmsg(b->num, &msg, MSG_PEEK);
1874280304Sjkim        fcntl(b->num, F_SETFL, sockflags);
1875238405Sjkim
1876280304Sjkim        /* if notification, process and try again */
1877280304Sjkim        if (n > 0 && (msg.msg_flags & MSG_NOTIFICATION)) {
1878280304Sjkim#  ifdef SCTP_AUTHENTICATION_EVENT
1879280304Sjkim            if (snp.sn_header.sn_type == SCTP_AUTHENTICATION_EVENT)
1880280304Sjkim                dgram_sctp_handle_auth_free_key_event(b, &snp);
1881280304Sjkim#  endif
1882238405Sjkim
1883280304Sjkim            memset(&snp, 0x00, sizeof(union sctp_notification));
1884280304Sjkim            iov.iov_base = (char *)&snp;
1885280304Sjkim            iov.iov_len = sizeof(union sctp_notification);
1886280304Sjkim            msg.msg_name = NULL;
1887280304Sjkim            msg.msg_namelen = 0;
1888280304Sjkim            msg.msg_iov = &iov;
1889280304Sjkim            msg.msg_iovlen = 1;
1890280304Sjkim            msg.msg_control = NULL;
1891280304Sjkim            msg.msg_controllen = 0;
1892280304Sjkim            msg.msg_flags = 0;
1893280304Sjkim            n = recvmsg(b->num, &msg, 0);
1894238405Sjkim
1895280304Sjkim            if (data->handle_notifications != NULL)
1896280304Sjkim                data->handle_notifications(b, data->notification_context,
1897280304Sjkim                                           (void *)&snp);
1898280304Sjkim        }
1899238405Sjkim
1900280304Sjkim    } while (n > 0 && (msg.msg_flags & MSG_NOTIFICATION));
1901238405Sjkim
1902280304Sjkim    /* Return 1 if there is a message to be read, return 0 otherwise. */
1903280304Sjkim    if (n > 0)
1904280304Sjkim        return 1;
1905280304Sjkim    else
1906280304Sjkim        return 0;
1907280304Sjkim}
1908238405Sjkim
1909238405Sjkimstatic int dgram_sctp_puts(BIO *bp, const char *str)
1910280304Sjkim{
1911280304Sjkim    int n, ret;
1912238405Sjkim
1913280304Sjkim    n = strlen(str);
1914280304Sjkim    ret = dgram_sctp_write(bp, str, n);
1915280304Sjkim    return (ret);
1916280304Sjkim}
1917280304Sjkim# endif
1918238405Sjkim
1919194206Ssimonstatic int BIO_dgram_should_retry(int i)
1920280304Sjkim{
1921280304Sjkim    int err;
1922160814Ssimon
1923280304Sjkim    if ((i == 0) || (i == -1)) {
1924280304Sjkim        err = get_last_socket_error();
1925160814Ssimon
1926280304Sjkim# if defined(OPENSSL_SYS_WINDOWS)
1927280304Sjkim        /*
1928280304Sjkim         * If the socket return value (i) is -1 and err is unexpectedly 0 at
1929280304Sjkim         * this point, the error code was overwritten by another system call
1930280304Sjkim         * before this error handling is called.
1931280304Sjkim         */
1932280304Sjkim# endif
1933160814Ssimon
1934280304Sjkim        return (BIO_dgram_non_fatal_error(err));
1935280304Sjkim    }
1936280304Sjkim    return (0);
1937280304Sjkim}
1938160814Ssimon
1939160814Ssimonint BIO_dgram_non_fatal_error(int err)
1940280304Sjkim{
1941280304Sjkim    switch (err) {
1942280304Sjkim# if defined(OPENSSL_SYS_WINDOWS)
1943280304Sjkim#  if defined(WSAEWOULDBLOCK)
1944280304Sjkim    case WSAEWOULDBLOCK:
1945280304Sjkim#  endif
1946160814Ssimon
1947280304Sjkim#  if 0                         /* This appears to always be an error */
1948280304Sjkim#   if defined(WSAENOTCONN)
1949280304Sjkim    case WSAENOTCONN:
1950280304Sjkim#   endif
1951160814Ssimon#  endif
1952160814Ssimon# endif
1953160814Ssimon
1954280304Sjkim# ifdef EWOULDBLOCK
1955280304Sjkim#  ifdef WSAEWOULDBLOCK
1956280304Sjkim#   if WSAEWOULDBLOCK != EWOULDBLOCK
1957280304Sjkim    case EWOULDBLOCK:
1958280304Sjkim#   endif
1959280304Sjkim#  else
1960280304Sjkim    case EWOULDBLOCK:
1961160814Ssimon#  endif
1962160814Ssimon# endif
1963160814Ssimon
1964280304Sjkim# ifdef EINTR
1965280304Sjkim    case EINTR:
1966280304Sjkim# endif
1967160814Ssimon
1968280304Sjkim# ifdef EAGAIN
1969280304Sjkim#  if EWOULDBLOCK != EAGAIN
1970280304Sjkim    case EAGAIN:
1971280304Sjkim#  endif
1972160814Ssimon# endif
1973160814Ssimon
1974280304Sjkim# ifdef EPROTO
1975280304Sjkim    case EPROTO:
1976280304Sjkim# endif
1977160814Ssimon
1978280304Sjkim# ifdef EINPROGRESS
1979280304Sjkim    case EINPROGRESS:
1980280304Sjkim# endif
1981160814Ssimon
1982280304Sjkim# ifdef EALREADY
1983280304Sjkim    case EALREADY:
1984280304Sjkim# endif
1985160814Ssimon
1986280304Sjkim        return (1);
1987280304Sjkim        /* break; */
1988280304Sjkim    default:
1989280304Sjkim        break;
1990280304Sjkim    }
1991280304Sjkim    return (0);
1992280304Sjkim}
1993205128Ssimon
1994205128Ssimonstatic void get_current_time(struct timeval *t)
1995280304Sjkim{
1996280304Sjkim# ifdef OPENSSL_SYS_WIN32
1997280304Sjkim    struct _timeb tb;
1998280304Sjkim    _ftime(&tb);
1999280304Sjkim    t->tv_sec = (long)tb.time;
2000280304Sjkim    t->tv_usec = (long)tb.millitm * 1000;
2001280304Sjkim# elif defined(OPENSSL_SYS_VMS)
2002280304Sjkim    struct timeb tb;
2003280304Sjkim    ftime(&tb);
2004280304Sjkim    t->tv_sec = (long)tb.time;
2005280304Sjkim    t->tv_usec = (long)tb.millitm * 1000;
2006280304Sjkim# else
2007280304Sjkim    gettimeofday(t, NULL);
2008280304Sjkim# endif
2009280304Sjkim}
2010237657Sjkim
2011237657Sjkim#endif
2012