1 /* $OpenBSD: bss_dgram.c,v 1.42 2018/05/12 17:47:53 tb Exp $ */ 2 /* 3 * DTLS implementation written by Nagendra Modadugu 4 * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. 5 */ 6 /* ==================================================================== 7 * Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 21 * 3. All advertising materials mentioning features or use of this 22 * software must display the following acknowledgment: 23 * "This product includes software developed by the OpenSSL Project 24 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 25 * 26 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 27 * endorse or promote products derived from this software without 28 * prior written permission. For written permission, please contact 29 * openssl-core@OpenSSL.org. 30 * 31 * 5. Products derived from this software may not be called "OpenSSL" 32 * nor may "OpenSSL" appear in their names without prior written 33 * permission of the OpenSSL Project. 34 * 35 * 6. Redistributions of any form whatsoever must retain the following 36 * acknowledgment: 37 * "This product includes software developed by the OpenSSL Project 38 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 39 * 40 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 41 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 42 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 43 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 44 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 45 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 47 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 49 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 50 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 51 * OF THE POSSIBILITY OF SUCH DAMAGE. 52 * ==================================================================== 53 * 54 * This product includes cryptographic software written by Eric Young 55 * (eay@cryptsoft.com). This product includes software written by Tim 56 * Hudson (tjh@cryptsoft.com). 57 * 58 */ 59 60 #include <sys/socket.h> 61 #include <sys/time.h> 62 63 #include <netinet/in.h> 64 65 #include <errno.h> 66 #include <netdb.h> 67 #include <stdio.h> 68 #include <string.h> 69 #include <unistd.h> 70 71 #include <openssl/opensslconf.h> 72 73 #include <openssl/bio.h> 74 75 #ifndef OPENSSL_NO_DGRAM 76 77 78 static int dgram_write(BIO *h, const char *buf, int num); 79 static int dgram_read(BIO *h, char *buf, int size); 80 static int dgram_puts(BIO *h, const char *str); 81 static long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2); 82 static int dgram_new(BIO *h); 83 static int dgram_free(BIO *data); 84 static int dgram_clear(BIO *bio); 85 86 87 static int BIO_dgram_should_retry(int s); 88 89 static const BIO_METHOD methods_dgramp = { 90 .type = BIO_TYPE_DGRAM, 91 .name = "datagram socket", 92 .bwrite = dgram_write, 93 .bread = dgram_read, 94 .bputs = dgram_puts, 95 .ctrl = dgram_ctrl, 96 .create = dgram_new, 97 .destroy = dgram_free 98 }; 99 100 101 typedef struct bio_dgram_data_st { 102 union { 103 struct sockaddr sa; 104 struct sockaddr_in sa_in; 105 struct sockaddr_in6 sa_in6; 106 } peer; 107 unsigned int connected; 108 unsigned int _errno; 109 unsigned int mtu; 110 struct timeval next_timeout; 111 struct timeval socket_timeout; 112 } bio_dgram_data; 113 114 115 const BIO_METHOD * 116 BIO_s_datagram(void) 117 { 118 return (&methods_dgramp); 119 } 120 121 BIO * 122 BIO_new_dgram(int fd, int close_flag) 123 { 124 BIO *ret; 125 126 ret = BIO_new(BIO_s_datagram()); 127 if (ret == NULL) 128 return (NULL); 129 BIO_set_fd(ret, fd, close_flag); 130 return (ret); 131 } 132 133 static int 134 dgram_new(BIO *bi) 135 { 136 bio_dgram_data *data = NULL; 137 138 bi->init = 0; 139 bi->num = 0; 140 data = calloc(1, sizeof(bio_dgram_data)); 141 if (data == NULL) 142 return 0; 143 bi->ptr = data; 144 145 bi->flags = 0; 146 return (1); 147 } 148 149 static int 150 dgram_free(BIO *a) 151 { 152 bio_dgram_data *data; 153 154 if (a == NULL) 155 return (0); 156 if (!dgram_clear(a)) 157 return 0; 158 159 data = (bio_dgram_data *)a->ptr; 160 free(data); 161 162 return (1); 163 } 164 165 static int 166 dgram_clear(BIO *a) 167 { 168 if (a == NULL) 169 return (0); 170 if (a->shutdown) { 171 if (a->init) { 172 shutdown(a->num, SHUT_RDWR); 173 close(a->num); 174 } 175 a->init = 0; 176 a->flags = 0; 177 } 178 return (1); 179 } 180 181 static void 182 dgram_adjust_rcv_timeout(BIO *b) 183 { 184 #if defined(SO_RCVTIMEO) 185 bio_dgram_data *data = (bio_dgram_data *)b->ptr; 186 187 /* Is a timer active? */ 188 if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) { 189 struct timeval timenow, timeleft; 190 191 /* Read current socket timeout */ 192 socklen_t sz = sizeof(data->socket_timeout); 193 if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, 194 &(data->socket_timeout), &sz) < 0) { 195 perror("getsockopt"); 196 } 197 198 /* Get current time */ 199 gettimeofday(&timenow, NULL); 200 201 /* Calculate time left until timer expires */ 202 memcpy(&timeleft, &(data->next_timeout), sizeof(struct timeval)); 203 timeleft.tv_sec -= timenow.tv_sec; 204 timeleft.tv_usec -= timenow.tv_usec; 205 if (timeleft.tv_usec < 0) { 206 timeleft.tv_sec--; 207 timeleft.tv_usec += 1000000; 208 } 209 210 if (timeleft.tv_sec < 0) { 211 timeleft.tv_sec = 0; 212 timeleft.tv_usec = 1; 213 } 214 215 /* Adjust socket timeout if next handhake message timer 216 * will expire earlier. 217 */ 218 if ((data->socket_timeout.tv_sec == 0 && 219 data->socket_timeout.tv_usec == 0) || 220 (data->socket_timeout.tv_sec > timeleft.tv_sec) || 221 (data->socket_timeout.tv_sec == timeleft.tv_sec && 222 data->socket_timeout.tv_usec >= timeleft.tv_usec)) { 223 if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, 224 &timeleft, sizeof(struct timeval)) < 0) { 225 perror("setsockopt"); 226 } 227 } 228 } 229 #endif 230 } 231 232 static void 233 dgram_reset_rcv_timeout(BIO *b) 234 { 235 #if defined(SO_RCVTIMEO) 236 bio_dgram_data *data = (bio_dgram_data *)b->ptr; 237 238 /* Is a timer active? */ 239 if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) { 240 if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, 241 &(data->socket_timeout), sizeof(struct timeval)) < 0) { 242 perror("setsockopt"); 243 } 244 } 245 #endif 246 } 247 248 static int 249 dgram_read(BIO *b, char *out, int outl) 250 { 251 int ret = 0; 252 bio_dgram_data *data = (bio_dgram_data *)b->ptr; 253 254 struct { 255 socklen_t len; 256 union { 257 struct sockaddr sa; 258 struct sockaddr_in sa_in; 259 struct sockaddr_in6 sa_in6; 260 } peer; 261 } sa; 262 263 sa.len = sizeof(sa.peer); 264 265 if (out != NULL) { 266 errno = 0; 267 memset(&sa.peer, 0, sizeof(sa.peer)); 268 dgram_adjust_rcv_timeout(b); 269 ret = recvfrom(b->num, out, outl, 0, &sa.peer.sa, &sa.len); 270 271 if (! data->connected && ret >= 0) 272 BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &sa.peer); 273 274 BIO_clear_retry_flags(b); 275 if (ret < 0) { 276 if (BIO_dgram_should_retry(ret)) { 277 BIO_set_retry_read(b); 278 data->_errno = errno; 279 } 280 } 281 282 dgram_reset_rcv_timeout(b); 283 } 284 return (ret); 285 } 286 287 static int 288 dgram_write(BIO *b, const char *in, int inl) 289 { 290 int ret; 291 bio_dgram_data *data = (bio_dgram_data *)b->ptr; 292 errno = 0; 293 294 if (data->connected) 295 ret = write(b->num, in, inl); 296 else { 297 int peerlen = sizeof(data->peer); 298 299 if (data->peer.sa.sa_family == AF_INET) 300 peerlen = sizeof(data->peer.sa_in); 301 else if (data->peer.sa.sa_family == AF_INET6) 302 peerlen = sizeof(data->peer.sa_in6); 303 ret = sendto(b->num, in, inl, 0, &data->peer.sa, peerlen); 304 } 305 306 BIO_clear_retry_flags(b); 307 if (ret <= 0) { 308 if (BIO_dgram_should_retry(ret)) { 309 BIO_set_retry_write(b); 310 311 data->_errno = errno; 312 /* 313 * higher layers are responsible for querying MTU, 314 * if necessary 315 */ 316 } 317 } 318 return (ret); 319 } 320 321 static long 322 dgram_ctrl(BIO *b, int cmd, long num, void *ptr) 323 { 324 long ret = 1; 325 int *ip; 326 struct sockaddr *to = NULL; 327 bio_dgram_data *data = NULL; 328 #if (defined(IP_MTU_DISCOVER) || defined(IP_MTU)) 329 int sockopt_val = 0; 330 socklen_t sockopt_len; /* assume that system supporting IP_MTU is 331 * modern enough to define socklen_t */ 332 socklen_t addr_len; 333 union { 334 struct sockaddr sa; 335 struct sockaddr_in s4; 336 struct sockaddr_in6 s6; 337 } addr; 338 #endif 339 340 data = (bio_dgram_data *)b->ptr; 341 342 switch (cmd) { 343 case BIO_CTRL_RESET: 344 num = 0; 345 case BIO_C_FILE_SEEK: 346 ret = 0; 347 break; 348 case BIO_C_FILE_TELL: 349 case BIO_CTRL_INFO: 350 ret = 0; 351 break; 352 case BIO_C_SET_FD: 353 dgram_clear(b); 354 b->num= *((int *)ptr); 355 b->shutdown = (int)num; 356 b->init = 1; 357 break; 358 case BIO_C_GET_FD: 359 if (b->init) { 360 ip = (int *)ptr; 361 if (ip != NULL) 362 *ip = b->num; 363 ret = b->num; 364 } else 365 ret = -1; 366 break; 367 case BIO_CTRL_GET_CLOSE: 368 ret = b->shutdown; 369 break; 370 case BIO_CTRL_SET_CLOSE: 371 b->shutdown = (int)num; 372 break; 373 case BIO_CTRL_PENDING: 374 case BIO_CTRL_WPENDING: 375 ret = 0; 376 break; 377 case BIO_CTRL_DUP: 378 case BIO_CTRL_FLUSH: 379 ret = 1; 380 break; 381 case BIO_CTRL_DGRAM_CONNECT: 382 to = (struct sockaddr *)ptr; 383 switch (to->sa_family) { 384 case AF_INET: 385 memcpy(&data->peer, to, sizeof(data->peer.sa_in)); 386 break; 387 case AF_INET6: 388 memcpy(&data->peer, to, sizeof(data->peer.sa_in6)); 389 break; 390 default: 391 memcpy(&data->peer, to, sizeof(data->peer.sa)); 392 break; 393 } 394 break; 395 /* (Linux)kernel sets DF bit on outgoing IP packets */ 396 case BIO_CTRL_DGRAM_MTU_DISCOVER: 397 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) 398 addr_len = (socklen_t)sizeof(addr); 399 memset((void *)&addr, 0, sizeof(addr)); 400 if (getsockname(b->num, &addr.sa, &addr_len) < 0) { 401 ret = 0; 402 break; 403 } 404 switch (addr.sa.sa_family) { 405 case AF_INET: 406 sockopt_val = IP_PMTUDISC_DO; 407 ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER, 408 &sockopt_val, sizeof(sockopt_val)); 409 if (ret < 0) 410 perror("setsockopt"); 411 break; 412 #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) 413 case AF_INET6: 414 sockopt_val = IPV6_PMTUDISC_DO; 415 ret = setsockopt(b->num, IPPROTO_IPV6, 416 IPV6_MTU_DISCOVER, &sockopt_val, 417 sizeof(sockopt_val)); 418 if (ret < 0) 419 perror("setsockopt"); 420 break; 421 #endif 422 default: 423 ret = -1; 424 break; 425 } 426 #else 427 ret = -1; 428 #endif 429 break; 430 case BIO_CTRL_DGRAM_QUERY_MTU: 431 #if defined(IP_MTU) 432 addr_len = (socklen_t)sizeof(addr); 433 memset((void *)&addr, 0, sizeof(addr)); 434 if (getsockname(b->num, &addr.sa, &addr_len) < 0) { 435 ret = 0; 436 break; 437 } 438 sockopt_len = sizeof(sockopt_val); 439 switch (addr.sa.sa_family) { 440 case AF_INET: 441 ret = getsockopt(b->num, IPPROTO_IP, IP_MTU, 442 &sockopt_val, &sockopt_len); 443 if (ret < 0 || sockopt_val < 0) { 444 ret = 0; 445 } else { 446 /* we assume that the transport protocol is UDP and no 447 * IP options are used. 448 */ 449 data->mtu = sockopt_val - 8 - 20; 450 ret = data->mtu; 451 } 452 break; 453 #if defined(IPV6_MTU) 454 case AF_INET6: 455 ret = getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU, 456 &sockopt_val, &sockopt_len); 457 if (ret < 0 || sockopt_val < 0) { 458 ret = 0; 459 } else { 460 /* we assume that the transport protocol is UDP and no 461 * IPV6 options are used. 462 */ 463 data->mtu = sockopt_val - 8 - 40; 464 ret = data->mtu; 465 } 466 break; 467 #endif 468 default: 469 ret = 0; 470 break; 471 } 472 #else 473 ret = 0; 474 #endif 475 break; 476 case BIO_CTRL_DGRAM_GET_FALLBACK_MTU: 477 switch (data->peer.sa.sa_family) { 478 case AF_INET: 479 ret = 576 - 20 - 8; 480 break; 481 case AF_INET6: 482 #ifdef IN6_IS_ADDR_V4MAPPED 483 if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr)) 484 ret = 576 - 20 - 8; 485 else 486 #endif 487 ret = 1280 - 40 - 8; 488 break; 489 default: 490 ret = 576 - 20 - 8; 491 break; 492 } 493 break; 494 case BIO_CTRL_DGRAM_GET_MTU: 495 return data->mtu; 496 break; 497 case BIO_CTRL_DGRAM_SET_MTU: 498 data->mtu = num; 499 ret = num; 500 break; 501 case BIO_CTRL_DGRAM_SET_CONNECTED: 502 to = (struct sockaddr *)ptr; 503 504 if (to != NULL) { 505 data->connected = 1; 506 switch (to->sa_family) { 507 case AF_INET: 508 memcpy(&data->peer, to, sizeof(data->peer.sa_in)); 509 break; 510 case AF_INET6: 511 memcpy(&data->peer, to, sizeof(data->peer.sa_in6)); 512 break; 513 default: 514 memcpy(&data->peer, to, sizeof(data->peer.sa)); 515 break; 516 } 517 } else { 518 data->connected = 0; 519 memset(&(data->peer), 0, sizeof(data->peer)); 520 } 521 break; 522 case BIO_CTRL_DGRAM_GET_PEER: 523 switch (data->peer.sa.sa_family) { 524 case AF_INET: 525 ret = sizeof(data->peer.sa_in); 526 break; 527 case AF_INET6: 528 ret = sizeof(data->peer.sa_in6); 529 break; 530 default: 531 ret = sizeof(data->peer.sa); 532 break; 533 } 534 if (num == 0 || num > ret) 535 num = ret; 536 memcpy(ptr, &data->peer, (ret = num)); 537 break; 538 case BIO_CTRL_DGRAM_SET_PEER: 539 to = (struct sockaddr *) ptr; 540 switch (to->sa_family) { 541 case AF_INET: 542 memcpy(&data->peer, to, sizeof(data->peer.sa_in)); 543 break; 544 case AF_INET6: 545 memcpy(&data->peer, to, sizeof(data->peer.sa_in6)); 546 break; 547 default: 548 memcpy(&data->peer, to, sizeof(data->peer.sa)); 549 break; 550 } 551 break; 552 case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: 553 memcpy(&(data->next_timeout), ptr, sizeof(struct timeval)); 554 break; 555 #if defined(SO_RCVTIMEO) 556 case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT: 557 if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr, 558 sizeof(struct timeval)) < 0) { 559 perror("setsockopt"); 560 ret = -1; 561 } 562 break; 563 case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT: 564 { 565 socklen_t sz = sizeof(struct timeval); 566 if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, 567 ptr, &sz) < 0) { 568 perror("getsockopt"); 569 ret = -1; 570 } else 571 ret = sz; 572 } 573 break; 574 #endif 575 #if defined(SO_SNDTIMEO) 576 case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT: 577 if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr, 578 sizeof(struct timeval)) < 0) { 579 perror("setsockopt"); 580 ret = -1; 581 } 582 break; 583 case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT: 584 { 585 socklen_t sz = sizeof(struct timeval); 586 if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, 587 ptr, &sz) < 0) { 588 perror("getsockopt"); 589 ret = -1; 590 } else 591 ret = sz; 592 } 593 break; 594 #endif 595 case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP: 596 /* fall-through */ 597 case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP: 598 if (data->_errno == EAGAIN) { 599 ret = 1; 600 data->_errno = 0; 601 } else 602 ret = 0; 603 break; 604 #ifdef EMSGSIZE 605 case BIO_CTRL_DGRAM_MTU_EXCEEDED: 606 if (data->_errno == EMSGSIZE) { 607 ret = 1; 608 data->_errno = 0; 609 } else 610 ret = 0; 611 break; 612 #endif 613 default: 614 ret = 0; 615 break; 616 } 617 return (ret); 618 } 619 620 static int 621 dgram_puts(BIO *bp, const char *str) 622 { 623 int n, ret; 624 625 n = strlen(str); 626 ret = dgram_write(bp, str, n); 627 return (ret); 628 } 629 630 631 static int 632 BIO_dgram_should_retry(int i) 633 { 634 int err; 635 636 if ((i == 0) || (i == -1)) { 637 err = errno; 638 return (BIO_dgram_non_fatal_error(err)); 639 } 640 return (0); 641 } 642 643 int 644 BIO_dgram_non_fatal_error(int err) 645 { 646 switch (err) { 647 case EINTR: 648 case EAGAIN: 649 case EINPROGRESS: 650 case EALREADY: 651 return (1); 652 default: 653 break; 654 } 655 return (0); 656 } 657 658 #endif 659