1 /* $OpenBSD: bss_dgram.c,v 1.43 2022/01/07 09:02:17 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 #include "bio_local.h" 76 77 #ifndef OPENSSL_NO_DGRAM 78 79 80 static int dgram_write(BIO *h, const char *buf, int num); 81 static int dgram_read(BIO *h, char *buf, int size); 82 static int dgram_puts(BIO *h, const char *str); 83 static long dgram_ctrl(BIO *h, int cmd, long arg1, void *arg2); 84 static int dgram_new(BIO *h); 85 static int dgram_free(BIO *data); 86 static int dgram_clear(BIO *bio); 87 88 89 static int BIO_dgram_should_retry(int s); 90 91 static const BIO_METHOD methods_dgramp = { 92 .type = BIO_TYPE_DGRAM, 93 .name = "datagram socket", 94 .bwrite = dgram_write, 95 .bread = dgram_read, 96 .bputs = dgram_puts, 97 .ctrl = dgram_ctrl, 98 .create = dgram_new, 99 .destroy = dgram_free 100 }; 101 102 103 typedef struct bio_dgram_data_st { 104 union { 105 struct sockaddr sa; 106 struct sockaddr_in sa_in; 107 struct sockaddr_in6 sa_in6; 108 } peer; 109 unsigned int connected; 110 unsigned int _errno; 111 unsigned int mtu; 112 struct timeval next_timeout; 113 struct timeval socket_timeout; 114 } bio_dgram_data; 115 116 117 const BIO_METHOD * 118 BIO_s_datagram(void) 119 { 120 return (&methods_dgramp); 121 } 122 123 BIO * 124 BIO_new_dgram(int fd, int close_flag) 125 { 126 BIO *ret; 127 128 ret = BIO_new(BIO_s_datagram()); 129 if (ret == NULL) 130 return (NULL); 131 BIO_set_fd(ret, fd, close_flag); 132 return (ret); 133 } 134 135 static int 136 dgram_new(BIO *bi) 137 { 138 bio_dgram_data *data = NULL; 139 140 bi->init = 0; 141 bi->num = 0; 142 data = calloc(1, sizeof(bio_dgram_data)); 143 if (data == NULL) 144 return 0; 145 bi->ptr = data; 146 147 bi->flags = 0; 148 return (1); 149 } 150 151 static int 152 dgram_free(BIO *a) 153 { 154 bio_dgram_data *data; 155 156 if (a == NULL) 157 return (0); 158 if (!dgram_clear(a)) 159 return 0; 160 161 data = (bio_dgram_data *)a->ptr; 162 free(data); 163 164 return (1); 165 } 166 167 static int 168 dgram_clear(BIO *a) 169 { 170 if (a == NULL) 171 return (0); 172 if (a->shutdown) { 173 if (a->init) { 174 shutdown(a->num, SHUT_RDWR); 175 close(a->num); 176 } 177 a->init = 0; 178 a->flags = 0; 179 } 180 return (1); 181 } 182 183 static void 184 dgram_adjust_rcv_timeout(BIO *b) 185 { 186 #if defined(SO_RCVTIMEO) 187 bio_dgram_data *data = (bio_dgram_data *)b->ptr; 188 189 /* Is a timer active? */ 190 if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) { 191 struct timeval timenow, timeleft; 192 193 /* Read current socket timeout */ 194 socklen_t sz = sizeof(data->socket_timeout); 195 if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, 196 &(data->socket_timeout), &sz) < 0) { 197 perror("getsockopt"); 198 } 199 200 /* Get current time */ 201 gettimeofday(&timenow, NULL); 202 203 /* Calculate time left until timer expires */ 204 memcpy(&timeleft, &(data->next_timeout), sizeof(struct timeval)); 205 timeleft.tv_sec -= timenow.tv_sec; 206 timeleft.tv_usec -= timenow.tv_usec; 207 if (timeleft.tv_usec < 0) { 208 timeleft.tv_sec--; 209 timeleft.tv_usec += 1000000; 210 } 211 212 if (timeleft.tv_sec < 0) { 213 timeleft.tv_sec = 0; 214 timeleft.tv_usec = 1; 215 } 216 217 /* Adjust socket timeout if next handhake message timer 218 * will expire earlier. 219 */ 220 if ((data->socket_timeout.tv_sec == 0 && 221 data->socket_timeout.tv_usec == 0) || 222 (data->socket_timeout.tv_sec > timeleft.tv_sec) || 223 (data->socket_timeout.tv_sec == timeleft.tv_sec && 224 data->socket_timeout.tv_usec >= timeleft.tv_usec)) { 225 if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, 226 &timeleft, sizeof(struct timeval)) < 0) { 227 perror("setsockopt"); 228 } 229 } 230 } 231 #endif 232 } 233 234 static void 235 dgram_reset_rcv_timeout(BIO *b) 236 { 237 #if defined(SO_RCVTIMEO) 238 bio_dgram_data *data = (bio_dgram_data *)b->ptr; 239 240 /* Is a timer active? */ 241 if (data->next_timeout.tv_sec > 0 || data->next_timeout.tv_usec > 0) { 242 if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, 243 &(data->socket_timeout), sizeof(struct timeval)) < 0) { 244 perror("setsockopt"); 245 } 246 } 247 #endif 248 } 249 250 static int 251 dgram_read(BIO *b, char *out, int outl) 252 { 253 int ret = 0; 254 bio_dgram_data *data = (bio_dgram_data *)b->ptr; 255 256 struct { 257 socklen_t len; 258 union { 259 struct sockaddr sa; 260 struct sockaddr_in sa_in; 261 struct sockaddr_in6 sa_in6; 262 } peer; 263 } sa; 264 265 sa.len = sizeof(sa.peer); 266 267 if (out != NULL) { 268 errno = 0; 269 memset(&sa.peer, 0, sizeof(sa.peer)); 270 dgram_adjust_rcv_timeout(b); 271 ret = recvfrom(b->num, out, outl, 0, &sa.peer.sa, &sa.len); 272 273 if (! data->connected && ret >= 0) 274 BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &sa.peer); 275 276 BIO_clear_retry_flags(b); 277 if (ret < 0) { 278 if (BIO_dgram_should_retry(ret)) { 279 BIO_set_retry_read(b); 280 data->_errno = errno; 281 } 282 } 283 284 dgram_reset_rcv_timeout(b); 285 } 286 return (ret); 287 } 288 289 static int 290 dgram_write(BIO *b, const char *in, int inl) 291 { 292 int ret; 293 bio_dgram_data *data = (bio_dgram_data *)b->ptr; 294 errno = 0; 295 296 if (data->connected) 297 ret = write(b->num, in, inl); 298 else { 299 int peerlen = sizeof(data->peer); 300 301 if (data->peer.sa.sa_family == AF_INET) 302 peerlen = sizeof(data->peer.sa_in); 303 else if (data->peer.sa.sa_family == AF_INET6) 304 peerlen = sizeof(data->peer.sa_in6); 305 ret = sendto(b->num, in, inl, 0, &data->peer.sa, peerlen); 306 } 307 308 BIO_clear_retry_flags(b); 309 if (ret <= 0) { 310 if (BIO_dgram_should_retry(ret)) { 311 BIO_set_retry_write(b); 312 313 data->_errno = errno; 314 /* 315 * higher layers are responsible for querying MTU, 316 * if necessary 317 */ 318 } 319 } 320 return (ret); 321 } 322 323 static long 324 dgram_ctrl(BIO *b, int cmd, long num, void *ptr) 325 { 326 long ret = 1; 327 int *ip; 328 struct sockaddr *to = NULL; 329 bio_dgram_data *data = NULL; 330 #if (defined(IP_MTU_DISCOVER) || defined(IP_MTU)) 331 int sockopt_val = 0; 332 socklen_t sockopt_len; /* assume that system supporting IP_MTU is 333 * modern enough to define socklen_t */ 334 socklen_t addr_len; 335 union { 336 struct sockaddr sa; 337 struct sockaddr_in s4; 338 struct sockaddr_in6 s6; 339 } addr; 340 #endif 341 342 data = (bio_dgram_data *)b->ptr; 343 344 switch (cmd) { 345 case BIO_CTRL_RESET: 346 num = 0; 347 case BIO_C_FILE_SEEK: 348 ret = 0; 349 break; 350 case BIO_C_FILE_TELL: 351 case BIO_CTRL_INFO: 352 ret = 0; 353 break; 354 case BIO_C_SET_FD: 355 dgram_clear(b); 356 b->num= *((int *)ptr); 357 b->shutdown = (int)num; 358 b->init = 1; 359 break; 360 case BIO_C_GET_FD: 361 if (b->init) { 362 ip = (int *)ptr; 363 if (ip != NULL) 364 *ip = b->num; 365 ret = b->num; 366 } else 367 ret = -1; 368 break; 369 case BIO_CTRL_GET_CLOSE: 370 ret = b->shutdown; 371 break; 372 case BIO_CTRL_SET_CLOSE: 373 b->shutdown = (int)num; 374 break; 375 case BIO_CTRL_PENDING: 376 case BIO_CTRL_WPENDING: 377 ret = 0; 378 break; 379 case BIO_CTRL_DUP: 380 case BIO_CTRL_FLUSH: 381 ret = 1; 382 break; 383 case BIO_CTRL_DGRAM_CONNECT: 384 to = (struct sockaddr *)ptr; 385 switch (to->sa_family) { 386 case AF_INET: 387 memcpy(&data->peer, to, sizeof(data->peer.sa_in)); 388 break; 389 case AF_INET6: 390 memcpy(&data->peer, to, sizeof(data->peer.sa_in6)); 391 break; 392 default: 393 memcpy(&data->peer, to, sizeof(data->peer.sa)); 394 break; 395 } 396 break; 397 /* (Linux)kernel sets DF bit on outgoing IP packets */ 398 case BIO_CTRL_DGRAM_MTU_DISCOVER: 399 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DO) 400 addr_len = (socklen_t)sizeof(addr); 401 memset((void *)&addr, 0, sizeof(addr)); 402 if (getsockname(b->num, &addr.sa, &addr_len) < 0) { 403 ret = 0; 404 break; 405 } 406 switch (addr.sa.sa_family) { 407 case AF_INET: 408 sockopt_val = IP_PMTUDISC_DO; 409 ret = setsockopt(b->num, IPPROTO_IP, IP_MTU_DISCOVER, 410 &sockopt_val, sizeof(sockopt_val)); 411 if (ret < 0) 412 perror("setsockopt"); 413 break; 414 #if defined(IPV6_MTU_DISCOVER) && defined(IPV6_PMTUDISC_DO) 415 case AF_INET6: 416 sockopt_val = IPV6_PMTUDISC_DO; 417 ret = setsockopt(b->num, IPPROTO_IPV6, 418 IPV6_MTU_DISCOVER, &sockopt_val, 419 sizeof(sockopt_val)); 420 if (ret < 0) 421 perror("setsockopt"); 422 break; 423 #endif 424 default: 425 ret = -1; 426 break; 427 } 428 #else 429 ret = -1; 430 #endif 431 break; 432 case BIO_CTRL_DGRAM_QUERY_MTU: 433 #if defined(IP_MTU) 434 addr_len = (socklen_t)sizeof(addr); 435 memset((void *)&addr, 0, sizeof(addr)); 436 if (getsockname(b->num, &addr.sa, &addr_len) < 0) { 437 ret = 0; 438 break; 439 } 440 sockopt_len = sizeof(sockopt_val); 441 switch (addr.sa.sa_family) { 442 case AF_INET: 443 ret = getsockopt(b->num, IPPROTO_IP, IP_MTU, 444 &sockopt_val, &sockopt_len); 445 if (ret < 0 || sockopt_val < 0) { 446 ret = 0; 447 } else { 448 /* we assume that the transport protocol is UDP and no 449 * IP options are used. 450 */ 451 data->mtu = sockopt_val - 8 - 20; 452 ret = data->mtu; 453 } 454 break; 455 #if defined(IPV6_MTU) 456 case AF_INET6: 457 ret = getsockopt(b->num, IPPROTO_IPV6, IPV6_MTU, 458 &sockopt_val, &sockopt_len); 459 if (ret < 0 || sockopt_val < 0) { 460 ret = 0; 461 } else { 462 /* we assume that the transport protocol is UDP and no 463 * IPV6 options are used. 464 */ 465 data->mtu = sockopt_val - 8 - 40; 466 ret = data->mtu; 467 } 468 break; 469 #endif 470 default: 471 ret = 0; 472 break; 473 } 474 #else 475 ret = 0; 476 #endif 477 break; 478 case BIO_CTRL_DGRAM_GET_FALLBACK_MTU: 479 switch (data->peer.sa.sa_family) { 480 case AF_INET: 481 ret = 576 - 20 - 8; 482 break; 483 case AF_INET6: 484 #ifdef IN6_IS_ADDR_V4MAPPED 485 if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr)) 486 ret = 576 - 20 - 8; 487 else 488 #endif 489 ret = 1280 - 40 - 8; 490 break; 491 default: 492 ret = 576 - 20 - 8; 493 break; 494 } 495 break; 496 case BIO_CTRL_DGRAM_GET_MTU: 497 return data->mtu; 498 break; 499 case BIO_CTRL_DGRAM_SET_MTU: 500 data->mtu = num; 501 ret = num; 502 break; 503 case BIO_CTRL_DGRAM_SET_CONNECTED: 504 to = (struct sockaddr *)ptr; 505 506 if (to != NULL) { 507 data->connected = 1; 508 switch (to->sa_family) { 509 case AF_INET: 510 memcpy(&data->peer, to, sizeof(data->peer.sa_in)); 511 break; 512 case AF_INET6: 513 memcpy(&data->peer, to, sizeof(data->peer.sa_in6)); 514 break; 515 default: 516 memcpy(&data->peer, to, sizeof(data->peer.sa)); 517 break; 518 } 519 } else { 520 data->connected = 0; 521 memset(&(data->peer), 0, sizeof(data->peer)); 522 } 523 break; 524 case BIO_CTRL_DGRAM_GET_PEER: 525 switch (data->peer.sa.sa_family) { 526 case AF_INET: 527 ret = sizeof(data->peer.sa_in); 528 break; 529 case AF_INET6: 530 ret = sizeof(data->peer.sa_in6); 531 break; 532 default: 533 ret = sizeof(data->peer.sa); 534 break; 535 } 536 if (num == 0 || num > ret) 537 num = ret; 538 memcpy(ptr, &data->peer, (ret = num)); 539 break; 540 case BIO_CTRL_DGRAM_SET_PEER: 541 to = (struct sockaddr *) ptr; 542 switch (to->sa_family) { 543 case AF_INET: 544 memcpy(&data->peer, to, sizeof(data->peer.sa_in)); 545 break; 546 case AF_INET6: 547 memcpy(&data->peer, to, sizeof(data->peer.sa_in6)); 548 break; 549 default: 550 memcpy(&data->peer, to, sizeof(data->peer.sa)); 551 break; 552 } 553 break; 554 case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT: 555 memcpy(&(data->next_timeout), ptr, sizeof(struct timeval)); 556 break; 557 #if defined(SO_RCVTIMEO) 558 case BIO_CTRL_DGRAM_SET_RECV_TIMEOUT: 559 if (setsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, ptr, 560 sizeof(struct timeval)) < 0) { 561 perror("setsockopt"); 562 ret = -1; 563 } 564 break; 565 case BIO_CTRL_DGRAM_GET_RECV_TIMEOUT: 566 { 567 socklen_t sz = sizeof(struct timeval); 568 if (getsockopt(b->num, SOL_SOCKET, SO_RCVTIMEO, 569 ptr, &sz) < 0) { 570 perror("getsockopt"); 571 ret = -1; 572 } else 573 ret = sz; 574 } 575 break; 576 #endif 577 #if defined(SO_SNDTIMEO) 578 case BIO_CTRL_DGRAM_SET_SEND_TIMEOUT: 579 if (setsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, ptr, 580 sizeof(struct timeval)) < 0) { 581 perror("setsockopt"); 582 ret = -1; 583 } 584 break; 585 case BIO_CTRL_DGRAM_GET_SEND_TIMEOUT: 586 { 587 socklen_t sz = sizeof(struct timeval); 588 if (getsockopt(b->num, SOL_SOCKET, SO_SNDTIMEO, 589 ptr, &sz) < 0) { 590 perror("getsockopt"); 591 ret = -1; 592 } else 593 ret = sz; 594 } 595 break; 596 #endif 597 case BIO_CTRL_DGRAM_GET_SEND_TIMER_EXP: 598 /* fall-through */ 599 case BIO_CTRL_DGRAM_GET_RECV_TIMER_EXP: 600 if (data->_errno == EAGAIN) { 601 ret = 1; 602 data->_errno = 0; 603 } else 604 ret = 0; 605 break; 606 #ifdef EMSGSIZE 607 case BIO_CTRL_DGRAM_MTU_EXCEEDED: 608 if (data->_errno == EMSGSIZE) { 609 ret = 1; 610 data->_errno = 0; 611 } else 612 ret = 0; 613 break; 614 #endif 615 default: 616 ret = 0; 617 break; 618 } 619 return (ret); 620 } 621 622 static int 623 dgram_puts(BIO *bp, const char *str) 624 { 625 int n, ret; 626 627 n = strlen(str); 628 ret = dgram_write(bp, str, n); 629 return (ret); 630 } 631 632 633 static int 634 BIO_dgram_should_retry(int i) 635 { 636 int err; 637 638 if ((i == 0) || (i == -1)) { 639 err = errno; 640 return (BIO_dgram_non_fatal_error(err)); 641 } 642 return (0); 643 } 644 645 int 646 BIO_dgram_non_fatal_error(int err) 647 { 648 switch (err) { 649 case EINTR: 650 case EAGAIN: 651 case EINPROGRESS: 652 case EALREADY: 653 return (1); 654 default: 655 break; 656 } 657 return (0); 658 } 659 660 #endif 661