1 /* $OpenBSD: bss_dgram.c,v 1.40 2015/02/09 10:55:33 jsing Exp $ */
3 * DTLS implementation written by Nagendra Modadugu
4 * (nagendra@cs.stanford.edu) for the OpenSSL project 2005.
6 /* ====================================================================
7 * Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
21 * 3. All advertising materials mentioning features or use of this
22 * software must display the following acknowledgment:
23 * "This product includes software developed by the OpenSSL Project
24 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
26 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
27 * endorse or promote products derived from this software without
28 * prior written permission. For written permission, please contact
29 * openssl-core@OpenSSL.org.
31 * 5. Products derived from this software may not be called "OpenSSL"
32 * nor may "OpenSSL" appear in their names without prior written
33 * permission of the OpenSSL Project.
35 * 6. Redistributions of any form whatsoever must retain the following
37 * "This product includes software developed by the OpenSSL Project
38 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
40 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
41 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51 * OF THE POSSIBILITY OF SUCH DAMAGE.
52 * ====================================================================
54 * This product includes cryptographic software written by Eric Young
55 * (eay@cryptsoft.com). This product includes software written by Tim
56 * Hudson (tjh@cryptsoft.com).
60 #include <sys/socket.h>
63 #include <netinet/in.h>
71 #include <openssl/opensslconf.h>
73 #include <openssl/bio.h>
75 #ifndef OPENSSL_NO_DGRAM
78 static int dgram_write(BIO *h, const char *buf, int num);
79 static int dgram_read(BIO *h, char *buf, int size);
80 static int dgram_puts(BIO *h, const char *str);
81 static long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2);
82 static int dgram_new(BIO *h);
83 static int dgram_free(BIO *data);
84 static int dgram_clear(BIO *bio);
87 static int BIO_dgram_should_retry(int s);
89 static BIO_METHOD methods_dgramp = {
90 .type = BIO_TYPE_DGRAM,
91 .name = "datagram socket",
92 .bwrite = dgram_write,
101 typedef struct bio_dgram_data_st {
104 struct sockaddr_in sa_in;
105 struct sockaddr_in6 sa_in6;
107 unsigned int connected;
110 struct timeval next_timeout;
111 struct timeval socket_timeout;
118 return (&methods_dgramp);
122 BIO_new_dgram(int fd, int close_flag)
126 ret = BIO_new(BIO_s_datagram());
129 BIO_set_fd(ret, fd, close_flag);
136 bio_dgram_data *data = NULL;
140 data = calloc(1, sizeof(bio_dgram_data));
152 bio_dgram_data *data;
159 data = (bio_dgram_data *)a->ptr;
172 shutdown(a->num, SHUT_RDWR);
182 dgram_adjust_rcv_timeout(BIO *b)
184 #if defined(SO_RCVTIMEO)
185 bio_dgram_data *data = (bio_dgram_data *)b->ptr;
187 /* Is a timer active? */
188 if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
189 struct timeval timenow, timeleft;
191 /* Read current socket timeout */
192 socklen_t sz = sizeof(data->socket_timeout);
193 if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
194 &(data->socket_timeout), &sz) < 0) {
195 perror("getsockopt");
198 /* Get current time */
199 gettimeofday(&timenow, NULL);
201 /* Calculate time left until timer expires */
202 memcpy(&timeleft, &(data->next_timeout), sizeof(struct timeval));
203 timeleft.tv_sec -= timenow.tv_sec;
204 timeleft.tv_usec -= timenow.tv_usec;
205 if (timeleft.tv_usec < 0) {
207 timeleft.tv_usec += 1000000;
210 if (timeleft.tv_sec < 0) {
212 timeleft.tv_usec = 1;
215 /* Adjust socket timeout if next handhake message timer
216 * will expire earlier.
218 if ((data->socket_timeout.tv_sec == 0 &&
219 data->socket_timeout.tv_usec == 0) ||
220 (data->socket_timeout.tv_sec > timeleft.tv_sec) ||
221 (data->socket_timeout.tv_sec == timeleft.tv_sec &&
222 data->socket_timeout.tv_usec >= timeleft.tv_usec)) {
223 if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
224 &timeleft, sizeof(struct timeval)) < 0) {
225 perror("setsockopt");
233 dgram_reset_rcv_timeout(BIO *b)
235 #if defined(SO_RCVTIMEO)
236 bio_dgram_data *data = (bio_dgram_data *)b->ptr;
238 /* Is a timer active? */
239 if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) {
240 if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
241 &(data->socket_timeout), sizeof(struct timeval)) < 0) {
242 perror("setsockopt");
249 dgram_read(BIO *b, char *out, int outl)
252 bio_dgram_data *data = (bio_dgram_data *)b->ptr;
258 struct sockaddr_in sa_in;
259 struct sockaddr_in6 sa_in6;
263 sa.len = sizeof(sa.peer);
267 memset(&sa.peer, 0, sizeof(sa.peer));
268 dgram_adjust_rcv_timeout(b);
269 ret = recvfrom(b->num, out, outl, 0, &sa.peer.sa, &sa.len);
271 if (! data->connected && ret >= 0)
272 BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &sa.peer);
274 BIO_clear_retry_flags(b);
276 if (BIO_dgram_should_retry(ret)) {
277 BIO_set_retry_read(b);
278 data->_errno = errno;
282 dgram_reset_rcv_timeout(b);
288 dgram_write(BIO *b, const char *in, int inl)
291 bio_dgram_data *data = (bio_dgram_data *)b->ptr;
295 ret = write(b->num, in, inl);
297 int peerlen = sizeof(data->peer);
299 if (data->peer.sa.sa_family == AF_INET)
300 peerlen = sizeof(data->peer.sa_in);
301 else if (data->peer.sa.sa_family == AF_INET6)
302 peerlen = sizeof(data->peer.sa_in6);
303 ret = sendto(b->num, in, inl, 0, &data->peer.sa, peerlen);
306 BIO_clear_retry_flags(b);
308 if (BIO_dgram_should_retry(ret)) {
309 BIO_set_retry_write(b);
311 data->_errno = errno;
313 * higher layers are responsible for querying MTU,
322 dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
326 struct sockaddr *to = NULL;
327 bio_dgram_data *data = NULL;
328 #if (defined(IP_MTU_DISCOVER) || defined(IP_MTU))
330 socklen_t sockopt_len; /* assume that system supporting IP_MTU is
331 * modern enough to define socklen_t */
335 struct sockaddr_in s4;
336 struct sockaddr_in6 s6;
340 data = (bio_dgram_data *)b->ptr;
345 case BIO_C_FILE_SEEK:
348 case BIO_C_FILE_TELL:
354 b->num= *((int *)ptr);
355 b->shutdown = (int)num;
367 case BIO_CTRL_GET_CLOSE:
370 case BIO_CTRL_SET_CLOSE:
371 b->shutdown = (int)num;
373 case BIO_CTRL_PENDING:
374 case BIO_CTRL_WPENDING:
381 case BIO_CTRL_DGRAM_CONNECT:
382 to = (struct sockaddr *)ptr;
383 switch (to->sa_family) {
385 memcpy(&data->peer, to, sizeof(data->peer.sa_in));
388 memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
391 memcpy(&data->peer, to, sizeof(data->peer.sa));
395 /* (Linux)kernel sets DF bit on outgoing IP packets */
396 case BIO_CTRL_DGRAM_MTU_DISCOVER:
397 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO)
398 addr_len = (socklen_t)sizeof(addr);
399 memset((void *)&addr, 0, sizeof(addr));
400 if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
404 switch (addr.sa.sa_family) {
406 sockopt_val = IP_PMTUDISC_DO;
407 ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER,
408 &sockopt_val, sizeof(sockopt_val));
410 perror("setsockopt");
412 #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO)
414 sockopt_val = IPV6_PMTUDISC_DO;
415 ret = setsockopt(b->num, IPPROTO_IPV6,
416 IPV6_MTU_DISCOVER, &sockopt_val,
417 sizeof(sockopt_val));
419 perror("setsockopt");
430 case BIO_CTRL_DGRAM_QUERY_MTU:
432 addr_len = (socklen_t)sizeof(addr);
433 memset((void *)&addr, 0, sizeof(addr));
434 if (getsockname(b->num, &addr.sa, &addr_len) < 0) {
438 sockopt_len = sizeof(sockopt_val);
439 switch (addr.sa.sa_family) {
441 ret = getsockopt(b->num, IPPROTO_IP, IP_MTU,
442 &sockopt_val, &sockopt_len);
443 if (ret < 0 || sockopt_val < 0) {
446 /* we assume that the transport protocol is UDP and no
447 * IP options are used.
449 data->mtu = sockopt_val - 8 - 20;
453 #if defined(IPV6_MTU)
455 ret = getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU,
456 &sockopt_val, &sockopt_len);
457 if (ret < 0 || sockopt_val < 0) {
460 /* we assume that the transport protocol is UDP and no
461 * IPV6 options are used.
463 data->mtu = sockopt_val - 8 - 40;
476 case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
477 switch (data->peer.sa.sa_family) {
482 #ifdef IN6_IS_ADDR_V4MAPPED
483 if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr))
494 case BIO_CTRL_DGRAM_GET_MTU:
497 case BIO_CTRL_DGRAM_SET_MTU:
501 case BIO_CTRL_DGRAM_SET_CONNECTED:
502 to = (struct sockaddr *)ptr;
506 switch (to->sa_family) {
508 memcpy(&data->peer, to, sizeof(data->peer.sa_in));
511 memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
514 memcpy(&data->peer, to, sizeof(data->peer.sa));
519 memset(&(data->peer), 0, sizeof(data->peer));
522 case BIO_CTRL_DGRAM_GET_PEER:
523 switch (data->peer.sa.sa_family) {
525 ret = sizeof(data->peer.sa_in);
528 ret = sizeof(data->peer.sa_in6);
531 ret = sizeof(data->peer.sa);
534 if (num == 0 || num > ret)
536 memcpy(ptr, &data->peer, (ret = num));
538 case BIO_CTRL_DGRAM_SET_PEER:
539 to = (struct sockaddr *) ptr;
540 switch (to->sa_family) {
542 memcpy(&data->peer, to, sizeof(data->peer.sa_in));
545 memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
548 memcpy(&data->peer, to, sizeof(data->peer.sa));
552 case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
553 memcpy(&(data->next_timeout), ptr, sizeof(struct timeval));
555 #if defined(SO_RCVTIMEO)
556 case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT:
557 if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr,
558 sizeof(struct timeval)) < 0) {
559 perror("setsockopt");
563 case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT:
565 socklen_t sz = sizeof(struct timeval);
566 if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO,
568 perror("getsockopt");
575 #if defined(SO_SNDTIMEO)
576 case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT:
577 if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr,
578 sizeof(struct timeval)) < 0) {
579 perror("setsockopt");
583 case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT:
585 socklen_t sz = sizeof(struct timeval);
586 if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO,
588 perror("getsockopt");
595 case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP:
597 case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP:
598 if (data->_errno == EAGAIN) {
605 case BIO_CTRL_DGRAM_MTU_EXCEEDED:
606 if (data->_errno == EMSGSIZE) {
621 dgram_puts(BIO *bp, const char *str)
626 ret = dgram_write(bp, str, n);
632 BIO_dgram_should_retry(int i)
636 if ((i == 0) || (i == -1)) {
638 return (BIO_dgram_non_fatal_error(err));
644 BIO_dgram_non_fatal_error(int err)