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