1 /* $OpenBSD: to.c,v 1.46 2021/03/05 12:37:32 eric Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> 5 * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 6 * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 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 #include <sys/types.h> 22 #include <sys/queue.h> 23 #include <sys/tree.h> 24 #include <sys/socket.h> 25 #include <sys/stat.h> 26 #include <sys/resource.h> 27 28 #include <netinet/in.h> 29 #include <arpa/inet.h> 30 31 #include <ctype.h> 32 #include <err.h> 33 #include <errno.h> 34 #include <event.h> 35 #include <fcntl.h> 36 #include <imsg.h> 37 #include <limits.h> 38 #include <inttypes.h> 39 #include <netdb.h> 40 #include <pwd.h> 41 #include <stdarg.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <time.h> 46 #if IO_TLS 47 #include <tls.h> 48 #endif 49 #include <unistd.h> 50 51 #include "smtpd.h" 52 #include "log.h" 53 54 static int alias_is_filter(struct expandnode *, const char *, size_t); 55 static int alias_is_username(struct expandnode *, const char *, size_t); 56 static int alias_is_address(struct expandnode *, const char *, size_t); 57 static int alias_is_filename(struct expandnode *, const char *, size_t); 58 static int alias_is_include(struct expandnode *, const char *, size_t); 59 static int alias_is_error(struct expandnode *, const char *, size_t); 60 61 const char * 62 sockaddr_to_text(const struct sockaddr *sa) 63 { 64 static char buf[NI_MAXHOST]; 65 66 if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, 67 NI_NUMERICHOST)) 68 return ("(unknown)"); 69 else 70 return (buf); 71 } 72 73 int 74 text_to_mailaddr(struct mailaddr *maddr, const char *email) 75 { 76 char *username; 77 char *hostname; 78 char buffer[LINE_MAX]; 79 80 if (strlcpy(buffer, email, sizeof buffer) >= sizeof buffer) 81 return 0; 82 83 memset(maddr, 0, sizeof *maddr); 84 85 username = buffer; 86 hostname = strrchr(username, '@'); 87 88 if (hostname == NULL) { 89 if (strlcpy(maddr->user, username, sizeof maddr->user) 90 >= sizeof maddr->user) 91 return 0; 92 } 93 else if (username == hostname) { 94 *hostname++ = '\0'; 95 if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) 96 >= sizeof maddr->domain) 97 return 0; 98 } 99 else { 100 *hostname++ = '\0'; 101 if (strlcpy(maddr->user, username, sizeof maddr->user) 102 >= sizeof maddr->user) 103 return 0; 104 if (strlcpy(maddr->domain, hostname, sizeof maddr->domain) 105 >= sizeof maddr->domain) 106 return 0; 107 } 108 109 return 1; 110 } 111 112 const char * 113 mailaddr_to_text(const struct mailaddr *maddr) 114 { 115 static char buffer[LINE_MAX]; 116 117 (void)strlcpy(buffer, maddr->user, sizeof buffer); 118 (void)strlcat(buffer, "@", sizeof buffer); 119 if (strlcat(buffer, maddr->domain, sizeof buffer) >= sizeof buffer) 120 return NULL; 121 122 return buffer; 123 } 124 125 126 const char * 127 sa_to_text(const struct sockaddr *sa) 128 { 129 static char buf[NI_MAXHOST + 5]; 130 char *p; 131 132 buf[0] = '\0'; 133 p = buf; 134 135 if (sa->sa_family == AF_LOCAL) 136 (void)strlcpy(buf, "local", sizeof buf); 137 else if (sa->sa_family == AF_INET) { 138 in_addr_t addr; 139 140 addr = ((const struct sockaddr_in *)sa)->sin_addr.s_addr; 141 addr = ntohl(addr); 142 (void)bsnprintf(p, NI_MAXHOST, "%d.%d.%d.%d", 143 (addr >> 24) & 0xff, (addr >> 16) & 0xff, 144 (addr >> 8) & 0xff, addr & 0xff); 145 } 146 else if (sa->sa_family == AF_INET6) { 147 (void)bsnprintf(p, NI_MAXHOST, "[%s]", sockaddr_to_text(sa)); 148 } 149 150 return (buf); 151 } 152 153 const char * 154 ss_to_text(const struct sockaddr_storage *ss) 155 { 156 return (sa_to_text((const struct sockaddr*)ss)); 157 } 158 159 const char * 160 time_to_text(time_t when) 161 { 162 struct tm *lt; 163 static char buf[40]; 164 char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; 165 char *month[] = {"Jan","Feb","Mar","Apr","May","Jun", 166 "Jul","Aug","Sep","Oct","Nov","Dec"}; 167 char *tz; 168 long offset; 169 170 lt = localtime(&when); 171 if (lt == NULL || when == 0) 172 fatalx("time_to_text: localtime"); 173 174 offset = lt->tm_gmtoff; 175 tz = lt->tm_zone; 176 177 /* We do not use strftime because it is subject to locale substitution*/ 178 if (!bsnprintf(buf, sizeof(buf), 179 "%s, %d %s %d %02d:%02d:%02d %c%02d%02d (%s)", 180 day[lt->tm_wday], lt->tm_mday, month[lt->tm_mon], 181 lt->tm_year + 1900, 182 lt->tm_hour, lt->tm_min, lt->tm_sec, 183 offset >= 0 ? '+' : '-', 184 abs((int)offset / 3600), 185 abs((int)offset % 3600) / 60, 186 tz)) 187 fatalx("time_to_text: bsnprintf"); 188 189 return buf; 190 } 191 192 const char * 193 duration_to_text(time_t t) 194 { 195 static char dst[64]; 196 char buf[64]; 197 int h, m, s; 198 long long d; 199 200 if (t == 0) { 201 (void)strlcpy(dst, "0s", sizeof dst); 202 return (dst); 203 } 204 205 dst[0] = '\0'; 206 if (t < 0) { 207 (void)strlcpy(dst, "-", sizeof dst); 208 t = -t; 209 } 210 211 s = t % 60; 212 t /= 60; 213 m = t % 60; 214 t /= 60; 215 h = t % 24; 216 d = t / 24; 217 218 if (d) { 219 (void)snprintf(buf, sizeof buf, "%lldd", d); 220 (void)strlcat(dst, buf, sizeof dst); 221 } 222 if (h) { 223 (void)snprintf(buf, sizeof buf, "%dh", h); 224 (void)strlcat(dst, buf, sizeof dst); 225 } 226 if (m) { 227 (void)snprintf(buf, sizeof buf, "%dm", m); 228 (void)strlcat(dst, buf, sizeof dst); 229 } 230 if (s) { 231 (void)snprintf(buf, sizeof buf, "%ds", s); 232 (void)strlcat(dst, buf, sizeof dst); 233 } 234 235 return (dst); 236 } 237 238 int 239 text_to_netaddr(struct netaddr *netaddr, const char *s) 240 { 241 struct sockaddr_storage ss; 242 struct sockaddr_in ssin; 243 struct sockaddr_in6 ssin6; 244 int bits; 245 char buf[NI_MAXHOST]; 246 size_t len; 247 248 memset(&ssin, 0, sizeof(struct sockaddr_in)); 249 memset(&ssin6, 0, sizeof(struct sockaddr_in6)); 250 251 if (strncasecmp("IPv6:", s, 5) == 0) 252 s += 5; 253 254 bits = inet_net_pton(AF_INET, s, &ssin.sin_addr, 255 sizeof(struct in_addr)); 256 if (bits != -1) { 257 ssin.sin_family = AF_INET; 258 memcpy(&ss, &ssin, sizeof(ssin)); 259 ss.ss_len = sizeof(struct sockaddr_in); 260 } else { 261 if (s[0] != '[') { 262 if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf) 263 return 0; 264 } 265 else { 266 s++; 267 if (strncasecmp("IPv6:", s, 5) == 0) 268 s += 5; 269 if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf) 270 return 0; 271 if (buf[len-1] != ']') 272 return 0; 273 buf[len-1] = 0; 274 } 275 bits = inet_net_pton(AF_INET6, buf, &ssin6.sin6_addr, 276 sizeof(struct in6_addr)); 277 if (bits == -1) 278 return 0; 279 ssin6.sin6_family = AF_INET6; 280 memcpy(&ss, &ssin6, sizeof(ssin6)); 281 ss.ss_len = sizeof(struct sockaddr_in6); 282 } 283 284 netaddr->ss = ss; 285 netaddr->bits = bits; 286 return 1; 287 } 288 289 int 290 text_to_relayhost(struct relayhost *relay, const char *s) 291 { 292 static const struct schema { 293 const char *name; 294 int tls; 295 uint16_t flags; 296 uint16_t port; 297 } schemas [] = { 298 /* 299 * new schemas should be *appended* otherwise the default 300 * schema index needs to be updated later in this function. 301 */ 302 { "smtp://", RELAY_TLS_OPPORTUNISTIC, 0, 25 }, 303 { "smtp+tls://", RELAY_TLS_STARTTLS, 0, 25 }, 304 { "smtp+notls://", RELAY_TLS_NO, 0, 25 }, 305 /* need to specify an explicit port for LMTP */ 306 { "lmtp://", RELAY_TLS_NO, RELAY_LMTP, 0 }, 307 { "smtps://", RELAY_TLS_SMTPS, 0, 465 } 308 }; 309 const char *errstr = NULL; 310 char *p, *q; 311 char buffer[1024]; 312 char *beg, *end; 313 size_t i; 314 size_t len; 315 316 memset(buffer, 0, sizeof buffer); 317 if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) 318 return 0; 319 320 for (i = 0; i < nitems(schemas); ++i) 321 if (strncasecmp(schemas[i].name, s, 322 strlen(schemas[i].name)) == 0) 323 break; 324 325 if (i == nitems(schemas)) { 326 /* there is a schema, but it's not recognized */ 327 if (strstr(buffer, "://")) 328 return 0; 329 330 /* no schema, default to smtp:// */ 331 i = 0; 332 p = buffer; 333 } 334 else 335 p = buffer + strlen(schemas[i].name); 336 337 relay->tls = schemas[i].tls; 338 relay->flags = schemas[i].flags; 339 relay->port = schemas[i].port; 340 341 /* first, we extract the label if any */ 342 if ((q = strchr(p, '@')) != NULL) { 343 *q = 0; 344 if (strlcpy(relay->authlabel, p, sizeof (relay->authlabel)) 345 >= sizeof (relay->authlabel)) 346 return 0; 347 p = q + 1; 348 } 349 350 /* then, we extract the mail exchanger */ 351 beg = end = p; 352 if (*beg == '[') { 353 if ((end = strchr(beg, ']')) == NULL) 354 return 0; 355 /* skip ']', it has to be included in the relay hostname */ 356 ++end; 357 len = end - beg; 358 } 359 else { 360 for (end = beg; *end; ++end) 361 if (!isalnum((unsigned char)*end) && 362 *end != '_' && *end != '.' && *end != '-') 363 break; 364 len = end - beg; 365 } 366 if (len >= sizeof relay->hostname) 367 return 0; 368 for (i = 0; i < len; ++i) 369 relay->hostname[i] = beg[i]; 370 relay->hostname[i] = 0; 371 372 /* finally, we extract the port */ 373 p = beg + len; 374 if (*p == ':') { 375 relay->port = strtonum(p+1, 1, IPPORT_HILASTAUTO, &errstr); 376 if (errstr) 377 return 0; 378 } 379 380 if (!valid_domainpart(relay->hostname)) 381 return 0; 382 if ((relay->flags & RELAY_LMTP) && (relay->port == 0)) 383 return 0; 384 if (relay->authlabel[0]) { 385 /* disallow auth on non-tls scheme. */ 386 if (relay->tls != RELAY_TLS_STARTTLS && 387 relay->tls != RELAY_TLS_SMTPS) 388 return 0; 389 relay->flags |= RELAY_AUTH; 390 } 391 392 return 1; 393 } 394 395 uint64_t 396 text_to_evpid(const char *s) 397 { 398 uint64_t ulval; 399 char *ep; 400 401 errno = 0; 402 ulval = strtoull(s, &ep, 16); 403 if (s[0] == '\0' || *ep != '\0') 404 return 0; 405 if (errno == ERANGE && ulval == ULLONG_MAX) 406 return 0; 407 if (ulval == 0) 408 return 0; 409 return (ulval); 410 } 411 412 uint32_t 413 text_to_msgid(const char *s) 414 { 415 uint64_t ulval; 416 char *ep; 417 418 errno = 0; 419 ulval = strtoull(s, &ep, 16); 420 if (s[0] == '\0' || *ep != '\0') 421 return 0; 422 if (errno == ERANGE && ulval == ULLONG_MAX) 423 return 0; 424 if (ulval == 0) 425 return 0; 426 if (ulval > 0xffffffff) 427 return 0; 428 return (ulval & 0xffffffff); 429 } 430 431 const char * 432 rule_to_text(struct rule *r) 433 { 434 static char buf[4096]; 435 436 memset(buf, 0, sizeof buf); 437 (void)strlcpy(buf, "match", sizeof buf); 438 if (r->flag_tag) { 439 if (r->flag_tag < 0) 440 (void)strlcat(buf, " !", sizeof buf); 441 (void)strlcat(buf, " tag ", sizeof buf); 442 (void)strlcat(buf, r->table_tag, sizeof buf); 443 } 444 445 if (r->flag_from) { 446 if (r->flag_from < 0) 447 (void)strlcat(buf, " !", sizeof buf); 448 if (r->flag_from_socket) 449 (void)strlcat(buf, " from socket", sizeof buf); 450 else if (r->flag_from_rdns) { 451 (void)strlcat(buf, " from rdns", sizeof buf); 452 if (r->table_from) { 453 (void)strlcat(buf, " ", sizeof buf); 454 (void)strlcat(buf, r->table_from, sizeof buf); 455 } 456 } 457 else if (strcmp(r->table_from, "<anyhost>") == 0) 458 (void)strlcat(buf, " from any", sizeof buf); 459 else if (strcmp(r->table_from, "<localhost>") == 0) 460 (void)strlcat(buf, " from local", sizeof buf); 461 else { 462 (void)strlcat(buf, " from src ", sizeof buf); 463 (void)strlcat(buf, r->table_from, sizeof buf); 464 } 465 } 466 467 if (r->flag_for) { 468 if (r->flag_for < 0) 469 (void)strlcat(buf, " !", sizeof buf); 470 if (strcmp(r->table_for, "<anydestination>") == 0) 471 (void)strlcat(buf, " for any", sizeof buf); 472 else if (strcmp(r->table_for, "<localnames>") == 0) 473 (void)strlcat(buf, " for local", sizeof buf); 474 else { 475 (void)strlcat(buf, " for domain ", sizeof buf); 476 (void)strlcat(buf, r->table_for, sizeof buf); 477 } 478 } 479 480 if (r->flag_smtp_helo) { 481 if (r->flag_smtp_helo < 0) 482 (void)strlcat(buf, " !", sizeof buf); 483 (void)strlcat(buf, " helo ", sizeof buf); 484 (void)strlcat(buf, r->table_smtp_helo, sizeof buf); 485 } 486 487 if (r->flag_smtp_auth) { 488 if (r->flag_smtp_auth < 0) 489 (void)strlcat(buf, " !", sizeof buf); 490 (void)strlcat(buf, " auth", sizeof buf); 491 if (r->table_smtp_auth) { 492 (void)strlcat(buf, " ", sizeof buf); 493 (void)strlcat(buf, r->table_smtp_auth, sizeof buf); 494 } 495 } 496 497 if (r->flag_smtp_starttls) { 498 if (r->flag_smtp_starttls < 0) 499 (void)strlcat(buf, " !", sizeof buf); 500 (void)strlcat(buf, " tls", sizeof buf); 501 } 502 503 if (r->flag_smtp_mail_from) { 504 if (r->flag_smtp_mail_from < 0) 505 (void)strlcat(buf, " !", sizeof buf); 506 (void)strlcat(buf, " mail-from ", sizeof buf); 507 (void)strlcat(buf, r->table_smtp_mail_from, sizeof buf); 508 } 509 510 if (r->flag_smtp_rcpt_to) { 511 if (r->flag_smtp_rcpt_to < 0) 512 (void)strlcat(buf, " !", sizeof buf); 513 (void)strlcat(buf, " rcpt-to ", sizeof buf); 514 (void)strlcat(buf, r->table_smtp_rcpt_to, sizeof buf); 515 } 516 (void)strlcat(buf, " action ", sizeof buf); 517 if (r->reject) 518 (void)strlcat(buf, "reject", sizeof buf); 519 else 520 (void)strlcat(buf, r->dispatcher, sizeof buf); 521 return buf; 522 } 523 524 525 int 526 text_to_userinfo(struct userinfo *userinfo, const char *s) 527 { 528 char buf[PATH_MAX]; 529 char *p; 530 const char *errstr; 531 532 memset(buf, 0, sizeof buf); 533 p = buf; 534 while (*s && *s != ':') 535 *p++ = *s++; 536 if (*s++ != ':') 537 goto error; 538 539 if (strlcpy(userinfo->username, buf, 540 sizeof userinfo->username) >= sizeof userinfo->username) 541 goto error; 542 543 memset(buf, 0, sizeof buf); 544 p = buf; 545 while (*s && *s != ':') 546 *p++ = *s++; 547 if (*s++ != ':') 548 goto error; 549 userinfo->uid = strtonum(buf, 0, UID_MAX, &errstr); 550 if (errstr) 551 goto error; 552 553 memset(buf, 0, sizeof buf); 554 p = buf; 555 while (*s && *s != ':') 556 *p++ = *s++; 557 if (*s++ != ':') 558 goto error; 559 userinfo->gid = strtonum(buf, 0, GID_MAX, &errstr); 560 if (errstr) 561 goto error; 562 563 if (strlcpy(userinfo->directory, s, 564 sizeof userinfo->directory) >= sizeof userinfo->directory) 565 goto error; 566 567 return 1; 568 569 error: 570 return 0; 571 } 572 573 int 574 text_to_credentials(struct credentials *creds, const char *s) 575 { 576 char *p; 577 char buffer[LINE_MAX]; 578 size_t offset; 579 580 p = strchr(s, ':'); 581 if (p == NULL) { 582 creds->username[0] = '\0'; 583 if (strlcpy(creds->password, s, sizeof creds->password) 584 >= sizeof creds->password) 585 return 0; 586 return 1; 587 } 588 589 offset = p - s; 590 591 memset(buffer, 0, sizeof buffer); 592 if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) 593 return 0; 594 p = buffer + offset; 595 *p = '\0'; 596 597 if (strlcpy(creds->username, buffer, sizeof creds->username) 598 >= sizeof creds->username) 599 return 0; 600 if (strlcpy(creds->password, p+1, sizeof creds->password) 601 >= sizeof creds->password) 602 return 0; 603 604 return 1; 605 } 606 607 int 608 text_to_expandnode(struct expandnode *expandnode, const char *s) 609 { 610 size_t l; 611 612 l = strlen(s); 613 if (alias_is_error(expandnode, s, l) || 614 alias_is_include(expandnode, s, l) || 615 alias_is_filter(expandnode, s, l) || 616 alias_is_filename(expandnode, s, l) || 617 alias_is_address(expandnode, s, l) || 618 alias_is_username(expandnode, s, l)) 619 return (1); 620 621 return (0); 622 } 623 624 const char * 625 expandnode_to_text(struct expandnode *expandnode) 626 { 627 switch (expandnode->type) { 628 case EXPAND_FILTER: 629 case EXPAND_FILENAME: 630 case EXPAND_INCLUDE: 631 case EXPAND_ERROR: 632 case EXPAND_USERNAME: 633 return expandnode->u.user; 634 case EXPAND_ADDRESS: 635 return mailaddr_to_text(&expandnode->u.mailaddr); 636 case EXPAND_INVALID: 637 break; 638 } 639 640 return NULL; 641 } 642 643 /******/ 644 static int 645 alias_is_filter(struct expandnode *alias, const char *line, size_t len) 646 { 647 int v = 0; 648 649 if (*line == '"') 650 v = 1; 651 if (*(line+v) == '|') { 652 if (strlcpy(alias->u.buffer, line + v + 1, 653 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 654 return 0; 655 if (v) { 656 v = strlen(alias->u.buffer); 657 if (v == 0) 658 return (0); 659 if (alias->u.buffer[v-1] != '"') 660 return (0); 661 alias->u.buffer[v-1] = '\0'; 662 } 663 alias->type = EXPAND_FILTER; 664 return (1); 665 } 666 return (0); 667 } 668 669 static int 670 alias_is_username(struct expandnode *alias, const char *line, size_t len) 671 { 672 memset(alias, 0, sizeof *alias); 673 674 if (strlcpy(alias->u.user, line, 675 sizeof(alias->u.user)) >= sizeof(alias->u.user)) 676 return 0; 677 678 while (*line) { 679 if (!isalnum((unsigned char)*line) && 680 *line != '_' && *line != '.' && *line != '-' && *line != '+') 681 return 0; 682 ++line; 683 } 684 685 alias->type = EXPAND_USERNAME; 686 return 1; 687 } 688 689 static int 690 alias_is_address(struct expandnode *alias, const char *line, size_t len) 691 { 692 char *domain; 693 694 memset(alias, 0, sizeof *alias); 695 696 if (len < 3) /* x@y */ 697 return 0; 698 699 domain = strchr(line, '@'); 700 if (domain == NULL) 701 return 0; 702 703 /* @ cannot start or end an address */ 704 if (domain == line || domain == line + len - 1) 705 return 0; 706 707 /* scan pre @ for disallowed chars */ 708 *domain++ = '\0'; 709 (void)strlcpy(alias->u.mailaddr.user, line, sizeof(alias->u.mailaddr.user)); 710 (void)strlcpy(alias->u.mailaddr.domain, domain, 711 sizeof(alias->u.mailaddr.domain)); 712 713 while (*line) { 714 char allowedset[] = "!#$%*/?|^{}`~&'+-=_."; 715 if (!isalnum((unsigned char)*line) && 716 strchr(allowedset, *line) == NULL) 717 return 0; 718 ++line; 719 } 720 721 while (*domain) { 722 char allowedset[] = "-."; 723 if (!isalnum((unsigned char)*domain) && 724 strchr(allowedset, *domain) == NULL) 725 return 0; 726 ++domain; 727 } 728 729 alias->type = EXPAND_ADDRESS; 730 return 1; 731 } 732 733 static int 734 alias_is_filename(struct expandnode *alias, const char *line, size_t len) 735 { 736 memset(alias, 0, sizeof *alias); 737 738 if (*line != '/') 739 return 0; 740 741 if (strlcpy(alias->u.buffer, line, 742 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 743 return 0; 744 alias->type = EXPAND_FILENAME; 745 return 1; 746 } 747 748 static int 749 alias_is_include(struct expandnode *alias, const char *line, size_t len) 750 { 751 size_t skip; 752 753 memset(alias, 0, sizeof *alias); 754 755 if (strncasecmp(":include:", line, 9) == 0) 756 skip = 9; 757 else if (strncasecmp("include:", line, 8) == 0) 758 skip = 8; 759 else 760 return 0; 761 762 if (!alias_is_filename(alias, line + skip, len - skip)) 763 return 0; 764 765 alias->type = EXPAND_INCLUDE; 766 return 1; 767 } 768 769 static int 770 alias_is_error(struct expandnode *alias, const char *line, size_t len) 771 { 772 size_t skip; 773 774 memset(alias, 0, sizeof *alias); 775 776 if (strncasecmp(":error:", line, 7) == 0) 777 skip = 7; 778 else if (strncasecmp("error:", line, 6) == 0) 779 skip = 6; 780 else 781 return 0; 782 783 if (strlcpy(alias->u.buffer, line + skip, 784 sizeof(alias->u.buffer)) >= sizeof(alias->u.buffer)) 785 return 0; 786 787 if (strlen(alias->u.buffer) < 5) 788 return 0; 789 790 /* [45][0-9]{2} [a-zA-Z0-9].* */ 791 if (alias->u.buffer[3] != ' ' || 792 !isalnum((unsigned char)alias->u.buffer[4]) || 793 (alias->u.buffer[0] != '4' && alias->u.buffer[0] != '5') || 794 !isdigit((unsigned char)alias->u.buffer[1]) || 795 !isdigit((unsigned char)alias->u.buffer[2])) 796 return 0; 797 798 alias->type = EXPAND_ERROR; 799 return 1; 800 } 801 802 #if IO_TLS 803 const char * 804 tls_to_text(struct tls *tls) 805 { 806 static char buf[256]; 807 808 (void)snprintf(buf, sizeof buf, "%s:%s:%d", 809 tls_conn_version(tls), 810 tls_conn_cipher(tls), 811 tls_conn_cipher_strength(tls)); 812 813 return (buf); 814 } 815 #endif 816