1 /* $OpenBSD: util.c,v 1.66 2012/07/12 08:51:43 chl Exp $ */ 2 3 /* 4 * Copyright (c) 2000,2001 Markus Friedl. All rights reserved. 5 * Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org> 6 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 7 * 8 * Permission to use, copy, modify, and distribute this software for any 9 * purpose with or without fee is hereby granted, provided that the above 10 * copyright notice and this permission notice appear in all copies. 11 * 12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 13 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 14 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 15 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 16 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 17 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 18 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 19 */ 20 21 /* the mkdir_p() function is based on bin/mkdir/mkdir.c that is covered 22 * by the following license: */ 23 /* 24 * Copyright (c) 1983, 1992, 1993 25 * The Regents of the University of California. All rights reserved. 26 * 27 * Redistribution and use in source and binary forms, with or without 28 * modification, are permitted provided that the following conditions 29 * are met: 30 * 1. Redistributions of source code must retain the above copyright 31 * notice, this list of conditions and the following disclaimer. 32 * 2. Redistributions in binary form must reproduce the above copyright 33 * notice, this list of conditions and the following disclaimer in the 34 * documentation and/or other materials provided with the distribution. 35 * 3. Neither the name of the University nor the names of its contributors 36 * may be used to endorse or promote products derived from this software 37 * without specific prior written permission. 38 * 39 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 40 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 42 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 43 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 44 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 45 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 46 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 47 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 48 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 49 * SUCH DAMAGE. 50 */ 51 52 #include <sys/types.h> 53 #include <sys/param.h> 54 #include <sys/queue.h> 55 #include <sys/tree.h> 56 #include <sys/socket.h> 57 #include <sys/stat.h> 58 #include <sys/resource.h> 59 60 #include <netinet/in.h> 61 #include <arpa/inet.h> 62 63 #include <ctype.h> 64 #include <err.h> 65 #include <errno.h> 66 #include <event.h> 67 #include <fcntl.h> 68 #include <fts.h> 69 #include <imsg.h> 70 #include <libgen.h> 71 #include <netdb.h> 72 #include <pwd.h> 73 #include <stdarg.h> 74 #include <stdio.h> 75 #include <stdlib.h> 76 #include <string.h> 77 #include <time.h> 78 #include <unistd.h> 79 80 #include "smtpd.h" 81 #include "log.h" 82 83 const char *log_in6addr(const struct in6_addr *); 84 const char *log_sockaddr(struct sockaddr *); 85 86 static int temp_inet_net_pton_ipv6(const char *, void *, size_t); 87 88 int 89 bsnprintf(char *str, size_t size, const char *format, ...) 90 { 91 int ret; 92 va_list ap; 93 94 va_start(ap, format); 95 ret = vsnprintf(str, size, format, ap); 96 va_end(ap); 97 if (ret == -1 || ret >= (int)size) 98 return 0; 99 100 return 1; 101 } 102 103 /* 104 * mkdir -p. Based on bin/mkdir/mkdir.c:mkpath() 105 */ 106 int 107 mkdir_p(char *path, mode_t mode) 108 { 109 struct stat sb; 110 char *slash; 111 int done, exists; 112 mode_t dir_mode; 113 114 dir_mode = mode | S_IWUSR | S_IXUSR; 115 116 slash = path; 117 118 for (;;) { 119 slash += strspn(slash, "/"); 120 slash += strcspn(slash, "/"); 121 122 done = (*slash == '\0'); 123 *slash = '\0'; 124 125 /* skip existing path components */ 126 exists = !stat(path, &sb); 127 if (!done && exists && S_ISDIR(sb.st_mode)) { 128 *slash = '/'; 129 continue; 130 } 131 132 if (mkdir(path, done ? mode : dir_mode) == 0) { 133 if (mode > 0777 && chmod(path, mode) < 0) 134 return (-1); 135 } else { 136 if (!exists) { 137 /* Not there */ 138 return (-1); 139 } 140 if (!S_ISDIR(sb.st_mode)) { 141 /* Is there, but isn't a directory */ 142 errno = ENOTDIR; 143 return (-1); 144 } 145 } 146 147 if (done) 148 break; 149 150 *slash = '/'; 151 } 152 153 return (0); 154 } 155 156 int 157 ckdir(const char *path, mode_t mode, uid_t owner, gid_t group, int create) 158 { 159 char mode_str[12]; 160 int ret; 161 struct stat sb; 162 163 if (stat(path, &sb) == -1) { 164 if (errno != ENOENT || create == 0) { 165 warn("stat: %s", path); 166 return (0); 167 } 168 169 /* chmod is deferred to avoid umask effect */ 170 if (mkdir(path, 0) == -1) { 171 warn("mkdir: %s", path); 172 return (0); 173 } 174 175 if (chown(path, owner, group) == -1) { 176 warn("chown: %s", path); 177 return (0); 178 } 179 180 if (chmod(path, mode) == -1) { 181 warn("chmod: %s", path); 182 return (0); 183 } 184 185 if (stat(path, &sb) == -1) { 186 warn("stat: %s", path); 187 return (0); 188 } 189 } 190 191 ret = 1; 192 193 /* check if it's a directory */ 194 if (!S_ISDIR(sb.st_mode)) { 195 ret = 0; 196 warnx("%s is not a directory", path); 197 } 198 199 /* check that it is owned by owner/group */ 200 if (sb.st_uid != owner) { 201 ret = 0; 202 warnx("%s is not owned by uid %d", path, owner); 203 } 204 if (sb.st_gid != group) { 205 ret = 0; 206 warnx("%s is not owned by gid %d", path, group); 207 } 208 209 /* check permission */ 210 if ((sb.st_mode & 07777) != mode) { 211 ret = 0; 212 strmode(mode, mode_str); 213 mode_str[10] = '\0'; 214 warnx("%s must be %s (%o)", path, mode_str + 1, mode); 215 } 216 217 return ret; 218 } 219 220 int 221 rmtree(char *path, int keepdir) 222 { 223 char *path_argv[2]; 224 FTS *fts; 225 FTSENT *e; 226 int ret, depth; 227 228 path_argv[0] = path; 229 path_argv[1] = NULL; 230 ret = 0; 231 depth = 1; 232 233 if ((fts = fts_open(path_argv, FTS_PHYSICAL, NULL)) == NULL) { 234 warn("fts_open: %s", path); 235 return (-1); 236 } 237 238 while ((e = fts_read(fts)) != NULL) { 239 if (e->fts_number) { 240 depth--; 241 if (keepdir && e->fts_number == 1) 242 continue; 243 if (rmdir(e->fts_path) == -1) { 244 warn("rmdir: %s", e->fts_path); 245 ret = -1; 246 } 247 continue; 248 } 249 250 if (S_ISDIR(e->fts_statp->st_mode)) { 251 e->fts_number = depth++; 252 continue; 253 } 254 255 if (unlink(e->fts_path) == -1) { 256 warn("unlink: %s", e->fts_path); 257 ret = -1; 258 } 259 } 260 261 fts_close(fts); 262 263 return (ret); 264 } 265 266 int 267 mvpurge(char *from, char *to) 268 { 269 size_t n; 270 int retry; 271 const char *sep; 272 char buf[MAXPATHLEN]; 273 274 if ((n = strlen(to)) == 0) 275 fatalx("to is empty"); 276 277 sep = (to[n - 1] == '/') ? "" : "/"; 278 retry = 0; 279 280 again: 281 snprintf(buf, sizeof buf, "%s%s%u", to, sep, arc4random()); 282 if (rename(from, buf) == -1) { 283 /* ENOTDIR has actually 2 meanings, and incorrect input 284 * could lead to an infinite loop. Consider that after 285 * 20 tries something is hopelessly wrong. 286 */ 287 if (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR) { 288 if ((retry++) >= 20) 289 return (-1); 290 goto again; 291 } 292 return -1; 293 } 294 295 return 0; 296 } 297 298 299 /* Close file, signifying temporary error condition (if any) to the caller. */ 300 int 301 safe_fclose(FILE *fp) 302 { 303 if (ferror(fp)) { 304 fclose(fp); 305 return 0; 306 } 307 if (fflush(fp)) { 308 fclose(fp); 309 if (errno == ENOSPC) 310 return 0; 311 fatal("safe_fclose: fflush"); 312 } 313 if (fsync(fileno(fp))) 314 fatal("safe_fclose: fsync"); 315 if (fclose(fp)) 316 fatal("safe_fclose: fclose"); 317 318 return 1; 319 } 320 321 int 322 hostname_match(char *hostname, char *pattern) 323 { 324 while (*pattern != '\0' && *hostname != '\0') { 325 if (*pattern == '*') { 326 while (*pattern == '*') 327 pattern++; 328 while (*hostname != '\0' && 329 tolower((int)*hostname) != tolower((int)*pattern)) 330 hostname++; 331 continue; 332 } 333 334 if (tolower((int)*pattern) != tolower((int)*hostname)) 335 return 0; 336 pattern++; 337 hostname++; 338 } 339 340 return (*hostname == '\0' && *pattern == '\0'); 341 } 342 343 int 344 valid_localpart(const char *s) 345 { 346 /* 347 * RFC 5322 defines theses characters as valid: !#$%&'*+-/=?^_`{|}~ 348 * some of them are potentially dangerous, and not so used after all. 349 */ 350 #define IS_ATEXT(c) (isalnum((int)(c)) || strchr("%+-=_", (c))) 351 nextatom: 352 if (! IS_ATEXT(*s) || *s == '\0') 353 return 0; 354 while (*(++s) != '\0') { 355 if (*s == '.') 356 break; 357 if (IS_ATEXT(*s)) 358 continue; 359 return 0; 360 } 361 if (*s == '.') { 362 s++; 363 goto nextatom; 364 } 365 return 1; 366 } 367 368 int 369 valid_domainpart(const char *s) 370 { 371 struct in_addr ina; 372 struct in6_addr ina6; 373 char *c, domain[MAX_DOMAINPART_SIZE]; 374 375 if (*s == '[') { 376 strlcpy(domain, s + 1, sizeof domain); 377 378 c = strchr(domain, (int)']'); 379 if (!c || c[1] != '\0') 380 return 0; 381 382 *c = '\0'; 383 384 if (inet_pton(AF_INET6, domain, &ina6) == 1) 385 return 1; 386 if (inet_pton(AF_INET, domain, &ina) == 1) 387 return 1; 388 389 return 0; 390 } 391 392 nextsub: 393 if (!isalnum((int)*s)) 394 return 0; 395 while (*(++s) != '\0') { 396 if (*s == '.') 397 break; 398 if (isalnum((int)*s) || *s == '-') 399 continue; 400 return 0; 401 } 402 if (s[-1] == '-') 403 return 0; 404 if (*s == '.') { 405 s++; 406 goto nextsub; 407 } 408 return 1; 409 } 410 411 int 412 email_to_mailaddr(struct mailaddr *maddr, char *email) 413 { 414 char *username; 415 char *hostname; 416 417 username = email; 418 hostname = strrchr(username, '@'); 419 420 if (username[0] == '\0') { 421 *maddr->user = '\0'; 422 *maddr->domain = '\0'; 423 return 1; 424 } 425 426 if (hostname == NULL) { 427 if (strcasecmp(username, "postmaster") != 0) 428 return 0; 429 hostname = "localhost"; 430 } else { 431 *hostname++ = '\0'; 432 } 433 434 if (strlcpy(maddr->user, username, sizeof(maddr->user)) 435 >= sizeof(maddr->user)) 436 return 0; 437 438 if (strlcpy(maddr->domain, hostname, sizeof(maddr->domain)) 439 >= sizeof(maddr->domain)) 440 return 0; 441 442 return 1; 443 } 444 445 char * 446 ss_to_text(struct sockaddr_storage *ss) 447 { 448 static char buf[NI_MAXHOST + 5]; 449 char *p; 450 451 buf[0] = '\0'; 452 p = buf; 453 454 if (ss->ss_family == PF_INET) { 455 in_addr_t addr; 456 457 addr = ((struct sockaddr_in *)ss)->sin_addr.s_addr; 458 addr = ntohl(addr); 459 bsnprintf(p, NI_MAXHOST, 460 "%d.%d.%d.%d", 461 (addr >> 24) & 0xff, 462 (addr >> 16) & 0xff, 463 (addr >> 8) & 0xff, 464 addr & 0xff); 465 } 466 467 if (ss->ss_family == PF_INET6) { 468 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)ss; 469 struct in6_addr *in6_addr; 470 471 strlcpy(buf, "IPv6:", sizeof(buf)); 472 p = buf + 5; 473 in6_addr = &in6->sin6_addr; 474 bsnprintf(p, NI_MAXHOST, "%s", log_in6addr(in6_addr)); 475 } 476 477 return (buf); 478 } 479 480 char * 481 time_to_text(time_t when) 482 { 483 struct tm *lt; 484 static char buf[40]; 485 char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 486 char *month[] = {"Jan","Feb","Mar","Apr","May","Jun", 487 "Jul","Aug","Sep","Oct","Nov","Dec"}; 488 489 lt = localtime(&when); 490 if (lt == NULL || when == 0) 491 fatalx("time_to_text: localtime"); 492 493 /* We do not use strftime because it is subject to locale substitution*/ 494 if (! bsnprintf(buf, sizeof(buf), "%s, %d %s %d %02d:%02d:%02d %c%02d%02d (%s)", 495 day[lt->tm_wday], lt->tm_mday, month[lt->tm_mon], 496 lt->tm_year + 1900, 497 lt->tm_hour, lt->tm_min, lt->tm_sec, 498 lt->tm_gmtoff >= 0 ? '+' : '-', 499 abs((int)lt->tm_gmtoff / 3600), 500 abs((int)lt->tm_gmtoff % 3600) / 60, 501 lt->tm_zone)) 502 fatalx("time_to_text: bsnprintf"); 503 504 return buf; 505 } 506 507 int 508 text_to_netaddr(struct netaddr *netaddr, char *s) 509 { 510 struct sockaddr_storage ss; 511 struct sockaddr_in ssin; 512 struct sockaddr_in6 ssin6; 513 int bits; 514 515 if (strncmp("IPv6:", s, 5) == 0) 516 s += 5; 517 518 if (strchr(s, '/') != NULL) { 519 /* dealing with netmask */ 520 521 bzero(&ssin, sizeof(struct sockaddr_in)); 522 bits = inet_net_pton(AF_INET, s, &ssin.sin_addr, 523 sizeof(struct in_addr)); 524 525 if (bits != -1) { 526 ssin.sin_family = AF_INET; 527 memcpy(&ss, &ssin, sizeof(ssin)); 528 ss.ss_len = sizeof(struct sockaddr_in); 529 } 530 else { 531 bzero(&ssin6, sizeof(struct sockaddr_in6)); 532 bits = inet_net_pton(AF_INET6, s, &ssin6.sin6_addr, 533 sizeof(struct in6_addr)); 534 if (bits == -1) { 535 536 /* XXX - until AF_INET6 support gets in base */ 537 if (errno != EAFNOSUPPORT) { 538 log_warn("inet_net_pton"); 539 return 0; 540 } 541 bits = temp_inet_net_pton_ipv6(s, 542 &ssin6.sin6_addr, 543 sizeof(struct in6_addr)); 544 } 545 if (bits == -1) { 546 log_warn("inet_net_pton"); 547 return 0; 548 } 549 ssin6.sin6_family = AF_INET6; 550 memcpy(&ss, &ssin6, sizeof(ssin6)); 551 ss.ss_len = sizeof(struct sockaddr_in6); 552 } 553 } 554 else { 555 /* IP address ? */ 556 if (inet_pton(AF_INET, s, &ssin.sin_addr) == 1) { 557 ssin.sin_family = AF_INET; 558 bits = 32; 559 memcpy(&ss, &ssin, sizeof(ssin)); 560 ss.ss_len = sizeof(struct sockaddr_in); 561 } 562 else if (inet_pton(AF_INET6, s, &ssin6.sin6_addr) == 1) { 563 ssin6.sin6_family = AF_INET6; 564 bits = 128; 565 memcpy(&ss, &ssin6, sizeof(ssin6)); 566 ss.ss_len = sizeof(struct sockaddr_in6); 567 } 568 else return 0; 569 } 570 571 netaddr->ss = ss; 572 netaddr->bits = bits; 573 return 1; 574 } 575 576 int 577 text_to_relayhost(struct relayhost *relay, char *s) 578 { 579 u_int32_t i; 580 struct schema { 581 char *name; 582 u_int8_t flags; 583 } schemas [] = { 584 { "smtp://", 0 }, 585 { "smtps://", F_SMTPS }, 586 { "tls://", F_STARTTLS }, 587 { "smtps+auth://", F_SMTPS|F_AUTH }, 588 { "tls+auth://", F_STARTTLS|F_AUTH }, 589 { "ssl://", F_SMTPS|F_STARTTLS }, 590 { "ssl+auth://", F_SMTPS|F_STARTTLS|F_AUTH } 591 }; 592 const char *errstr = NULL; 593 char *p; 594 char *sep; 595 int len; 596 597 for (i = 0; i < nitems(schemas); ++i) 598 if (strncasecmp(schemas[i].name, s, strlen(schemas[i].name)) == 0) 599 break; 600 601 if (i == nitems(schemas)) { 602 /* there is a schema, but it's not recognized */ 603 if (strstr(s, "://")) 604 return 0; 605 606 /* no schema, default to smtp:// */ 607 i = 0; 608 p = s; 609 } 610 else 611 p = s + strlen(schemas[i].name); 612 613 relay->flags = schemas[i].flags; 614 615 if ((sep = strrchr(p, ':')) != NULL) { 616 relay->port = strtonum(sep+1, 1, 0xffff, &errstr); 617 if (errstr) 618 return 0; 619 len = sep - p; 620 } 621 else 622 len = strlen(p); 623 624 if (strlcpy(relay->hostname, p, sizeof (relay->hostname)) 625 >= sizeof (relay->hostname)) 626 return 0; 627 628 relay->hostname[len] = 0; 629 630 return 1; 631 } 632 633 /* 634 * Check file for security. Based on usr.bin/ssh/auth.c. 635 */ 636 int 637 secure_file(int fd, char *path, char *userdir, uid_t uid, int mayread) 638 { 639 char buf[MAXPATHLEN]; 640 char homedir[MAXPATHLEN]; 641 struct stat st; 642 char *cp; 643 644 if (realpath(path, buf) == NULL) 645 return 0; 646 647 if (realpath(userdir, homedir) == NULL) 648 homedir[0] = '\0'; 649 650 /* Check the open file to avoid races. */ 651 if (fstat(fd, &st) < 0 || 652 !S_ISREG(st.st_mode) || 653 (st.st_uid != 0 && st.st_uid != uid) || 654 (st.st_mode & (mayread ? 022 : 066)) != 0) 655 return 0; 656 657 /* For each component of the canonical path, walking upwards. */ 658 for (;;) { 659 if ((cp = dirname(buf)) == NULL) 660 return 0; 661 strlcpy(buf, cp, sizeof(buf)); 662 663 if (stat(buf, &st) < 0 || 664 (st.st_uid != 0 && st.st_uid != uid) || 665 (st.st_mode & 022) != 0) 666 return 0; 667 668 /* We can stop checking after reaching homedir level. */ 669 if (strcmp(homedir, buf) == 0) 670 break; 671 672 /* 673 * dirname should always complete with a "/" path, 674 * but we can be paranoid and check for "." too 675 */ 676 if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) 677 break; 678 } 679 680 return 1; 681 } 682 683 void 684 addargs(arglist *args, char *fmt, ...) 685 { 686 va_list ap; 687 char *cp; 688 u_int nalloc; 689 int r; 690 691 va_start(ap, fmt); 692 r = vasprintf(&cp, fmt, ap); 693 va_end(ap); 694 if (r == -1) 695 fatal("addargs: argument too long"); 696 697 nalloc = args->nalloc; 698 if (args->list == NULL) { 699 nalloc = 32; 700 args->num = 0; 701 } else if (args->num+2 >= nalloc) 702 nalloc *= 2; 703 704 if (SIZE_T_MAX / nalloc < sizeof(char *)) 705 fatalx("addargs: nalloc * size > SIZE_T_MAX"); 706 args->list = realloc(args->list, nalloc * sizeof(char *)); 707 if (args->list == NULL) 708 fatal("addargs: realloc"); 709 args->nalloc = nalloc; 710 args->list[args->num++] = cp; 711 args->list[args->num] = NULL; 712 } 713 714 void 715 lowercase(char *buf, char *s, size_t len) 716 { 717 if (len == 0) 718 fatalx("lowercase: len == 0"); 719 720 if (strlcpy(buf, s, len) >= len) 721 fatalx("lowercase: truncation"); 722 723 while (*buf != '\0') { 724 *buf = tolower((int)*buf); 725 buf++; 726 } 727 } 728 729 void 730 sa_set_port(struct sockaddr *sa, int port) 731 { 732 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 733 struct addrinfo hints, *res; 734 int error; 735 736 error = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST); 737 if (error) 738 fatalx("sa_set_port: getnameinfo failed"); 739 740 memset(&hints, 0, sizeof(hints)); 741 hints.ai_family = PF_UNSPEC; 742 hints.ai_socktype = SOCK_STREAM; 743 hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; 744 745 snprintf(sbuf, sizeof(sbuf), "%d", port); 746 747 error = getaddrinfo(hbuf, sbuf, &hints, &res); 748 if (error) 749 fatalx("sa_set_port: getaddrinfo failed"); 750 751 memcpy(sa, res->ai_addr, res->ai_addrlen); 752 freeaddrinfo(res); 753 } 754 755 u_int64_t 756 generate_uid(void) 757 { 758 static u_int32_t id = 0; 759 760 return ((uint64_t)(id++) << 32 | arc4random()); 761 } 762 763 void 764 fdlimit(double percent) 765 { 766 struct rlimit rl; 767 768 if (percent < 0 || percent > 1) 769 fatalx("fdlimit: parameter out of range"); 770 if (getrlimit(RLIMIT_NOFILE, &rl) == -1) 771 fatal("fdlimit: getrlimit"); 772 rl.rlim_cur = percent * rl.rlim_max; 773 if (setrlimit(RLIMIT_NOFILE, &rl) == -1) 774 fatal("fdlimit: setrlimit"); 775 } 776 777 int 778 availdesc(void) 779 { 780 int avail; 781 782 avail = getdtablesize(); 783 avail -= 3; /* stdin, stdout, stderr */ 784 avail -= PROC_COUNT; /* imsg channels */ 785 avail -= 5; /* safety buffer */ 786 787 return (avail); 788 } 789 790 void 791 session_socket_blockmode(int fd, enum blockmodes bm) 792 { 793 int flags; 794 795 if ((flags = fcntl(fd, F_GETFL, 0)) == -1) 796 fatal("fcntl F_GETFL"); 797 798 if (bm == BM_NONBLOCK) 799 flags |= O_NONBLOCK; 800 else 801 flags &= ~O_NONBLOCK; 802 803 if ((flags = fcntl(fd, F_SETFL, flags)) == -1) 804 fatal("fcntl F_SETFL"); 805 } 806 807 void 808 session_socket_no_linger(int fd) 809 { 810 struct linger lng; 811 812 bzero(&lng, sizeof(lng)); 813 if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lng, sizeof(lng)) == -1) 814 fatal("session_socket_no_linger"); 815 } 816 817 int 818 session_socket_error(int fd) 819 { 820 int error; 821 socklen_t len; 822 823 len = sizeof(error); 824 if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) 825 fatal("session_socket_error: getsockopt"); 826 827 return (error); 828 } 829 830 const char * 831 log_in6addr(const struct in6_addr *addr) 832 { 833 struct sockaddr_in6 sa_in6; 834 u_int16_t tmp16; 835 836 bzero(&sa_in6, sizeof(sa_in6)); 837 sa_in6.sin6_len = sizeof(sa_in6); 838 sa_in6.sin6_family = AF_INET6; 839 memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr)); 840 841 /* XXX thanks, KAME, for this ugliness... adopted from route/show.c */ 842 if (IN6_IS_ADDR_LINKLOCAL(&sa_in6.sin6_addr) || 843 IN6_IS_ADDR_MC_LINKLOCAL(&sa_in6.sin6_addr)) { 844 memcpy(&tmp16, &sa_in6.sin6_addr.s6_addr[2], sizeof(tmp16)); 845 sa_in6.sin6_scope_id = ntohs(tmp16); 846 sa_in6.sin6_addr.s6_addr[2] = 0; 847 sa_in6.sin6_addr.s6_addr[3] = 0; 848 } 849 850 return (log_sockaddr((struct sockaddr *)&sa_in6)); 851 } 852 853 const char * 854 log_sockaddr(struct sockaddr *sa) 855 { 856 static char buf[NI_MAXHOST]; 857 858 if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, 859 NI_NUMERICHOST)) 860 return ("(unknown)"); 861 else 862 return (buf); 863 } 864 865 u_int32_t 866 evpid_to_msgid(u_int64_t evpid) 867 { 868 return (evpid >> 32); 869 } 870 871 u_int64_t 872 msgid_to_evpid(u_int32_t msgid) 873 { 874 return ((u_int64_t)msgid << 32); 875 } 876 877 const char * 878 parse_smtp_response(char *line, size_t len, char **msg, int *cont) 879 { 880 size_t i; 881 882 if (len >= SMTP_LINE_MAX) 883 return "line too long"; 884 885 if (len > 3) { 886 if (msg) 887 *msg = line + 4; 888 if (cont) 889 *cont = (line[3] == '-'); 890 } else if (len == 3) { 891 if (msg) 892 *msg = line + 3; 893 if (cont) 894 *cont = 0; 895 } else 896 return "line too short"; 897 898 /* validate reply code */ 899 if (line[0] < '2' || line[0] > '5' || !isdigit(line[1]) || 900 !isdigit(line[2])) 901 return "reply code out of range"; 902 903 /* validate reply message */ 904 for (i = 0; i < len; i++) 905 if (!isprint(line[i])) 906 return "non-printable character in reply"; 907 908 return NULL; 909 } 910 911 static int 912 temp_inet_net_pton_ipv6(const char *src, void *dst, size_t size) 913 { 914 int ret; 915 int bits; 916 char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")]; 917 char *sep; 918 const char *errstr; 919 920 if (strlcpy(buf, src, sizeof buf) >= sizeof buf) { 921 errno = EMSGSIZE; 922 return (-1); 923 } 924 925 sep = strchr(buf, '/'); 926 if (sep != NULL) 927 *sep++ = '\0'; 928 929 ret = inet_pton(AF_INET6, buf, dst); 930 if (ret != 1) 931 return (-1); 932 933 if (sep == NULL) 934 return 128; 935 936 bits = strtonum(sep, 0, 128, &errstr); 937 if (errstr) 938 return (-1); 939 940 return bits; 941 } 942