1 /* 2 * Copyright (c) 1998-2008 Proofpoint, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #include <sendmail.h> 15 16 SM_RCSID("@(#)$Id: map.c,v 8.713 2013-11-22 20:51:55 ca Exp $") 17 #include <sm/sendmail.h> 18 19 #if LDAPMAP 20 # include <sm/ldap.h> 21 #endif 22 23 #if NDBM 24 # include <ndbm.h> 25 # ifdef R_FIRST 26 # error "README: You are running the Berkeley DB version of ndbm.h. See" 27 # error "README: the README file about tweaking Berkeley DB so it can" 28 # error "README: coexist with NDBM, or delete -DNDBM from the Makefile" 29 # error "README: and use -DNEWDB instead." 30 # endif /* R_FIRST */ 31 #endif /* NDBM */ 32 #if NEWDB 33 # include "sm/bdb.h" 34 #endif 35 #if NIS 36 struct dom_binding; /* forward reference needed on IRIX */ 37 # include <rpcsvc/ypclnt.h> 38 # if NDBM 39 # define NDBM_YP_COMPAT /* create YP-compatible NDBM files */ 40 # endif 41 #endif /* NIS */ 42 #if CDB 43 # include <cdb.h> 44 #endif 45 46 #include "map.h" 47 48 #if NEWDB 49 # if DB_VERSION_MAJOR < 2 50 static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *)); 51 # endif 52 # if DB_VERSION_MAJOR == 2 53 static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *)); 54 # endif 55 # if DB_VERSION_MAJOR > 2 56 static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **)); 57 # endif 58 #endif /* NEWDB */ 59 static bool extract_canonname __P((char *, char *, char *, char[], int)); 60 static void map_close __P((STAB *, int)); 61 static void map_init __P((STAB *, int)); 62 #ifdef LDAPMAP 63 static STAB *ldapmap_findconn __P((SM_LDAP_STRUCT *)); 64 #endif 65 #if NISPLUS 66 static bool nisplus_getcanonname __P((char *, int, int *)); 67 #endif 68 #if NIS 69 static bool nis_getcanonname __P((char *, int, int *)); 70 #endif 71 #if NETINFO 72 static bool ni_getcanonname __P((char *, int, int *)); 73 #endif 74 static bool text_getcanonname __P((char *, int, int *)); 75 #if SOCKETMAP 76 static STAB *socket_map_findconn __P((const char*)); 77 78 /* XXX arbitrary limit for sanity */ 79 # define SOCKETMAP_MAXL 1000000 80 #endif /* SOCKETMAP */ 81 82 /* default error message for trying to open a map in write mode */ 83 #ifdef ENOSYS 84 # define SM_EMAPCANTWRITE ENOSYS 85 #else /* ENOSYS */ 86 # ifdef EFTYPE 87 # define SM_EMAPCANTWRITE EFTYPE 88 # else 89 # define SM_EMAPCANTWRITE ENXIO 90 # endif 91 #endif /* ENOSYS */ 92 93 /* 94 ** MAP_HAS_CHGED -- check whether fd was updated or fn refers to a different file 95 ** 96 ** Parameters: 97 ** map -- map being checked 98 ** fn -- (full) file name of map. 99 ** fd -- fd of map. 100 ** 101 ** Returns: 102 ** true iff file referenced by fd was updated 103 ** or fn refers to a different file. 104 */ 105 106 static bool map_has_chged __P((MAP *, const char *, int)); 107 108 static bool 109 map_has_chged(map, fn, fd) 110 MAP *map; 111 const char *fn; 112 int fd; 113 { 114 struct stat stbuf; 115 #if _FFR_MAP_CHK_FILE 116 struct stat nstbuf; 117 #endif 118 119 #if _FFR_MAP_CHK_FILE > 1 120 if (tTd(38, 8)) 121 sm_dprintf("map_has_chged: fn=%s, fd=%d, checked=%d\n", 122 fn, fd, bitset(MF_CHKED_CHGD, map->map_mflags)); 123 if (fd < 0) 124 return true; 125 126 /* XXX check can be disabled via -d38.101 for testing */ 127 if (bitset(MF_CHKED_CHGD, map->map_mflags) && !tTd(38, 101)) 128 return false; 129 map->map_mflags |= MF_CHKED_CHGD; 130 #endif 131 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime) 132 { 133 if (tTd(38, 4)) 134 sm_dprintf("reopen map: name=%s, fd=%d\n", map->map_mname, fd); 135 return true; 136 } 137 #if _FFR_MAP_CHK_FILE 138 if (stat(fn, &nstbuf) == 0 && 139 (nstbuf.st_dev != stbuf.st_dev || nstbuf.st_ino != stbuf.st_ino)) 140 { 141 if (tTd(38, 4) && stat(fn, &nstbuf) == 0) 142 sm_dprintf("reopen map: fn=%s, ndev=%d, dev=%d, nino=%d, ino=%d\n", 143 fn, (int) nstbuf.st_dev, (int) stbuf.st_dev, 144 (int) nstbuf.st_ino, (int) stbuf.st_ino); 145 return true; 146 } 147 #endif 148 return false; 149 } 150 151 #if _FFR_MAP_CHK_FILE > 1 152 153 /* 154 ** MAP_RESET_CHGD -- reset MF_CHKED_CHGD in a map 155 ** 156 ** Parameters: 157 ** s -- STAB entry: if map: reset MF_CHKED_CHGD 158 ** unused -- unused variable 159 ** 160 ** Returns: 161 ** none. 162 */ 163 164 static void map_reset_chged __P((STAB *, int)); 165 166 /* ARGSUSED1 */ 167 static void 168 map_reset_chged(s, unused) 169 STAB *s; 170 int unused; 171 { 172 MAP *map; 173 174 /* has to be a map */ 175 if (ST_MAP != s->s_symtype 176 #if _FFR_DYN_CLASS 177 && ST_DYNMAP != s->s_symtype 178 #endif 179 ) 180 return; 181 map = &s->s_map; 182 if (!bitset(MF_VALID, map->map_mflags)) 183 return; 184 if (tTd(38, 8)) 185 sm_dprintf("map_reset_chged: name=%s, checked=%d\n", 186 map->map_mname, bitset(MF_CHKED_CHGD, map->map_mflags)); 187 map->map_mflags &= ~MF_CHKED_CHGD; 188 } 189 190 /* 191 ** MAPS_RESET_CHGD -- reset MF_CHKED_CHGD in all maps 192 ** 193 ** Parameters: 194 ** msg - caller (for debugging) 195 ** 196 ** Returns: 197 ** none. 198 */ 199 200 void 201 maps_reset_chged(msg) 202 const char *msg; 203 { 204 if (tTd(38, 16)) 205 sm_dprintf("maps_reset_chged: msg=%s\n", msg); 206 stabapply(map_reset_chged, 0); 207 } 208 #endif /* _FFR_MAP_CHK_FILE > 1 */ 209 210 211 #if NEWDB || CDB || (NDBM && _FFR_MAP_CHK_FILE) 212 static bool smdb_add_extension __P((char *, int, char *, char *)); 213 214 /* 215 ** SMDB_ADD_EXTENSION -- Adds an extension to a file name. 216 ** 217 ** Just adds a . followed by a string to a db_name if there 218 ** is room and the db_name does not already have that extension. 219 ** 220 ** Parameters: 221 ** full_name -- The final file name. 222 ** max_full_name_len -- The max length for full_name. 223 ** db_name -- The name of the db. 224 ** extension -- The extension to add. 225 ** 226 ** Returns: 227 ** SMDBE_OK -- Success. 228 ** Anything else is an error. Look up more info about the 229 ** error in the comments for the specific open() used. 230 */ 231 232 static bool 233 smdb_add_extension(full_name, max_full_name_len, db_name, extension) 234 char *full_name; 235 int max_full_name_len; 236 char *db_name; 237 char *extension; 238 { 239 int extension_len; 240 int db_name_len; 241 242 if (full_name == NULL || db_name == NULL || extension == NULL) 243 return false; /* SMDBE_INVALID_PARAMETER; */ 244 245 extension_len = strlen(extension); 246 db_name_len = strlen(db_name); 247 248 if (extension_len + db_name_len + 2 > max_full_name_len) 249 return false; /* SMDBE_DB_NAME_TOO_LONG; */ 250 251 if (db_name_len < extension_len + 1 || 252 db_name[db_name_len - extension_len - 1] != '.' || 253 strcmp(&db_name[db_name_len - extension_len], extension) != 0) 254 (void) sm_snprintf(full_name, max_full_name_len, "%s.%s", 255 db_name, extension); 256 else 257 (void) sm_strlcpy(full_name, db_name, max_full_name_len); 258 259 return true; 260 } 261 #endif /* NEWDB || CDB || (NDBM && _FFR_MAP_CHK_FILE) */ 262 263 /* 264 ** MAP.C -- implementations for various map classes. 265 ** 266 ** Each map class implements a series of functions: 267 ** 268 ** bool map_parse(MAP *map, char *args) 269 ** Parse the arguments from the config file. Return true 270 ** if they were ok, false otherwise. Fill in map with the 271 ** values. 272 ** 273 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat) 274 ** Look up the key in the given map. If found, do any 275 ** rewriting the map wants (including "args" if desired) 276 ** and return the value. Set *pstat to the appropriate status 277 ** on error and return NULL. Args will be NULL if called 278 ** from the alias routines, although this should probably 279 ** not be relied upon. It is suggested you call map_rewrite 280 ** to return the results -- it takes care of null termination 281 ** and uses a dynamically expanded buffer as needed. 282 ** 283 ** void map_store(MAP *map, char *key, char *value) 284 ** Store the key:value pair in the map. 285 ** 286 ** bool map_open(MAP *map, int mode) 287 ** Open the map for the indicated mode. Mode should 288 ** be either O_RDONLY or O_RDWR. Return true if it 289 ** was opened successfully, false otherwise. If the open 290 ** failed and the MF_OPTIONAL flag is not set, it should 291 ** also print an error. If the MF_ALIAS bit is set 292 ** and this map class understands the @:@ convention, it 293 ** should call aliaswait() before returning. 294 ** 295 ** void map_close(MAP *map) 296 ** Close the map. 297 ** 298 ** This file also includes the implementation for getcanonname. 299 ** It is currently implemented in a pretty ad-hoc manner; it ought 300 ** to be more properly integrated into the map structure. 301 */ 302 303 /* XREF: conf.c must use the same expression */ 304 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL 305 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */ 306 #else 307 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */ 308 #endif 309 310 /* 311 ** MAP_PARSEARGS -- parse config line arguments for database lookup 312 ** 313 ** This is a generic version of the map_parse method. 314 ** 315 ** Parameters: 316 ** map -- the map being initialized. 317 ** ap -- a pointer to the args on the config line. 318 ** 319 ** Returns: 320 ** true -- if everything parsed OK. 321 ** false -- otherwise. 322 ** 323 ** Side Effects: 324 ** null terminates the filename; stores it in map 325 */ 326 327 bool 328 map_parseargs(map, ap) 329 MAP *map; 330 char *ap; 331 { 332 register char *p = ap; 333 334 /* 335 ** There is no check whether there is really an argument, 336 ** but that's not important enough to warrant extra code. 337 */ 338 339 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 340 map->map_spacesub = SpaceSub; /* default value */ 341 for (;;) 342 { 343 while (SM_ISSPACE(*p)) 344 p++; 345 if (*p != '-') 346 break; 347 switch (*++p) 348 { 349 case 'A': 350 map->map_mflags |= MF_APPEND; 351 break; 352 353 case 'a': 354 map->map_app = ++p; 355 break; 356 357 case 'D': 358 map->map_mflags |= MF_DEFER; 359 break; 360 361 case 'd': 362 { 363 char *h; 364 365 ++p; 366 h = strchr(p, ' '); 367 if (h != NULL) 368 *h = '\0'; 369 map->map_timeout = convtime(p, 's'); 370 if (h != NULL) 371 *h = ' '; 372 } 373 break; 374 375 case 'f': 376 map->map_mflags |= MF_NOFOLDCASE; 377 break; 378 379 case 'k': 380 while (isascii(*++p) && isspace(*p)) 381 continue; 382 map->map_keycolnm = p; 383 break; 384 385 case 'm': 386 map->map_mflags |= MF_MATCHONLY; 387 break; 388 389 case 'N': 390 map->map_mflags |= MF_INCLNULL; 391 map->map_mflags &= ~MF_TRY0NULL; 392 break; 393 394 case 'O': 395 map->map_mflags &= ~MF_TRY1NULL; 396 break; 397 398 case 'o': 399 map->map_mflags |= MF_OPTIONAL; 400 break; 401 402 case 'q': 403 map->map_mflags |= MF_KEEPQUOTES; 404 break; 405 406 case 'S': 407 map->map_spacesub = *++p; 408 break; 409 410 case 'T': 411 map->map_tapp = ++p; 412 break; 413 414 case 't': 415 map->map_mflags |= MF_NODEFER; 416 break; 417 418 case 'v': 419 while (isascii(*++p) && isspace(*p)) 420 continue; 421 map->map_valcolnm = p; 422 break; 423 424 case 'z': 425 if (*++p != '\\') 426 map->map_coldelim = *p; 427 else 428 { 429 switch (*++p) 430 { 431 case 'n': 432 map->map_coldelim = '\n'; 433 break; 434 435 case 't': 436 map->map_coldelim = '\t'; 437 break; 438 439 default: 440 map->map_coldelim = '\\'; 441 } 442 } 443 break; 444 445 default: 446 syserr("Illegal option %c map %s", *p, map->map_mname); 447 break; 448 } 449 while (*p != '\0' && !(SM_ISSPACE(*p))) 450 p++; 451 if (*p != '\0') 452 *p++ = '\0'; 453 } 454 if (map->map_app != NULL) 455 map->map_app = newstr(map->map_app); 456 if (map->map_tapp != NULL) 457 map->map_tapp = newstr(map->map_tapp); 458 if (map->map_keycolnm != NULL) 459 map->map_keycolnm = newstr(map->map_keycolnm); 460 if (map->map_valcolnm != NULL) 461 map->map_valcolnm = newstr(map->map_valcolnm); 462 463 if (*p != '\0') 464 { 465 map->map_file = p; 466 while (*p != '\0' && !(SM_ISSPACE(*p))) 467 p++; 468 if (*p != '\0') 469 *p++ = '\0'; 470 map->map_file = newstr(map->map_file); 471 } 472 473 while (*p != '\0' && SM_ISSPACE(*p)) 474 p++; 475 if (*p != '\0') 476 map->map_rebuild = newstr(p); 477 478 if (map->map_file == NULL && 479 !bitset(MCF_OPTFILE, map->map_class->map_cflags)) 480 { 481 syserr("No file name for %s map %s", 482 map->map_class->map_cname, map->map_mname); 483 return false; 484 } 485 return true; 486 } 487 /* 488 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications. 489 ** 490 ** It also adds the map_app string. It can be used as a utility 491 ** in the map_lookup method. 492 ** 493 ** Parameters: 494 ** map -- the map that causes this. 495 ** s -- the string to rewrite, NOT necessarily null terminated. 496 ** slen -- the length of s. 497 ** av -- arguments to interpolate into buf. 498 ** 499 ** Returns: 500 ** Pointer to rewritten result. This is static data that 501 ** should be copied if it is to be saved! 502 */ 503 504 char * 505 map_rewrite(map, s, slen, av) 506 register MAP *map; 507 register const char *s; 508 size_t slen; 509 char **av; 510 { 511 register char *bp; 512 register char c; 513 char **avp; 514 register char *ap; 515 size_t l; 516 size_t len; 517 static size_t buflen = 0; 518 static char *buf = NULL; 519 520 if (tTd(39, 1)) 521 { 522 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s); 523 if (av == NULL) 524 sm_dprintf(" (nullv)"); 525 else 526 { 527 for (avp = av; *avp != NULL; avp++) 528 sm_dprintf("\n\t%s", *avp); 529 } 530 sm_dprintf("\n"); 531 } 532 533 /* count expected size of output (can safely overestimate) */ 534 l = len = slen; 535 if (av != NULL) 536 { 537 const char *sp = s; 538 539 while (l-- > 0 && (c = *sp++) != '\0') 540 { 541 if (c != '%') 542 continue; 543 if (l-- <= 0) 544 break; 545 c = *sp++; 546 if (!(isascii(c) && isdigit(c))) 547 continue; 548 for (avp = av; --c >= '0' && *avp != NULL; avp++) 549 continue; 550 if (*avp == NULL) 551 continue; 552 len += strlen(*avp); 553 } 554 } 555 if (map->map_app != NULL) 556 len += strlen(map->map_app); 557 if (buflen < ++len) 558 { 559 /* need to malloc additional space */ 560 buflen = len; 561 SM_FREE(buf); 562 buf = sm_pmalloc_x(buflen); 563 } 564 565 bp = buf; 566 if (av == NULL) 567 { 568 memmove(bp, s, slen); 569 bp += slen; 570 571 /* assert(len > slen); */ 572 len -= slen; 573 } 574 else 575 { 576 while (slen-- > 0 && (c = *s++) != '\0') 577 { 578 if (c != '%') 579 { 580 pushc: 581 if (len-- <= 1) 582 break; 583 *bp++ = c; 584 continue; 585 } 586 if (slen-- <= 0 || (c = *s++) == '\0') 587 c = '%'; 588 if (c == '%') 589 goto pushc; 590 if (!(isascii(c) && isdigit(c))) 591 { 592 if (len-- <= 1) 593 break; 594 *bp++ = '%'; 595 goto pushc; 596 } 597 for (avp = av; --c >= '0' && *avp != NULL; avp++) 598 continue; 599 if (*avp == NULL) 600 continue; 601 602 /* transliterate argument into output string */ 603 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len) 604 *bp++ = c; 605 } 606 } 607 if (map->map_app != NULL && len > 0) 608 (void) sm_strlcpy(bp, map->map_app, len); 609 else 610 *bp = '\0'; 611 #if _FFR_8BITENVADDR 612 if (!bitset(MF_KEEPXFMT, map->map_mflags)) 613 { 614 int newlen; 615 char *quoted; 616 617 newlen = 0; 618 quoted = quote_internal_chars(buf, NULL, &newlen, NULL); 619 if (newlen > buflen) 620 { 621 buflen = newlen; 622 SM_FREE(buf); 623 buf = sm_pmalloc_x(buflen); 624 } 625 (void) sm_strlcpy(buf, quoted, buflen); 626 SM_FREE(quoted); 627 } 628 #endif 629 if (tTd(39, 1)) 630 sm_dprintf("map_rewrite => %s\n", buf); 631 return buf; 632 } 633 634 /* 635 ** MAPCHOWN -- if available fchown() the fds to TrustedUid 636 ** 637 ** Parameters: 638 ** mapname - name of map (for error reporting) 639 ** fd0 - first fd (must be valid) 640 ** fd1 - second fd (<0: do not use) 641 ** filename - name of file (for error reporting) 642 ** 643 ** Returns: 644 ** none. 645 */ 646 647 static void mapchown __P((const char *, int, int, const char *)); 648 649 static void 650 mapchown(mapname, fd0, fd1, filename) 651 const char *mapname; 652 int fd0; 653 int fd1; 654 const char *filename; 655 { 656 if (!(geteuid() == 0 && TrustedUid != 0)) 657 return; 658 #if HASFCHOWN 659 if (fchown(fd0, TrustedUid, -1) < 0 || 660 (fd1 >= 0 && fchown(fd1, TrustedUid, -1) < 0)) 661 { 662 int err = errno; 663 664 sm_syslog(LOG_ALERT, NOQID, 665 "ownership change on %s failed: %s", 666 filename, sm_errstring(err)); 667 message("050 ownership change on %s failed: %s", 668 filename, sm_errstring(err)); 669 } 670 #else /* HASFCHOWN */ 671 sm_syslog(LOG_ALERT, NOQID, 672 "no fchown(): cannot change ownership on %s", 673 mapname); 674 message("050 no fchown(): cannot change ownership on %s", 675 mapname); 676 #endif /* HASFCHOWN */ 677 } 678 679 /* 680 ** INITMAPS -- rebuild alias maps 681 ** 682 ** Parameters: 683 ** none. 684 ** 685 ** Returns: 686 ** none. 687 */ 688 689 void 690 initmaps() 691 { 692 checkfd012("entering initmaps"); 693 stabapply(map_init, 0); 694 checkfd012("exiting initmaps"); 695 } 696 697 /* 698 ** MAP_INIT -- rebuild a map 699 ** 700 ** Parameters: 701 ** s -- STAB entry: if map: try to rebuild 702 ** unused -- unused variable 703 ** 704 ** Returns: 705 ** none. 706 ** 707 ** Side Effects: 708 ** will close already open rebuildable map. 709 */ 710 711 /* ARGSUSED1 */ 712 static void 713 map_init(s, unused) 714 register STAB *s; 715 int unused; 716 { 717 register MAP *map; 718 719 /* has to be a map */ 720 if (s->s_symtype != ST_MAP) 721 return; 722 723 map = &s->s_map; 724 if (!bitset(MF_VALID, map->map_mflags)) 725 return; 726 727 if (tTd(38, 2)) 728 sm_dprintf("map_init(%s:%s, %s)\n", 729 map->map_class->map_cname == NULL ? "NULL" : 730 map->map_class->map_cname, 731 map->map_mname == NULL ? "NULL" : map->map_mname, 732 map->map_file == NULL ? "NULL" : map->map_file); 733 734 if (!bitset(MF_ALIAS, map->map_mflags) || 735 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) 736 { 737 if (tTd(38, 3)) 738 sm_dprintf("\tnot rebuildable\n"); 739 return; 740 } 741 742 /* if already open, close it (for nested open) */ 743 if (bitset(MF_OPEN, map->map_mflags)) 744 { 745 map->map_mflags |= MF_CLOSING; 746 map->map_class->map_close(map); 747 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 748 } 749 750 (void) rebuildaliases(map); 751 return; 752 } 753 /* 754 ** OPENMAP -- open a map 755 ** 756 ** Parameters: 757 ** map -- map to open (it must not be open). 758 ** 759 ** Returns: 760 ** whether open succeeded. 761 */ 762 763 bool 764 openmap(map) 765 MAP *map; 766 { 767 bool restore = false; 768 bool savehold = HoldErrs; 769 bool savequick = QuickAbort; 770 int saveerrors = Errors; 771 772 if (!bitset(MF_VALID, map->map_mflags)) 773 return false; 774 775 /* better safe than sorry... */ 776 if (bitset(MF_OPEN, map->map_mflags)) 777 return true; 778 779 /* Don't send a map open error out via SMTP */ 780 if ((OnlyOneError || QuickAbort) && 781 (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 782 { 783 restore = true; 784 HoldErrs = true; 785 QuickAbort = false; 786 } 787 788 errno = 0; 789 if (map->map_class->map_open(map, O_RDONLY)) 790 { 791 if (tTd(38, 4)) 792 sm_dprintf("openmap()\t%s:%s %s: valid\n", 793 map->map_class->map_cname == NULL ? "NULL" : 794 map->map_class->map_cname, 795 map->map_mname == NULL ? "NULL" : 796 map->map_mname, 797 map->map_file == NULL ? "NULL" : 798 map->map_file); 799 map->map_mflags |= MF_OPEN; 800 map->map_pid = CurrentPid; 801 } 802 else 803 { 804 if (tTd(38, 4)) 805 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n", 806 map->map_class->map_cname == NULL ? "NULL" : 807 map->map_class->map_cname, 808 map->map_mname == NULL ? "NULL" : 809 map->map_mname, 810 map->map_file == NULL ? "NULL" : 811 map->map_file, 812 errno == 0 ? "" : ": ", 813 errno == 0 ? "" : sm_errstring(errno)); 814 if (!bitset(MF_OPTIONAL, map->map_mflags)) 815 { 816 extern MAPCLASS BogusMapClass; 817 818 map->map_orgclass = map->map_class; 819 map->map_class = &BogusMapClass; 820 map->map_mflags |= MF_OPEN|MF_OPENBOGUS; 821 map->map_pid = CurrentPid; 822 } 823 else 824 { 825 /* don't try again */ 826 map->map_mflags &= ~MF_VALID; 827 } 828 } 829 830 if (restore) 831 { 832 Errors = saveerrors; 833 HoldErrs = savehold; 834 QuickAbort = savequick; 835 } 836 837 return bitset(MF_OPEN, map->map_mflags); 838 } 839 /* 840 ** CLOSEMAPS -- close all open maps opened by the current pid. 841 ** 842 ** Parameters: 843 ** bogus -- only close bogus maps. 844 ** 845 ** Returns: 846 ** none. 847 */ 848 849 void 850 closemaps(bogus) 851 bool bogus; 852 { 853 stabapply(map_close, bogus); 854 } 855 /* 856 ** MAP_CLOSE -- close a map opened by the current pid. 857 ** 858 ** Parameters: 859 ** s -- STAB entry: if map: try to close 860 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps. 861 ** 862 ** Returns: 863 ** none. 864 */ 865 866 static void 867 map_close(s, bogus) 868 register STAB *s; 869 int bogus; /* int because of stabapply(), used as bool */ 870 { 871 MAP *map; 872 extern MAPCLASS BogusMapClass; 873 874 if (s->s_symtype != ST_MAP) 875 return; 876 877 map = &s->s_map; 878 879 /* 880 ** close the map iff: 881 ** it is valid and open and opened by this process 882 ** and (!bogus or it's a bogus map or it is not persistent) 883 ** negate this: return iff 884 ** it is not valid or it is not open or not opened by this process 885 ** or (bogus and it's not a bogus map and it's not not-persistent) 886 */ 887 888 if (!bitset(MF_VALID, map->map_mflags) || 889 !bitset(MF_OPEN, map->map_mflags) || 890 bitset(MF_CLOSING, map->map_mflags) || 891 map->map_pid != CurrentPid || 892 (bogus && map->map_class != &BogusMapClass && 893 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags))) 894 return; 895 896 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL && 897 map->map_orgclass != &BogusMapClass) 898 map->map_class = map->map_orgclass; 899 if (tTd(38, 5)) 900 sm_dprintf("closemaps: closing %s (%s)\n", 901 map->map_mname == NULL ? "NULL" : map->map_mname, 902 map->map_file == NULL ? "NULL" : map->map_file); 903 904 if (!bitset(MF_OPENBOGUS, map->map_mflags)) 905 { 906 map->map_mflags |= MF_CLOSING; 907 map->map_class->map_close(map); 908 } 909 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING|MF_CHKED_CHGD); 910 } 911 912 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 913 extern int getdomainname(); 914 915 /* this is mainly for backward compatibility in Sun environment */ 916 static char * 917 sun_init_domain() 918 { 919 /* 920 ** Get the domain name from the kernel. 921 ** If it does not start with a leading dot, then remove 922 ** the first component. Since leading dots are funny Unix 923 ** files, we treat a leading "+" the same as a leading dot. 924 ** Finally, force there to be at least one dot in the domain name 925 ** (i.e. top-level domains are not allowed, like "com", must be 926 ** something like "sun.com"). 927 */ 928 929 char buf[MAXNAME]; /* EAI:ok (domainname) */ 930 char *period, *autodomain; 931 932 if (getdomainname(buf, sizeof buf) < 0) 933 return NULL; 934 935 if (buf[0] == '\0') 936 return NULL; 937 938 if (tTd(0, 20)) 939 printf("domainname = %s\n", buf); 940 941 if (buf[0] == '+') 942 buf[0] = '.'; 943 period = strchr(buf, '.'); 944 if (period == NULL) 945 autodomain = buf; 946 else 947 autodomain = period + 1; 948 if (strchr(autodomain, '.') == NULL) 949 return newstr(buf); 950 else 951 return newstr(autodomain); 952 } 953 #endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */ 954 955 /* 956 ** GETCANONNAME -- look up name using service switch 957 ** 958 ** Parameters: 959 ** host -- the host name to look up. 960 ** hbsize -- the size of the host buffer. 961 ** trymx -- if set, try MX records. 962 ** pttl -- pointer to return TTL (can be NULL). 963 ** 964 ** Returns: 965 ** >0 -- if the host was found. 966 ** 0 -- otherwise. 967 */ 968 969 int 970 getcanonname(host, hbsize, trymx, pttl) 971 char *host; 972 int hbsize; 973 bool trymx; 974 int *pttl; 975 { 976 int nmaps; 977 int mapno; 978 bool found = false; 979 bool got_tempfail = false; 980 auto int status = EX_UNAVAILABLE; 981 char *maptype[MAXMAPSTACK]; 982 short mapreturn[MAXMAPACTIONS]; 983 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 984 bool should_try_nis_domain = false; 985 static char *nis_domain = NULL; 986 #endif 987 bool secure = true; /* consider all maps secure by default */ 988 989 nmaps = switch_map_find("hosts", maptype, mapreturn); 990 if (pttl != NULL) 991 *pttl = SM_DEFAULT_TTL; 992 for (mapno = 0; mapno < nmaps; mapno++) 993 { 994 int i; 995 996 if (tTd(38, 20)) 997 sm_dprintf("getcanonname(%s), trying %s\n", 998 host, maptype[mapno]); 999 if (strcmp("files", maptype[mapno]) == 0) 1000 { 1001 found = text_getcanonname(host, hbsize, &status); 1002 } 1003 #if NIS 1004 else if (strcmp("nis", maptype[mapno]) == 0) 1005 { 1006 found = nis_getcanonname(host, hbsize, &status); 1007 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 1008 if (nis_domain == NULL) 1009 nis_domain = sun_init_domain(); 1010 # endif 1011 } 1012 #endif /* NIS */ 1013 #if NISPLUS 1014 else if (strcmp("nisplus", maptype[mapno]) == 0) 1015 { 1016 found = nisplus_getcanonname(host, hbsize, &status); 1017 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 1018 if (nis_domain == NULL) 1019 nis_domain = sun_init_domain(); 1020 # endif 1021 } 1022 #endif /* NISPLUS */ 1023 #if NAMED_BIND 1024 else if (strcmp("dns", maptype[mapno]) == 0) 1025 { 1026 int r; 1027 1028 r = dns_getcanonname(host, hbsize, trymx, &status, 1029 pttl); 1030 secure = HOST_SECURE == r; 1031 found = r > 0; 1032 } 1033 #endif /* NAMED_BIND */ 1034 #if NETINFO 1035 else if (strcmp("netinfo", maptype[mapno]) == 0) 1036 { 1037 found = ni_getcanonname(host, hbsize, &status); 1038 } 1039 #endif /* NETINFO */ 1040 else 1041 { 1042 found = false; 1043 status = EX_UNAVAILABLE; 1044 } 1045 1046 /* 1047 ** Heuristic: if $m is not set, we are running during system 1048 ** startup. In this case, when a name is apparently found 1049 ** but has no dot, treat is as not found. This avoids 1050 ** problems if /etc/hosts has no FQDN but is listed first 1051 ** in the service switch. 1052 */ 1053 1054 if (found && 1055 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL)) 1056 break; 1057 1058 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 1059 if (found) 1060 should_try_nis_domain = true; 1061 /* but don't break, as we need to try all methods first */ 1062 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */ 1063 1064 /* see if we should continue */ 1065 if (status == EX_TEMPFAIL) 1066 { 1067 i = MA_TRYAGAIN; 1068 got_tempfail = true; 1069 } 1070 else if (status == EX_NOTFOUND) 1071 i = MA_NOTFOUND; 1072 else 1073 i = MA_UNAVAIL; 1074 if (bitset(1 << mapno, mapreturn[i])) 1075 break; 1076 } 1077 1078 if (found) 1079 { 1080 char *d; 1081 1082 if (tTd(38, 20)) 1083 sm_dprintf("getcanonname(%s), found, ad=%d\n", host, secure); 1084 1085 /* 1086 ** If returned name is still single token, compensate 1087 ** by tagging on $m. This is because some sites set 1088 ** up their DNS or NIS databases wrong. 1089 */ 1090 1091 if ((d = strchr(host, '.')) == NULL || d[1] == '\0') 1092 { 1093 d = macvalue('m', CurEnv); 1094 if (d != NULL && 1095 hbsize > (int) (strlen(host) + strlen(d) + 1)) 1096 { 1097 if (host[strlen(host) - 1] != '.') 1098 (void) sm_strlcat2(host, ".", d, 1099 hbsize); 1100 else 1101 (void) sm_strlcat(host, d, hbsize); 1102 } 1103 else 1104 { 1105 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 1106 if (VendorCode == VENDOR_SUN && 1107 should_try_nis_domain) 1108 { 1109 goto try_nis_domain; 1110 } 1111 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */ 1112 return HOST_NOTFOUND; 1113 } 1114 } 1115 return secure ? HOST_SECURE : HOST_OK; 1116 } 1117 1118 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 1119 if (VendorCode == VENDOR_SUN && should_try_nis_domain) 1120 { 1121 try_nis_domain: 1122 if (nis_domain != NULL && 1123 strlen(nis_domain) + strlen(host) + 1 < hbsize) 1124 { 1125 (void) sm_strlcat2(host, ".", nis_domain, hbsize); 1126 return HOST_OK; 1127 } 1128 } 1129 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */ 1130 1131 if (tTd(38, 20)) 1132 sm_dprintf("getcanonname(%s), failed, status=%d\n", host, 1133 status); 1134 1135 if (got_tempfail) 1136 SM_SET_H_ERRNO(TRY_AGAIN); 1137 else 1138 SM_SET_H_ERRNO(HOST_NOT_FOUND); 1139 1140 return HOST_NOTFOUND; 1141 } 1142 /* 1143 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry 1144 ** 1145 ** Parameters: 1146 ** name -- the name against which to match. 1147 ** dot -- where to reinsert '.' to get FQDN 1148 ** line -- the /etc/hosts line. 1149 ** cbuf -- the location to store the result. 1150 ** cbuflen -- the size of cbuf. 1151 ** 1152 ** Returns: 1153 ** true -- if the line matched the desired name. 1154 ** false -- otherwise. 1155 */ 1156 1157 static bool 1158 extract_canonname(name, dot, line, cbuf, cbuflen) 1159 char *name; 1160 char *dot; 1161 char *line; 1162 char cbuf[]; 1163 int cbuflen; 1164 { 1165 int i; 1166 char *p; 1167 bool found = false; 1168 1169 cbuf[0] = '\0'; 1170 if (line[0] == '#') 1171 return false; 1172 1173 for (i = 1; ; i++) 1174 { 1175 char nbuf[MAXNAME + 1]; /* EAI:hostname */ 1176 1177 p = get_column(line, i, '\0', nbuf, sizeof(nbuf)); 1178 if (p == NULL) 1179 break; 1180 if (*p == '\0') 1181 continue; 1182 if (cbuf[0] == '\0' || 1183 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL)) 1184 { 1185 (void) sm_strlcpy(cbuf, p, cbuflen); 1186 } 1187 if (SM_STRCASEEQ(name, p)) 1188 found = true; 1189 else if (dot != NULL) 1190 { 1191 /* try looking for the FQDN as well */ 1192 *dot = '.'; 1193 if (SM_STRCASEEQ(name, p)) 1194 found = true; 1195 *dot = '\0'; 1196 } 1197 } 1198 if (found && strchr(cbuf, '.') == NULL) 1199 { 1200 /* try to add a domain on the end of the name */ 1201 char *domain = macvalue('m', CurEnv); 1202 1203 if (domain != NULL && 1204 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen) 1205 { 1206 p = &cbuf[i]; 1207 *p++ = '.'; 1208 (void) sm_strlcpy(p, domain, cbuflen - i - 1); 1209 } 1210 } 1211 return found; 1212 } 1213 1214 /* 1215 ** DNS modules 1216 */ 1217 1218 #if NAMED_BIND 1219 # if DNSMAP 1220 1221 # include "sm_resolve.h" 1222 # if NETINET || NETINET6 1223 # include <arpa/inet.h> 1224 # endif 1225 1226 /* 1227 ** DNS_MAP_OPEN -- stub to check proper value for dns map type 1228 */ 1229 1230 bool 1231 dns_map_open(map, mode) 1232 MAP *map; 1233 int mode; 1234 { 1235 if (tTd(38,2)) 1236 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode); 1237 1238 mode &= O_ACCMODE; 1239 if (mode != O_RDONLY) 1240 { 1241 /* issue a pseudo-error message */ 1242 errno = SM_EMAPCANTWRITE; 1243 return false; 1244 } 1245 return true; 1246 } 1247 1248 /* 1249 ** DNS_MAP_PARSEARGS -- parse dns map definition args. 1250 ** 1251 ** Parameters: 1252 ** map -- pointer to MAP 1253 ** args -- pointer to the args on the config line. 1254 ** 1255 ** Returns: 1256 ** true -- if everything parsed OK. 1257 ** false -- otherwise. 1258 */ 1259 1260 #define map_sizelimit map_lockfd /* overload field */ 1261 1262 struct dns_map 1263 { 1264 int dns_m_type; 1265 unsigned int dns_m_options; 1266 }; 1267 1268 bool 1269 dns_map_parseargs(map,args) 1270 MAP *map; 1271 char *args; 1272 { 1273 register char *p = args; 1274 struct dns_map *map_p; 1275 1276 map_p = (struct dns_map *) xalloc(sizeof(*map_p)); 1277 memset(map_p, '\0', sizeof(*map_p)); 1278 map_p->dns_m_type = -1; 1279 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 1280 1281 for (;;) 1282 { 1283 while (SM_ISSPACE(*p)) 1284 p++; 1285 if (*p != '-') 1286 break; 1287 switch (*++p) 1288 { 1289 # if DNSSEC_TEST || _FFR_NAMESERVER 1290 case '@': 1291 ++p; 1292 if (nsportip(p) < 0) 1293 syserr("dns map %s: nsportip(%s)=failed", 1294 map->map_mname, p); 1295 break; 1296 # endif /* DNSSEC_TEST || _FFR_NAMESERVER */ 1297 1298 case 'A': 1299 map->map_mflags |= MF_APPEND; 1300 break; 1301 1302 case 'a': 1303 map->map_app = ++p; 1304 break; 1305 1306 case 'B': /* base domain */ 1307 { 1308 char *h; 1309 1310 while (isascii(*++p) && isspace(*p)) 1311 continue; 1312 h = strchr(p, ' '); 1313 if (h != NULL) 1314 *h = '\0'; 1315 1316 /* 1317 ** slight abuse of map->map_file; it isn't 1318 ** used otherwise in this map type. 1319 */ 1320 1321 map->map_file = newstr(p); 1322 if (h != NULL) 1323 *h = ' '; 1324 } 1325 break; 1326 1327 case 'd': 1328 { 1329 char *h; 1330 1331 ++p; 1332 h = strchr(p, ' '); 1333 if (h != NULL) 1334 *h = '\0'; 1335 map->map_timeout = convtime(p, 's'); 1336 if (h != NULL) 1337 *h = ' '; 1338 } 1339 break; 1340 1341 case 'f': 1342 map->map_mflags |= MF_NOFOLDCASE; 1343 break; 1344 1345 case 'm': 1346 map->map_mflags |= MF_MATCHONLY; 1347 break; 1348 1349 case 'N': 1350 map->map_mflags |= MF_INCLNULL; 1351 map->map_mflags &= ~MF_TRY0NULL; 1352 break; 1353 1354 case 'O': 1355 map->map_mflags &= ~MF_TRY1NULL; 1356 break; 1357 1358 case 'o': 1359 map->map_mflags |= MF_OPTIONAL; 1360 break; 1361 1362 case 'q': 1363 map->map_mflags |= MF_KEEPQUOTES; 1364 break; 1365 1366 case 'S': 1367 # if defined(RES_USE_EDNS0) && defined(RES_USE_DNSSEC) 1368 map_p->dns_m_options |= SM_RES_DNSSEC; 1369 # endif 1370 break; 1371 1372 case 'r': 1373 while (isascii(*++p) && isspace(*p)) 1374 continue; 1375 map->map_retry = atoi(p); 1376 break; 1377 1378 case 't': 1379 map->map_mflags |= MF_NODEFER; 1380 break; 1381 1382 case 'T': 1383 map->map_tapp = ++p; 1384 break; 1385 1386 case 'z': 1387 if (*++p != '\\') 1388 map->map_coldelim = *p; 1389 else 1390 { 1391 switch (*++p) 1392 { 1393 case 'n': 1394 map->map_coldelim = '\n'; 1395 break; 1396 1397 case 't': 1398 map->map_coldelim = '\t'; 1399 break; 1400 1401 default: 1402 map->map_coldelim = '\\'; 1403 } 1404 } 1405 break; 1406 1407 case 'Z': 1408 while (isascii(*++p) && isspace(*p)) 1409 continue; 1410 map->map_sizelimit = atoi(p); 1411 break; 1412 1413 /* Start of dns_map specific args */ 1414 case 'R': /* search field */ 1415 { 1416 char *h; 1417 1418 while (isascii(*++p) && isspace(*p)) 1419 continue; 1420 h = strchr(p, ' '); 1421 if (h != NULL) 1422 *h = '\0'; 1423 map_p->dns_m_type = dns_string_to_type(p); 1424 if (h != NULL) 1425 *h = ' '; 1426 if (map_p->dns_m_type < 0) 1427 syserr("dns map %s: wrong type %s", 1428 map->map_mname, p); 1429 } 1430 break; 1431 1432 } 1433 while (*p != '\0' && !(SM_ISSPACE(*p))) 1434 p++; 1435 if (*p != '\0') 1436 *p++ = '\0'; 1437 } 1438 if (map_p->dns_m_type < 0) 1439 syserr("dns map %s: missing -R type", map->map_mname); 1440 if (map->map_app != NULL) 1441 map->map_app = newstr(map->map_app); 1442 if (map->map_tapp != NULL) 1443 map->map_tapp = newstr(map->map_tapp); 1444 1445 /* 1446 ** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T)); 1447 ** Even if this assumption is wrong, we use only one byte, 1448 ** so it doesn't really matter. 1449 */ 1450 1451 map->map_db1 = (ARBPTR_T) map_p; 1452 return true; 1453 } 1454 1455 /* 1456 ** DNS_MAP_LOOKUP -- perform dns map lookup. 1457 ** 1458 ** Parameters: 1459 ** map -- pointer to MAP 1460 ** name -- name to look up 1461 ** av -- arguments to interpolate into buf. 1462 ** statp -- pointer to status (EX_) 1463 ** 1464 ** Returns: 1465 ** result of lookup if succeeded. 1466 ** NULL -- otherwise. 1467 */ 1468 1469 char * 1470 dns_map_lookup(map, name, av, statp) 1471 MAP *map; 1472 char *name; 1473 char **av; 1474 int *statp; 1475 { 1476 int resnum = 0; 1477 char *vp = NULL, *result = NULL; 1478 size_t vsize; 1479 struct dns_map *map_p; 1480 RESOURCE_RECORD_T *rr = NULL; 1481 DNS_REPLY_T *r = NULL; 1482 unsigned int options; 1483 # if NETINET6 1484 static char buf6[INET6_ADDRSTRLEN]; 1485 # endif 1486 1487 if (tTd(38, 20)) 1488 sm_dprintf("dns_map_lookup(%s, %s)\n", 1489 map->map_mname, name); 1490 1491 map_p = (struct dns_map *)(map->map_db1); 1492 options = map_p->dns_m_options; 1493 if (map->map_file != NULL && *map->map_file != '\0') 1494 { 1495 size_t len; 1496 char *appdomain; 1497 1498 len = strlen(map->map_file) + strlen(name) + 2; 1499 appdomain = (char *) sm_malloc(len); 1500 if (appdomain == NULL) 1501 { 1502 *statp = EX_UNAVAILABLE; 1503 return NULL; 1504 } 1505 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file); 1506 r = dns_lookup_map(appdomain, C_IN, map_p->dns_m_type, 1507 map->map_timeout, map->map_retry, options); 1508 sm_free(appdomain); 1509 } 1510 else 1511 { 1512 r = dns_lookup_map(name, C_IN, map_p->dns_m_type, 1513 map->map_timeout, map->map_retry, options); 1514 } 1515 1516 if (r == NULL) 1517 { 1518 result = NULL; 1519 if (h_errno == TRY_AGAIN || transienterror(errno)) 1520 *statp = EX_TEMPFAIL; 1521 else 1522 *statp = EX_NOTFOUND; 1523 goto cleanup; 1524 } 1525 *statp = EX_OK; 1526 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next) 1527 { 1528 char *type = NULL; 1529 char *value = NULL; 1530 1531 switch (rr->rr_type) 1532 { 1533 case T_NS: 1534 type = "T_NS"; 1535 value = rr->rr_u.rr_txt; 1536 break; 1537 case T_CNAME: 1538 type = "T_CNAME"; 1539 value = rr->rr_u.rr_txt; 1540 break; 1541 case T_AFSDB: 1542 type = "T_AFSDB"; 1543 value = rr->rr_u.rr_mx->mx_r_domain; 1544 break; 1545 case T_SRV: 1546 type = "T_SRV"; 1547 value = rr->rr_u.rr_srv->srv_r_target; 1548 break; 1549 case T_PTR: 1550 type = "T_PTR"; 1551 value = rr->rr_u.rr_txt; 1552 break; 1553 case T_TXT: 1554 type = "T_TXT"; 1555 value = rr->rr_u.rr_txt; 1556 break; 1557 case T_MX: 1558 type = "T_MX"; 1559 value = rr->rr_u.rr_mx->mx_r_domain; 1560 break; 1561 # if NETINET 1562 case T_A: 1563 type = "T_A"; 1564 value = inet_ntoa(*(rr->rr_u.rr_a)); 1565 break; 1566 # endif /* NETINET */ 1567 # if NETINET6 1568 case T_AAAA: 1569 type = "T_AAAA"; 1570 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6, 1571 sizeof(buf6)); 1572 break; 1573 # endif /* NETINET6 */ 1574 # ifdef T_TLSA 1575 case T_TLSA: 1576 type = "T_TLSA"; 1577 value = rr->rr_u.rr_txt; 1578 break; 1579 # endif /* T_TLSA */ 1580 } 1581 1582 (void) strreplnonprt(value, 'X'); 1583 if (map_p->dns_m_type != rr->rr_type) 1584 { 1585 if (tTd(38, 40)) 1586 sm_dprintf("\tskipping type %s (%d) value %s\n", 1587 type != NULL ? type : "<UNKNOWN>", 1588 rr->rr_type, 1589 value != NULL ? value : "<NO VALUE>"); 1590 continue; 1591 } 1592 1593 # if NETINET6 1594 if (rr->rr_type == T_AAAA && value == NULL) 1595 { 1596 result = NULL; 1597 *statp = EX_DATAERR; 1598 if (tTd(38, 40)) 1599 sm_dprintf("\tbad T_AAAA conversion\n"); 1600 goto cleanup; 1601 } 1602 # endif /* NETINET6 */ 1603 if (tTd(38, 40)) 1604 sm_dprintf("\tfound type %s (%d) value %s\n", 1605 type != NULL ? type : "<UNKNOWN>", 1606 rr->rr_type, 1607 value != NULL ? value : "<NO VALUE>"); 1608 if (value != NULL && 1609 (map->map_coldelim == '\0' || 1610 map->map_sizelimit == 1 || 1611 bitset(MF_MATCHONLY, map->map_mflags))) 1612 { 1613 /* Only care about the first match */ 1614 vp = newstr(value); 1615 break; 1616 } 1617 else if (vp == NULL) 1618 { 1619 /* First result */ 1620 vp = newstr(value); 1621 } 1622 else 1623 { 1624 /* concatenate the results */ 1625 int sz; 1626 char *new; 1627 1628 sz = strlen(vp) + strlen(value) + 2; 1629 new = xalloc(sz); 1630 (void) sm_snprintf(new, sz, "%s%c%s", 1631 vp, map->map_coldelim, value); 1632 sm_free(vp); 1633 vp = new; 1634 if (map->map_sizelimit > 0 && 1635 ++resnum >= map->map_sizelimit) 1636 break; 1637 } 1638 } 1639 if (vp == NULL) 1640 { 1641 result = NULL; 1642 *statp = EX_NOTFOUND; 1643 if (tTd(38, 40)) 1644 sm_dprintf("\tno match found\n"); 1645 goto cleanup; 1646 } 1647 1648 /* Cleanly truncate for rulesets */ 1649 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim); 1650 1651 vsize = strlen(vp); 1652 1653 if (LogLevel > 9) 1654 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s", 1655 name, vp); 1656 if (bitset(MF_MATCHONLY, map->map_mflags)) 1657 result = map_rewrite(map, name, strlen(name), NULL); 1658 else 1659 result = map_rewrite(map, vp, vsize, av); 1660 1661 cleanup: 1662 SM_FREE(vp); 1663 dns_free_data(r); 1664 return result; 1665 } 1666 # endif /* DNSMAP */ 1667 #endif /* NAMED_BIND */ 1668 1669 /* 1670 ** NDBM modules 1671 */ 1672 1673 #if NDBM 1674 1675 /* 1676 ** NDBM_MAP_OPEN -- DBM-style map open 1677 */ 1678 1679 bool 1680 ndbm_map_open(map, mode) 1681 MAP *map; 1682 int mode; 1683 { 1684 register DBM *dbm; 1685 int save_errno; 1686 int dfd; 1687 int pfd; 1688 long sff; 1689 int ret; 1690 int smode = S_IREAD; 1691 char dirfile[MAXPATHLEN]; 1692 char pagfile[MAXPATHLEN]; 1693 struct stat st; 1694 struct stat std, stp; 1695 1696 if (tTd(38, 2)) 1697 sm_dprintf("ndbm_map_open(%s, %s, %d)\n", 1698 map->map_mname, map->map_file, mode); 1699 map->map_lockfd = -1; 1700 mode &= O_ACCMODE; 1701 1702 /* do initial file and directory checks */ 1703 if (sm_strlcpyn(dirfile, sizeof(dirfile), 2, 1704 map->map_file, ".dir") >= sizeof(dirfile) || 1705 sm_strlcpyn(pagfile, sizeof(pagfile), 2, 1706 map->map_file, ".pag") >= sizeof(pagfile)) 1707 { 1708 errno = 0; 1709 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1710 syserr("dbm map \"%s\": map file %s name too long", 1711 map->map_mname, map->map_file); 1712 return false; 1713 } 1714 sff = SFF_ROOTOK|SFF_REGONLY; 1715 if (mode == O_RDWR) 1716 { 1717 sff |= SFF_CREAT; 1718 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 1719 sff |= SFF_NOSLINK; 1720 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 1721 sff |= SFF_NOHLINK; 1722 smode = S_IWRITE; 1723 } 1724 else 1725 { 1726 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 1727 sff |= SFF_NOWLINK; 1728 } 1729 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 1730 sff |= SFF_SAFEDIRPATH; 1731 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName, 1732 sff, smode, &std); 1733 if (ret == 0) 1734 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName, 1735 sff, smode, &stp); 1736 1737 if (ret != 0) 1738 { 1739 char *prob = "unsafe"; 1740 1741 /* cannot open this map */ 1742 if (ret == ENOENT) 1743 prob = "missing"; 1744 if (tTd(38, 2)) 1745 sm_dprintf("\t%s map file: %d\n", prob, ret); 1746 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1747 syserr("dbm map \"%s\": %s map file %s", 1748 map->map_mname, prob, map->map_file); 1749 return false; 1750 } 1751 if (std.st_mode == ST_MODE_NOFILE) 1752 mode |= O_CREAT|O_EXCL; 1753 1754 # if LOCK_ON_OPEN 1755 if (mode == O_RDONLY) 1756 mode |= O_SHLOCK; 1757 else 1758 mode |= O_TRUNC|O_EXLOCK; 1759 # else /* LOCK_ON_OPEN */ 1760 if ((mode & O_ACCMODE) == O_RDWR) 1761 { 1762 # if NOFTRUNCATE 1763 /* 1764 ** Warning: race condition. Try to lock the file as 1765 ** quickly as possible after opening it. 1766 ** This may also have security problems on some systems, 1767 ** but there isn't anything we can do about it. 1768 */ 1769 1770 mode |= O_TRUNC; 1771 # else /* NOFTRUNCATE */ 1772 /* 1773 ** This ugly code opens the map without truncating it, 1774 ** locks the file, then truncates it. Necessary to 1775 ** avoid race conditions. 1776 */ 1777 1778 int dirfd; 1779 int pagfd; 1780 long sff = SFF_CREAT|SFF_OPENASROOT; 1781 1782 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 1783 sff |= SFF_NOSLINK; 1784 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 1785 sff |= SFF_NOHLINK; 1786 1787 dirfd = safeopen(dirfile, mode, DBMMODE, sff); 1788 pagfd = safeopen(pagfile, mode, DBMMODE, sff); 1789 1790 if (dirfd < 0 || pagfd < 0) 1791 { 1792 save_errno = errno; 1793 if (dirfd >= 0) 1794 (void) close(dirfd); 1795 if (pagfd >= 0) 1796 (void) close(pagfd); 1797 errno = save_errno; 1798 syserr("ndbm_map_open: cannot create database %s", 1799 map->map_file); 1800 return false; 1801 } 1802 if (ftruncate(dirfd, (off_t) 0) < 0 || 1803 ftruncate(pagfd, (off_t) 0) < 0) 1804 { 1805 save_errno = errno; 1806 (void) close(dirfd); 1807 (void) close(pagfd); 1808 errno = save_errno; 1809 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}", 1810 map->map_file); 1811 return false; 1812 } 1813 1814 /* if new file, get "before" bits for later filechanged check */ 1815 if (std.st_mode == ST_MODE_NOFILE && 1816 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0)) 1817 { 1818 save_errno = errno; 1819 (void) close(dirfd); 1820 (void) close(pagfd); 1821 errno = save_errno; 1822 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file", 1823 map->map_file); 1824 return false; 1825 } 1826 1827 /* have to save the lock for the duration (bletch) */ 1828 map->map_lockfd = dirfd; 1829 (void) close(pagfd); 1830 1831 /* twiddle bits for dbm_open */ 1832 mode &= ~(O_CREAT|O_EXCL); 1833 # endif /* NOFTRUNCATE */ 1834 } 1835 # endif /* LOCK_ON_OPEN */ 1836 1837 /* open the database */ 1838 dbm = dbm_open(map->map_file, mode, DBMMODE); 1839 if (dbm == NULL) 1840 { 1841 save_errno = errno; 1842 if (bitset(MF_ALIAS, map->map_mflags) && 1843 aliaswait(map, ".pag", false)) 1844 return true; 1845 # if !LOCK_ON_OPEN && !NOFTRUNCATE 1846 if (map->map_lockfd >= 0) 1847 (void) close(map->map_lockfd); 1848 # endif 1849 errno = save_errno; 1850 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1851 syserr("Cannot open DBM database %s", map->map_file); 1852 return false; 1853 } 1854 dfd = dbm_dirfno(dbm); 1855 pfd = dbm_pagfno(dbm); 1856 if (dfd == pfd) 1857 { 1858 /* heuristic: if files are linked, this is actually gdbm */ 1859 dbm_close(dbm); 1860 # if !LOCK_ON_OPEN && !NOFTRUNCATE 1861 if (map->map_lockfd >= 0) 1862 (void) close(map->map_lockfd); 1863 # endif 1864 errno = 0; 1865 syserr("dbm map \"%s\": cannot support GDBM", 1866 map->map_mname); 1867 return false; 1868 } 1869 1870 if (filechanged(dirfile, dfd, &std) || 1871 filechanged(pagfile, pfd, &stp)) 1872 { 1873 save_errno = errno; 1874 dbm_close(dbm); 1875 # if !LOCK_ON_OPEN && !NOFTRUNCATE 1876 if (map->map_lockfd >= 0) 1877 (void) close(map->map_lockfd); 1878 # endif 1879 errno = save_errno; 1880 syserr("ndbm_map_open(%s): file changed after open", 1881 map->map_file); 1882 return false; 1883 } 1884 1885 map->map_db1 = (ARBPTR_T) dbm; 1886 1887 /* 1888 ** Need to set map_mtime before the call to aliaswait() 1889 ** as aliaswait() will call map_lookup() which requires 1890 ** map_mtime to be set 1891 */ 1892 1893 if (fstat(pfd, &st) >= 0) 1894 map->map_mtime = st.st_mtime; 1895 1896 if (mode == O_RDONLY) 1897 { 1898 # if LOCK_ON_OPEN 1899 if (dfd >= 0) 1900 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 1901 if (pfd >= 0) 1902 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN); 1903 # endif /* LOCK_ON_OPEN */ 1904 if (bitset(MF_ALIAS, map->map_mflags) && 1905 !aliaswait(map, ".pag", true)) 1906 return false; 1907 } 1908 else 1909 { 1910 map->map_mflags |= MF_LOCKED; 1911 mapchown(map->map_file, dfd, pfd, map->map_file); 1912 } 1913 return true; 1914 } 1915 1916 1917 /* 1918 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map 1919 */ 1920 1921 char * 1922 ndbm_map_lookup(map, name, av, statp) 1923 MAP *map; 1924 char *name; 1925 char **av; 1926 int *statp; 1927 { 1928 datum key, val; 1929 int dfd, pfd; 1930 char keybuf[MAXNAME + 1]; /* EAI:ok */ 1931 # if _FFR_MAP_CHK_FILE 1932 char buf[MAXPATHLEN]; 1933 # endif 1934 const char *fn = NULL; 1935 1936 if (tTd(38, 20)) 1937 sm_dprintf("ndbm_map_lookup(%s, %s)\n", 1938 map->map_mname, name); 1939 1940 key.dptr = name; 1941 key.dsize = strlen(name); 1942 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 1943 { 1944 if (key.dsize > sizeof(keybuf) - 1) 1945 key.dsize = sizeof(keybuf) - 1; 1946 memmove(keybuf, key.dptr, key.dsize); 1947 keybuf[key.dsize] = '\0'; 1948 makelower_buf(keybuf, keybuf, sizeof(keybuf)); 1949 key.dptr = keybuf; 1950 } 1951 # if _FFR_MAP_CHK_FILE 1952 if (!smdb_add_extension(buf, sizeof(buf), map->map_file, "pag")) 1953 { 1954 errno = 0; 1955 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1956 syserr("ndbm map \"%s\": map file %s name too long", 1957 map->map_mname, map->map_file); 1958 return NULL; 1959 } 1960 fn = buf; 1961 # endif 1962 lockdbm: 1963 dfd = dbm_dirfno((DBM *) map->map_db1); 1964 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1965 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH); 1966 pfd = dbm_pagfno((DBM *) map->map_db1); 1967 1968 if (map_has_chged(map, fn, pfd)) 1969 { 1970 /* Reopen the database to sync the cache */ 1971 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR 1972 : O_RDONLY; 1973 1974 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1975 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 1976 map->map_mflags |= MF_CLOSING; 1977 map->map_class->map_close(map); 1978 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 1979 if (map->map_class->map_open(map, omode)) 1980 { 1981 map->map_mflags |= MF_OPEN; 1982 map->map_pid = CurrentPid; 1983 if ((omode & O_ACCMODE) == O_RDWR) 1984 map->map_mflags |= MF_WRITABLE; 1985 goto lockdbm; 1986 } 1987 else 1988 { 1989 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1990 { 1991 extern MAPCLASS BogusMapClass; 1992 1993 *statp = EX_TEMPFAIL; 1994 map->map_orgclass = map->map_class; 1995 map->map_class = &BogusMapClass; 1996 map->map_mflags |= MF_OPEN; 1997 map->map_pid = CurrentPid; 1998 syserr("Cannot reopen NDBM database %s", 1999 map->map_file); 2000 } 2001 return NULL; 2002 } 2003 } 2004 val.dptr = NULL; 2005 if (bitset(MF_TRY0NULL, map->map_mflags)) 2006 { 2007 val = dbm_fetch((DBM *) map->map_db1, key); 2008 if (val.dptr != NULL) 2009 map->map_mflags &= ~MF_TRY1NULL; 2010 } 2011 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags)) 2012 { 2013 key.dsize++; 2014 val = dbm_fetch((DBM *) map->map_db1, key); 2015 if (val.dptr != NULL) 2016 map->map_mflags &= ~MF_TRY0NULL; 2017 } 2018 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2019 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 2020 if (val.dptr == NULL) 2021 return NULL; 2022 if (bitset(MF_MATCHONLY, map->map_mflags)) 2023 return map_rewrite(map, name, strlen(name), NULL); 2024 else 2025 return map_rewrite(map, val.dptr, val.dsize, av); 2026 } 2027 2028 2029 /* 2030 ** NDBM_MAP_STORE -- store a datum in the database 2031 */ 2032 2033 void 2034 ndbm_map_store(map, lhs, rhs) 2035 register MAP *map; 2036 char *lhs; 2037 char *rhs; 2038 { 2039 datum key; 2040 datum data; 2041 int status; 2042 char keybuf[MAXNAME + 1]; /* EAI:ok */ 2043 2044 if (tTd(38, 12)) 2045 sm_dprintf("ndbm_map_store(%s, %s, %s)\n", 2046 map->map_mname, lhs, rhs); 2047 2048 key.dsize = strlen(lhs); 2049 key.dptr = lhs; 2050 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2051 { 2052 if (key.dsize > sizeof(keybuf) - 1) 2053 key.dsize = sizeof(keybuf) - 1; 2054 memmove(keybuf, key.dptr, key.dsize); 2055 keybuf[key.dsize] = '\0'; 2056 makelower_buf(keybuf, keybuf, sizeof(keybuf)); 2057 key.dptr = keybuf; 2058 } 2059 2060 data.dsize = strlen(rhs); 2061 data.dptr = rhs; 2062 2063 if (bitset(MF_INCLNULL, map->map_mflags)) 2064 { 2065 key.dsize++; 2066 data.dsize++; 2067 } 2068 2069 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT); 2070 if (status > 0) 2071 { 2072 if (!bitset(MF_APPEND, map->map_mflags)) 2073 message("050 Warning: duplicate alias name %s", lhs); 2074 else 2075 { 2076 static char *buf = NULL; 2077 static int bufsiz = 0; 2078 auto int xstat; 2079 datum old; 2080 2081 old.dptr = ndbm_map_lookup(map, key.dptr, 2082 (char **) NULL, &xstat); 2083 if (old.dptr != NULL && *(char *) old.dptr != '\0') 2084 { 2085 old.dsize = strlen(old.dptr); 2086 if (data.dsize + old.dsize + 2 > bufsiz) 2087 { 2088 SM_FREE(buf); 2089 bufsiz = data.dsize + old.dsize + 2; 2090 buf = sm_pmalloc_x(bufsiz); 2091 } 2092 (void) sm_strlcpyn(buf, bufsiz, 3, 2093 data.dptr, ",", old.dptr); 2094 data.dsize = data.dsize + old.dsize + 1; 2095 data.dptr = buf; 2096 if (tTd(38, 9)) 2097 sm_dprintf("ndbm_map_store append=%s\n", 2098 data.dptr); 2099 } 2100 } 2101 status = dbm_store((DBM *) map->map_db1, 2102 key, data, DBM_REPLACE); 2103 } 2104 if (status != 0) 2105 syserr("readaliases: dbm put (%s): %d", lhs, status); 2106 } 2107 2108 2109 /* 2110 ** NDBM_MAP_CLOSE -- close the database 2111 */ 2112 2113 void 2114 ndbm_map_close(map) 2115 register MAP *map; 2116 { 2117 if (tTd(38, 9)) 2118 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n", 2119 map->map_mname, map->map_file, map->map_mflags); 2120 2121 if (bitset(MF_WRITABLE, map->map_mflags)) 2122 { 2123 # ifdef NDBM_YP_COMPAT 2124 bool inclnull; 2125 char buf[MAXHOSTNAMELEN]; 2126 2127 inclnull = bitset(MF_INCLNULL, map->map_mflags); 2128 map->map_mflags &= ~MF_INCLNULL; 2129 2130 if (strstr(map->map_file, "/yp/") != NULL) 2131 { 2132 long save_mflags = map->map_mflags; 2133 2134 map->map_mflags |= MF_NOFOLDCASE; 2135 2136 (void) sm_snprintf(buf, sizeof(buf), "%010ld", curtime()); 2137 ndbm_map_store(map, "YP_LAST_MODIFIED", buf); 2138 2139 (void) gethostname(buf, sizeof(buf)); 2140 ndbm_map_store(map, "YP_MASTER_NAME", buf); 2141 2142 map->map_mflags = save_mflags; 2143 } 2144 2145 if (inclnull) 2146 map->map_mflags |= MF_INCLNULL; 2147 # endif /* NDBM_YP_COMPAT */ 2148 2149 /* write out the distinguished alias */ 2150 ndbm_map_store(map, "@", "@"); 2151 } 2152 dbm_close((DBM *) map->map_db1); 2153 2154 /* release lock (if needed) */ 2155 # if !LOCK_ON_OPEN 2156 if (map->map_lockfd >= 0) 2157 (void) close(map->map_lockfd); 2158 # endif 2159 } 2160 2161 #endif /* NDBM */ 2162 /* 2163 ** NEWDB (Hash and BTree) Modules 2164 */ 2165 2166 #if NEWDB 2167 2168 /* 2169 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives. 2170 ** 2171 ** These do rather bizarre locking. If you can lock on open, 2172 ** do that to avoid the condition of opening a database that 2173 ** is being rebuilt. If you don't, we'll try to fake it, but 2174 ** there will be a race condition. If opening for read-only, 2175 ** we immediately release the lock to avoid freezing things up. 2176 ** We really ought to hold the lock, but guarantee that we won't 2177 ** be pokey about it. That's hard to do. 2178 */ 2179 2180 /* these should be K line arguments */ 2181 # if DB_VERSION_MAJOR < 2 2182 # define db_cachesize cachesize 2183 # define h_nelem nelem 2184 # ifndef DB_CACHE_SIZE 2185 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */ 2186 # endif 2187 # ifndef DB_HASH_NELEM 2188 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */ 2189 # endif 2190 # endif /* DB_VERSION_MAJOR < 2 */ 2191 2192 bool 2193 bt_map_open(map, mode) 2194 MAP *map; 2195 int mode; 2196 { 2197 # if DB_VERSION_MAJOR < 2 2198 BTREEINFO btinfo; 2199 # endif 2200 # if DB_VERSION_MAJOR == 2 2201 DB_INFO btinfo; 2202 # endif 2203 # if DB_VERSION_MAJOR > 2 2204 void *btinfo = NULL; 2205 # endif 2206 2207 if (tTd(38, 2)) 2208 sm_dprintf("bt_map_open(%s, %s, %d)\n", 2209 map->map_mname, map->map_file, mode); 2210 2211 # if DB_VERSION_MAJOR < 3 2212 memset(&btinfo, '\0', sizeof(btinfo)); 2213 # ifdef DB_CACHE_SIZE 2214 btinfo.db_cachesize = DB_CACHE_SIZE; 2215 # endif 2216 # endif /* DB_VERSION_MAJOR < 3 */ 2217 2218 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo); 2219 } 2220 2221 bool 2222 hash_map_open(map, mode) 2223 MAP *map; 2224 int mode; 2225 { 2226 # if DB_VERSION_MAJOR < 2 2227 HASHINFO hinfo; 2228 # endif 2229 # if DB_VERSION_MAJOR == 2 2230 DB_INFO hinfo; 2231 # endif 2232 # if DB_VERSION_MAJOR > 2 2233 void *hinfo = NULL; 2234 # endif 2235 2236 if (tTd(38, 2)) 2237 sm_dprintf("hash_map_open(%s, %s, %d)\n", 2238 map->map_mname, map->map_file, mode); 2239 2240 # if DB_VERSION_MAJOR < 3 2241 memset(&hinfo, '\0', sizeof(hinfo)); 2242 # ifdef DB_HASH_NELEM 2243 hinfo.h_nelem = DB_HASH_NELEM; 2244 # endif 2245 # ifdef DB_CACHE_SIZE 2246 hinfo.db_cachesize = DB_CACHE_SIZE; 2247 # endif 2248 # endif /* DB_VERSION_MAJOR < 3 */ 2249 2250 return db_map_open(map, mode, "hash", DB_HASH, &hinfo); 2251 } 2252 2253 static bool 2254 db_map_open(map, mode, mapclassname, dbtype, openinfo) 2255 MAP *map; 2256 int mode; 2257 char *mapclassname; 2258 DBTYPE dbtype; 2259 # if DB_VERSION_MAJOR < 2 2260 const void *openinfo; 2261 # endif 2262 # if DB_VERSION_MAJOR == 2 2263 DB_INFO *openinfo; 2264 # endif 2265 # if DB_VERSION_MAJOR > 2 2266 void **openinfo; 2267 # endif 2268 { 2269 DB *db = NULL; 2270 int i; 2271 int omode; 2272 int smode = S_IREAD; 2273 int fd; 2274 long sff; 2275 int save_errno; 2276 struct stat st; 2277 char buf[MAXPATHLEN]; 2278 2279 /* do initial file and directory checks */ 2280 if (!smdb_add_extension(buf, sizeof(buf), map->map_file, "db")) 2281 { 2282 errno = 0; 2283 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2284 syserr("db map \"%s\": map file %s name too long", 2285 map->map_mname, map->map_file); 2286 return false; 2287 } 2288 2289 mode &= O_ACCMODE; 2290 omode = mode; 2291 2292 sff = SFF_ROOTOK|SFF_REGONLY; 2293 if (mode == O_RDWR) 2294 { 2295 sff |= SFF_CREAT; 2296 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 2297 sff |= SFF_NOSLINK; 2298 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 2299 sff |= SFF_NOHLINK; 2300 smode = S_IWRITE; 2301 } 2302 else 2303 { 2304 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 2305 sff |= SFF_NOWLINK; 2306 } 2307 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 2308 sff |= SFF_SAFEDIRPATH; 2309 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st); 2310 2311 if (i != 0) 2312 { 2313 char *prob = "unsafe"; 2314 2315 /* cannot open this map */ 2316 if (i == ENOENT) 2317 prob = "missing"; 2318 if (tTd(38, 2)) 2319 sm_dprintf("\t%s map file %s: %s\n", prob, buf, sm_errstring(i)); 2320 errno = i; 2321 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2322 syserr("%s map \"%s\": %s map file %s", 2323 mapclassname, map->map_mname, prob, buf); 2324 return false; 2325 } 2326 if (st.st_mode == ST_MODE_NOFILE) 2327 omode |= O_CREAT|O_EXCL; 2328 2329 map->map_lockfd = -1; 2330 2331 # if LOCK_ON_OPEN 2332 if (mode == O_RDWR) 2333 omode |= O_TRUNC|O_EXLOCK; 2334 else 2335 omode |= O_SHLOCK; 2336 # else /* LOCK_ON_OPEN */ 2337 /* 2338 ** Pre-lock the file to avoid race conditions. In particular, 2339 ** since dbopen returns NULL if the file is zero length, we 2340 ** must have a locked instance around the dbopen. 2341 */ 2342 2343 fd = open(buf, omode, DBMMODE); 2344 if (fd < 0) 2345 { 2346 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2347 syserr("db_map_open: cannot pre-open database %s", buf); 2348 return false; 2349 } 2350 2351 /* make sure no baddies slipped in just before the open... */ 2352 if (filechanged(buf, fd, &st)) 2353 { 2354 save_errno = errno; 2355 (void) close(fd); 2356 errno = save_errno; 2357 syserr("db_map_open(%s): file changed after pre-open", buf); 2358 return false; 2359 } 2360 2361 /* if new file, get the "before" bits for later filechanged check */ 2362 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0) 2363 { 2364 save_errno = errno; 2365 (void) close(fd); 2366 errno = save_errno; 2367 syserr("db_map_open(%s): cannot fstat pre-opened file", 2368 buf); 2369 return false; 2370 } 2371 2372 /* actually lock the pre-opened file */ 2373 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX)) 2374 syserr("db_map_open: cannot lock %s", buf); 2375 2376 /* set up mode bits for dbopen */ 2377 if (mode == O_RDWR) 2378 omode |= O_TRUNC; 2379 omode &= ~(O_EXCL|O_CREAT); 2380 # endif /* LOCK_ON_OPEN */ 2381 2382 # if DB_VERSION_MAJOR < 2 2383 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo); 2384 # else /* DB_VERSION_MAJOR < 2 */ 2385 { 2386 int flags = 0; 2387 # if DB_VERSION_MAJOR > 2 2388 int ret; 2389 # endif 2390 2391 if (mode == O_RDONLY) 2392 flags |= DB_RDONLY; 2393 if (bitset(O_CREAT, omode)) 2394 flags |= DB_CREATE; 2395 if (bitset(O_TRUNC, omode)) 2396 flags |= DB_TRUNCATE; 2397 SM_DB_FLAG_ADD(flags); 2398 2399 # if DB_VERSION_MAJOR > 2 2400 ret = db_create(&db, NULL, 0); 2401 # ifdef DB_CACHE_SIZE 2402 if (ret == 0 && db != NULL) 2403 { 2404 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0); 2405 if (ret != 0) 2406 { 2407 (void) db->close(db, 0); 2408 db = NULL; 2409 } 2410 } 2411 # endif /* DB_CACHE_SIZE */ 2412 # ifdef DB_HASH_NELEM 2413 if (dbtype == DB_HASH && ret == 0 && db != NULL) 2414 { 2415 ret = db->set_h_nelem(db, DB_HASH_NELEM); 2416 if (ret != 0) 2417 { 2418 (void) db->close(db, 0); 2419 db = NULL; 2420 } 2421 } 2422 # endif /* DB_HASH_NELEM */ 2423 if (ret == 0 && db != NULL) 2424 { 2425 ret = db->open(db, 2426 DBTXN /* transaction for DB 4.1 */ 2427 buf, NULL, dbtype, flags, DBMMODE); 2428 if (ret != 0) 2429 { 2430 # ifdef DB_OLD_VERSION 2431 if (ret == DB_OLD_VERSION) 2432 ret = EINVAL; 2433 # endif /* DB_OLD_VERSION */ 2434 (void) db->close(db, 0); 2435 db = NULL; 2436 } 2437 } 2438 errno = ret; 2439 # else /* DB_VERSION_MAJOR > 2 */ 2440 errno = db_open(buf, dbtype, flags, DBMMODE, 2441 NULL, openinfo, &db); 2442 # endif /* DB_VERSION_MAJOR > 2 */ 2443 } 2444 # endif /* DB_VERSION_MAJOR < 2 */ 2445 save_errno = errno; 2446 2447 # if !LOCK_ON_OPEN 2448 if (mode == O_RDWR) 2449 map->map_lockfd = fd; 2450 else 2451 (void) close(fd); 2452 # endif /* !LOCK_ON_OPEN */ 2453 2454 if (db == NULL) 2455 { 2456 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && 2457 aliaswait(map, ".db", false)) 2458 return true; 2459 # if !LOCK_ON_OPEN 2460 if (map->map_lockfd >= 0) 2461 (void) close(map->map_lockfd); 2462 # endif 2463 errno = save_errno; 2464 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2465 syserr("Cannot open %s database %s", 2466 mapclassname, buf); 2467 return false; 2468 } 2469 2470 # if DB_VERSION_MAJOR < 2 2471 fd = db->fd(db); 2472 # else 2473 fd = -1; 2474 errno = db->fd(db, &fd); 2475 # endif /* DB_VERSION_MAJOR < 2 */ 2476 if (filechanged(buf, fd, &st)) 2477 { 2478 save_errno = errno; 2479 # if DB_VERSION_MAJOR < 2 2480 (void) db->close(db); 2481 # else 2482 errno = db->close(db, 0); 2483 # endif /* DB_VERSION_MAJOR < 2 */ 2484 # if !LOCK_ON_OPEN 2485 if (map->map_lockfd >= 0) 2486 (void) close(map->map_lockfd); 2487 # endif 2488 errno = save_errno; 2489 syserr("db_map_open(%s): file changed after open", buf); 2490 return false; 2491 } 2492 2493 if (mode == O_RDWR) 2494 map->map_mflags |= MF_LOCKED; 2495 # if LOCK_ON_OPEN 2496 if (fd >= 0 && mode == O_RDONLY) 2497 (void) lockfile(fd, buf, NULL, LOCK_UN); 2498 # endif 2499 2500 /* try to make sure that at least the database header is on disk */ 2501 if (mode == O_RDWR) 2502 { 2503 (void) db->sync(db, 0); 2504 mapchown(map->map_file, fd, -1, buf); 2505 } 2506 2507 map->map_db2 = (ARBPTR_T) db; 2508 2509 /* 2510 ** Need to set map_mtime before the call to aliaswait() 2511 ** as aliaswait() will call map_lookup() which requires 2512 ** map_mtime to be set 2513 */ 2514 2515 if (fd >= 0 && fstat(fd, &st) >= 0) 2516 map->map_mtime = st.st_mtime; 2517 2518 # if _FFR_TESTS 2519 if (tTd(68, 101) && fd >= 0 && mode == O_RDONLY) 2520 { 2521 int sl; 2522 2523 sl = tTdlevel(68) - 100; 2524 /* XXX test checks for map type!!! */ 2525 sm_dprintf("hash_map_open: sleep=%d\n", sl); 2526 sleep(sl); 2527 } 2528 # endif 2529 2530 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && 2531 !aliaswait(map, ".db", true)) 2532 return false; 2533 return true; 2534 } 2535 2536 2537 /* 2538 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map 2539 */ 2540 2541 char * 2542 db_map_lookup(map, name, av, statp) 2543 MAP *map; 2544 char *name; 2545 char **av; 2546 int *statp; 2547 { 2548 DBT key, val; 2549 register DB *db = (DB *) map->map_db2; 2550 int st; 2551 int save_errno; 2552 int fd; 2553 char keybuf[MAXNAME + 1]; /* EAI:ok */ 2554 char buf[MAXPATHLEN]; 2555 2556 memset(&key, '\0', sizeof(key)); 2557 memset(&val, '\0', sizeof(val)); 2558 2559 if (tTd(38, 20)) 2560 sm_dprintf("db_map_lookup(%s, %s)\n", 2561 map->map_mname, name); 2562 if (!smdb_add_extension(buf, sizeof(buf), map->map_file, "db")) 2563 { 2564 errno = 0; 2565 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2566 syserr("db map \"%s\": map file %s name too long", 2567 map->map_mname, map->map_file); 2568 return NULL; 2569 } 2570 2571 key.size = strlen(name); 2572 if (key.size > sizeof(keybuf) - 1) 2573 key.size = sizeof(keybuf) - 1; 2574 key.data = keybuf; 2575 memmove(keybuf, name, key.size); 2576 keybuf[key.size] = '\0'; 2577 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2578 makelower_buf(keybuf, keybuf, sizeof(keybuf)); 2579 lockdb: 2580 # if DB_VERSION_MAJOR < 2 2581 fd = db->fd(db); 2582 # else 2583 fd = -1; 2584 errno = db->fd(db, &fd); 2585 # endif /* DB_VERSION_MAJOR < 2 */ 2586 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2587 (void) lockfile(fd, buf, NULL, LOCK_SH); 2588 if (map_has_chged(map, buf, fd)) 2589 { 2590 /* Reopen the database to sync the cache */ 2591 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR 2592 : O_RDONLY; 2593 2594 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2595 (void) lockfile(fd, buf, NULL, LOCK_UN); 2596 map->map_mflags |= MF_CLOSING; 2597 map->map_class->map_close(map); 2598 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 2599 if (map->map_class->map_open(map, omode)) 2600 { 2601 map->map_mflags |= MF_OPEN; 2602 map->map_pid = CurrentPid; 2603 if ((omode & O_ACCMODE) == O_RDWR) 2604 map->map_mflags |= MF_WRITABLE; 2605 db = (DB *) map->map_db2; 2606 goto lockdb; 2607 } 2608 else 2609 { 2610 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2611 { 2612 extern MAPCLASS BogusMapClass; 2613 2614 *statp = EX_TEMPFAIL; 2615 map->map_orgclass = map->map_class; 2616 map->map_class = &BogusMapClass; 2617 map->map_mflags |= MF_OPEN; 2618 map->map_pid = CurrentPid; 2619 syserr("Cannot reopen DB database %s", 2620 map->map_file); 2621 } 2622 return NULL; 2623 } 2624 } 2625 2626 st = 1; 2627 if (bitset(MF_TRY0NULL, map->map_mflags)) 2628 { 2629 # if DB_VERSION_MAJOR < 2 2630 st = db->get(db, &key, &val, 0); 2631 # else /* DB_VERSION_MAJOR < 2 */ 2632 errno = db->get(db, NULL, &key, &val, 0); 2633 switch (errno) 2634 { 2635 case DB_NOTFOUND: 2636 case DB_KEYEMPTY: 2637 st = 1; 2638 break; 2639 2640 case 0: 2641 st = 0; 2642 break; 2643 2644 default: 2645 st = -1; 2646 break; 2647 } 2648 # endif /* DB_VERSION_MAJOR < 2 */ 2649 if (st == 0) 2650 map->map_mflags &= ~MF_TRY1NULL; 2651 } 2652 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags)) 2653 { 2654 key.size++; 2655 # if DB_VERSION_MAJOR < 2 2656 st = db->get(db, &key, &val, 0); 2657 # else /* DB_VERSION_MAJOR < 2 */ 2658 errno = db->get(db, NULL, &key, &val, 0); 2659 switch (errno) 2660 { 2661 case DB_NOTFOUND: 2662 case DB_KEYEMPTY: 2663 st = 1; 2664 break; 2665 2666 case 0: 2667 st = 0; 2668 break; 2669 2670 default: 2671 st = -1; 2672 break; 2673 } 2674 # endif /* DB_VERSION_MAJOR < 2 */ 2675 if (st == 0) 2676 map->map_mflags &= ~MF_TRY0NULL; 2677 } 2678 save_errno = errno; 2679 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2680 (void) lockfile(fd, buf, NULL, LOCK_UN); 2681 if (st != 0) 2682 { 2683 errno = save_errno; 2684 if (st < 0) 2685 syserr("db_map_lookup: get (%s)", name); 2686 return NULL; 2687 } 2688 if (bitset(MF_MATCHONLY, map->map_mflags)) 2689 return map_rewrite(map, name, strlen(name), NULL); 2690 else 2691 return map_rewrite(map, val.data, val.size, av); 2692 } 2693 2694 2695 /* 2696 ** DB_MAP_STORE -- store a datum in the NEWDB database 2697 */ 2698 2699 void 2700 db_map_store(map, lhs, rhs) 2701 register MAP *map; 2702 char *lhs; 2703 char *rhs; 2704 { 2705 int status; 2706 DBT key; 2707 DBT data; 2708 register DB *db = map->map_db2; 2709 char keybuf[MAXNAME + 1]; /* EAI:ok */ 2710 2711 memset(&key, '\0', sizeof(key)); 2712 memset(&data, '\0', sizeof(data)); 2713 2714 if (tTd(38, 12)) 2715 sm_dprintf("db_map_store(%s, %s, %s)\n", 2716 map->map_mname, lhs, rhs); 2717 2718 key.size = strlen(lhs); 2719 key.data = lhs; 2720 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2721 { 2722 if (key.size > sizeof(keybuf) - 1) 2723 key.size = sizeof(keybuf) - 1; 2724 memmove(keybuf, key.data, key.size); 2725 keybuf[key.size] = '\0'; 2726 makelower_buf(keybuf, keybuf, sizeof(keybuf)); 2727 key.data = keybuf; 2728 } 2729 2730 data.size = strlen(rhs); 2731 data.data = rhs; 2732 2733 if (bitset(MF_INCLNULL, map->map_mflags)) 2734 { 2735 key.size++; 2736 data.size++; 2737 } 2738 2739 # if DB_VERSION_MAJOR < 2 2740 status = db->put(db, &key, &data, R_NOOVERWRITE); 2741 # else /* DB_VERSION_MAJOR < 2 */ 2742 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE); 2743 switch (errno) 2744 { 2745 case DB_KEYEXIST: 2746 status = 1; 2747 break; 2748 2749 case 0: 2750 status = 0; 2751 break; 2752 2753 default: 2754 status = -1; 2755 break; 2756 } 2757 # endif /* DB_VERSION_MAJOR < 2 */ 2758 if (status > 0) 2759 { 2760 if (!bitset(MF_APPEND, map->map_mflags)) 2761 message("050 Warning: duplicate alias name %s", lhs); 2762 else 2763 { 2764 static char *buf = NULL; 2765 static int bufsiz = 0; 2766 DBT old; 2767 2768 memset(&old, '\0', sizeof(old)); 2769 2770 old.data = db_map_lookup(map, key.data, 2771 (char **) NULL, &status); 2772 if (old.data != NULL) 2773 { 2774 old.size = strlen(old.data); 2775 if (data.size + old.size + 2 > (size_t) bufsiz) 2776 { 2777 SM_FREE(buf); 2778 bufsiz = data.size + old.size + 2; 2779 buf = sm_pmalloc_x(bufsiz); 2780 } 2781 (void) sm_strlcpyn(buf, bufsiz, 3, 2782 (char *) data.data, ",", 2783 (char *) old.data); 2784 data.size = data.size + old.size + 1; 2785 data.data = buf; 2786 if (tTd(38, 9)) 2787 sm_dprintf("db_map_store append=%s\n", 2788 (char *) data.data); 2789 } 2790 } 2791 # if DB_VERSION_MAJOR < 2 2792 status = db->put(db, &key, &data, 0); 2793 # else 2794 status = errno = db->put(db, NULL, &key, &data, 0); 2795 # endif /* DB_VERSION_MAJOR < 2 */ 2796 } 2797 if (status != 0) 2798 syserr("readaliases: db put (%s)", lhs); 2799 } 2800 2801 2802 /* 2803 ** DB_MAP_CLOSE -- add distinguished entries and close the database 2804 */ 2805 2806 void 2807 db_map_close(map) 2808 MAP *map; 2809 { 2810 register DB *db = map->map_db2; 2811 2812 if (tTd(38, 9)) 2813 sm_dprintf("db_map_close(%s, %s, %lx)\n", 2814 map->map_mname, map->map_file, map->map_mflags); 2815 2816 if (bitset(MF_WRITABLE, map->map_mflags)) 2817 { 2818 /* write out the distinguished alias */ 2819 db_map_store(map, "@", "@"); 2820 } 2821 2822 (void) db->sync(db, 0); 2823 2824 # if !LOCK_ON_OPEN 2825 if (map->map_lockfd >= 0) 2826 (void) close(map->map_lockfd); 2827 # endif /* !LOCK_ON_OPEN */ 2828 2829 # if DB_VERSION_MAJOR < 2 2830 if (db->close(db) != 0) 2831 # else /* DB_VERSION_MAJOR < 2 */ 2832 /* 2833 ** Berkeley DB can use internal shared memory 2834 ** locking for its memory pool. Closing a map 2835 ** opened by another process will interfere 2836 ** with the shared memory and locks of the parent 2837 ** process leaving things in a bad state. 2838 */ 2839 2840 /* 2841 ** If this map was not opened by the current 2842 ** process, do not close the map but recover 2843 ** the file descriptor. 2844 */ 2845 2846 if (map->map_pid != CurrentPid) 2847 { 2848 int fd = -1; 2849 2850 errno = db->fd(db, &fd); 2851 if (fd >= 0) 2852 (void) close(fd); 2853 return; 2854 } 2855 2856 if ((errno = db->close(db, 0)) != 0) 2857 # endif /* DB_VERSION_MAJOR < 2 */ 2858 syserr("db_map_close(%s, %s, %lx): db close failure", 2859 map->map_mname, map->map_file, map->map_mflags); 2860 } 2861 #endif /* NEWDB */ 2862 2863 #if CDB 2864 /* 2865 ** CDB Modules 2866 */ 2867 2868 bool 2869 cdb_map_open(map, mode) 2870 MAP *map; 2871 int mode; 2872 { 2873 int fd, status, omode, smode; 2874 long sff; 2875 struct stat st; 2876 struct cdb *cdbp; 2877 char buf[MAXPATHLEN]; 2878 2879 if (tTd(38, 2)) 2880 sm_dprintf("cdb_map_open(%s, %s, %s)\n", 2881 map->map_mname, map->map_file, 2882 O_RDWR == (mode & O_ACCMODE) ? "rdwr" : "rdonly"); 2883 map->map_db1 = (ARBPTR_T)NULL; 2884 map->map_db2 = (ARBPTR_T)NULL; 2885 2886 mode &= O_ACCMODE; 2887 omode = mode; 2888 2889 /* 2890 ** Note: 2891 ** The code to add the extension and to set up safefile() 2892 ** and open() should be in a common function 2893 ** (it would be nice to re-use libsmdb?) 2894 */ 2895 2896 if (!smdb_add_extension(buf, sizeof(buf), map->map_file, CDBext)) 2897 { 2898 errno = 0; 2899 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2900 syserr("cdb map \"%s\": map file %s name too long", 2901 map->map_mname, map->map_file); 2902 return false; 2903 } 2904 2905 sff = SFF_ROOTOK|SFF_REGONLY; 2906 if (mode == O_RDWR) 2907 { 2908 sff |= SFF_CREAT; 2909 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 2910 sff |= SFF_NOSLINK; 2911 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 2912 sff |= SFF_NOHLINK; 2913 smode = S_IWRITE; 2914 map->map_mflags |= MF_LOCKED; 2915 } 2916 else 2917 { 2918 smode = S_IREAD; 2919 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 2920 sff |= SFF_NOWLINK; 2921 } 2922 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 2923 sff |= SFF_SAFEDIRPATH; 2924 status = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st); 2925 if (status != 0) 2926 { 2927 char *prob = "unsafe"; 2928 2929 /* cannot open this map */ 2930 if (status == ENOENT) 2931 prob = "missing"; 2932 errno = status; 2933 if (tTd(38, 2)) 2934 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(status)); 2935 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2936 syserr("%s map \"%s\": %s map file %s", 2937 map->map_mname, prob, buf, sm_errstring(status)); 2938 return false; 2939 } 2940 2941 if (st.st_mode == ST_MODE_NOFILE) 2942 omode |= O_CREAT|O_EXCL; 2943 # if LOCK_ON_OPEN 2944 if (mode == O_RDWR) 2945 omode |= O_TRUNC|O_EXLOCK; 2946 else 2947 omode |= O_SHLOCK; 2948 # else 2949 if (mode == O_RDWR) 2950 omode |= O_TRUNC; 2951 # endif /* LOCK_ON_OPEN */ 2952 2953 fd = open(buf, omode, DBMMODE); 2954 if (fd < 0) 2955 { 2956 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2957 syserr("cdb_map_open: cannot open database %s", buf); 2958 return false; 2959 } 2960 2961 # if !LOCK_ON_OPEN 2962 /* make sure no baddies slipped in just before the open... */ 2963 if (filechanged(buf, fd, &st)) 2964 { 2965 int save_errno; 2966 2967 save_errno = errno; 2968 (void) close(fd); 2969 errno = save_errno; 2970 syserr("cdb_map_open(%s): file changed after open", buf); 2971 return false; 2972 } 2973 2974 /* actually lock the opened file */ 2975 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX)) 2976 syserr("cdb_map_open: cannot lock %s", buf); 2977 # else /* !LOCK_ON_OPEN */ 2978 if (tTd(55, 60)) 2979 sm_dprintf("lockopen(%s, fd=%d, action=nb, type=%s): SUCCESS\n", 2980 buf, fd, mode == O_RDONLY ? "rd" : "wr"); 2981 # endif /* !LOCK_ON_OPEN */ 2982 2983 map->map_lockfd = fd; 2984 2985 if (fd >= 0 && fstat(fd, &st) >= 0) 2986 map->map_mtime = st.st_mtime; 2987 2988 /* only for aliases! */ 2989 if (mode == O_RDWR) 2990 { 2991 struct cdb_make *cdbmp; 2992 2993 cdbmp = (struct cdb_make *) xalloc(sizeof(*cdbmp)); 2994 status = cdb_make_start(cdbmp, fd); 2995 if (status != 0) 2996 { 2997 close(fd); 2998 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2999 syserr("initialization of cdb map (make) failed"); 3000 return false; 3001 } 3002 3003 map->map_db2 = (ARBPTR_T)cdbmp; 3004 mapchown(map->map_file, fd, -1, buf); 3005 return true; 3006 } 3007 (void) lockfile(fd, buf, NULL, LOCK_UN); 3008 # if _FFR_TESTS 3009 if (tTd(68, 101)) 3010 { 3011 int sl; 3012 3013 sl = tTdlevel(68) - 100; 3014 sm_dprintf("cdb_map_open: sleep=%d\n", sl); 3015 sleep(sl); 3016 } 3017 # endif 3018 3019 cdbp = (struct cdb *) xalloc(sizeof(*cdbp)); 3020 status = cdb_init(cdbp, fd); 3021 if (status != 0) 3022 { 3023 close(fd); 3024 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3025 syserr("initialization of cdb map failed"); 3026 return false; 3027 } 3028 3029 map->map_db1 = (ARBPTR_T)cdbp; 3030 if (bitset(MF_ALIAS, map->map_mflags) && !aliaswait(map, CDBEXT, true)) 3031 { 3032 close(fd); /* XXX more error handling needed? */ 3033 return false; 3034 } 3035 return true; 3036 } 3037 3038 char * 3039 cdb_map_lookup(map, name, av, statp) 3040 MAP *map; 3041 char *name; 3042 char **av; 3043 int *statp; 3044 { 3045 char *data; 3046 struct cdb *cdbmap; 3047 unsigned int klen, dlen; 3048 int st, fd; 3049 char key[MAXNAME + 1]; /* EAI:ok */ 3050 char buf[MAXPATHLEN]; 3051 3052 data = NULL; 3053 cdbmap = map->map_db1; 3054 if (tTd(38, 20)) 3055 sm_dprintf("cdb_map_lookup(%s, %s)\n", map->map_mname, name); 3056 3057 if (!smdb_add_extension(buf, sizeof(buf), map->map_file, CDBext)) 3058 { 3059 errno = 0; 3060 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3061 syserr("cdb map \"%s\": map file %s name too long", 3062 map->map_mname, map->map_file); 3063 return NULL; 3064 } 3065 3066 klen = strlen(name); 3067 if (klen > sizeof(key) - 1) 3068 klen = sizeof(key) - 1; 3069 memmove(key, name, klen); 3070 key[klen] = '\0'; 3071 3072 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3073 makelower_buf(key, key, sizeof(key)); 3074 3075 lockdb: 3076 fd = map->map_lockfd; 3077 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 3078 (void) lockfile(fd, buf, NULL, LOCK_SH); 3079 if (map_has_chged(map, buf, fd)) 3080 { 3081 /* Reopen the database to sync the cache */ 3082 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR 3083 : O_RDONLY; 3084 3085 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 3086 (void) lockfile(fd, buf, NULL, LOCK_UN); 3087 map->map_mflags |= MF_CLOSING; 3088 map->map_class->map_close(map); 3089 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 3090 if (map->map_class->map_open(map, omode)) 3091 { 3092 map->map_mflags |= MF_OPEN; 3093 if ((omode & O_ACCMODE) == O_RDWR) 3094 map->map_mflags |= MF_WRITABLE; 3095 cdbmap = map->map_db1; 3096 goto lockdb; 3097 } 3098 else 3099 { 3100 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3101 { 3102 extern MAPCLASS BogusMapClass; 3103 3104 *statp = EX_TEMPFAIL; 3105 map->map_orgclass = map->map_class; 3106 map->map_class = &BogusMapClass; 3107 map->map_mflags |= MF_OPEN; 3108 syserr("Cannot reopen CDB database %s", 3109 map->map_file); 3110 } 3111 return NULL; 3112 } 3113 } 3114 3115 st = 0; 3116 if (bitset(MF_TRY0NULL, map->map_mflags)) 3117 { 3118 st = cdb_find(cdbmap, key, klen); 3119 if (st == 1) 3120 map->map_mflags &= ~MF_TRY1NULL; 3121 } 3122 if (st != 1 && bitset(MF_TRY1NULL, map->map_mflags)) 3123 { 3124 st = cdb_find(cdbmap, key, klen + 1); 3125 if (st == 1) 3126 map->map_mflags &= ~MF_TRY0NULL; 3127 } 3128 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 3129 (void) lockfile(fd, buf, NULL, LOCK_UN); 3130 if (st != 1) 3131 { 3132 if (st < 0) 3133 syserr("cdb_map_lookup: get (%s)", name); 3134 return NULL; 3135 } 3136 else 3137 { 3138 dlen = cdb_datalen(cdbmap); 3139 data = malloc(dlen + 1); 3140 cdb_read(cdbmap, data, dlen, cdb_datapos(cdbmap)); 3141 data[dlen] = '\0'; 3142 } 3143 if (bitset(MF_MATCHONLY, map->map_mflags)) 3144 return map_rewrite(map, name, strlen(name), NULL); 3145 else 3146 return map_rewrite(map, data, dlen, av); 3147 } 3148 3149 /* 3150 ** CDB_MAP_STORE -- store a datum in the CDB database 3151 */ 3152 3153 void 3154 cdb_map_store(map, lhs, rhs) 3155 MAP *map; 3156 char *lhs; 3157 char *rhs; 3158 { 3159 struct cdb_make *cdbmp; 3160 size_t klen; 3161 size_t vlen; 3162 int status; 3163 char keybuf[MAXNAME + 1]; /* EAI:ok */ 3164 3165 cdbmp = map->map_db2; 3166 if (cdbmp == NULL) 3167 return; /* XXX */ 3168 3169 klen = strlen(lhs); 3170 vlen = strlen(rhs); 3171 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3172 { 3173 if (klen > sizeof(keybuf) - 1) 3174 klen = sizeof(keybuf) - 1; 3175 memmove(keybuf, lhs, klen); 3176 keybuf[klen] = '\0'; 3177 makelower_buf(keybuf, keybuf, sizeof(keybuf)); 3178 lhs = keybuf; 3179 } 3180 3181 if (bitset(MF_INCLNULL, map->map_mflags)) 3182 { 3183 klen++; 3184 vlen++; 3185 } 3186 3187 /* flags? */ 3188 status = cdb_make_put(cdbmp, lhs, klen, rhs, vlen, 0); 3189 /* and now? */ 3190 } 3191 3192 void 3193 cdb_map_close(map) 3194 MAP * map; 3195 { 3196 struct cdb *cdbp; 3197 struct cdb_make *cdbmp; 3198 int fd; 3199 3200 fd = -1; 3201 cdbp = map->map_db1; 3202 if (cdbp != NULL) 3203 { 3204 if (tTd(38, 20)) 3205 sm_dprintf("cdb_map_close(%p): cdbp\n", (void *)cdbp); 3206 fd = cdb_fileno(cdbp); 3207 cdb_free(cdbp); 3208 SM_FREE(cdbp); 3209 } 3210 cdbmp = map->map_db2; 3211 if (cdbmp != NULL) 3212 { 3213 if (tTd(38, 20)) 3214 sm_dprintf("cdb_map_close(%p): cdmbp\n", (void *)cdbmp); 3215 fd = cdb_fileno(cdbmp); 3216 3217 /* write out the distinguished alias */ 3218 /* XXX Why isn't this in a common place? */ 3219 cdb_map_store(map, "@", "@"); 3220 3221 if (cdb_make_finish(cdbmp) != 0) 3222 syserr("cdb: cdb_make_finish(%s) failed", 3223 map->map_file); 3224 if (fd >= 0) 3225 { 3226 if (fsync(fd) == -1) 3227 syserr("cdb: fsync(%s) failed", map->map_file); 3228 if (close(fd) == -1) 3229 syserr("cdb: close(%s) failed", map->map_file); 3230 fd = -1; 3231 } 3232 SM_FREE(cdbmp); 3233 } 3234 if (fd >=0) 3235 close(fd); 3236 } 3237 #endif /* CDB */ 3238 3239 /* 3240 ** NIS Modules 3241 */ 3242 3243 #if NIS 3244 3245 # ifndef YPERR_BUSY 3246 # define YPERR_BUSY 16 3247 # endif 3248 3249 /* 3250 ** NIS_MAP_OPEN -- open NIS map 3251 */ 3252 3253 bool 3254 nis_map_open(map, mode) 3255 MAP *map; 3256 int mode; 3257 { 3258 int yperr; 3259 register char *p; 3260 auto char *vp; 3261 auto int vsize; 3262 3263 if (tTd(38, 2)) 3264 sm_dprintf("nis_map_open(%s, %s, %d)\n", 3265 map->map_mname, map->map_file, mode); 3266 3267 mode &= O_ACCMODE; 3268 if (mode != O_RDONLY) 3269 { 3270 /* issue a pseudo-error message */ 3271 errno = SM_EMAPCANTWRITE; 3272 return false; 3273 } 3274 3275 p = strchr(map->map_file, '@'); 3276 if (p != NULL) 3277 { 3278 *p++ = '\0'; 3279 if (*p != '\0') 3280 map->map_domain = p; 3281 } 3282 3283 if (*map->map_file == '\0') 3284 map->map_file = "mail.aliases"; 3285 3286 if (map->map_domain == NULL) 3287 { 3288 yperr = yp_get_default_domain(&map->map_domain); 3289 if (yperr != 0) 3290 { 3291 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3292 syserr("451 4.3.5 NIS map %s specified, but NIS not running", 3293 map->map_file); 3294 return false; 3295 } 3296 } 3297 3298 /* check to see if this map actually exists */ 3299 vp = NULL; 3300 yperr = yp_match(map->map_domain, map->map_file, "@", 1, 3301 &vp, &vsize); 3302 if (tTd(38, 10)) 3303 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n", 3304 map->map_domain, map->map_file, yperr_string(yperr)); 3305 SM_FREE(vp); 3306 3307 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY) 3308 { 3309 /* 3310 ** We ought to be calling aliaswait() here if this is an 3311 ** alias file, but powerful HP-UX NIS servers apparently 3312 ** don't insert the @:@ token into the alias map when it 3313 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX. 3314 */ 3315 3316 # if 0 3317 if (!bitset(MF_ALIAS, map->map_mflags) || 3318 aliaswait(map, NULL, true)) 3319 # endif 3320 return true; 3321 } 3322 3323 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3324 { 3325 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s", 3326 map->map_file, map->map_domain, yperr_string(yperr)); 3327 } 3328 3329 return false; 3330 } 3331 3332 3333 /* 3334 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map 3335 */ 3336 3337 /* ARGSUSED3 */ 3338 char * 3339 nis_map_lookup(map, name, av, statp) 3340 MAP *map; 3341 char *name; 3342 char **av; 3343 int *statp; 3344 { 3345 char *vp; 3346 auto int vsize; 3347 int buflen; 3348 int yperr; 3349 char keybuf[MAXNAME + 1]; /* EAI:ok */ 3350 char *SM_NONVOLATILE result = NULL; 3351 3352 if (tTd(38, 20)) 3353 sm_dprintf("nis_map_lookup(%s, %s)\n", 3354 map->map_mname, name); 3355 3356 buflen = strlen(name); 3357 if (buflen > sizeof(keybuf) - 1) 3358 buflen = sizeof(keybuf) - 1; 3359 memmove(keybuf, name, buflen); 3360 keybuf[buflen] = '\0'; 3361 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3362 makelower_buf(keybuf, keybuf, sizeof(keybuf)); 3363 yperr = YPERR_KEY; 3364 vp = NULL; 3365 if (bitset(MF_TRY0NULL, map->map_mflags)) 3366 { 3367 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 3368 &vp, &vsize); 3369 if (yperr == 0) 3370 map->map_mflags &= ~MF_TRY1NULL; 3371 } 3372 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags)) 3373 { 3374 SM_FREE(vp); 3375 buflen++; 3376 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 3377 &vp, &vsize); 3378 if (yperr == 0) 3379 map->map_mflags &= ~MF_TRY0NULL; 3380 } 3381 if (yperr != 0) 3382 { 3383 if (yperr != YPERR_KEY && yperr != YPERR_BUSY) 3384 map->map_mflags &= ~(MF_VALID|MF_OPEN); 3385 SM_FREE(vp); 3386 return NULL; 3387 } 3388 SM_TRY 3389 if (bitset(MF_MATCHONLY, map->map_mflags)) 3390 result = map_rewrite(map, name, strlen(name), NULL); 3391 else 3392 result = map_rewrite(map, vp, vsize, av); 3393 SM_FINALLY 3394 SM_FREE(vp); 3395 SM_END_TRY 3396 return result; 3397 } 3398 3399 3400 /* 3401 ** NIS_GETCANONNAME -- look up canonical name in NIS 3402 */ 3403 3404 static bool 3405 nis_getcanonname(name, hbsize, statp) 3406 char *name; 3407 int hbsize; 3408 int *statp; 3409 { 3410 char *vp; 3411 auto int vsize; 3412 int keylen; 3413 int yperr; 3414 static bool try0null = true; 3415 static bool try1null = true; 3416 static char *yp_domain = NULL; 3417 char host_record[MAXLINE]; 3418 char cbuf[MAXNAME]; /* EAI:hostname */ 3419 char nbuf[MAXNAME + 1]; /* EAI:hostname */ 3420 3421 if (tTd(38, 20)) 3422 sm_dprintf("nis_getcanonname(%s)\n", name); 3423 3424 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf)) 3425 { 3426 *statp = EX_UNAVAILABLE; 3427 return false; 3428 } 3429 (void) shorten_hostname(nbuf); 3430 keylen = strlen(nbuf); 3431 3432 if (yp_domain == NULL) 3433 (void) yp_get_default_domain(&yp_domain); 3434 makelower_buf(nbuf, nbuf, sizeof(nbuf)); 3435 yperr = YPERR_KEY; 3436 vp = NULL; 3437 if (try0null) 3438 { 3439 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 3440 &vp, &vsize); 3441 if (yperr == 0) 3442 try1null = false; 3443 } 3444 if (yperr == YPERR_KEY && try1null) 3445 { 3446 SM_FREE(vp); 3447 keylen++; 3448 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 3449 &vp, &vsize); 3450 if (yperr == 0) 3451 try0null = false; 3452 } 3453 if (yperr != 0) 3454 { 3455 if (yperr == YPERR_KEY) 3456 *statp = EX_NOHOST; 3457 else if (yperr == YPERR_BUSY) 3458 *statp = EX_TEMPFAIL; 3459 else 3460 *statp = EX_UNAVAILABLE; 3461 SM_FREE(vp); 3462 return false; 3463 } 3464 (void) sm_strlcpy(host_record, vp, sizeof(host_record)); 3465 sm_free(vp); 3466 if (tTd(38, 44)) 3467 sm_dprintf("got record `%s'\n", host_record); 3468 vp = strpbrk(host_record, "#\n"); 3469 if (vp != NULL) 3470 *vp = '\0'; 3471 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof(cbuf))) 3472 { 3473 /* this should not happen, but.... */ 3474 *statp = EX_NOHOST; 3475 return false; 3476 } 3477 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize) 3478 { 3479 *statp = EX_UNAVAILABLE; 3480 return false; 3481 } 3482 *statp = EX_OK; 3483 return true; 3484 } 3485 3486 #endif /* NIS */ 3487 /* 3488 ** NISPLUS Modules 3489 ** 3490 ** This code donated by Sun Microsystems. 3491 */ 3492 3493 #if NISPLUS 3494 3495 # undef NIS /* symbol conflict in nis.h */ 3496 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */ 3497 # include <rpcsvc/nis.h> 3498 # include <rpcsvc/nislib.h> 3499 # ifndef NIS_TABLE_OBJ 3500 # define NIS_TABLE_OBJ TABLE_OBJ 3501 # endif 3502 3503 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val 3504 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name 3505 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len) 3506 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.') 3507 3508 /* 3509 ** NISPLUS_MAP_OPEN -- open nisplus table 3510 */ 3511 3512 bool 3513 nisplus_map_open(map, mode) 3514 MAP *map; 3515 int mode; 3516 { 3517 nis_result *res = NULL; 3518 int retry_cnt, max_col, i; 3519 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 3520 3521 if (tTd(38, 2)) 3522 sm_dprintf("nisplus_map_open(%s, %s, %d)\n", 3523 map->map_mname, map->map_file, mode); 3524 3525 mode &= O_ACCMODE; 3526 if (mode != O_RDONLY) 3527 { 3528 errno = EPERM; 3529 return false; 3530 } 3531 3532 if (*map->map_file == '\0') 3533 map->map_file = "mail_aliases.org_dir"; 3534 3535 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL) 3536 { 3537 /* set default NISPLUS Domain to $m */ 3538 map->map_domain = newstr(nisplus_default_domain()); 3539 if (tTd(38, 2)) 3540 sm_dprintf("nisplus_map_open(%s): using domain %s\n", 3541 map->map_file, map->map_domain); 3542 } 3543 if (!PARTIAL_NAME(map->map_file)) 3544 { 3545 map->map_domain = newstr(""); 3546 (void) sm_strlcpy(qbuf, map->map_file, sizeof(qbuf)); 3547 } 3548 else 3549 { 3550 /* check to see if this map actually exists */ 3551 (void) sm_strlcpyn(qbuf, sizeof(qbuf), 3, 3552 map->map_file, ".", map->map_domain); 3553 } 3554 3555 retry_cnt = 0; 3556 while (res == NULL || res->status != NIS_SUCCESS) 3557 { 3558 res = nis_lookup(qbuf, FOLLOW_LINKS); 3559 switch (res->status) 3560 { 3561 case NIS_SUCCESS: 3562 break; 3563 3564 case NIS_TRYAGAIN: 3565 case NIS_RPCERROR: 3566 case NIS_NAMEUNREACHABLE: 3567 if (retry_cnt++ > 4) 3568 { 3569 errno = EAGAIN; 3570 return false; 3571 } 3572 /* try not to overwhelm hosed server */ 3573 sleep(2); 3574 break; 3575 3576 default: /* all other nisplus errors */ 3577 # if 0 3578 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3579 syserr("451 4.3.5 Cannot find table %s.%s: %s", 3580 map->map_file, map->map_domain, 3581 nis_sperrno(res->status)); 3582 # endif /* 0 */ 3583 errno = EAGAIN; 3584 return false; 3585 } 3586 } 3587 3588 if (NIS_RES_NUMOBJ(res) != 1 || 3589 (NIS_RES_OBJECT(res)->zo_data.zo_type != NIS_TABLE_OBJ)) 3590 { 3591 if (tTd(38, 10)) 3592 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf); 3593 # if 0 3594 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3595 syserr("451 4.3.5 %s.%s: %s is not a table", 3596 map->map_file, map->map_domain, 3597 nis_sperrno(res->status)); 3598 # endif /* 0 */ 3599 errno = EBADF; 3600 return false; 3601 } 3602 /* default key column is column 0 */ 3603 if (map->map_keycolnm == NULL) 3604 map->map_keycolnm = newstr(COL_NAME(res,0)); 3605 3606 max_col = COL_MAX(res); 3607 3608 /* verify the key column exist */ 3609 for (i = 0; i < max_col; i++) 3610 { 3611 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0) 3612 break; 3613 } 3614 if (i == max_col) 3615 { 3616 if (tTd(38, 2)) 3617 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n", 3618 map->map_file, map->map_keycolnm); 3619 errno = ENOENT; 3620 return false; 3621 } 3622 3623 /* default value column is the last column */ 3624 if (map->map_valcolnm == NULL) 3625 { 3626 map->map_valcolno = max_col - 1; 3627 return true; 3628 } 3629 3630 for (i = 0; i< max_col; i++) 3631 { 3632 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0) 3633 { 3634 map->map_valcolno = i; 3635 return true; 3636 } 3637 } 3638 3639 if (tTd(38, 2)) 3640 sm_dprintf("nisplus_map_open(%s): can not find column %s\n", 3641 map->map_file, map->map_keycolnm); 3642 errno = ENOENT; 3643 return false; 3644 } 3645 3646 3647 /* 3648 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table 3649 */ 3650 3651 char * 3652 nisplus_map_lookup(map, name, av, statp) 3653 MAP *map; 3654 char *name; 3655 char **av; 3656 int *statp; 3657 { 3658 char *p; 3659 auto int vsize; 3660 char *skp; 3661 int skleft; 3662 char search_key[MAXNAME + 4]; /* EAI:ok */ 3663 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 3664 nis_result *result; 3665 3666 if (tTd(38, 20)) 3667 sm_dprintf("nisplus_map_lookup(%s, %s)\n", 3668 map->map_mname, name); 3669 3670 if (!bitset(MF_OPEN, map->map_mflags)) 3671 { 3672 if (nisplus_map_open(map, O_RDONLY)) 3673 { 3674 map->map_mflags |= MF_OPEN; 3675 map->map_pid = CurrentPid; 3676 } 3677 else 3678 { 3679 *statp = EX_UNAVAILABLE; 3680 return NULL; 3681 } 3682 } 3683 3684 /* 3685 ** Copy the name to the key buffer, escaping double quote characters 3686 ** by doubling them and quoting "]" and "," to avoid having the 3687 ** NIS+ parser choke on them. 3688 */ 3689 3690 skleft = sizeof(search_key) - 4; 3691 skp = search_key; 3692 for (p = name; *p != '\0' && skleft > 0; p++) 3693 { 3694 switch (*p) 3695 { 3696 case ']': 3697 case ',': 3698 /* quote the character */ 3699 *skp++ = '"'; 3700 *skp++ = *p; 3701 *skp++ = '"'; 3702 skleft -= 3; 3703 break; 3704 3705 case '"': 3706 /* double the quote */ 3707 *skp++ = '"'; 3708 skleft--; 3709 /* FALLTHROUGH */ 3710 3711 default: 3712 *skp++ = *p; 3713 skleft--; 3714 break; 3715 } 3716 } 3717 *skp = '\0'; 3718 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3719 makelower_buf(search_key, search_key, sizeof(search_key)); 3720 3721 /* construct the query */ 3722 if (PARTIAL_NAME(map->map_file)) 3723 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s.%s", 3724 map->map_keycolnm, search_key, map->map_file, 3725 map->map_domain); 3726 else 3727 (void) sm_snprintf(qbuf, sizeof(qbuf), "[%s=%s],%s", 3728 map->map_keycolnm, search_key, map->map_file); 3729 3730 if (tTd(38, 20)) 3731 sm_dprintf("qbuf=%s\n", qbuf); 3732 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL); 3733 if (result->status == NIS_SUCCESS) 3734 { 3735 int count; 3736 char *str; 3737 3738 if ((count = NIS_RES_NUMOBJ(result)) != 1) 3739 { 3740 if (LogLevel > 10) 3741 sm_syslog(LOG_WARNING, CurEnv->e_id, 3742 "%s: lookup error, expected 1 entry, got %d", 3743 map->map_file, count); 3744 3745 /* ignore second entry */ 3746 if (tTd(38, 20)) 3747 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n", 3748 name, count); 3749 } 3750 3751 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno)); 3752 /* set the length of the result */ 3753 if (p == NULL) 3754 p = ""; 3755 vsize = strlen(p); 3756 if (tTd(38, 20)) 3757 sm_dprintf("nisplus_map_lookup(%s), found %s\n", 3758 name, p); 3759 if (bitset(MF_MATCHONLY, map->map_mflags)) 3760 str = map_rewrite(map, name, strlen(name), NULL); 3761 else 3762 str = map_rewrite(map, p, vsize, av); 3763 nis_freeresult(result); 3764 *statp = EX_OK; 3765 return str; 3766 } 3767 else 3768 { 3769 if (result->status == NIS_NOTFOUND) 3770 *statp = EX_NOTFOUND; 3771 else if (result->status == NIS_TRYAGAIN) 3772 *statp = EX_TEMPFAIL; 3773 else 3774 { 3775 *statp = EX_UNAVAILABLE; 3776 map->map_mflags &= ~(MF_VALID|MF_OPEN); 3777 } 3778 } 3779 if (tTd(38, 20)) 3780 sm_dprintf("nisplus_map_lookup(%s), failed\n", name); 3781 nis_freeresult(result); 3782 return NULL; 3783 } 3784 3785 3786 3787 /* 3788 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+ 3789 */ 3790 3791 static bool 3792 nisplus_getcanonname(name, hbsize, statp) 3793 char *name; 3794 int hbsize; 3795 int *statp; 3796 { 3797 char *vp; 3798 auto int vsize; 3799 nis_result *result; 3800 char *p; 3801 char nbuf[MAXNAME + 1]; /* EAI:hostname */ 3802 char qbuf[MAXLINE + NIS_MAXNAMELEN]; /* EAI:hostname */ 3803 3804 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf)) 3805 { 3806 *statp = EX_UNAVAILABLE; 3807 return false; 3808 } 3809 (void) shorten_hostname(nbuf); 3810 3811 p = strchr(nbuf, '.'); 3812 if (p == NULL) 3813 { 3814 /* single token */ 3815 (void) sm_snprintf(qbuf, sizeof(qbuf), 3816 "[name=%s],hosts.org_dir", nbuf); 3817 } 3818 else if (p[1] != '\0') 3819 { 3820 /* multi token -- take only first token in nbuf */ 3821 *p = '\0'; 3822 (void) sm_snprintf(qbuf, sizeof(qbuf), 3823 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]); 3824 } 3825 else 3826 { 3827 *statp = EX_NOHOST; 3828 return false; 3829 } 3830 3831 if (tTd(38, 20)) 3832 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n", 3833 name, qbuf); 3834 3835 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH, 3836 NULL, NULL); 3837 3838 if (result->status == NIS_SUCCESS) 3839 { 3840 int count; 3841 char *domain; 3842 3843 if ((count = NIS_RES_NUMOBJ(result)) != 1) 3844 { 3845 if (LogLevel > 10) 3846 sm_syslog(LOG_WARNING, CurEnv->e_id, 3847 "nisplus_getcanonname: lookup error, expected 1 entry, got %d", 3848 count); 3849 3850 /* ignore second entry */ 3851 if (tTd(38, 20)) 3852 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n", 3853 name, count); 3854 } 3855 3856 if (tTd(38, 20)) 3857 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n", 3858 name, (NIS_RES_OBJECT(result))->zo_domain); 3859 3860 3861 vp = ((NIS_RES_OBJECT(result))->EN_col(0)); 3862 vsize = strlen(vp); 3863 if (tTd(38, 20)) 3864 sm_dprintf("nisplus_getcanonname(%s), found %s\n", 3865 name, vp); 3866 if (strchr(vp, '.') != NULL) 3867 { 3868 domain = ""; 3869 } 3870 else 3871 { 3872 domain = macvalue('m', CurEnv); 3873 if (domain == NULL) 3874 domain = ""; 3875 } 3876 if (hbsize > vsize + (int) strlen(domain) + 1) 3877 { 3878 if (domain[0] == '\0') 3879 (void) sm_strlcpy(name, vp, hbsize); 3880 else 3881 (void) sm_snprintf(name, hbsize, 3882 "%s.%s", vp, domain); 3883 *statp = EX_OK; 3884 } 3885 else 3886 *statp = EX_NOHOST; 3887 nis_freeresult(result); 3888 return true; 3889 } 3890 else 3891 { 3892 if (result->status == NIS_NOTFOUND) 3893 *statp = EX_NOHOST; 3894 else if (result->status == NIS_TRYAGAIN) 3895 *statp = EX_TEMPFAIL; 3896 else 3897 *statp = EX_UNAVAILABLE; 3898 } 3899 if (tTd(38, 20)) 3900 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n", 3901 name, result->status, *statp); 3902 nis_freeresult(result); 3903 return false; 3904 } 3905 3906 char * 3907 nisplus_default_domain() 3908 { 3909 static char default_domain[MAXNAME + 1] = ""; /* EAI:hostname */ 3910 char *p; 3911 3912 if (default_domain[0] != '\0') 3913 return default_domain; 3914 3915 p = nis_local_directory(); 3916 (void) sm_strlcpy(default_domain, p, sizeof(default_domain)); 3917 return default_domain; 3918 } 3919 3920 #endif /* NISPLUS */ 3921 /* 3922 ** LDAP Modules 3923 */ 3924 3925 /* 3926 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs 3927 */ 3928 3929 #if defined(LDAPMAP) || defined(PH_MAP) 3930 3931 # if PH_MAP 3932 # define ph_map_dequote ldapmap_dequote 3933 # endif 3934 3935 static char *ldapmap_dequote __P((char *)); 3936 3937 static char * 3938 ldapmap_dequote(str) 3939 char *str; 3940 { 3941 char *p; 3942 char *start; 3943 3944 if (str == NULL) 3945 return NULL; 3946 3947 p = str; 3948 if (*p == '"') 3949 { 3950 /* Should probably swallow initial whitespace here */ 3951 start = ++p; 3952 } 3953 else 3954 return str; 3955 while (*p != '"' && *p != '\0') 3956 p++; 3957 if (*p != '\0') 3958 *p = '\0'; 3959 return start; 3960 } 3961 #endif /* defined(LDAPMAP) || defined(PH_MAP) */ 3962 3963 #if LDAPMAP 3964 3965 static SM_LDAP_STRUCT *LDAPDefaults = NULL; 3966 3967 /* 3968 ** LDAPMAP_OPEN -- open LDAP map 3969 ** 3970 ** Connect to the LDAP server. Re-use existing connections since a 3971 ** single server connection to a host (with the same host, port, 3972 ** bind DN, and secret) can answer queries for multiple maps. 3973 */ 3974 3975 bool 3976 ldapmap_open(map, mode) 3977 MAP *map; 3978 int mode; 3979 { 3980 SM_LDAP_STRUCT *lmap; 3981 STAB *s; 3982 char *id; 3983 3984 if (tTd(38, 2)) 3985 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode); 3986 3987 # if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \ 3988 HASLDAPGETALIASBYNAME 3989 if (VendorCode == VENDOR_SUN && 3990 strcmp(map->map_mname, "aliases.ldap") == 0) 3991 { 3992 return true; 3993 } 3994 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */ 3995 3996 mode &= O_ACCMODE; 3997 3998 /* sendmail doesn't have the ability to write to LDAP (yet) */ 3999 if (mode != O_RDONLY) 4000 { 4001 /* issue a pseudo-error message */ 4002 errno = SM_EMAPCANTWRITE; 4003 return false; 4004 } 4005 4006 lmap = (SM_LDAP_STRUCT *) map->map_db1; 4007 4008 s = ldapmap_findconn(lmap); 4009 if (s->s_lmap != NULL) 4010 { 4011 /* Already have a connection open to this LDAP server */ 4012 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld; 4013 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid; 4014 4015 /* Add this map as head of linked list */ 4016 lmap->ldap_next = s->s_lmap; 4017 s->s_lmap = map; 4018 4019 if (tTd(38, 2)) 4020 sm_dprintf("using cached connection\n"); 4021 return true; 4022 } 4023 4024 if (tTd(38, 2)) 4025 sm_dprintf("opening new connection\n"); 4026 4027 if (lmap->ldap_host != NULL) 4028 id = lmap->ldap_host; 4029 else if (lmap->ldap_uri != NULL) 4030 id = lmap->ldap_uri; 4031 else 4032 id = "localhost"; 4033 4034 if (tTd(74, 104)) 4035 { 4036 extern MAPCLASS NullMapClass; 4037 4038 /* debug mode: don't actually open an LDAP connection */ 4039 map->map_orgclass = map->map_class; 4040 map->map_class = &NullMapClass; 4041 map->map_mflags |= MF_OPEN; 4042 map->map_pid = CurrentPid; 4043 return true; 4044 } 4045 4046 /* No connection yet, connect */ 4047 if (!sm_ldap_start(map->map_mname, lmap)) 4048 { 4049 if (errno == ETIMEDOUT) 4050 { 4051 if (LogLevel > 1) 4052 sm_syslog(LOG_NOTICE, CurEnv->e_id, 4053 "timeout connecting to LDAP server %.100s", 4054 id); 4055 } 4056 4057 if (!bitset(MF_OPTIONAL, map->map_mflags)) 4058 { 4059 if (bitset(MF_NODEFER, map->map_mflags)) 4060 { 4061 syserr("%s failed to %s in map %s", 4062 # if USE_LDAP_INIT 4063 "ldap_init/ldap_bind", 4064 # else 4065 "ldap_open", 4066 # endif 4067 id, map->map_mname); 4068 } 4069 else 4070 { 4071 syserr("451 4.3.5 %s failed to %s in map %s", 4072 # if USE_LDAP_INIT 4073 "ldap_init/ldap_bind", 4074 # else 4075 "ldap_open", 4076 # endif 4077 id, map->map_mname); 4078 } 4079 } 4080 return false; 4081 } 4082 4083 /* Save connection for reuse */ 4084 s->s_lmap = map; 4085 return true; 4086 } 4087 4088 /* 4089 ** LDAPMAP_CLOSE -- close ldap map 4090 */ 4091 4092 void 4093 ldapmap_close(map) 4094 MAP *map; 4095 { 4096 SM_LDAP_STRUCT *lmap; 4097 STAB *s; 4098 4099 if (tTd(38, 2)) 4100 sm_dprintf("ldapmap_close(%s)\n", map->map_mname); 4101 4102 lmap = (SM_LDAP_STRUCT *) map->map_db1; 4103 4104 /* Check if already closed */ 4105 if (lmap->ldap_ld == NULL) 4106 return; 4107 4108 /* Close the LDAP connection */ 4109 sm_ldap_close(lmap); 4110 4111 /* Mark all the maps that share the connection as closed */ 4112 s = ldapmap_findconn(lmap); 4113 4114 while (s->s_lmap != NULL) 4115 { 4116 MAP *smap = s->s_lmap; 4117 4118 if (tTd(38, 2) && smap != map) 4119 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n", 4120 map->map_mname, smap->map_mname); 4121 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 4122 lmap = (SM_LDAP_STRUCT *) smap->map_db1; 4123 lmap->ldap_ld = NULL; 4124 s->s_lmap = lmap->ldap_next; 4125 lmap->ldap_next = NULL; 4126 } 4127 } 4128 4129 # ifdef SUNET_ID 4130 /* 4131 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form 4132 ** This only makes sense at Stanford University. 4133 */ 4134 4135 static char * 4136 sunet_id_hash(str) 4137 char *str; 4138 { 4139 char *p, *p_last; 4140 4141 p = str; 4142 p_last = p; 4143 while (*p != '\0') 4144 { 4145 if (isascii(*p) && (islower(*p) || isdigit(*p))) 4146 { 4147 *p_last = *p; 4148 p_last++; 4149 } 4150 else if (isascii(*p) && isupper(*p)) 4151 { 4152 *p_last = tolower(*p); 4153 p_last++; 4154 } 4155 ++p; 4156 } 4157 if (*p_last != '\0') 4158 *p_last = '\0'; 4159 return str; 4160 } 4161 # define SM_CONVERT_ID(str) sunet_id_hash(str) 4162 # else /* SUNET_ID */ 4163 # define SM_CONVERT_ID(str) makelower(str) 4164 # endif /* SUNET_ID */ 4165 4166 /* 4167 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map 4168 */ 4169 4170 char * 4171 ldapmap_lookup(map, name, av, statp) 4172 MAP *map; 4173 char *name; 4174 char **av; 4175 int *statp; 4176 { 4177 int flags; 4178 int i; 4179 int plen = 0; 4180 int psize = 0; 4181 int msgid; 4182 int save_errno; 4183 char *vp, *p; 4184 char *result = NULL; 4185 SM_RPOOL_T *rpool; 4186 SM_LDAP_STRUCT *lmap = NULL; 4187 char *argv[SM_LDAP_ARGS]; 4188 char keybuf[MAXKEY]; 4189 # if SM_LDAP_ARGS != MAX_MAP_ARGS 4190 # error "SM_LDAP_ARGS must be the same as MAX_MAP_ARGS" 4191 # endif 4192 4193 # define AV_FREE(av) \ 4194 do \ 4195 { \ 4196 int ai; \ 4197 for (ai = 0; ai < SM_LDAP_ARGS && av[ai] != NULL; ai++) \ 4198 SM_FREE(av[ai]); \ 4199 } while (0) 4200 4201 # if USE_EAI 4202 bool allascii; 4203 char *largv[SM_LDAP_ARGS]; 4204 char **largs; 4205 4206 # define LARGV_FREE AV_FREE(largv) 4207 # else 4208 # define largs av 4209 # define LARGV_FREE 4210 # endif 4211 4212 # if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \ 4213 HASLDAPGETALIASBYNAME 4214 if (VendorCode == VENDOR_SUN && 4215 strcmp(map->map_mname, "aliases.ldap") == 0) 4216 { 4217 int rc; 4218 # if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2) 4219 extern char *__getldapaliasbyname(); 4220 char *answer; 4221 4222 answer = __getldapaliasbyname(name, &rc); 4223 # else 4224 char answer[MAXNAME + 1]; /* EAI:Sun only, ignore */ 4225 4226 rc = __getldapaliasbyname(name, answer, sizeof(answer)); 4227 # endif 4228 if (rc != 0) 4229 { 4230 if (tTd(38, 20)) 4231 sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n", 4232 name, errno); 4233 *statp = EX_NOTFOUND; 4234 return NULL; 4235 } 4236 *statp = EX_OK; 4237 if (tTd(38, 20)) 4238 sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name, 4239 answer); 4240 if (bitset(MF_MATCHONLY, map->map_mflags)) 4241 result = map_rewrite(map, name, strlen(name), NULL); 4242 else 4243 result = map_rewrite(map, answer, strlen(answer), av); 4244 # if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2) 4245 free(answer); 4246 # endif 4247 return result; 4248 } 4249 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */ 4250 4251 /* Get ldap struct pointer from map */ 4252 lmap = (SM_LDAP_STRUCT *) map->map_db1; 4253 sm_ldap_setopts(lmap->ldap_ld, lmap); 4254 4255 /* initialize first element so AV_FREE can work */ 4256 argv[0] = NULL; 4257 # if USE_EAI 4258 largv[0] = NULL; 4259 # endif 4260 4261 if (lmap->ldap_multi_args) 4262 { 4263 SM_REQUIRE(av != NULL); 4264 memset(argv, '\0', sizeof(argv)); 4265 # if USE_EAI 4266 largs = av; 4267 memset(largv, '\0', sizeof(largv)); 4268 4269 /* this is ugly - can we merge it with the next loop? */ 4270 allascii = true; 4271 if (!bitset(MF_MATCHONLY, map->map_mflags)) 4272 { 4273 for (i = 0, allascii = true; 4274 i < SM_LDAP_ARGS && av[i] != NULL; 4275 i++) 4276 { 4277 if (!addr_is_ascii(av[i])) 4278 { 4279 allascii = false; 4280 largs = largv; 4281 break; 4282 } 4283 } 4284 } 4285 # endif /* USE_EAI */ 4286 for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++) 4287 { 4288 argv[i] = sm_strdup(av[i]); 4289 if (argv[i] == NULL) 4290 { 4291 *statp = EX_TEMPFAIL; 4292 goto none; 4293 } 4294 4295 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 4296 /* && !bitset(MF_MATCHONLY, map->map_mflags)) */ 4297 /* see below: av[]/largs onluy used if !MF_MATCHONLY !? */ 4298 { 4299 # if USE_EAI 4300 if (!allascii) 4301 { 4302 char *lower; 4303 4304 lower = makelower(av[i]); 4305 largv[i] = sm_strdup(lower); 4306 if (largv[i] == NULL) 4307 { 4308 *statp = EX_TEMPFAIL; 4309 goto none; 4310 } 4311 } 4312 else 4313 # endif /* USE_EAI */ 4314 /* NOTE: see else above! */ 4315 SM_CONVERT_ID(av[i]); 4316 } 4317 } 4318 } 4319 else 4320 { 4321 (void) sm_strlcpy(keybuf, name, sizeof(keybuf)); 4322 4323 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 4324 SM_CONVERT_ID(keybuf); 4325 } 4326 4327 if (tTd(38, 20)) 4328 { 4329 if (lmap->ldap_multi_args) 4330 { 4331 sm_dprintf("ldapmap_lookup(%s, argv)\n", 4332 map->map_mname); 4333 for (i = 0; i < SM_LDAP_ARGS; i++) 4334 { 4335 sm_dprintf(" argv[%d] = %s\n", i, 4336 argv[i] == NULL ? "NULL" : argv[i]); 4337 } 4338 } 4339 else 4340 { 4341 sm_dprintf("ldapmap_lookup(%s, %s)\n", 4342 map->map_mname, name); 4343 } 4344 } 4345 4346 if (lmap->ldap_multi_args) 4347 { 4348 msgid = sm_ldap_search_m(lmap, argv); 4349 4350 /* free the argv array and its content, no longer needed */ 4351 AV_FREE(argv); 4352 } 4353 else 4354 msgid = sm_ldap_search(lmap, keybuf); 4355 if (msgid == SM_LDAP_ERR) 4356 { 4357 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE; 4358 save_errno = errno; 4359 if (!bitset(MF_OPTIONAL, map->map_mflags)) 4360 { 4361 /* 4362 ** Do not include keybuf as this error may be shown 4363 ** to outsiders. 4364 */ 4365 4366 if (bitset(MF_NODEFER, map->map_mflags)) 4367 syserr("Error in ldap_search in map %s", 4368 map->map_mname); 4369 else 4370 syserr("451 4.3.5 Error in ldap_search in map %s", 4371 map->map_mname); 4372 } 4373 *statp = EX_TEMPFAIL; 4374 switch (save_errno - E_LDAPBASE) 4375 { 4376 # ifdef LDAP_SERVER_DOWN 4377 case LDAP_SERVER_DOWN: 4378 # endif 4379 case LDAP_TIMEOUT: 4380 case LDAP_UNAVAILABLE: 4381 /* server disappeared, try reopen on next search */ 4382 ldapmap_close(map); 4383 break; 4384 } 4385 errno = save_errno; 4386 goto none; 4387 } 4388 # if SM_LDAP_ERROR_ON_MISSING_ARGS 4389 else if (msgid == SM_LDAP_ERR_ARG_MISS) 4390 { 4391 if (bitset(MF_NODEFER, map->map_mflags)) 4392 syserr("Error in ldap_search in map %s, too few arguments", 4393 map->map_mname); 4394 else 4395 syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments", 4396 map->map_mname); 4397 *statp = EX_CONFIG; 4398 goto none; 4399 } 4400 # endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */ 4401 4402 *statp = EX_NOTFOUND; 4403 vp = NULL; 4404 4405 flags = 0; 4406 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 4407 flags |= SM_LDAP_SINGLEMATCH; 4408 if (bitset(MF_MATCHONLY, map->map_mflags)) 4409 flags |= SM_LDAP_MATCHONLY; 4410 # if _FFR_LDAP_SINGLEDN 4411 if (bitset(MF_SINGLEDN, map->map_mflags)) 4412 flags |= SM_LDAP_SINGLEDN; 4413 # endif 4414 4415 /* Create an rpool for search related memory usage */ 4416 rpool = sm_rpool_new_x(NULL); 4417 4418 p = NULL; 4419 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim, 4420 rpool, &p, &plen, &psize, NULL); 4421 save_errno = errno; 4422 4423 /* Copy result so rpool can be freed */ 4424 if (*statp == EX_OK && p != NULL) 4425 vp = newstr(p); 4426 sm_rpool_free(rpool); 4427 4428 /* need to restart LDAP connection? */ 4429 if (*statp == EX_RESTART) 4430 { 4431 *statp = EX_TEMPFAIL; 4432 ldapmap_close(map); 4433 } 4434 4435 errno = save_errno; 4436 if (*statp != EX_OK && *statp != EX_NOTFOUND) 4437 { 4438 if (!bitset(MF_OPTIONAL, map->map_mflags)) 4439 { 4440 if (bitset(MF_NODEFER, map->map_mflags)) 4441 syserr("Error getting LDAP results, map=%s, name=%s", 4442 map->map_mname, name); 4443 else 4444 syserr("451 4.3.5 Error getting LDAP results, map=%s, name=%s", 4445 map->map_mname, name); 4446 } 4447 errno = save_errno; 4448 goto none; 4449 } 4450 4451 /* Did we match anything? */ 4452 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags)) 4453 goto none; 4454 4455 if (*statp == EX_OK) 4456 { 4457 if (LogLevel > 9) 4458 sm_syslog(LOG_INFO, CurEnv->e_id, 4459 "ldap=%s, %.100s=>%s", map->map_mname, name, 4460 vp == NULL ? "<NULL>" : vp); 4461 if (bitset(MF_MATCHONLY, map->map_mflags)) 4462 result = map_rewrite(map, name, strlen(name), NULL); 4463 else 4464 { 4465 /* vp != NULL according to test above */ 4466 result = map_rewrite(map, vp, strlen(vp), largs); 4467 } 4468 SM_FREE(vp); /* XXX */ 4469 } 4470 LARGV_FREE; 4471 return result; 4472 4473 none: 4474 /* other cleanup? */ 4475 save_errno = errno; 4476 AV_FREE(argv); 4477 LARGV_FREE; 4478 errno = save_errno; 4479 return NULL; 4480 } 4481 4482 /* 4483 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server 4484 ** 4485 ** Cache LDAP connections based on the host, port, bind DN, 4486 ** secret, and PID so we don't have multiple connections open to 4487 ** the same server for different maps. Need a separate connection 4488 ** per PID since a parent process may close the map before the 4489 ** child is done with it. 4490 ** 4491 ** Parameters: 4492 ** lmap -- LDAP map information 4493 ** 4494 ** Returns: 4495 ** Symbol table entry for the LDAP connection. 4496 */ 4497 4498 static STAB * 4499 ldapmap_findconn(lmap) 4500 SM_LDAP_STRUCT *lmap; 4501 { 4502 char *format; 4503 char *nbuf; 4504 char *id; 4505 STAB *SM_NONVOLATILE s = NULL; 4506 4507 if (lmap->ldap_host != NULL) 4508 id = lmap->ldap_host; 4509 else if (lmap->ldap_uri != NULL) 4510 id = lmap->ldap_uri; 4511 else 4512 id = "localhost"; 4513 4514 format = "%s%c%d%c%d%c%s%c%s%d"; 4515 nbuf = sm_stringf_x(format, 4516 id, 4517 CONDELSE, 4518 lmap->ldap_port, 4519 CONDELSE, 4520 lmap->ldap_version, 4521 CONDELSE, 4522 (lmap->ldap_binddn == NULL ? "" 4523 : lmap->ldap_binddn), 4524 CONDELSE, 4525 (lmap->ldap_secret == NULL ? "" 4526 : lmap->ldap_secret), 4527 (int) CurrentPid); 4528 SM_TRY 4529 s = stab(nbuf, ST_LMAP, ST_ENTER); 4530 SM_FINALLY 4531 sm_free(nbuf); 4532 SM_END_TRY 4533 return s; 4534 } 4535 /* 4536 ** LDAPMAP_PARSEARGS -- parse ldap map definition args. 4537 */ 4538 4539 static struct lamvalues LDAPAuthMethods[] = 4540 { 4541 { "none", LDAP_AUTH_NONE }, 4542 { "simple", LDAP_AUTH_SIMPLE }, 4543 # ifdef LDAP_AUTH_KRBV4 4544 { "krbv4", LDAP_AUTH_KRBV4 }, 4545 # endif 4546 { NULL, 0 } 4547 }; 4548 4549 static struct ladvalues LDAPAliasDereference[] = 4550 { 4551 { "never", LDAP_DEREF_NEVER }, 4552 { "always", LDAP_DEREF_ALWAYS }, 4553 { "search", LDAP_DEREF_SEARCHING }, 4554 { "find", LDAP_DEREF_FINDING }, 4555 { NULL, 0 } 4556 }; 4557 4558 static struct lssvalues LDAPSearchScope[] = 4559 { 4560 { "base", LDAP_SCOPE_BASE }, 4561 { "one", LDAP_SCOPE_ONELEVEL }, 4562 { "sub", LDAP_SCOPE_SUBTREE }, 4563 { NULL, 0 } 4564 }; 4565 4566 bool 4567 ldapmap_parseargs(map, args) 4568 MAP *map; 4569 char *args; 4570 { 4571 bool secretread = true; 4572 bool attrssetup = false; 4573 int i; 4574 register char *p = args; 4575 SM_LDAP_STRUCT *lmap; 4576 SM_LDAP_STRUCT *lmap_alloc; 4577 struct lamvalues *lam; 4578 struct ladvalues *lad; 4579 struct lssvalues *lss; 4580 char ldapfilt[MAXLINE]; 4581 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD]; 4582 4583 /* Get ldap struct pointer from map */ 4584 lmap = (SM_LDAP_STRUCT *) map->map_db1; 4585 4586 /* Check if setting the initial LDAP defaults */ 4587 if (lmap == NULL || lmap != LDAPDefaults) 4588 { 4589 /* We need to alloc an SM_LDAP_STRUCT struct */ 4590 lmap_alloc = lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap)); 4591 if (LDAPDefaults == NULL) 4592 sm_ldap_clear(lmap); 4593 else 4594 STRUCTCOPY(*LDAPDefaults, *lmap); 4595 } 4596 else 4597 lmap_alloc = NULL; 4598 4599 /* there is no check whether there is really an argument */ 4600 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 4601 map->map_spacesub = SpaceSub; /* default value */ 4602 4603 /* Check if setting up an alias or file class LDAP map */ 4604 if (bitset(MF_ALIAS, map->map_mflags)) 4605 { 4606 /* Comma separate if used as an alias file */ 4607 map->map_coldelim = ','; 4608 if (*args == '\0') 4609 { 4610 int n; 4611 char *lc; 4612 char jbuf[MAXHOSTNAMELEN]; 4613 char lcbuf[MAXLINE]; 4614 4615 /* Get $j */ 4616 expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope); 4617 if (jbuf[0] == '\0') 4618 { 4619 (void) sm_strlcpy(jbuf, "localhost", 4620 sizeof(jbuf)); 4621 } 4622 4623 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv); 4624 if (lc == NULL) 4625 lc = ""; 4626 else 4627 { 4628 expand(lc, lcbuf, sizeof(lcbuf), CurEnv); 4629 lc = lcbuf; 4630 } 4631 4632 n = sm_snprintf(ldapfilt, sizeof(ldapfilt), 4633 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))", 4634 lc, jbuf); 4635 if (n >= sizeof(ldapfilt)) 4636 { 4637 syserr("%s: Default LDAP string too long", 4638 map->map_mname); 4639 goto fail; 4640 } 4641 4642 /* default args for an alias LDAP entry */ 4643 lmap->ldap_filter = ldapfilt; 4644 lmap->ldap_attr[0] = "objectClass"; 4645 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS; 4646 lmap->ldap_attr_needobjclass[0] = NULL; 4647 lmap->ldap_attr[1] = "sendmailMTAAliasValue"; 4648 lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL; 4649 lmap->ldap_attr_needobjclass[1] = NULL; 4650 lmap->ldap_attr[2] = "sendmailMTAAliasSearch"; 4651 lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER; 4652 lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject"; 4653 lmap->ldap_attr[3] = "sendmailMTAAliasURL"; 4654 lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL; 4655 lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject"; 4656 lmap->ldap_attr[4] = NULL; 4657 lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE; 4658 lmap->ldap_attr_needobjclass[4] = NULL; 4659 attrssetup = true; 4660 } 4661 } 4662 else if (bitset(MF_FILECLASS, map->map_mflags)) 4663 { 4664 /* Space separate if used as a file class file */ 4665 map->map_coldelim = ' '; 4666 } 4667 4668 # if LDAP_NETWORK_TIMEOUT 4669 if (0 == lmap->ldap_networktmo) 4670 lmap->ldap_networktmo = (LDAP_NETWORK_TIMEOUT > 1) 4671 ? LDAP_NETWORK_TIMEOUT : 60; 4672 # endif 4673 4674 for (;;) 4675 { 4676 while (SM_ISSPACE(*p)) 4677 p++; 4678 if (*p != '-') 4679 break; 4680 switch (*++p) 4681 { 4682 case 'A': 4683 map->map_mflags |= MF_APPEND; 4684 break; 4685 4686 case 'a': 4687 map->map_app = ++p; 4688 break; 4689 4690 case 'D': 4691 map->map_mflags |= MF_DEFER; 4692 break; 4693 4694 case 'f': 4695 map->map_mflags |= MF_NOFOLDCASE; 4696 break; 4697 4698 case 'm': 4699 map->map_mflags |= MF_MATCHONLY; 4700 break; 4701 4702 case 'N': 4703 map->map_mflags |= MF_INCLNULL; 4704 map->map_mflags &= ~MF_TRY0NULL; 4705 break; 4706 4707 case 'O': 4708 map->map_mflags &= ~MF_TRY1NULL; 4709 break; 4710 4711 case 'o': 4712 map->map_mflags |= MF_OPTIONAL; 4713 break; 4714 4715 case 'q': 4716 map->map_mflags |= MF_KEEPQUOTES; 4717 break; 4718 4719 case 'S': 4720 map->map_spacesub = *++p; 4721 break; 4722 4723 case 'T': 4724 map->map_tapp = ++p; 4725 break; 4726 4727 case 't': 4728 map->map_mflags |= MF_NODEFER; 4729 break; 4730 4731 case 'z': 4732 if (*++p != '\\') 4733 map->map_coldelim = *p; 4734 else 4735 { 4736 switch (*++p) 4737 { 4738 case 'n': 4739 map->map_coldelim = '\n'; 4740 break; 4741 4742 case 't': 4743 map->map_coldelim = '\t'; 4744 break; 4745 4746 default: 4747 map->map_coldelim = '\\'; 4748 } 4749 } 4750 break; 4751 4752 /* Start of ldapmap specific args */ 4753 case '1': 4754 map->map_mflags |= MF_SINGLEMATCH; 4755 break; 4756 4757 # if _FFR_LDAP_SINGLEDN 4758 case '2': 4759 map->map_mflags |= MF_SINGLEDN; 4760 break; 4761 # endif /* _FFR_LDAP_SINGLEDN */ 4762 4763 case 'b': /* search base */ 4764 while (isascii(*++p) && isspace(*p)) 4765 continue; 4766 lmap->ldap_base = p; 4767 break; 4768 4769 # if LDAP_NETWORK_TIMEOUT 4770 case 'c': /* network (connect) timeout */ 4771 while (isascii(*++p) && isspace(*p)) 4772 continue; 4773 lmap->ldap_networktmo = atoi(p); 4774 break; 4775 # endif /* LDAP_NETWORK_TIMEOUT */ 4776 4777 case 'd': /* Dn to bind to server as */ 4778 while (isascii(*++p) && isspace(*p)) 4779 continue; 4780 lmap->ldap_binddn = p; 4781 break; 4782 4783 case 'H': /* Use LDAP URI */ 4784 # if !USE_LDAP_INIT 4785 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s", 4786 map->map_mname); 4787 goto fail; 4788 # else /* !USE_LDAP_INIT */ 4789 if (lmap->ldap_host != NULL) 4790 { 4791 syserr("Can not specify both an LDAP host and an LDAP URI in map %s", 4792 map->map_mname); 4793 goto fail; 4794 } 4795 while (isascii(*++p) && isspace(*p)) 4796 continue; 4797 lmap->ldap_uri = p; 4798 break; 4799 # endif /* !USE_LDAP_INIT */ 4800 4801 case 'h': /* ldap host */ 4802 while (isascii(*++p) && isspace(*p)) 4803 continue; 4804 if (lmap->ldap_uri != NULL) 4805 { 4806 syserr("Can not specify both an LDAP host and an LDAP URI in map %s", 4807 map->map_mname); 4808 goto fail; 4809 } 4810 lmap->ldap_host = p; 4811 break; 4812 4813 case 'K': 4814 lmap->ldap_multi_args = true; 4815 break; 4816 4817 case 'k': /* search field */ 4818 while (isascii(*++p) && isspace(*p)) 4819 continue; 4820 lmap->ldap_filter = p; 4821 break; 4822 4823 case 'l': /* time limit */ 4824 while (isascii(*++p) && isspace(*p)) 4825 continue; 4826 lmap->ldap_timelimit = atoi(p); 4827 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit; 4828 break; 4829 4830 case 'M': /* Method for binding */ 4831 while (isascii(*++p) && isspace(*p)) 4832 continue; 4833 4834 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0) 4835 p += 10; 4836 4837 for (lam = LDAPAuthMethods; 4838 lam != NULL && lam->lam_name != NULL; lam++) 4839 { 4840 if (sm_strncasecmp(p, lam->lam_name, 4841 strlen(lam->lam_name)) == 0) 4842 break; 4843 } 4844 if (lam->lam_name != NULL) 4845 lmap->ldap_method = lam->lam_code; 4846 else 4847 { 4848 /* bad config line */ 4849 if (!bitset(MCF_OPTFILE, 4850 map->map_class->map_cflags)) 4851 { 4852 char *ptr; 4853 4854 if ((ptr = strchr(p, ' ')) != NULL) 4855 *ptr = '\0'; 4856 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s", 4857 p, map->map_mname); 4858 if (ptr != NULL) 4859 *ptr = ' '; 4860 goto fail; 4861 } 4862 } 4863 break; 4864 4865 case 'n': /* retrieve attribute names only */ 4866 lmap->ldap_attrsonly = LDAPMAP_TRUE; 4867 break; 4868 4869 /* 4870 ** This is a string that is dependent on the 4871 ** method used defined by 'M'. 4872 */ 4873 4874 case 'P': /* Secret password for binding */ 4875 while (isascii(*++p) && isspace(*p)) 4876 continue; 4877 lmap->ldap_secret = p; 4878 secretread = false; 4879 break; 4880 4881 case 'p': /* ldap port */ 4882 while (isascii(*++p) && isspace(*p)) 4883 continue; 4884 lmap->ldap_port = atoi(p); 4885 break; 4886 4887 /* args stolen from ldapsearch.c */ 4888 case 'R': /* don't auto chase referrals */ 4889 # ifdef LDAP_REFERRALS 4890 lmap->ldap_options &= ~LDAP_OPT_REFERRALS; 4891 # else 4892 syserr("compile with -DLDAP_REFERRALS for referral support"); 4893 # endif /* LDAP_REFERRALS */ 4894 break; 4895 4896 case 'r': /* alias dereferencing */ 4897 while (isascii(*++p) && isspace(*p)) 4898 continue; 4899 4900 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0) 4901 p += 11; 4902 4903 for (lad = LDAPAliasDereference; 4904 lad != NULL && lad->lad_name != NULL; lad++) 4905 { 4906 if (sm_strncasecmp(p, lad->lad_name, 4907 strlen(lad->lad_name)) == 0) 4908 break; 4909 } 4910 if (lad->lad_name != NULL) 4911 lmap->ldap_deref = lad->lad_code; 4912 else 4913 { 4914 /* bad config line */ 4915 if (!bitset(MCF_OPTFILE, 4916 map->map_class->map_cflags)) 4917 { 4918 char *ptr; 4919 4920 if ((ptr = strchr(p, ' ')) != NULL) 4921 *ptr = '\0'; 4922 syserr("Deref must be [never|always|search|find] (not %s) in map %s", 4923 p, map->map_mname); 4924 if (ptr != NULL) 4925 *ptr = ' '; 4926 goto fail; 4927 } 4928 } 4929 break; 4930 4931 case 's': /* search scope */ 4932 while (isascii(*++p) && isspace(*p)) 4933 continue; 4934 4935 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0) 4936 p += 11; 4937 4938 for (lss = LDAPSearchScope; 4939 lss != NULL && lss->lss_name != NULL; lss++) 4940 { 4941 if (sm_strncasecmp(p, lss->lss_name, 4942 strlen(lss->lss_name)) == 0) 4943 break; 4944 } 4945 if (lss->lss_name != NULL) 4946 lmap->ldap_scope = lss->lss_code; 4947 else 4948 { 4949 /* bad config line */ 4950 if (!bitset(MCF_OPTFILE, 4951 map->map_class->map_cflags)) 4952 { 4953 char *ptr; 4954 4955 if ((ptr = strchr(p, ' ')) != NULL) 4956 *ptr = '\0'; 4957 syserr("Scope must be [base|one|sub] (not %s) in map %s", 4958 p, map->map_mname); 4959 if (ptr != NULL) 4960 *ptr = ' '; 4961 goto fail; 4962 } 4963 } 4964 break; 4965 4966 case 'V': 4967 if (*++p != '\\') 4968 lmap->ldap_attrsep = *p; 4969 else 4970 { 4971 switch (*++p) 4972 { 4973 case 'n': 4974 lmap->ldap_attrsep = '\n'; 4975 break; 4976 4977 case 't': 4978 lmap->ldap_attrsep = '\t'; 4979 break; 4980 4981 default: 4982 lmap->ldap_attrsep = '\\'; 4983 } 4984 } 4985 break; 4986 4987 case 'v': /* attr to return */ 4988 while (isascii(*++p) && isspace(*p)) 4989 continue; 4990 lmap->ldap_attr[0] = p; 4991 lmap->ldap_attr[1] = NULL; 4992 break; 4993 4994 case 'w': 4995 /* -w should be for passwd, -P should be for version */ 4996 while (isascii(*++p) && isspace(*p)) 4997 continue; 4998 lmap->ldap_version = atoi(p); 4999 # ifdef LDAP_VERSION_MAX 5000 if (lmap->ldap_version > LDAP_VERSION_MAX) 5001 { 5002 syserr("LDAP version %d exceeds max of %d in map %s", 5003 lmap->ldap_version, LDAP_VERSION_MAX, 5004 map->map_mname); 5005 goto fail; 5006 } 5007 # endif /* LDAP_VERSION_MAX */ 5008 # ifdef LDAP_VERSION_MIN 5009 if (lmap->ldap_version < LDAP_VERSION_MIN) 5010 { 5011 syserr("LDAP version %d is lower than min of %d in map %s", 5012 lmap->ldap_version, LDAP_VERSION_MIN, 5013 map->map_mname); 5014 goto fail; 5015 } 5016 # endif /* LDAP_VERSION_MIN */ 5017 break; 5018 5019 case 'x': 5020 # if _FFR_SM_LDAP_DBG 5021 while (isascii(*++p) && isspace(*p)) 5022 continue; 5023 lmap->ldap_debug = atoi(p); 5024 # endif 5025 break; 5026 5027 case 'Z': 5028 while (isascii(*++p) && isspace(*p)) 5029 continue; 5030 lmap->ldap_sizelimit = atoi(p); 5031 break; 5032 5033 default: 5034 syserr("Illegal option %c map %s", *p, map->map_mname); 5035 break; 5036 } 5037 5038 /* need to account for quoted strings here */ 5039 while (*p != '\0' && !(SM_ISSPACE(*p))) 5040 { 5041 if (*p == '"') 5042 { 5043 while (*++p != '"' && *p != '\0') 5044 continue; 5045 if (*p != '\0') 5046 p++; 5047 } 5048 else 5049 p++; 5050 } 5051 5052 if (*p != '\0') 5053 *p++ = '\0'; 5054 } 5055 5056 if (map->map_app != NULL) 5057 map->map_app = newstr(ldapmap_dequote(map->map_app)); 5058 if (map->map_tapp != NULL) 5059 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp)); 5060 5061 /* 5062 ** We need to swallow up all the stuff into a struct 5063 ** and dump it into map->map_dbptr1 5064 */ 5065 5066 if (lmap->ldap_host != NULL && 5067 (LDAPDefaults == NULL || 5068 LDAPDefaults == lmap || 5069 LDAPDefaults->ldap_host != lmap->ldap_host)) 5070 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host)); 5071 map->map_domain = lmap->ldap_host; 5072 5073 if (lmap->ldap_uri != NULL && 5074 (LDAPDefaults == NULL || 5075 LDAPDefaults == lmap || 5076 LDAPDefaults->ldap_uri != lmap->ldap_uri)) 5077 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri)); 5078 map->map_domain = lmap->ldap_uri; 5079 5080 if (lmap->ldap_binddn != NULL && 5081 (LDAPDefaults == NULL || 5082 LDAPDefaults == lmap || 5083 LDAPDefaults->ldap_binddn != lmap->ldap_binddn)) 5084 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn)); 5085 5086 if (lmap->ldap_secret != NULL && 5087 (LDAPDefaults == NULL || 5088 LDAPDefaults == lmap || 5089 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 5090 { 5091 SM_FILE_T *sfd; 5092 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES; 5093 5094 if (DontLockReadFiles) 5095 sff |= SFF_NOLOCK; 5096 5097 /* need to use method to map secret to passwd string */ 5098 switch (lmap->ldap_method) 5099 { 5100 case LDAP_AUTH_NONE: 5101 /* Do nothing */ 5102 break; 5103 5104 case LDAP_AUTH_SIMPLE: 5105 5106 /* 5107 ** Secret is the name of a file with 5108 ** the first line as the password. 5109 */ 5110 5111 /* Already read in the secret? */ 5112 if (secretread) 5113 break; 5114 5115 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret), 5116 O_RDONLY, 0, sff); 5117 if (sfd == NULL) 5118 { 5119 syserr("LDAP map: cannot open secret %s", 5120 ldapmap_dequote(lmap->ldap_secret)); 5121 goto fail; 5122 } 5123 lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp), 5124 sfd, TimeOuts.to_fileopen, 5125 "ldapmap_parseargs"); 5126 (void) sm_io_close(sfd, SM_TIME_DEFAULT); 5127 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD) 5128 { 5129 syserr("LDAP map: secret in %s too long", 5130 ldapmap_dequote(lmap->ldap_secret)); 5131 goto fail; 5132 } 5133 if (lmap->ldap_secret != NULL && 5134 strlen(m_tmp) > 0) 5135 { 5136 /* chomp newline */ 5137 if (m_tmp[strlen(m_tmp) - 1] == '\n') 5138 m_tmp[strlen(m_tmp) - 1] = '\0'; 5139 5140 lmap->ldap_secret = m_tmp; 5141 } 5142 break; 5143 5144 # ifdef LDAP_AUTH_KRBV4 5145 case LDAP_AUTH_KRBV4: 5146 5147 /* 5148 ** Secret is where the ticket file is 5149 ** stashed 5150 */ 5151 5152 (void) sm_snprintf(m_tmp, sizeof(m_tmp), 5153 "KRBTKFILE=%s", 5154 ldapmap_dequote(lmap->ldap_secret)); 5155 lmap->ldap_secret = m_tmp; 5156 break; 5157 # endif /* LDAP_AUTH_KRBV4 */ 5158 5159 default: /* Should NEVER get here */ 5160 syserr("LDAP map: Illegal value in lmap method"); 5161 goto fail; 5162 /* NOTREACHED */ 5163 break; 5164 } 5165 } 5166 5167 if (lmap->ldap_secret != NULL && 5168 (LDAPDefaults == NULL || 5169 LDAPDefaults == lmap || 5170 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 5171 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret)); 5172 5173 if (lmap->ldap_base != NULL && 5174 (LDAPDefaults == NULL || 5175 LDAPDefaults == lmap || 5176 LDAPDefaults->ldap_base != lmap->ldap_base)) 5177 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base)); 5178 5179 /* 5180 ** Save the server from extra work. If request is for a single 5181 ** match, tell the server to only return enough records to 5182 ** determine if there is a single match or not. This can not 5183 ** be one since the server would only return one and we wouldn't 5184 ** know if there were others available. 5185 */ 5186 5187 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 5188 lmap->ldap_sizelimit = 2; 5189 5190 /* If setting defaults, don't process ldap_filter and ldap_attr */ 5191 if (lmap == LDAPDefaults) 5192 return true; 5193 5194 if (lmap->ldap_filter != NULL) 5195 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter)); 5196 else 5197 { 5198 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 5199 { 5200 syserr("No filter given in map %s", map->map_mname); 5201 goto fail; 5202 } 5203 } 5204 5205 if (!attrssetup && lmap->ldap_attr[0] != NULL) 5206 { 5207 bool recurse = false; 5208 bool normalseen = false; 5209 5210 i = 0; 5211 p = ldapmap_dequote(lmap->ldap_attr[0]); 5212 lmap->ldap_attr[0] = NULL; 5213 5214 /* Prime the attr list with the objectClass attribute */ 5215 lmap->ldap_attr[i] = "objectClass"; 5216 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS; 5217 lmap->ldap_attr_needobjclass[i] = NULL; 5218 i++; 5219 5220 while (p != NULL) 5221 { 5222 char *v; 5223 5224 while (SM_ISSPACE(*p)) 5225 p++; 5226 if (*p == '\0') 5227 break; 5228 v = p; 5229 p = strchr(v, ','); 5230 if (p != NULL) 5231 *p++ = '\0'; 5232 5233 if (i >= LDAPMAP_MAX_ATTR) 5234 { 5235 syserr("Too many return attributes in %s (max %d)", 5236 map->map_mname, LDAPMAP_MAX_ATTR); 5237 goto fail; 5238 } 5239 if (*v != '\0') 5240 { 5241 int j; 5242 int use; 5243 char *type; 5244 char *needobjclass; 5245 5246 type = strchr(v, ':'); 5247 if (type != NULL) 5248 { 5249 *type++ = '\0'; 5250 needobjclass = strchr(type, ':'); 5251 if (needobjclass != NULL) 5252 *needobjclass++ = '\0'; 5253 } 5254 else 5255 { 5256 needobjclass = NULL; 5257 } 5258 5259 use = i; 5260 5261 /* allow override on "objectClass" type */ 5262 if (SM_STRCASEEQ(v, "objectClass") && 5263 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS) 5264 { 5265 use = 0; 5266 } 5267 else 5268 { 5269 /* 5270 ** Don't add something to attribute 5271 ** list twice. 5272 */ 5273 5274 for (j = 1; j < i; j++) 5275 { 5276 if (SM_STRCASEEQ(v, lmap->ldap_attr[j])) 5277 { 5278 syserr("Duplicate attribute (%s) in %s", 5279 v, map->map_mname); 5280 goto fail; 5281 } 5282 } 5283 5284 lmap->ldap_attr[use] = newstr(v); 5285 if (needobjclass != NULL && 5286 *needobjclass != '\0' && 5287 *needobjclass != '*') 5288 { 5289 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass); 5290 } 5291 else 5292 { 5293 lmap->ldap_attr_needobjclass[use] = NULL; 5294 } 5295 5296 } 5297 5298 if (type != NULL && *type != '\0') 5299 { 5300 if (SM_STRCASEEQ(type, "dn")) 5301 { 5302 recurse = true; 5303 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN; 5304 } 5305 else if (SM_STRCASEEQ(type, "filter")) 5306 { 5307 recurse = true; 5308 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER; 5309 } 5310 else if (SM_STRCASEEQ(type, "url")) 5311 { 5312 recurse = true; 5313 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL; 5314 } 5315 else if (SM_STRCASEEQ(type, "normal")) 5316 { 5317 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL; 5318 normalseen = true; 5319 } 5320 else 5321 { 5322 syserr("Unknown attribute type (%s) in %s", 5323 type, map->map_mname); 5324 goto fail; 5325 } 5326 } 5327 else 5328 { 5329 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL; 5330 normalseen = true; 5331 } 5332 i++; 5333 } 5334 } 5335 lmap->ldap_attr[i] = NULL; 5336 5337 /* Set in case needed in future code */ 5338 attrssetup = true; 5339 5340 if (recurse && !normalseen) 5341 { 5342 syserr("LDAP recursion requested in %s but no returnable attribute given", 5343 map->map_mname); 5344 goto fail; 5345 } 5346 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE) 5347 { 5348 syserr("LDAP recursion requested in %s can not be used with -n", 5349 map->map_mname); 5350 goto fail; 5351 } 5352 } 5353 map->map_db1 = (ARBPTR_T) lmap; 5354 return true; 5355 fail: 5356 SM_FREE(lmap_alloc); 5357 return false; 5358 } 5359 5360 /* 5361 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf 5362 ** 5363 ** Parameters: 5364 ** spec -- map argument string from LDAPDefaults option 5365 ** 5366 ** Returns: 5367 ** None. 5368 */ 5369 5370 void 5371 ldapmap_set_defaults(spec) 5372 char *spec; 5373 { 5374 STAB *class; 5375 MAP map; 5376 5377 /* Allocate and set the default values */ 5378 if (LDAPDefaults == NULL) 5379 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults)); 5380 sm_ldap_clear(LDAPDefaults); 5381 5382 memset(&map, '\0', sizeof(map)); 5383 5384 /* look up the class */ 5385 class = stab("ldap", ST_MAPCLASS, ST_FIND); 5386 if (class == NULL) 5387 { 5388 syserr("readcf: LDAPDefaultSpec: class ldap not available"); 5389 return; 5390 } 5391 map.map_class = &class->s_mapclass; 5392 map.map_db1 = (ARBPTR_T) LDAPDefaults; 5393 map.map_mname = "O LDAPDefaultSpec"; 5394 5395 (void) ldapmap_parseargs(&map, spec); 5396 5397 /* These should never be set in LDAPDefaults */ 5398 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) || 5399 map.map_spacesub != SpaceSub || 5400 map.map_app != NULL || 5401 map.map_tapp != NULL) 5402 { 5403 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags"); 5404 SM_FREE(map.map_app); 5405 SM_FREE(map.map_tapp); 5406 } 5407 5408 if (LDAPDefaults->ldap_filter != NULL) 5409 { 5410 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter"); 5411 5412 /* don't free, it isn't malloc'ed in parseargs */ 5413 LDAPDefaults->ldap_filter = NULL; 5414 } 5415 5416 if (LDAPDefaults->ldap_attr[0] != NULL) 5417 { 5418 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes"); 5419 /* don't free, they aren't malloc'ed in parseargs */ 5420 LDAPDefaults->ldap_attr[0] = NULL; 5421 } 5422 } 5423 #endif /* LDAPMAP */ 5424 /* 5425 ** PH map 5426 */ 5427 5428 #if PH_MAP 5429 5430 /* 5431 ** Support for the CCSO Nameserver (ph/qi). 5432 ** This code is intended to replace the so-called "ph mailer". 5433 ** Contributed by Mark D. Roth. Contact him for support. 5434 */ 5435 5436 /* what version of the ph map code we're running */ 5437 static char phmap_id[128]; 5438 5439 /* sendmail version for phmap id string */ 5440 extern const char Version[]; 5441 5442 /* assume we're using nph-1.2.x if not specified */ 5443 # ifndef NPH_VERSION 5444 # define NPH_VERSION 10200 5445 # endif 5446 5447 /* compatibility for versions older than nph-1.2.0 */ 5448 # if NPH_VERSION < 10200 5449 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN 5450 # define PH_OPEN_DONTID PH_DONTID 5451 # define PH_CLOSE_FAST PH_FASTCLOSE 5452 # define PH_ERR_DATAERR PH_DATAERR 5453 # define PH_ERR_NOMATCH PH_NOMATCH 5454 # endif /* NPH_VERSION < 10200 */ 5455 5456 /* 5457 ** PH_MAP_PARSEARGS -- parse ph map definition args. 5458 */ 5459 5460 bool 5461 ph_map_parseargs(map, args) 5462 MAP *map; 5463 char *args; 5464 { 5465 register bool done; 5466 register char *p = args; 5467 PH_MAP_STRUCT *pmap = NULL; 5468 5469 /* initialize version string */ 5470 (void) sm_snprintf(phmap_id, sizeof(phmap_id), 5471 "sendmail-%s phmap-20010529 libphclient-%s", 5472 Version, libphclient_version); 5473 5474 pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap)); 5475 5476 /* defaults */ 5477 pmap->ph_servers = NULL; 5478 pmap->ph_field_list = NULL; 5479 pmap->ph = NULL; 5480 pmap->ph_timeout = 0; 5481 pmap->ph_fastclose = 0; 5482 5483 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 5484 for (;;) 5485 { 5486 while (SM_ISSPACE(*p)) 5487 p++; 5488 if (*p != '-') 5489 break; 5490 switch (*++p) 5491 { 5492 case 'N': 5493 map->map_mflags |= MF_INCLNULL; 5494 map->map_mflags &= ~MF_TRY0NULL; 5495 break; 5496 5497 case 'O': 5498 map->map_mflags &= ~MF_TRY1NULL; 5499 break; 5500 5501 case 'o': 5502 map->map_mflags |= MF_OPTIONAL; 5503 break; 5504 5505 case 'f': 5506 map->map_mflags |= MF_NOFOLDCASE; 5507 break; 5508 5509 case 'm': 5510 map->map_mflags |= MF_MATCHONLY; 5511 break; 5512 5513 case 'A': 5514 map->map_mflags |= MF_APPEND; 5515 break; 5516 5517 case 'q': 5518 map->map_mflags |= MF_KEEPQUOTES; 5519 break; 5520 5521 case 't': 5522 map->map_mflags |= MF_NODEFER; 5523 break; 5524 5525 case 'a': 5526 map->map_app = ++p; 5527 break; 5528 5529 case 'T': 5530 map->map_tapp = ++p; 5531 break; 5532 5533 case 'l': 5534 while (isascii(*++p) && isspace(*p)) 5535 continue; 5536 pmap->ph_timeout = atoi(p); 5537 break; 5538 5539 case 'S': 5540 map->map_spacesub = *++p; 5541 break; 5542 5543 case 'D': 5544 map->map_mflags |= MF_DEFER; 5545 break; 5546 5547 case 'h': /* PH server list */ 5548 while (isascii(*++p) && isspace(*p)) 5549 continue; 5550 pmap->ph_servers = p; 5551 break; 5552 5553 case 'k': /* fields to search for */ 5554 while (isascii(*++p) && isspace(*p)) 5555 continue; 5556 pmap->ph_field_list = p; 5557 break; 5558 5559 default: 5560 syserr("ph_map_parseargs: unknown option -%c", *p); 5561 } 5562 5563 /* try to account for quoted strings */ 5564 done = SM_ISSPACE(*p); 5565 while (*p != '\0' && !done) 5566 { 5567 if (*p == '"') 5568 { 5569 while (*++p != '"' && *p != '\0') 5570 continue; 5571 if (*p != '\0') 5572 p++; 5573 } 5574 else 5575 p++; 5576 done = SM_ISSPACE(*p); 5577 } 5578 5579 if (*p != '\0') 5580 *p++ = '\0'; 5581 } 5582 5583 if (map->map_app != NULL) 5584 map->map_app = newstr(ph_map_dequote(map->map_app)); 5585 if (map->map_tapp != NULL) 5586 map->map_tapp = newstr(ph_map_dequote(map->map_tapp)); 5587 5588 if (pmap->ph_field_list != NULL) 5589 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list)); 5590 5591 if (pmap->ph_servers != NULL) 5592 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers)); 5593 else 5594 { 5595 syserr("ph_map_parseargs: -h flag is required"); 5596 return false; 5597 } 5598 5599 map->map_db1 = (ARBPTR_T) pmap; 5600 return true; 5601 } 5602 5603 /* 5604 ** PH_MAP_CLOSE -- close the connection to the ph server 5605 */ 5606 5607 void 5608 ph_map_close(map) 5609 MAP *map; 5610 { 5611 PH_MAP_STRUCT *pmap; 5612 5613 pmap = (PH_MAP_STRUCT *)map->map_db1; 5614 if (tTd(38, 9)) 5615 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n", 5616 map->map_mname, pmap->ph_fastclose); 5617 5618 5619 if (pmap->ph != NULL) 5620 { 5621 ph_set_sendhook(pmap->ph, NULL); 5622 ph_set_recvhook(pmap->ph, NULL); 5623 ph_close(pmap->ph, pmap->ph_fastclose); 5624 } 5625 5626 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 5627 } 5628 5629 static jmp_buf PHTimeout; 5630 5631 /* ARGSUSED */ 5632 static void 5633 ph_timeout(unused) 5634 int unused; 5635 { 5636 /* 5637 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 5638 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 5639 ** DOING. 5640 */ 5641 5642 errno = ETIMEDOUT; 5643 longjmp(PHTimeout, 1); 5644 } 5645 5646 static void 5647 # if NPH_VERSION >= 10200 5648 ph_map_send_debug(appdata, text) 5649 void *appdata; 5650 # else 5651 ph_map_send_debug(text) 5652 # endif 5653 char *text; 5654 { 5655 if (LogLevel > 9) 5656 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5657 "ph_map_send_debug: ==> %s", text); 5658 if (tTd(38, 20)) 5659 sm_dprintf("ph_map_send_debug: ==> %s\n", text); 5660 } 5661 5662 static void 5663 # if NPH_VERSION >= 10200 5664 ph_map_recv_debug(appdata, text) 5665 void *appdata; 5666 # else 5667 ph_map_recv_debug(text) 5668 # endif 5669 char *text; 5670 { 5671 if (LogLevel > 10) 5672 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5673 "ph_map_recv_debug: <== %s", text); 5674 if (tTd(38, 21)) 5675 sm_dprintf("ph_map_recv_debug: <== %s\n", text); 5676 } 5677 5678 /* 5679 ** PH_MAP_OPEN -- sub for opening PH map 5680 */ 5681 bool 5682 ph_map_open(map, mode) 5683 MAP *map; 5684 int mode; 5685 { 5686 PH_MAP_STRUCT *pmap; 5687 register SM_EVENT *ev = NULL; 5688 int save_errno = 0; 5689 char *hostlist, *host; 5690 5691 if (tTd(38, 2)) 5692 sm_dprintf("ph_map_open(%s)\n", map->map_mname); 5693 5694 mode &= O_ACCMODE; 5695 if (mode != O_RDONLY) 5696 { 5697 /* issue a pseudo-error message */ 5698 errno = SM_EMAPCANTWRITE; 5699 return false; 5700 } 5701 5702 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER && 5703 bitset(MF_DEFER, map->map_mflags)) 5704 { 5705 if (tTd(9, 1)) 5706 sm_dprintf("ph_map_open(%s) => DEFERRED\n", 5707 map->map_mname); 5708 5709 /* 5710 ** Unset MF_DEFER here so that map_lookup() returns 5711 ** a temporary failure using the bogus map and 5712 ** map->map_tapp instead of the default permanent error. 5713 */ 5714 5715 map->map_mflags &= ~MF_DEFER; 5716 return false; 5717 } 5718 5719 pmap = (PH_MAP_STRUCT *)map->map_db1; 5720 pmap->ph_fastclose = 0; /* refresh field for reopen */ 5721 5722 /* try each host in the list */ 5723 hostlist = newstr(pmap->ph_servers); 5724 for (host = strtok(hostlist, " "); 5725 host != NULL; 5726 host = strtok(NULL, " ")) 5727 { 5728 /* set timeout */ 5729 if (pmap->ph_timeout != 0) 5730 { 5731 if (setjmp(PHTimeout) != 0) 5732 { 5733 ev = NULL; 5734 if (LogLevel > 1) 5735 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5736 "timeout connecting to PH server %.100s", 5737 host); 5738 errno = ETIMEDOUT; 5739 goto ph_map_open_abort; 5740 } 5741 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); 5742 } 5743 5744 /* open connection to server */ 5745 if (ph_open(&(pmap->ph), host, 5746 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID, 5747 ph_map_send_debug, ph_map_recv_debug 5748 # if NPH_VERSION >= 10200 5749 , NULL 5750 # endif 5751 ) == 0 5752 && ph_id(pmap->ph, phmap_id) == 0) 5753 { 5754 if (ev != NULL) 5755 sm_clrevent(ev); 5756 sm_free(hostlist); /* XXX */ 5757 return true; 5758 } 5759 5760 ph_map_open_abort: 5761 save_errno = errno; 5762 if (ev != NULL) 5763 sm_clrevent(ev); 5764 pmap->ph_fastclose = PH_CLOSE_FAST; 5765 ph_map_close(map); 5766 errno = save_errno; 5767 } 5768 5769 if (bitset(MF_NODEFER, map->map_mflags)) 5770 { 5771 if (errno == 0) 5772 errno = EAGAIN; 5773 syserr("ph_map_open: %s: cannot connect to PH server", 5774 map->map_mname); 5775 } 5776 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1) 5777 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5778 "ph_map_open: %s: cannot connect to PH server", 5779 map->map_mname); 5780 sm_free(hostlist); /* XXX */ 5781 return false; 5782 } 5783 5784 /* 5785 ** PH_MAP_LOOKUP -- look up key from ph server 5786 */ 5787 5788 char * 5789 ph_map_lookup(map, key, args, pstat) 5790 MAP *map; 5791 char *key; 5792 char **args; 5793 int *pstat; 5794 { 5795 int i, save_errno = 0; 5796 register SM_EVENT *ev = NULL; 5797 PH_MAP_STRUCT *pmap; 5798 char *value = NULL; 5799 5800 pmap = (PH_MAP_STRUCT *)map->map_db1; 5801 5802 *pstat = EX_OK; 5803 5804 /* set timeout */ 5805 if (pmap->ph_timeout != 0) 5806 { 5807 if (setjmp(PHTimeout) != 0) 5808 { 5809 ev = NULL; 5810 if (LogLevel > 1) 5811 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5812 "timeout during PH lookup of %.100s", 5813 key); 5814 errno = ETIMEDOUT; 5815 *pstat = EX_TEMPFAIL; 5816 goto ph_map_lookup_abort; 5817 } 5818 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); 5819 } 5820 5821 /* perform lookup */ 5822 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value); 5823 if (i == -1) 5824 *pstat = EX_TEMPFAIL; 5825 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR) 5826 *pstat = EX_UNAVAILABLE; 5827 5828 ph_map_lookup_abort: 5829 if (ev != NULL) 5830 sm_clrevent(ev); 5831 5832 /* 5833 ** Close the connection if the timer popped 5834 ** or we got a temporary PH error 5835 */ 5836 5837 if (*pstat == EX_TEMPFAIL) 5838 { 5839 save_errno = errno; 5840 pmap->ph_fastclose = PH_CLOSE_FAST; 5841 ph_map_close(map); 5842 errno = save_errno; 5843 } 5844 5845 if (*pstat == EX_OK) 5846 { 5847 if (tTd(38,20)) 5848 sm_dprintf("ph_map_lookup: %s => %s\n", key, value); 5849 5850 if (bitset(MF_MATCHONLY, map->map_mflags)) 5851 return map_rewrite(map, key, strlen(key), NULL); 5852 else 5853 return map_rewrite(map, value, strlen(value), args); 5854 } 5855 5856 return NULL; 5857 } 5858 #endif /* PH_MAP */ 5859 5860 /* 5861 ** syslog map 5862 */ 5863 5864 #define map_prio map_lockfd /* overload field */ 5865 5866 /* 5867 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages. 5868 */ 5869 5870 bool 5871 syslog_map_parseargs(map, args) 5872 MAP *map; 5873 char *args; 5874 { 5875 char *p = args; 5876 char *priority = NULL; 5877 5878 /* there is no check whether there is really an argument */ 5879 while (*p != '\0') 5880 { 5881 while (SM_ISSPACE(*p)) 5882 p++; 5883 if (*p != '-') 5884 break; 5885 ++p; 5886 if (*p == 'D') 5887 { 5888 map->map_mflags |= MF_DEFER; 5889 ++p; 5890 } 5891 else if (*p == 'S') 5892 { 5893 map->map_spacesub = *++p; 5894 if (*p != '\0') 5895 p++; 5896 } 5897 else if (*p == 'L') 5898 { 5899 while (*++p != '\0' && SM_ISSPACE(*p)) 5900 continue; 5901 if (*p == '\0') 5902 break; 5903 priority = p; 5904 while (*p != '\0' && !(SM_ISSPACE(*p))) 5905 p++; 5906 if (*p != '\0') 5907 *p++ = '\0'; 5908 } 5909 else 5910 { 5911 syserr("Illegal option %c map syslog", *p); 5912 ++p; 5913 } 5914 } 5915 5916 if (priority == NULL) 5917 map->map_prio = LOG_INFO; 5918 else 5919 { 5920 if (sm_strncasecmp("LOG_", priority, 4) == 0) 5921 priority += 4; 5922 5923 #ifdef LOG_EMERG 5924 if (SM_STRCASEEQ("EMERG", priority)) 5925 map->map_prio = LOG_EMERG; 5926 else 5927 #endif 5928 #ifdef LOG_ALERT 5929 if (SM_STRCASEEQ("ALERT", priority)) 5930 map->map_prio = LOG_ALERT; 5931 else 5932 #endif 5933 #ifdef LOG_CRIT 5934 if (SM_STRCASEEQ("CRIT", priority)) 5935 map->map_prio = LOG_CRIT; 5936 else 5937 #endif 5938 #ifdef LOG_ERR 5939 if (SM_STRCASEEQ("ERR", priority)) 5940 map->map_prio = LOG_ERR; 5941 else 5942 #endif 5943 #ifdef LOG_WARNING 5944 if (SM_STRCASEEQ("WARNING", priority)) 5945 map->map_prio = LOG_WARNING; 5946 else 5947 #endif 5948 #ifdef LOG_NOTICE 5949 if (SM_STRCASEEQ("NOTICE", priority)) 5950 map->map_prio = LOG_NOTICE; 5951 else 5952 #endif 5953 #ifdef LOG_INFO 5954 if (SM_STRCASEEQ("INFO", priority)) 5955 map->map_prio = LOG_INFO; 5956 else 5957 #endif 5958 #ifdef LOG_DEBUG 5959 if (SM_STRCASEEQ("DEBUG", priority)) 5960 map->map_prio = LOG_DEBUG; 5961 else 5962 #endif 5963 { 5964 syserr("syslog_map_parseargs: Unknown priority %s", 5965 priority); 5966 return false; 5967 } 5968 } 5969 #if _FFR_8BITENVADDR 5970 map->map_mflags |= MF_KEEPXFMT; 5971 #endif 5972 return true; 5973 } 5974 5975 /* 5976 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string 5977 */ 5978 5979 char * 5980 syslog_map_lookup(map, string, args, statp) 5981 MAP *map; 5982 char *string; 5983 char **args; 5984 int *statp; 5985 { 5986 char *ptr = map_rewrite(map, string, strlen(string), args); 5987 5988 if (ptr != NULL) 5989 { 5990 if (tTd(38, 20)) 5991 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n", 5992 map->map_mname, map->map_prio, ptr); 5993 5994 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr); 5995 } 5996 5997 *statp = EX_OK; 5998 return ""; 5999 } 6000 6001 #if _FFR_DPRINTF_MAP 6002 /* 6003 ** dprintf map 6004 */ 6005 6006 #define map_dbg_level map_lockfd /* overload field */ 6007 6008 /* 6009 ** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages. 6010 */ 6011 6012 bool 6013 dprintf_map_parseargs(map, args) 6014 MAP *map; 6015 char *args; 6016 { 6017 char *p = args; 6018 char *dbg_level = NULL; 6019 6020 /* there is no check whether there is really an argument */ 6021 while (*p != '\0') 6022 { 6023 while (SM_ISSPACE(*p)) 6024 p++; 6025 if (*p != '-') 6026 break; 6027 ++p; 6028 if (*p == 'D') 6029 { 6030 map->map_mflags |= MF_DEFER; 6031 ++p; 6032 } 6033 else if (*p == 'S') 6034 { 6035 map->map_spacesub = *++p; 6036 if (*p != '\0') 6037 p++; 6038 } 6039 else if (*p == 'd') 6040 { 6041 while (*++p != '\0' && SM_ISSPACE(*p)) 6042 continue; 6043 if (*p == '\0') 6044 break; 6045 dbg_level = p; 6046 while (*p != '\0' && !(SM_ISSPACE(*p))) 6047 p++; 6048 if (*p != '\0') 6049 *p++ = '\0'; 6050 } 6051 else 6052 { 6053 syserr("Illegal option %c map dprintf", *p); 6054 ++p; 6055 } 6056 } 6057 6058 if (dbg_level == NULL) 6059 map->map_dbg_level = 0; 6060 else 6061 { 6062 if (!(isascii(*dbg_level) && isdigit(*dbg_level))) 6063 { 6064 syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s", 6065 map->map_mname, map->map_file, 6066 dbg_level); 6067 return false; 6068 } 6069 map->map_dbg_level = atoi(dbg_level); 6070 } 6071 # if _FFR_8BITENVADDR 6072 map->map_mflags |= MF_KEEPXFMT; 6073 # endif 6074 return true; 6075 } 6076 6077 /* 6078 ** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string 6079 */ 6080 6081 char * 6082 dprintf_map_lookup(map, string, args, statp) 6083 MAP *map; 6084 char *string; 6085 char **args; 6086 int *statp; 6087 { 6088 char *ptr = map_rewrite(map, string, strlen(string), args); 6089 6090 if (ptr != NULL && tTd(85, map->map_dbg_level)) 6091 sm_dprintf("%s\n", ptr); 6092 *statp = EX_OK; 6093 return ""; 6094 } 6095 #endif /* _FFR_DPRINTF_MAP */ 6096 6097 /* 6098 ** HESIOD Modules 6099 */ 6100 6101 #if HESIOD 6102 6103 bool 6104 hes_map_open(map, mode) 6105 MAP *map; 6106 int mode; 6107 { 6108 if (tTd(38, 2)) 6109 sm_dprintf("hes_map_open(%s, %s, %d)\n", 6110 map->map_mname, map->map_file, mode); 6111 6112 if (mode != O_RDONLY) 6113 { 6114 /* issue a pseudo-error message */ 6115 errno = SM_EMAPCANTWRITE; 6116 return false; 6117 } 6118 6119 # ifdef HESIOD_INIT 6120 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0) 6121 return true; 6122 6123 if (!bitset(MF_OPTIONAL, map->map_mflags)) 6124 syserr("451 4.3.5 cannot initialize Hesiod map (%s)", 6125 sm_errstring(errno)); 6126 return false; 6127 # else /* HESIOD_INIT */ 6128 if (hes_error() == HES_ER_UNINIT) 6129 hes_init(); 6130 switch (hes_error()) 6131 { 6132 case HES_ER_OK: 6133 case HES_ER_NOTFOUND: 6134 return true; 6135 } 6136 6137 if (!bitset(MF_OPTIONAL, map->map_mflags)) 6138 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error()); 6139 6140 return false; 6141 # endif /* HESIOD_INIT */ 6142 } 6143 6144 char * 6145 hes_map_lookup(map, name, av, statp) 6146 MAP *map; 6147 char *name; 6148 char **av; 6149 int *statp; 6150 { 6151 char **hp; 6152 6153 if (tTd(38, 20)) 6154 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name); 6155 6156 if (name[0] == '\\') 6157 { 6158 char *np; 6159 int nl; 6160 int save_errno; 6161 char nbuf[MAXNAME]; /* EAI:ok - larger buffer used if needed */ 6162 6163 nl = strlen(name); 6164 if (nl < sizeof(nbuf) - 1) 6165 np = nbuf; 6166 else 6167 np = xalloc(nl + 2); 6168 np[0] = '\\'; 6169 (void) sm_strlcpy(&np[1], name, sizeof(nbuf) - 1); 6170 # ifdef HESIOD_INIT 6171 hp = hesiod_resolve(HesiodContext, np, map->map_file); 6172 # else 6173 hp = hes_resolve(np, map->map_file); 6174 # endif 6175 save_errno = errno; 6176 if (np != nbuf) 6177 SM_FREE(np); /* XXX */ 6178 errno = save_errno; 6179 } 6180 else 6181 { 6182 # ifdef HESIOD_INIT 6183 hp = hesiod_resolve(HesiodContext, name, map->map_file); 6184 # else 6185 hp = hes_resolve(name, map->map_file); 6186 # endif /* HESIOD_INIT */ 6187 } 6188 # ifdef HESIOD_INIT 6189 if (hp == NULL || *hp == NULL) 6190 { 6191 switch (errno) 6192 { 6193 case ENOENT: 6194 *statp = EX_NOTFOUND; 6195 break; 6196 case ECONNREFUSED: 6197 *statp = EX_TEMPFAIL; 6198 break; 6199 case EMSGSIZE: 6200 case ENOMEM: 6201 default: 6202 *statp = EX_UNAVAILABLE; 6203 break; 6204 } 6205 if (hp != NULL) 6206 hesiod_free_list(HesiodContext, hp); 6207 return NULL; 6208 } 6209 # else /* HESIOD_INIT */ 6210 if (hp == NULL || hp[0] == NULL) 6211 { 6212 switch (hes_error()) 6213 { 6214 case HES_ER_OK: 6215 *statp = EX_OK; 6216 break; 6217 6218 case HES_ER_NOTFOUND: 6219 *statp = EX_NOTFOUND; 6220 break; 6221 6222 case HES_ER_CONFIG: 6223 *statp = EX_UNAVAILABLE; 6224 break; 6225 6226 case HES_ER_NET: 6227 *statp = EX_TEMPFAIL; 6228 break; 6229 } 6230 return NULL; 6231 } 6232 # endif /* HESIOD_INIT */ 6233 6234 if (bitset(MF_MATCHONLY, map->map_mflags)) 6235 return map_rewrite(map, name, strlen(name), NULL); 6236 else 6237 return map_rewrite(map, hp[0], strlen(hp[0]), av); 6238 } 6239 6240 /* 6241 ** HES_MAP_CLOSE -- free the Hesiod context 6242 */ 6243 6244 void 6245 hes_map_close(map) 6246 MAP *map; 6247 { 6248 if (tTd(38, 20)) 6249 sm_dprintf("hes_map_close(%s)\n", map->map_file); 6250 6251 # ifdef HESIOD_INIT 6252 /* Free the hesiod context */ 6253 if (HesiodContext != NULL) 6254 { 6255 hesiod_end(HesiodContext); 6256 HesiodContext = NULL; 6257 } 6258 # endif /* HESIOD_INIT */ 6259 } 6260 6261 #endif /* HESIOD */ 6262 /* 6263 ** NeXT NETINFO Modules 6264 */ 6265 6266 #if NETINFO 6267 6268 # define NETINFO_DEFAULT_DIR "/aliases" 6269 # define NETINFO_DEFAULT_PROPERTY "members" 6270 6271 /* 6272 ** NI_MAP_OPEN -- open NetInfo Aliases 6273 */ 6274 6275 bool 6276 ni_map_open(map, mode) 6277 MAP *map; 6278 int mode; 6279 { 6280 if (tTd(38, 2)) 6281 sm_dprintf("ni_map_open(%s, %s, %d)\n", 6282 map->map_mname, map->map_file, mode); 6283 mode &= O_ACCMODE; 6284 6285 if (*map->map_file == '\0') 6286 map->map_file = NETINFO_DEFAULT_DIR; 6287 6288 if (map->map_valcolnm == NULL) 6289 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; 6290 6291 if (map->map_coldelim == '\0') 6292 { 6293 if (bitset(MF_ALIAS, map->map_mflags)) 6294 map->map_coldelim = ','; 6295 else if (bitset(MF_FILECLASS, map->map_mflags)) 6296 map->map_coldelim = ' '; 6297 } 6298 return true; 6299 } 6300 6301 6302 /* 6303 ** NI_MAP_LOOKUP -- look up a datum in NetInfo 6304 */ 6305 6306 char * 6307 ni_map_lookup(map, name, av, statp) 6308 MAP *map; 6309 char *name; 6310 char **av; 6311 int *statp; 6312 { 6313 char *res; 6314 char *propval; 6315 6316 if (tTd(38, 20)) 6317 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name); 6318 6319 propval = ni_propval(map->map_file, map->map_keycolnm, name, 6320 map->map_valcolnm, map->map_coldelim); 6321 6322 if (propval == NULL) 6323 return NULL; 6324 6325 SM_TRY 6326 if (bitset(MF_MATCHONLY, map->map_mflags)) 6327 res = map_rewrite(map, name, strlen(name), NULL); 6328 else 6329 res = map_rewrite(map, propval, strlen(propval), av); 6330 SM_FINALLY 6331 sm_free(propval); 6332 SM_END_TRY 6333 return res; 6334 } 6335 6336 6337 static bool 6338 ni_getcanonname(name, hbsize, statp) 6339 char *name; 6340 int hbsize; 6341 int *statp; 6342 { 6343 char *vptr; 6344 char *ptr; 6345 char nbuf[MAXNAME + 1]; /* EAI:hostname */ 6346 6347 if (tTd(38, 20)) 6348 sm_dprintf("ni_getcanonname(%s)\n", name); 6349 6350 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf)) 6351 { 6352 *statp = EX_UNAVAILABLE; 6353 return false; 6354 } 6355 (void) shorten_hostname(nbuf); 6356 6357 /* we only accept single token search key */ 6358 if (strchr(nbuf, '.')) 6359 { 6360 *statp = EX_NOHOST; 6361 return false; 6362 } 6363 6364 /* Do the search */ 6365 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n'); 6366 6367 if (vptr == NULL) 6368 { 6369 *statp = EX_NOHOST; 6370 return false; 6371 } 6372 6373 /* Only want the first machine name */ 6374 if ((ptr = strchr(vptr, '\n')) != NULL) 6375 *ptr = '\0'; 6376 6377 if (sm_strlcpy(name, vptr, hbsize) >= hbsize) 6378 { 6379 sm_free(vptr); 6380 *statp = EX_UNAVAILABLE; 6381 return true; 6382 } 6383 sm_free(vptr); 6384 *statp = EX_OK; 6385 return false; 6386 } 6387 #endif /* NETINFO */ 6388 /* 6389 ** TEXT (unindexed text file) Modules 6390 ** 6391 ** This code donated by Sun Microsystems. 6392 */ 6393 6394 #define map_sff map_lockfd /* overload field */ 6395 6396 6397 /* 6398 ** TEXT_MAP_OPEN -- open text table 6399 */ 6400 6401 bool 6402 text_map_open(map, mode) 6403 MAP *map; 6404 int mode; 6405 { 6406 long sff; 6407 int i; 6408 6409 if (tTd(38, 2)) 6410 sm_dprintf("text_map_open(%s, %s, %d)\n", 6411 map->map_mname, map->map_file, mode); 6412 6413 mode &= O_ACCMODE; 6414 if (mode != O_RDONLY) 6415 { 6416 errno = EPERM; 6417 return false; 6418 } 6419 6420 if (*map->map_file == '\0') 6421 { 6422 syserr("text map \"%s\": file name required", 6423 map->map_mname); 6424 return false; 6425 } 6426 6427 if (map->map_file[0] != '/') 6428 { 6429 syserr("text map \"%s\": file name must be fully qualified", 6430 map->map_mname); 6431 return false; 6432 } 6433 6434 sff = SFF_ROOTOK|SFF_REGONLY; 6435 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 6436 sff |= SFF_NOWLINK; 6437 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 6438 sff |= SFF_SAFEDIRPATH; 6439 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, 6440 sff, S_IRUSR, NULL)) != 0) 6441 { 6442 int save_errno = errno; 6443 6444 /* cannot open this map */ 6445 if (tTd(38, 2)) 6446 sm_dprintf("\tunsafe map file: %d\n", i); 6447 errno = save_errno; 6448 if (!bitset(MF_OPTIONAL, map->map_mflags)) 6449 syserr("text map \"%s\": unsafe map file %s", 6450 map->map_mname, map->map_file); 6451 return false; 6452 } 6453 6454 if (map->map_keycolnm == NULL) 6455 map->map_keycolno = 0; 6456 else 6457 { 6458 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm))) 6459 { 6460 syserr("text map \"%s\", file %s: -k should specify a number, not %s", 6461 map->map_mname, map->map_file, 6462 map->map_keycolnm); 6463 return false; 6464 } 6465 map->map_keycolno = atoi(map->map_keycolnm); 6466 } 6467 6468 if (map->map_valcolnm == NULL) 6469 map->map_valcolno = 0; 6470 else 6471 { 6472 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm))) 6473 { 6474 syserr("text map \"%s\", file %s: -v should specify a number, not %s", 6475 map->map_mname, map->map_file, 6476 map->map_valcolnm); 6477 return false; 6478 } 6479 map->map_valcolno = atoi(map->map_valcolnm); 6480 } 6481 6482 if (tTd(38, 2)) 6483 { 6484 sm_dprintf("text_map_open(%s, %s): delimiter = ", 6485 map->map_mname, map->map_file); 6486 if (map->map_coldelim == '\0') 6487 sm_dprintf("(white space)\n"); 6488 else 6489 sm_dprintf("%c\n", map->map_coldelim); 6490 } 6491 6492 map->map_sff = sff; 6493 return true; 6494 } 6495 6496 6497 /* 6498 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table 6499 */ 6500 6501 char * 6502 text_map_lookup(map, name, av, statp) 6503 MAP *map; 6504 char *name; 6505 char **av; 6506 int *statp; 6507 { 6508 char *vp; 6509 auto int vsize; 6510 int buflen; 6511 SM_FILE_T *f; 6512 char delim; 6513 int key_idx; 6514 bool found_it; 6515 long sff = map->map_sff; 6516 char search_key[MAXNAME + 1]; /* EAI:ok */ 6517 char linebuf[MAXLINE]; 6518 char buf[MAXNAME + 1]; /* EAI:ok */ 6519 6520 found_it = false; 6521 if (tTd(38, 20)) 6522 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name); 6523 6524 buflen = strlen(name); 6525 if (buflen > sizeof(search_key) - 1) 6526 buflen = sizeof(search_key) - 1; /* XXX just cut if off? */ 6527 memmove(search_key, name, buflen); 6528 search_key[buflen] = '\0'; 6529 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 6530 makelower_buf(search_key, search_key, sizeof(search_key)); 6531 6532 f = safefopen(map->map_file, O_RDONLY, FileMode, sff); 6533 if (f == NULL) 6534 { 6535 map->map_mflags &= ~(MF_VALID|MF_OPEN); 6536 *statp = EX_UNAVAILABLE; 6537 return NULL; 6538 } 6539 key_idx = map->map_keycolno; 6540 delim = map->map_coldelim; 6541 while (sm_io_fgets(f, SM_TIME_DEFAULT, 6542 linebuf, sizeof(linebuf)) >= 0) 6543 { 6544 char *p; 6545 6546 /* skip comment line */ 6547 if (linebuf[0] == '#') 6548 continue; 6549 p = strchr(linebuf, '\n'); 6550 if (p != NULL) 6551 *p = '\0'; 6552 p = get_column(linebuf, key_idx, delim, buf, sizeof(buf)); 6553 if (p != NULL && SM_STRCASEEQ(search_key, p)) 6554 { 6555 found_it = true; 6556 break; 6557 } 6558 } 6559 (void) sm_io_close(f, SM_TIME_DEFAULT); 6560 if (!found_it) 6561 { 6562 *statp = EX_NOTFOUND; 6563 return NULL; 6564 } 6565 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf)); 6566 if (vp == NULL) 6567 { 6568 *statp = EX_NOTFOUND; 6569 return NULL; 6570 } 6571 vsize = strlen(vp); 6572 *statp = EX_OK; 6573 if (bitset(MF_MATCHONLY, map->map_mflags)) 6574 return map_rewrite(map, name, strlen(name), NULL); 6575 else 6576 return map_rewrite(map, vp, vsize, av); 6577 } 6578 6579 /* 6580 ** TEXT_GETCANONNAME -- look up canonical name in hosts file 6581 */ 6582 6583 static bool 6584 text_getcanonname(name, hbsize, statp) 6585 char *name; 6586 int hbsize; 6587 int *statp; 6588 { 6589 bool found; 6590 char *dot; 6591 SM_FILE_T *f; 6592 char linebuf[MAXLINE]; 6593 char cbuf[MAXNAME + 1]; /* EAI:hostname */ 6594 char nbuf[MAXNAME + 1]; /* EAI:hostname */ 6595 6596 if (tTd(38, 20)) 6597 sm_dprintf("text_getcanonname(%s)\n", name); 6598 6599 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf)) 6600 { 6601 *statp = EX_UNAVAILABLE; 6602 return false; 6603 } 6604 dot = shorten_hostname(nbuf); 6605 6606 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY, 6607 NULL); 6608 if (f == NULL) 6609 { 6610 *statp = EX_UNAVAILABLE; 6611 return false; 6612 } 6613 found = false; 6614 while (!found && 6615 sm_io_fgets(f, SM_TIME_DEFAULT, 6616 linebuf, sizeof(linebuf)) >= 0) 6617 { 6618 char *p = strpbrk(linebuf, "#\n"); 6619 6620 if (p != NULL) 6621 *p = '\0'; 6622 if (linebuf[0] != '\0') 6623 found = extract_canonname(nbuf, dot, linebuf, 6624 cbuf, sizeof(cbuf)); 6625 } 6626 (void) sm_io_close(f, SM_TIME_DEFAULT); 6627 if (!found) 6628 { 6629 *statp = EX_NOHOST; 6630 return false; 6631 } 6632 6633 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize) 6634 { 6635 *statp = EX_UNAVAILABLE; 6636 return false; 6637 } 6638 *statp = EX_OK; 6639 return true; 6640 } 6641 /* 6642 ** STAB (Symbol Table) Modules 6643 */ 6644 6645 6646 /* 6647 ** STAB_MAP_LOOKUP -- look up alias in symbol table 6648 */ 6649 6650 /* ARGSUSED2 */ 6651 char * 6652 stab_map_lookup(map, name, av, pstat) 6653 register MAP *map; 6654 char *name; 6655 char **av; 6656 int *pstat; 6657 { 6658 register STAB *s; 6659 6660 if (tTd(38, 20)) 6661 sm_dprintf("stab_lookup(%s, %s)\n", 6662 map->map_mname, name); 6663 6664 s = stab(name, ST_ALIAS, ST_FIND); 6665 if (s == NULL) 6666 return NULL; 6667 if (bitset(MF_MATCHONLY, map->map_mflags)) 6668 return map_rewrite(map, name, strlen(name), NULL); 6669 else 6670 return map_rewrite(map, s->s_alias, strlen(s->s_alias), av); 6671 } 6672 6673 /* 6674 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) 6675 */ 6676 6677 void 6678 stab_map_store(map, lhs, rhs) 6679 register MAP *map; 6680 char *lhs; 6681 char *rhs; 6682 { 6683 register STAB *s; 6684 6685 s = stab(lhs, ST_ALIAS, ST_ENTER); 6686 s->s_alias = newstr(rhs); 6687 } 6688 6689 6690 /* 6691 ** STAB_MAP_OPEN -- initialize (reads data file) 6692 ** 6693 ** This is a weird case -- it is only intended as a fallback for 6694 ** aliases. For this reason, opens for write (only during a 6695 ** "newaliases") always fails, and opens for read open the 6696 ** actual underlying text file instead of the database. 6697 */ 6698 6699 bool 6700 stab_map_open(map, mode) 6701 register MAP *map; 6702 int mode; 6703 { 6704 SM_FILE_T *af; 6705 long sff; 6706 struct stat st; 6707 6708 if (tTd(38, 2)) 6709 sm_dprintf("stab_map_open(%s, %s, %d)\n", 6710 map->map_mname, map->map_file, mode); 6711 6712 mode &= O_ACCMODE; 6713 if (mode != O_RDONLY) 6714 { 6715 errno = EPERM; 6716 return false; 6717 } 6718 6719 sff = SFF_ROOTOK|SFF_REGONLY; 6720 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 6721 sff |= SFF_NOWLINK; 6722 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 6723 sff |= SFF_SAFEDIRPATH; 6724 af = safefopen(map->map_file, O_RDONLY, 0444, sff); 6725 if (af == NULL) 6726 return false; 6727 readaliases(map, af, false, false); 6728 6729 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0) 6730 map->map_mtime = st.st_mtime; 6731 (void) sm_io_close(af, SM_TIME_DEFAULT); 6732 6733 return true; 6734 } 6735 /* 6736 ** Implicit Modules 6737 ** 6738 ** Tries several types. For back compatibility of aliases. 6739 */ 6740 6741 6742 /* 6743 ** IMPL_MAP_LOOKUP -- lookup in best open database 6744 */ 6745 6746 char * 6747 impl_map_lookup(map, name, av, pstat) 6748 MAP *map; 6749 char *name; 6750 char **av; 6751 int *pstat; 6752 { 6753 if (tTd(38, 20)) 6754 sm_dprintf("impl_map_lookup(%s, %s)\n", 6755 map->map_mname, name); 6756 6757 #if NEWDB 6758 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6759 return db_map_lookup(map, name, av, pstat); 6760 #endif 6761 #if NDBM 6762 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6763 return ndbm_map_lookup(map, name, av, pstat); 6764 #endif 6765 #if CDB 6766 if (bitset(MF_IMPL_CDB, map->map_mflags)) 6767 return cdb_map_lookup(map, name, av, pstat); 6768 #endif 6769 return stab_map_lookup(map, name, av, pstat); 6770 } 6771 6772 /* 6773 ** IMPL_MAP_STORE -- store in open databases 6774 */ 6775 6776 void 6777 impl_map_store(map, lhs, rhs) 6778 MAP *map; 6779 char *lhs; 6780 char *rhs; 6781 { 6782 if (tTd(38, 12)) 6783 sm_dprintf("impl_map_store(%s, %s, %s)\n", 6784 map->map_mname, lhs, rhs); 6785 #if NEWDB 6786 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6787 db_map_store(map, lhs, rhs); 6788 #endif 6789 #if NDBM 6790 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6791 ndbm_map_store(map, lhs, rhs); 6792 #endif 6793 #if CDB 6794 if (bitset(MF_IMPL_CDB, map->map_mflags)) 6795 cdb_map_store(map, lhs, rhs); 6796 #endif 6797 stab_map_store(map, lhs, rhs); 6798 } 6799 6800 /* 6801 ** IMPL_MAP_OPEN -- implicit database open 6802 */ 6803 6804 bool 6805 impl_map_open(map, mode) 6806 MAP *map; 6807 int mode; 6808 { 6809 bool wasopt; 6810 6811 if (tTd(38, 2)) 6812 sm_dprintf("impl_map_open(%s, %s, %d)\n", 6813 map->map_mname, map->map_file, mode); 6814 6815 mode &= O_ACCMODE; 6816 wasopt = bitset(MF_OPTIONAL, map->map_mflags); 6817 6818 /* suppress error msgs */ 6819 map->map_mflags |= MF_OPTIONAL; 6820 #if NEWDB 6821 map->map_mflags |= MF_IMPL_HASH; 6822 if (hash_map_open(map, mode)) 6823 { 6824 # ifdef NDBM_YP_COMPAT 6825 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) 6826 # endif 6827 goto ok; 6828 } 6829 else 6830 map->map_mflags &= ~MF_IMPL_HASH; 6831 #endif /* NEWDB */ 6832 #if NDBM 6833 map->map_mflags |= MF_IMPL_NDBM; 6834 if (ndbm_map_open(map, mode)) 6835 goto ok; 6836 else 6837 map->map_mflags &= ~MF_IMPL_NDBM; 6838 #endif /* NDBM */ 6839 6840 #if CDB 6841 map->map_mflags |= MF_IMPL_CDB; 6842 if (cdb_map_open(map, mode)) 6843 goto ok; 6844 else 6845 map->map_mflags &= ~MF_IMPL_CDB; 6846 #endif /* CDB */ 6847 6848 if (!bitset(MF_ALIAS, map->map_mflags)) 6849 goto fail; 6850 #if NEWDB || NDBM || CDB 6851 if (Verbose) 6852 message("WARNING: cannot open alias database %s%s", 6853 map->map_file, 6854 mode == O_RDONLY ? "; reading text version" : ""); 6855 #else 6856 if (mode != O_RDONLY) 6857 usrerr("Cannot rebuild aliases: no database format defined"); 6858 #endif 6859 6860 if (mode == O_RDONLY && stab_map_open(map, mode)) 6861 goto ok; 6862 6863 fail: 6864 if (!wasopt) 6865 map->map_mflags &= ~MF_OPTIONAL; 6866 return false; 6867 6868 ok: 6869 if (!wasopt) 6870 map->map_mflags &= ~MF_OPTIONAL; 6871 return true; 6872 } 6873 6874 6875 /* 6876 ** IMPL_MAP_CLOSE -- close any open database(s) 6877 */ 6878 6879 void 6880 impl_map_close(map) 6881 MAP *map; 6882 { 6883 if (tTd(38, 9)) 6884 sm_dprintf("impl_map_close(%s, %s, %lx)\n", 6885 map->map_mname, map->map_file, map->map_mflags); 6886 #if NEWDB 6887 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6888 { 6889 db_map_close(map); 6890 map->map_mflags &= ~MF_IMPL_HASH; 6891 } 6892 #endif /* NEWDB */ 6893 6894 #if NDBM 6895 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6896 { 6897 ndbm_map_close(map); 6898 map->map_mflags &= ~MF_IMPL_NDBM; 6899 } 6900 #endif /* NDBM */ 6901 #if CDB 6902 if (bitset(MF_IMPL_CDB, map->map_mflags)) 6903 { 6904 cdb_map_close(map); 6905 map->map_mflags &= ~MF_IMPL_CDB; 6906 } 6907 #endif /* CDB */ 6908 } 6909 6910 /* 6911 ** User map class. 6912 ** 6913 ** Provides access to the system password file. 6914 */ 6915 6916 /* 6917 ** USER_MAP_OPEN -- open user map 6918 ** 6919 ** Really just binds field names to field numbers. 6920 */ 6921 6922 bool 6923 user_map_open(map, mode) 6924 MAP *map; 6925 int mode; 6926 { 6927 if (tTd(38, 2)) 6928 sm_dprintf("user_map_open(%s, %d)\n", 6929 map->map_mname, mode); 6930 6931 mode &= O_ACCMODE; 6932 if (mode != O_RDONLY) 6933 { 6934 /* issue a pseudo-error message */ 6935 errno = SM_EMAPCANTWRITE; 6936 return false; 6937 } 6938 if (map->map_valcolnm == NULL) 6939 /* EMPTY */ 6940 /* nothing */ ; 6941 else if (SM_STRCASEEQ(map->map_valcolnm, "name")) 6942 map->map_valcolno = 1; 6943 else if (SM_STRCASEEQ(map->map_valcolnm, "passwd")) 6944 map->map_valcolno = 2; 6945 else if (SM_STRCASEEQ(map->map_valcolnm, "uid")) 6946 map->map_valcolno = 3; 6947 else if (SM_STRCASEEQ(map->map_valcolnm, "gid")) 6948 map->map_valcolno = 4; 6949 else if (SM_STRCASEEQ(map->map_valcolnm, "gecos")) 6950 map->map_valcolno = 5; 6951 else if (SM_STRCASEEQ(map->map_valcolnm, "dir")) 6952 map->map_valcolno = 6; 6953 else if (SM_STRCASEEQ(map->map_valcolnm, "shell")) 6954 map->map_valcolno = 7; 6955 else 6956 { 6957 syserr("User map %s: unknown column name %s", 6958 map->map_mname, map->map_valcolnm); 6959 return false; 6960 } 6961 return true; 6962 } 6963 6964 6965 /* 6966 ** USER_MAP_LOOKUP -- look up a user in the passwd file. 6967 */ 6968 6969 /* ARGSUSED3 */ 6970 char * 6971 user_map_lookup(map, key, av, statp) 6972 MAP *map; 6973 char *key; 6974 char **av; 6975 int *statp; 6976 { 6977 auto bool fuzzy; 6978 SM_MBDB_T user; 6979 6980 if (tTd(38, 20)) 6981 sm_dprintf("user_map_lookup(%s, %s)\n", 6982 map->map_mname, key); 6983 6984 *statp = finduser(key, &fuzzy, &user); 6985 if (*statp != EX_OK) 6986 return NULL; 6987 if (bitset(MF_MATCHONLY, map->map_mflags)) 6988 return map_rewrite(map, key, strlen(key), NULL); 6989 else 6990 { 6991 char *rwval = NULL; 6992 char buf[30]; 6993 6994 switch (map->map_valcolno) 6995 { 6996 case 0: 6997 case 1: 6998 rwval = user.mbdb_name; 6999 break; 7000 7001 case 2: 7002 rwval = "x"; /* passwd no longer supported */ 7003 break; 7004 7005 case 3: 7006 (void) sm_snprintf(buf, sizeof(buf), "%d", 7007 (int) user.mbdb_uid); 7008 rwval = buf; 7009 break; 7010 7011 case 4: 7012 (void) sm_snprintf(buf, sizeof(buf), "%d", 7013 (int) user.mbdb_gid); 7014 rwval = buf; 7015 break; 7016 7017 case 5: 7018 rwval = user.mbdb_fullname; 7019 break; 7020 7021 case 6: 7022 rwval = user.mbdb_homedir; 7023 break; 7024 7025 case 7: 7026 rwval = user.mbdb_shell; 7027 break; 7028 default: 7029 syserr("user_map %s: bogus field %d", 7030 map->map_mname, map->map_valcolno); 7031 return NULL; 7032 } 7033 return map_rewrite(map, rwval, strlen(rwval), av); 7034 } 7035 } 7036 /* 7037 ** Program map type. 7038 ** 7039 ** This provides access to arbitrary programs. It should be used 7040 ** only very sparingly, since there is no way to bound the cost 7041 ** of invoking an arbitrary program. 7042 */ 7043 7044 char * 7045 prog_map_lookup(map, name, av, statp) 7046 MAP *map; 7047 char *name; 7048 char **av; 7049 int *statp; 7050 { 7051 int i; 7052 int save_errno; 7053 int fd; 7054 int status; 7055 auto pid_t pid; 7056 register char *p; 7057 char *rval; 7058 char *argv[MAXPV + 1]; 7059 char buf[MAXLINE]; 7060 7061 if (tTd(38, 20)) 7062 sm_dprintf("prog_map_lookup(%s, %s) %s\n", 7063 map->map_mname, name, map->map_file); 7064 7065 i = 0; 7066 argv[i++] = map->map_file; 7067 if (map->map_rebuild != NULL) 7068 { 7069 (void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf)); 7070 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) 7071 { 7072 if (i >= MAXPV - 1) 7073 break; 7074 argv[i++] = p; 7075 } 7076 } 7077 argv[i++] = name; 7078 argv[i] = NULL; 7079 if (tTd(38, 21)) 7080 { 7081 sm_dprintf("prog_open:"); 7082 for (i = 0; argv[i] != NULL; i++) 7083 sm_dprintf(" %s", argv[i]); 7084 sm_dprintf("\n"); 7085 } 7086 (void) sm_blocksignal(SIGCHLD); 7087 pid = prog_open(argv, &fd, CurEnv); 7088 if (pid < 0) 7089 { 7090 if (!bitset(MF_OPTIONAL, map->map_mflags)) 7091 syserr("prog_map_lookup(%s) failed (%s) -- closing", 7092 map->map_mname, sm_errstring(errno)); 7093 else if (tTd(38, 9)) 7094 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing", 7095 map->map_mname, sm_errstring(errno)); 7096 map->map_mflags &= ~(MF_VALID|MF_OPEN); 7097 *statp = EX_OSFILE; 7098 return NULL; 7099 } 7100 i = read(fd, buf, sizeof(buf) - 1); 7101 if (i < 0) 7102 { 7103 syserr("prog_map_lookup(%s): read error %s", 7104 map->map_mname, sm_errstring(errno)); 7105 rval = NULL; 7106 } 7107 else if (i == 0) 7108 { 7109 if (tTd(38, 20)) 7110 sm_dprintf("prog_map_lookup(%s): empty answer\n", 7111 map->map_mname); 7112 rval = NULL; 7113 } 7114 else 7115 { 7116 buf[i] = '\0'; 7117 p = strchr(buf, '\n'); 7118 if (p != NULL) 7119 *p = '\0'; 7120 7121 /* collect the return value */ 7122 if (bitset(MF_MATCHONLY, map->map_mflags)) 7123 rval = map_rewrite(map, name, strlen(name), NULL); 7124 else 7125 rval = map_rewrite(map, buf, strlen(buf), av); 7126 7127 /* now flush any additional output */ 7128 while ((i = read(fd, buf, sizeof(buf))) > 0) 7129 continue; 7130 } 7131 7132 /* wait for the process to terminate */ 7133 (void) close(fd); 7134 status = waitfor(pid); 7135 save_errno = errno; 7136 (void) sm_releasesignal(SIGCHLD); 7137 errno = save_errno; 7138 7139 if (status == -1) 7140 { 7141 syserr("prog_map_lookup(%s): wait error %s", 7142 map->map_mname, sm_errstring(errno)); 7143 *statp = EX_SOFTWARE; 7144 rval = NULL; 7145 } 7146 else if (WIFEXITED(status)) 7147 { 7148 if ((*statp = WEXITSTATUS(status)) != EX_OK) 7149 rval = NULL; 7150 } 7151 else 7152 { 7153 syserr("prog_map_lookup(%s): child died on signal %d", 7154 map->map_mname, status); 7155 *statp = EX_UNAVAILABLE; 7156 rval = NULL; 7157 } 7158 return rval; 7159 } 7160 /* 7161 ** Sequenced map type. 7162 ** 7163 ** Tries each map in order until something matches, much like 7164 ** implicit. Stores go to the first map in the list that can 7165 ** support storing. 7166 ** 7167 ** This is slightly unusual in that there are two interfaces. 7168 ** The "sequence" interface lets you stack maps arbitrarily. 7169 ** The "switch" interface builds a sequence map by looking 7170 ** at a system-dependent configuration file such as 7171 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. 7172 ** 7173 ** We don't need an explicit open, since all maps are 7174 ** opened on demand. 7175 */ 7176 7177 /* 7178 ** SEQ_MAP_PARSE -- Sequenced map parsing 7179 */ 7180 7181 bool 7182 seq_map_parse(map, ap) 7183 MAP *map; 7184 char *ap; 7185 { 7186 int maxmap; 7187 7188 if (tTd(38, 2)) 7189 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap); 7190 maxmap = 0; 7191 while (*ap != '\0') 7192 { 7193 register char *p; 7194 STAB *s; 7195 7196 /* find beginning of map name */ 7197 while (SM_ISSPACE(*ap)) 7198 ap++; 7199 for (p = ap; 7200 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.'; 7201 p++) 7202 continue; 7203 if (*p != '\0') 7204 *p++ = '\0'; 7205 while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) 7206 p++; 7207 if (*ap == '\0') 7208 { 7209 ap = p; 7210 continue; 7211 } 7212 s = stab(ap, ST_MAP, ST_FIND); 7213 if (s == NULL) 7214 { 7215 syserr("Sequence map %s: unknown member map %s", 7216 map->map_mname, ap); 7217 } 7218 else if (maxmap >= MAXMAPSTACK) 7219 { 7220 syserr("Sequence map %s: too many member maps (%d max)", 7221 map->map_mname, MAXMAPSTACK); 7222 maxmap++; 7223 } 7224 else if (maxmap < MAXMAPSTACK) 7225 { 7226 map->map_stack[maxmap++] = &s->s_map; 7227 } 7228 ap = p; 7229 } 7230 return true; 7231 } 7232 7233 /* 7234 ** SWITCH_MAP_OPEN -- open a switched map 7235 ** 7236 ** This looks at the system-dependent configuration and builds 7237 ** a sequence map that does the same thing. 7238 ** 7239 ** Every system must define a switch_map_find routine in conf.c 7240 ** that will return the list of service types associated with a 7241 ** given service class. 7242 */ 7243 7244 bool 7245 switch_map_open(map, mode) 7246 MAP *map; 7247 int mode; 7248 { 7249 int mapno; 7250 int nmaps; 7251 char *maptype[MAXMAPSTACK]; 7252 7253 if (tTd(38, 2)) 7254 sm_dprintf("switch_map_open(%s, %s, %d)\n", 7255 map->map_mname, map->map_file, mode); 7256 7257 mode &= O_ACCMODE; 7258 nmaps = switch_map_find(map->map_file, maptype, map->map_return); 7259 if (tTd(38, 19)) 7260 { 7261 sm_dprintf("\tswitch_map_find => %d\n", nmaps); 7262 for (mapno = 0; mapno < nmaps; mapno++) 7263 sm_dprintf("\t\t%s\n", maptype[mapno]); 7264 } 7265 if (nmaps <= 0 || nmaps > MAXMAPSTACK) 7266 return false; 7267 7268 for (mapno = 0; mapno < nmaps; mapno++) 7269 { 7270 register STAB *s; 7271 char nbuf[MAXNAME + 1]; /* EAI:not relevant (map name) */ 7272 7273 if (maptype[mapno] == NULL) 7274 continue; 7275 (void) sm_strlcpyn(nbuf, sizeof(nbuf), 3, 7276 map->map_mname, ".", maptype[mapno]); 7277 s = stab(nbuf, ST_MAP, ST_FIND); 7278 if (s == NULL) 7279 { 7280 syserr("Switch map %s: unknown member map %s", 7281 map->map_mname, nbuf); 7282 } 7283 else 7284 { 7285 map->map_stack[mapno] = &s->s_map; 7286 if (tTd(38, 4)) 7287 sm_dprintf("\tmap_stack[%d] = %s:%s\n", 7288 mapno, 7289 s->s_map.map_class->map_cname, 7290 nbuf); 7291 } 7292 } 7293 return true; 7294 } 7295 7296 #if 0 7297 /* 7298 ** SEQ_MAP_CLOSE -- close all underlying maps 7299 */ 7300 7301 void 7302 seq_map_close(map) 7303 MAP *map; 7304 { 7305 int mapno; 7306 7307 if (tTd(38, 9)) 7308 sm_dprintf("seq_map_close(%s)\n", map->map_mname); 7309 7310 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 7311 { 7312 MAP *mm = map->map_stack[mapno]; 7313 7314 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) 7315 continue; 7316 mm->map_mflags |= MF_CLOSING; 7317 mm->map_class->map_close(mm); 7318 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 7319 } 7320 } 7321 #endif /* 0 */ 7322 7323 /* 7324 ** SEQ_MAP_LOOKUP -- sequenced map lookup 7325 */ 7326 7327 char * 7328 seq_map_lookup(map, key, args, pstat) 7329 MAP *map; 7330 char *key; 7331 char **args; 7332 int *pstat; 7333 { 7334 int mapno; 7335 int mapbit = 0x01; 7336 bool tempfail = false; 7337 7338 if (tTd(38, 20)) 7339 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key); 7340 7341 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) 7342 { 7343 MAP *mm = map->map_stack[mapno]; 7344 char *rv; 7345 7346 if (mm == NULL) 7347 continue; 7348 if (!bitset(MF_OPEN, mm->map_mflags) && 7349 !openmap(mm)) 7350 { 7351 if (bitset(mapbit, map->map_return[MA_UNAVAIL])) 7352 { 7353 *pstat = EX_UNAVAILABLE; 7354 return NULL; 7355 } 7356 continue; 7357 } 7358 *pstat = EX_OK; 7359 rv = mm->map_class->map_lookup(mm, key, args, pstat); 7360 if (rv != NULL) 7361 return rv; 7362 if (*pstat == EX_TEMPFAIL) 7363 { 7364 if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) 7365 return NULL; 7366 tempfail = true; 7367 } 7368 else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) 7369 break; 7370 } 7371 if (tempfail) 7372 *pstat = EX_TEMPFAIL; 7373 else if (*pstat == EX_OK) 7374 *pstat = EX_NOTFOUND; 7375 return NULL; 7376 } 7377 7378 /* 7379 ** SEQ_MAP_STORE -- sequenced map store 7380 */ 7381 7382 void 7383 seq_map_store(map, key, val) 7384 MAP *map; 7385 char *key; 7386 char *val; 7387 { 7388 int mapno; 7389 7390 if (tTd(38, 12)) 7391 sm_dprintf("seq_map_store(%s, %s, %s)\n", 7392 map->map_mname, key, val); 7393 7394 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 7395 { 7396 MAP *mm = map->map_stack[mapno]; 7397 7398 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) 7399 continue; 7400 7401 mm->map_class->map_store(mm, key, val); 7402 return; 7403 } 7404 syserr("seq_map_store(%s, %s, %s): no writable map", 7405 map->map_mname, key, val); 7406 } 7407 /* 7408 ** NULL stubs 7409 */ 7410 7411 /* ARGSUSED */ 7412 bool 7413 null_map_open(map, mode) 7414 MAP *map; 7415 int mode; 7416 { 7417 return true; 7418 } 7419 7420 /* ARGSUSED */ 7421 void 7422 null_map_close(map) 7423 MAP *map; 7424 { 7425 return; 7426 } 7427 7428 char * 7429 null_map_lookup(map, key, args, pstat) 7430 MAP *map; 7431 char *key; 7432 char **args; 7433 int *pstat; 7434 { 7435 *pstat = EX_NOTFOUND; 7436 return NULL; 7437 } 7438 7439 /* ARGSUSED */ 7440 void 7441 null_map_store(map, key, val) 7442 MAP *map; 7443 char *key; 7444 char *val; 7445 { 7446 return; 7447 } 7448 7449 MAPCLASS NullMapClass = 7450 { 7451 "null-map", NULL, 0, 7452 NULL, null_map_lookup, null_map_store, 7453 null_map_open, null_map_close, 7454 }; 7455 7456 /* 7457 ** BOGUS stubs 7458 */ 7459 7460 char * 7461 bogus_map_lookup(map, key, args, pstat) 7462 MAP *map; 7463 char *key; 7464 char **args; 7465 int *pstat; 7466 { 7467 *pstat = EX_TEMPFAIL; 7468 return NULL; 7469 } 7470 7471 MAPCLASS BogusMapClass = 7472 { 7473 "bogus-map", NULL, 0, 7474 NULL, bogus_map_lookup, null_map_store, 7475 null_map_open, null_map_close, 7476 }; 7477 /* 7478 ** MACRO modules 7479 */ 7480 7481 char * 7482 macro_map_lookup(map, name, av, statp) 7483 MAP *map; 7484 char *name; 7485 char **av; 7486 int *statp; 7487 { 7488 int mid; 7489 7490 if (tTd(38, 20)) 7491 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname, 7492 name == NULL ? "NULL" : name); 7493 7494 if (name == NULL || 7495 *name == '\0' || 7496 (mid = macid(name)) == 0) 7497 { 7498 *statp = EX_CONFIG; 7499 return NULL; 7500 } 7501 7502 if (av[1] == NULL) 7503 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL); 7504 else 7505 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]); 7506 7507 *statp = EX_OK; 7508 return ""; 7509 } 7510 /* 7511 ** REGEX modules 7512 */ 7513 7514 #if MAP_REGEX 7515 7516 # include <regex.h> 7517 7518 # define DEFAULT_DELIM CONDELSE 7519 # define END_OF_FIELDS -1 7520 # define ERRBUF_SIZE 80 7521 # define MAX_MATCH 32 7522 7523 # define xnalloc(s) memset(xalloc(s), '\0', s); 7524 7525 struct regex_map 7526 { 7527 regex_t *regex_pattern_buf; /* xalloc it */ 7528 int *regex_subfields; /* move to type MAP */ 7529 char *regex_delim; /* move to type MAP */ 7530 }; 7531 7532 static int parse_fields __P((char *, int *, int, int)); 7533 static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **)); 7534 7535 static int 7536 parse_fields(s, ibuf, blen, nr_substrings) 7537 char *s; 7538 int *ibuf; /* array */ 7539 int blen; /* number of elements in ibuf */ 7540 int nr_substrings; /* number of substrings in the pattern */ 7541 { 7542 register char *cp; 7543 int i = 0; 7544 bool lastone = false; 7545 7546 blen--; /* for terminating END_OF_FIELDS */ 7547 cp = s; 7548 do 7549 { 7550 for (;; cp++) 7551 { 7552 if (*cp == ',') 7553 { 7554 *cp = '\0'; 7555 break; 7556 } 7557 if (*cp == '\0') 7558 { 7559 lastone = true; 7560 break; 7561 } 7562 } 7563 if (i < blen) 7564 { 7565 int val = atoi(s); 7566 7567 if (val < 0 || val >= nr_substrings) 7568 { 7569 syserr("field (%d) out of range, only %d substrings in pattern", 7570 val, nr_substrings); 7571 return -1; 7572 } 7573 ibuf[i++] = val; 7574 } 7575 else 7576 { 7577 syserr("too many fields, %d max", blen); 7578 return -1; 7579 } 7580 s = ++cp; 7581 } while (!lastone); 7582 ibuf[i] = END_OF_FIELDS; 7583 return i; 7584 } 7585 7586 bool 7587 regex_map_init(map, ap) 7588 MAP *map; 7589 char *ap; 7590 { 7591 int regerr; 7592 struct regex_map *map_p; 7593 register char *p; 7594 char *sub_param = NULL; 7595 int pflags; 7596 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' }; 7597 7598 if (tTd(38, 2)) 7599 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n", 7600 map->map_mname, ap); 7601 7602 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; 7603 p = ap; 7604 map_p = (struct regex_map *) xnalloc(sizeof(*map_p)); 7605 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t)); 7606 7607 for (;;) 7608 { 7609 while (SM_ISSPACE(*p)) 7610 p++; 7611 if (*p != '-') 7612 break; 7613 switch (*++p) 7614 { 7615 case 'n': /* not */ 7616 map->map_mflags |= MF_REGEX_NOT; 7617 break; 7618 7619 case 'f': /* case sensitive */ 7620 map->map_mflags |= MF_NOFOLDCASE; 7621 pflags &= ~REG_ICASE; 7622 break; 7623 7624 case 'b': /* basic regular expressions */ 7625 pflags &= ~REG_EXTENDED; 7626 break; 7627 7628 case 's': /* substring match () syntax */ 7629 sub_param = ++p; 7630 pflags &= ~REG_NOSUB; 7631 break; 7632 7633 case 'd': /* delimiter */ 7634 map_p->regex_delim = ++p; 7635 break; 7636 7637 case 'a': /* map append */ 7638 map->map_app = ++p; 7639 break; 7640 7641 case 'm': /* matchonly */ 7642 map->map_mflags |= MF_MATCHONLY; 7643 break; 7644 7645 case 'q': 7646 map->map_mflags |= MF_KEEPQUOTES; 7647 break; 7648 7649 case 'S': 7650 map->map_spacesub = *++p; 7651 break; 7652 7653 case 'D': 7654 map->map_mflags |= MF_DEFER; 7655 break; 7656 7657 } 7658 while (*p != '\0' && !(SM_ISSPACE(*p))) 7659 p++; 7660 if (*p != '\0') 7661 *p++ = '\0'; 7662 } 7663 # if _FFR_8BITENVADDR 7664 (void) dequote_internal_chars(p, p, strlen(p) + 1); 7665 # endif 7666 if (tTd(38, 3)) 7667 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags); 7668 7669 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0) 7670 { 7671 /* Errorhandling */ 7672 char errbuf[ERRBUF_SIZE]; 7673 7674 (void) regerror(regerr, map_p->regex_pattern_buf, 7675 errbuf, sizeof(errbuf)); 7676 syserr("pattern-compile-error: %s", errbuf); 7677 sm_free(map_p->regex_pattern_buf); /* XXX */ 7678 sm_free(map_p); /* XXX */ 7679 return false; 7680 } 7681 7682 if (map->map_app != NULL) 7683 map->map_app = newstr(map->map_app); 7684 if (map_p->regex_delim != NULL) 7685 map_p->regex_delim = newstr(map_p->regex_delim); 7686 else 7687 map_p->regex_delim = defdstr; 7688 7689 if (!bitset(REG_NOSUB, pflags)) 7690 { 7691 /* substring matching */ 7692 int substrings; 7693 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1)); 7694 7695 substrings = map_p->regex_pattern_buf->re_nsub + 1; 7696 7697 if (tTd(38, 3)) 7698 sm_dprintf("regex_map_init: nr of substrings %d\n", 7699 substrings); 7700 7701 if (substrings >= MAX_MATCH) 7702 { 7703 syserr("too many substrings, %d max", MAX_MATCH); 7704 SM_FREE(map_p->regex_pattern_buf); /* XXX */ 7705 SM_FREE(map_p); /* XXX */ 7706 SM_FREE(fields); 7707 return false; 7708 } 7709 if (sub_param != NULL && sub_param[0] != '\0') 7710 { 7711 /* optional parameter -sfields */ 7712 if (parse_fields(sub_param, fields, 7713 MAX_MATCH + 1, substrings) == -1) 7714 { 7715 SM_FREE(map_p->regex_pattern_buf); /* XXX */ 7716 SM_FREE(map_p); /* XXX */ 7717 SM_FREE(fields); 7718 return false; 7719 } 7720 } 7721 else 7722 { 7723 int i; 7724 7725 /* set default fields */ 7726 for (i = 0; i < substrings; i++) 7727 fields[i] = i; 7728 fields[i] = END_OF_FIELDS; 7729 } 7730 map_p->regex_subfields = fields; 7731 if (tTd(38, 3)) 7732 { 7733 int *ip; 7734 7735 sm_dprintf("regex_map_init: subfields"); 7736 for (ip = fields; *ip != END_OF_FIELDS; ip++) 7737 sm_dprintf(" %d", *ip); 7738 sm_dprintf("\n"); 7739 } 7740 } 7741 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */ 7742 return true; 7743 } 7744 7745 static char * 7746 regex_map_rewrite(map, s, slen, av) 7747 MAP *map; 7748 const char *s; 7749 size_t slen; 7750 char **av; 7751 { 7752 if (bitset(MF_MATCHONLY, map->map_mflags)) 7753 return map_rewrite(map, av[0], strlen(av[0]), NULL); 7754 else 7755 return map_rewrite(map, s, slen, av); 7756 } 7757 7758 char * 7759 regex_map_lookup(map, name, av, statp) 7760 MAP *map; 7761 char *name; 7762 char **av; 7763 int *statp; 7764 { 7765 int reg_res; 7766 struct regex_map *map_p; 7767 regmatch_t pmatch[MAX_MATCH]; 7768 7769 if (tTd(38, 20)) 7770 { 7771 char **cpp; 7772 7773 sm_dprintf("regex_map_lookup: name=%s, key='%s'\n", 7774 map->map_mname, name); 7775 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7776 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp); 7777 } 7778 7779 map_p = (struct regex_map *)(map->map_db1); 7780 reg_res = regexec(map_p->regex_pattern_buf, 7781 name, MAX_MATCH, pmatch, 0); 7782 7783 if (bitset(MF_REGEX_NOT, map->map_mflags)) 7784 { 7785 /* option -n */ 7786 if (reg_res == REG_NOMATCH) 7787 return regex_map_rewrite(map, "", (size_t) 0, av); 7788 else 7789 return NULL; 7790 } 7791 if (reg_res == REG_NOMATCH) 7792 return NULL; 7793 7794 if (map_p->regex_subfields != NULL) 7795 { 7796 /* option -s */ 7797 static char retbuf[MAXNAME]; /* EAI:not relevant */ 7798 int fields[MAX_MATCH + 1]; 7799 bool first = true; 7800 int anglecnt = 0, cmntcnt = 0, spacecnt = 0; 7801 bool quotemode = false, bslashmode = false; 7802 register char *dp, *sp; 7803 char *endp, *ldp; 7804 int *ip; 7805 7806 dp = retbuf; 7807 ldp = retbuf + sizeof(retbuf) - 1; 7808 7809 if (av[1] != NULL) 7810 { 7811 if (parse_fields(av[1], fields, MAX_MATCH + 1, 7812 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1) 7813 { 7814 *statp = EX_CONFIG; 7815 return NULL; 7816 } 7817 ip = fields; 7818 } 7819 else 7820 ip = map_p->regex_subfields; 7821 7822 for ( ; *ip != END_OF_FIELDS; ip++) 7823 { 7824 if (!first) 7825 { 7826 for (sp = map_p->regex_delim; *sp; sp++) 7827 { 7828 if (dp < ldp) 7829 *dp++ = *sp; 7830 } 7831 } 7832 else 7833 first = false; 7834 7835 if (*ip >= MAX_MATCH || 7836 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) 7837 continue; 7838 7839 sp = name + pmatch[*ip].rm_so; 7840 endp = name + pmatch[*ip].rm_eo; 7841 for (; endp > sp; sp++) 7842 { 7843 if (dp < ldp) 7844 { 7845 if (bslashmode) 7846 { 7847 *dp++ = *sp; 7848 bslashmode = false; 7849 } 7850 else if (quotemode && *sp != '"' && 7851 *sp != '\\') 7852 { 7853 *dp++ = *sp; 7854 } 7855 else switch (*dp++ = *sp) 7856 { 7857 case '\\': 7858 bslashmode = true; 7859 break; 7860 7861 case '(': 7862 cmntcnt++; 7863 break; 7864 7865 case ')': 7866 cmntcnt--; 7867 break; 7868 7869 case '<': 7870 anglecnt++; 7871 break; 7872 7873 case '>': 7874 anglecnt--; 7875 break; 7876 7877 case ' ': 7878 spacecnt++; 7879 break; 7880 7881 case '"': 7882 quotemode = !quotemode; 7883 break; 7884 } 7885 } 7886 } 7887 } 7888 if (anglecnt != 0 || cmntcnt != 0 || quotemode || 7889 bslashmode || spacecnt != 0) 7890 { 7891 sm_syslog(LOG_WARNING, NOQID, 7892 "Warning: regex may cause prescan() failure map=%s lookup=%s", 7893 map->map_mname, name); 7894 return NULL; 7895 } 7896 *dp = '\0'; 7897 return regex_map_rewrite(map, retbuf, strlen(retbuf), av); 7898 } 7899 return regex_map_rewrite(map, "", (size_t)0, av); 7900 } 7901 #endif /* MAP_REGEX */ 7902 /* 7903 ** NSD modules 7904 */ 7905 #if MAP_NSD 7906 7907 # include <ndbm.h> 7908 # define _DATUM_DEFINED 7909 # include <ns_api.h> 7910 7911 typedef struct ns_map_list 7912 { 7913 ns_map_t *map; /* XXX ns_ ? */ 7914 char *mapname; 7915 struct ns_map_list *next; 7916 } ns_map_list_t; 7917 7918 static ns_map_t * 7919 ns_map_t_find(mapname) 7920 char *mapname; 7921 { 7922 static ns_map_list_t *ns_maps = NULL; 7923 ns_map_list_t *ns_map; 7924 7925 /* walk the list of maps looking for the correctly named map */ 7926 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next) 7927 { 7928 if (strcmp(ns_map->mapname, mapname) == 0) 7929 break; 7930 } 7931 7932 /* if we are looking at a NULL ns_map_list_t, then create a new one */ 7933 if (ns_map == NULL) 7934 { 7935 ns_map = (ns_map_list_t *) xalloc(sizeof(*ns_map)); 7936 ns_map->mapname = newstr(mapname); 7937 ns_map->map = (ns_map_t *) xalloc(sizeof(*ns_map->map)); 7938 memset(ns_map->map, '\0', sizeof(*ns_map->map)); 7939 ns_map->next = ns_maps; 7940 ns_maps = ns_map; 7941 } 7942 return ns_map->map; 7943 } 7944 7945 char * 7946 nsd_map_lookup(map, name, av, statp) 7947 MAP *map; 7948 char *name; 7949 char **av; 7950 int *statp; 7951 { 7952 int buflen, r; 7953 char *p; 7954 ns_map_t *ns_map; 7955 char keybuf[MAXNAME + 1]; /* EAI:ok */ 7956 char buf[MAXLINE]; 7957 7958 if (tTd(38, 20)) 7959 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name); 7960 7961 buflen = strlen(name); 7962 if (buflen > sizeof(keybuf) - 1) 7963 buflen = sizeof(keybuf) - 1; /* XXX simply cut off? */ 7964 memmove(keybuf, name, buflen); 7965 keybuf[buflen] = '\0'; 7966 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 7967 makelower_buf(keybuf, keybuf, sizeof(keybuf)); 7968 7969 ns_map = ns_map_t_find(map->map_file); 7970 if (ns_map == NULL) 7971 { 7972 if (tTd(38, 20)) 7973 sm_dprintf("nsd_map_t_find failed\n"); 7974 *statp = EX_UNAVAILABLE; 7975 return NULL; 7976 } 7977 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL, 7978 buf, sizeof(buf)); 7979 if (r == NS_UNAVAIL || r == NS_TRYAGAIN) 7980 { 7981 *statp = EX_TEMPFAIL; 7982 return NULL; 7983 } 7984 if (r == NS_BADREQ 7985 # ifdef NS_NOPERM 7986 || r == NS_NOPERM 7987 # endif 7988 ) 7989 { 7990 *statp = EX_CONFIG; 7991 return NULL; 7992 } 7993 if (r != NS_SUCCESS) 7994 { 7995 *statp = EX_NOTFOUND; 7996 return NULL; 7997 } 7998 7999 *statp = EX_OK; 8000 8001 /* Null out trailing \n */ 8002 if ((p = strchr(buf, '\n')) != NULL) 8003 *p = '\0'; 8004 8005 return map_rewrite(map, buf, strlen(buf), av); 8006 } 8007 #endif /* MAP_NSD */ 8008 8009 char * 8010 arith_map_lookup(map, name, av, statp) 8011 MAP *map; 8012 char *name; 8013 char **av; 8014 int *statp; 8015 { 8016 long r; 8017 long v[2]; 8018 bool res = false; 8019 bool boolres; 8020 static char result[16]; 8021 char **cpp; 8022 8023 if (tTd(38, 2)) 8024 { 8025 sm_dprintf("arith_map_lookup: key '%s'\n", name); 8026 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 8027 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp); 8028 } 8029 r = 0; 8030 boolres = false; 8031 cpp = av; 8032 *statp = EX_OK; 8033 8034 /* 8035 ** read arguments for arith map 8036 ** - no check is made whether they are really numbers 8037 ** - just ignores args after the second 8038 */ 8039 8040 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++) 8041 v[r++] = strtol(*cpp, NULL, 0); 8042 8043 /* operator and (at least) two operands given? */ 8044 if (name != NULL && r == 2) 8045 { 8046 switch (*name) 8047 { 8048 case '|': 8049 r = v[0] | v[1]; 8050 break; 8051 8052 case '&': 8053 r = v[0] & v[1]; 8054 break; 8055 8056 case '%': 8057 if (v[1] == 0) 8058 return NULL; 8059 r = v[0] % v[1]; 8060 break; 8061 case '+': 8062 r = v[0] + v[1]; 8063 break; 8064 8065 case '-': 8066 r = v[0] - v[1]; 8067 break; 8068 8069 case '*': 8070 r = v[0] * v[1]; 8071 break; 8072 8073 case '/': 8074 if (v[1] == 0) 8075 return NULL; 8076 r = v[0] / v[1]; 8077 break; 8078 8079 case 'l': 8080 res = v[0] < v[1]; 8081 boolres = true; 8082 break; 8083 8084 case '=': 8085 res = v[0] == v[1]; 8086 boolres = true; 8087 break; 8088 8089 case 'r': 8090 r = v[1] - v[0] + 1; 8091 if (r <= 0) 8092 return NULL; 8093 r = get_random() % r + v[0]; 8094 break; 8095 8096 default: 8097 /* XXX */ 8098 *statp = EX_CONFIG; 8099 if (LogLevel > 10) 8100 sm_syslog(LOG_WARNING, NOQID, 8101 "arith_map: unknown operator %c", 8102 (isascii(*name) && isprint(*name)) ? 8103 *name : '?'); 8104 return NULL; 8105 } 8106 if (boolres) 8107 (void) sm_snprintf(result, sizeof(result), 8108 res ? "TRUE" : "FALSE"); 8109 else 8110 (void) sm_snprintf(result, sizeof(result), "%ld", r); 8111 return result; 8112 } 8113 *statp = EX_CONFIG; 8114 return NULL; 8115 } 8116 8117 char * 8118 arpa_map_lookup(map, name, av, statp) 8119 MAP *map; 8120 char *name; 8121 char **av; 8122 int *statp; 8123 { 8124 int r; 8125 char *rval; 8126 char result[128]; /* IPv6: 64 + 10 + 1 would be enough */ 8127 8128 if (tTd(38, 2)) 8129 sm_dprintf("arpa_map_lookup: key '%s'\n", name); 8130 *statp = EX_DATAERR; 8131 r = 1; 8132 memset(result, '\0', sizeof(result)); 8133 rval = NULL; 8134 8135 #if NETINET6 8136 if (sm_strncasecmp(name, "IPv6:", 5) == 0) 8137 { 8138 struct in6_addr in6_addr; 8139 8140 r = anynet_pton(AF_INET6, name, &in6_addr); 8141 if (r == 1) 8142 { 8143 static char hex_digits[] = 8144 { '0', '1', '2', '3', '4', '5', '6', '7', '8', 8145 '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 8146 8147 unsigned char *src; 8148 char *dst; 8149 int i; 8150 8151 src = (unsigned char *) &in6_addr; 8152 dst = result; 8153 for (i = 15; i >= 0; i--) { 8154 *dst++ = hex_digits[src[i] & 0x0f]; 8155 *dst++ = '.'; 8156 *dst++ = hex_digits[(src[i] >> 4) & 0x0f]; 8157 if (i > 0) 8158 *dst++ = '.'; 8159 } 8160 *statp = EX_OK; 8161 } 8162 } 8163 else 8164 #endif /* NETINET6 */ 8165 #if NETINET 8166 { 8167 struct in_addr in_addr; 8168 8169 r = inet_pton(AF_INET, name, &in_addr); 8170 if (r == 1) 8171 { 8172 unsigned char *src; 8173 8174 src = (unsigned char *) &in_addr; 8175 (void) snprintf(result, sizeof(result), 8176 "%u.%u.%u.%u", 8177 src[3], src[2], src[1], src[0]); 8178 *statp = EX_OK; 8179 } 8180 } 8181 #endif /* NETINET */ 8182 if (r < 0) 8183 *statp = EX_UNAVAILABLE; 8184 if (tTd(38, 2)) 8185 sm_dprintf("arpa_map_lookup: r=%d, result='%s'\n", r, result); 8186 if (*statp == EX_OK) 8187 { 8188 if (bitset(MF_MATCHONLY, map->map_mflags)) 8189 rval = map_rewrite(map, name, strlen(name), NULL); 8190 else 8191 rval = map_rewrite(map, result, strlen(result), av); 8192 } 8193 return rval; 8194 } 8195 8196 #if _FFR_SETDEBUG_MAP 8197 char * 8198 setdebug_map_lookup(map, name, av, statp) 8199 MAP *map; 8200 char *name; 8201 char **av; 8202 int *statp; 8203 { 8204 8205 if (tTd(38, 2)) 8206 { 8207 char **cpp; 8208 8209 sm_dprintf("setdebug_map_lookup: key '%s'\n", name); 8210 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 8211 sm_dprintf("setdebug_map_lookup: arg '%s'\n", *cpp); 8212 } 8213 *statp = EX_OK; 8214 tTflag(name); 8215 return NULL; 8216 } 8217 #endif 8218 8219 #if _FFR_SETOPT_MAP 8220 char * 8221 setopt_map_lookup(map, name, av, statp) 8222 MAP *map; 8223 char *name; 8224 char **av; 8225 int *statp; 8226 { 8227 # if !_FFR_SETANYOPT 8228 int val; 8229 # endif 8230 char **cpp; 8231 8232 if (tTd(38, 2)) 8233 { 8234 sm_dprintf("setopt_map_lookup: key '%s'\n", name); 8235 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 8236 sm_dprintf("setopt_map_lookup: arg '%s'\n", *cpp); 8237 } 8238 # if _FFR_SETANYOPT 8239 /* 8240 ** API screwed up... 8241 ** first arg is the "short" name and second is the entire string... 8242 */ 8243 8244 sm_dprintf("setoption: name=%s\n", name); 8245 setoption(' ', name, true, false, CurEnv); 8246 *statp = EX_OK; 8247 return NULL; 8248 # else /* _FFR_SETANYOPT */ 8249 *statp = EX_CONFIG; 8250 8251 cpp = av; 8252 if (cpp == NULL || ++cpp == NULL || *cpp == NULL) 8253 return NULL; 8254 *statp = EX_OK; 8255 errno = 0; 8256 val = strtol(*cpp, NULL, 0); 8257 /* check for valid number? */ 8258 8259 /* use a table? */ 8260 if (SM_STRCASEEQ(name, "LogLevel")) 8261 { 8262 LogLevel = val; 8263 sm_dprintf("LogLevel=%d\n", val); 8264 return NULL; 8265 } 8266 # endif /* _FFR_SETANYOPT */ 8267 *statp = EX_CONFIG; 8268 return NULL; 8269 } 8270 #endif 8271 8272 8273 #if SOCKETMAP 8274 8275 # if NETINET || NETINET6 8276 # include <arpa/inet.h> 8277 # endif 8278 8279 # define socket_map_next map_stack[0] 8280 8281 /* 8282 ** SOCKET_MAP_OPEN -- open socket table 8283 */ 8284 8285 bool 8286 socket_map_open(map, mode) 8287 MAP *map; 8288 int mode; 8289 { 8290 STAB *s; 8291 int sock = 0; 8292 int tmo; 8293 SOCKADDR_LEN_T addrlen = 0; 8294 int addrno = 0; 8295 int save_errno; 8296 char *p; 8297 char *colon; 8298 char *at; 8299 struct hostent *hp = NULL; 8300 SOCKADDR addr; 8301 8302 if (tTd(38, 2)) 8303 sm_dprintf("socket_map_open(%s, %s, %d)\n", 8304 map->map_mname, map->map_file, mode); 8305 8306 mode &= O_ACCMODE; 8307 8308 /* sendmail doesn't have the ability to write to SOCKET (yet) */ 8309 if (mode != O_RDONLY) 8310 { 8311 /* issue a pseudo-error message */ 8312 errno = SM_EMAPCANTWRITE; 8313 return false; 8314 } 8315 8316 if (*map->map_file == '\0') 8317 { 8318 syserr("socket map \"%s\": empty or missing socket information", 8319 map->map_mname); 8320 return false; 8321 } 8322 8323 s = socket_map_findconn(map->map_file); 8324 if (s->s_socketmap != NULL) 8325 { 8326 /* Copy open connection */ 8327 map->map_db1 = s->s_socketmap->map_db1; 8328 8329 /* Add this map as head of linked list */ 8330 map->socket_map_next = s->s_socketmap; 8331 s->s_socketmap = map; 8332 8333 if (tTd(38, 2)) 8334 sm_dprintf("using cached connection\n"); 8335 return true; 8336 } 8337 8338 if (tTd(38, 2)) 8339 sm_dprintf("opening new connection\n"); 8340 8341 /* following code is ripped from milter.c */ 8342 /* XXX It should be put in a library... */ 8343 8344 /* protocol:filename or protocol:port@host */ 8345 memset(&addr, '\0', sizeof(addr)); 8346 p = map->map_file; 8347 colon = strchr(p, ':'); 8348 if (colon != NULL) 8349 { 8350 *colon = '\0'; 8351 8352 if (*p == '\0') 8353 { 8354 # if NETUNIX 8355 /* default to AF_UNIX */ 8356 addr.sa.sa_family = AF_UNIX; 8357 # else /* NETUNIX */ 8358 # if NETINET 8359 /* default to AF_INET */ 8360 addr.sa.sa_family = AF_INET; 8361 # else /* NETINET */ 8362 # if NETINET6 8363 /* default to AF_INET6 */ 8364 addr.sa.sa_family = AF_INET6; 8365 # else /* NETINET6 */ 8366 /* no protocols available */ 8367 syserr("socket map \"%s\": no valid socket protocols available", 8368 map->map_mname); 8369 return false; 8370 # endif /* NETINET6 */ 8371 # endif /* NETINET */ 8372 # endif /* NETUNIX */ 8373 } 8374 # if NETUNIX 8375 else if (SM_STRCASEEQ(p, "unix") || 8376 SM_STRCASEEQ(p, "local")) 8377 addr.sa.sa_family = AF_UNIX; 8378 # endif /* NETUNIX */ 8379 # if NETINET 8380 else if (SM_STRCASEEQ(p, "inet")) 8381 addr.sa.sa_family = AF_INET; 8382 # endif /* NETINET */ 8383 # if NETINET6 8384 else if (SM_STRCASEEQ(p, "inet6")) 8385 addr.sa.sa_family = AF_INET6; 8386 # endif /* NETINET6 */ 8387 else 8388 { 8389 # ifdef EPROTONOSUPPORT 8390 errno = EPROTONOSUPPORT; 8391 # else 8392 errno = EINVAL; 8393 # endif /* EPROTONOSUPPORT */ 8394 syserr("socket map \"%s\": unknown socket type %s", 8395 map->map_mname, p); 8396 return false; 8397 } 8398 *colon++ = ':'; 8399 } 8400 else 8401 { 8402 colon = p; 8403 # if NETUNIX 8404 /* default to AF_UNIX */ 8405 addr.sa.sa_family = AF_UNIX; 8406 # else /* NETUNIX */ 8407 # if NETINET 8408 /* default to AF_INET */ 8409 addr.sa.sa_family = AF_INET; 8410 # else /* NETINET */ 8411 # if NETINET6 8412 /* default to AF_INET6 */ 8413 addr.sa.sa_family = AF_INET6; 8414 # else /* NETINET6 */ 8415 syserr("socket map \"%s\": unknown socket type %s", 8416 map->map_mname, p); 8417 return false; 8418 # endif /* NETINET6 */ 8419 # endif /* NETINET */ 8420 # endif /* NETUNIX */ 8421 } 8422 8423 # if NETUNIX 8424 if (addr.sa.sa_family == AF_UNIX) 8425 { 8426 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK; 8427 8428 at = colon; 8429 if (strlen(colon) >= sizeof(addr.sunix.sun_path)) 8430 { 8431 syserr("socket map \"%s\": local socket name %s too long", 8432 map->map_mname, colon); 8433 return false; 8434 } 8435 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 8436 S_IRUSR|S_IWUSR, NULL); 8437 8438 if (errno != 0) 8439 { 8440 /* if not safe, don't create */ 8441 syserr("socket map \"%s\": local socket name %s unsafe", 8442 map->map_mname, colon); 8443 return false; 8444 } 8445 8446 (void) sm_strlcpy(addr.sunix.sun_path, colon, 8447 sizeof(addr.sunix.sun_path)); 8448 addrlen = sizeof(struct sockaddr_un); 8449 } 8450 else 8451 # endif /* NETUNIX */ 8452 # if NETINET || NETINET6 8453 if (false 8454 # if NETINET 8455 || addr.sa.sa_family == AF_INET 8456 # endif 8457 # if NETINET6 8458 || addr.sa.sa_family == AF_INET6 8459 # endif 8460 ) 8461 { 8462 unsigned short port; 8463 8464 /* Parse port@host */ 8465 at = strchr(colon, '@'); 8466 if (at == NULL) 8467 { 8468 syserr("socket map \"%s\": bad address %s (expected port@host)", 8469 map->map_mname, colon); 8470 return false; 8471 } 8472 *at = '\0'; 8473 if (isascii(*colon) && isdigit(*colon)) 8474 port = htons((unsigned short) atoi(colon)); 8475 else 8476 { 8477 # ifdef NO_GETSERVBYNAME 8478 syserr("socket map \"%s\": invalid port number %s", 8479 map->map_mname, colon); 8480 return false; 8481 # else /* NO_GETSERVBYNAME */ 8482 register struct servent *sp; 8483 8484 sp = getservbyname(colon, "tcp"); 8485 if (sp == NULL) 8486 { 8487 syserr("socket map \"%s\": unknown port name %s", 8488 map->map_mname, colon); 8489 return false; 8490 } 8491 port = sp->s_port; 8492 # endif /* NO_GETSERVBYNAME */ 8493 } 8494 *at++ = '@'; 8495 if (*at == '[') 8496 { 8497 char *end; 8498 8499 end = strchr(at, ']'); 8500 if (end != NULL) 8501 { 8502 bool found = false; 8503 # if NETINET 8504 unsigned long hid = INADDR_NONE; 8505 # endif 8506 # if NETINET6 8507 struct sockaddr_in6 hid6; 8508 # endif 8509 8510 *end = '\0'; 8511 # if NETINET 8512 if (addr.sa.sa_family == AF_INET && 8513 (hid = inet_addr(&at[1])) != INADDR_NONE) 8514 { 8515 addr.sin.sin_addr.s_addr = hid; 8516 addr.sin.sin_port = port; 8517 found = true; 8518 } 8519 # endif /* NETINET */ 8520 # if NETINET6 8521 (void) memset(&hid6, '\0', sizeof(hid6)); 8522 if (addr.sa.sa_family == AF_INET6 && 8523 anynet_pton(AF_INET6, &at[1], 8524 &hid6.sin6_addr) == 1) 8525 { 8526 addr.sin6.sin6_addr = hid6.sin6_addr; 8527 addr.sin6.sin6_port = port; 8528 found = true; 8529 } 8530 # endif /* NETINET6 */ 8531 *end = ']'; 8532 if (!found) 8533 { 8534 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"", 8535 map->map_mname, at); 8536 return false; 8537 } 8538 } 8539 else 8540 { 8541 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"", 8542 map->map_mname, at); 8543 return false; 8544 } 8545 } 8546 else 8547 { 8548 hp = sm_gethostbyname(at, addr.sa.sa_family); 8549 if (hp == NULL) 8550 { 8551 syserr("socket map \"%s\": Unknown host name %s", 8552 map->map_mname, at); 8553 return false; 8554 } 8555 addr.sa.sa_family = hp->h_addrtype; 8556 switch (hp->h_addrtype) 8557 { 8558 # if NETINET 8559 case AF_INET: 8560 memmove(&addr.sin.sin_addr, 8561 hp->h_addr, INADDRSZ); 8562 addr.sin.sin_port = port; 8563 addrlen = sizeof(struct sockaddr_in); 8564 addrno = 1; 8565 break; 8566 # endif /* NETINET */ 8567 8568 # if NETINET6 8569 case AF_INET6: 8570 memmove(&addr.sin6.sin6_addr, 8571 hp->h_addr, IN6ADDRSZ); 8572 addr.sin6.sin6_port = port; 8573 addrlen = sizeof(struct sockaddr_in6); 8574 addrno = 1; 8575 break; 8576 # endif /* NETINET6 */ 8577 8578 default: 8579 syserr("socket map \"%s\": Unknown protocol for %s (%d)", 8580 map->map_mname, at, hp->h_addrtype); 8581 # if NETINET6 8582 freehostent(hp); 8583 # endif 8584 return false; 8585 } 8586 } 8587 } 8588 else 8589 # endif /* NETINET || NETINET6 */ 8590 { 8591 syserr("socket map \"%s\": unknown socket protocol", 8592 map->map_mname); 8593 return false; 8594 } 8595 8596 /* nope, actually connecting */ 8597 for (;;) 8598 { 8599 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 8600 if (sock < 0) 8601 { 8602 save_errno = errno; 8603 if (tTd(38, 5)) 8604 sm_dprintf("socket map \"%s\": error creating socket: %s\n", 8605 map->map_mname, 8606 sm_errstring(save_errno)); 8607 # if NETINET6 8608 if (hp != NULL) 8609 freehostent(hp); 8610 # endif /* NETINET6 */ 8611 return false; 8612 } 8613 8614 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0) 8615 break; 8616 8617 /* couldn't connect.... try next address */ 8618 save_errno = errno; 8619 p = CurHostName; 8620 CurHostName = at; 8621 if (tTd(38, 5)) 8622 sm_dprintf("socket_open (%s): open %s failed: %s\n", 8623 map->map_mname, at, sm_errstring(save_errno)); 8624 CurHostName = p; 8625 (void) close(sock); 8626 8627 /* try next address */ 8628 if (hp != NULL && hp->h_addr_list[addrno] != NULL) 8629 { 8630 switch (addr.sa.sa_family) 8631 { 8632 # if NETINET 8633 case AF_INET: 8634 memmove(&addr.sin.sin_addr, 8635 hp->h_addr_list[addrno++], 8636 INADDRSZ); 8637 break; 8638 # endif /* NETINET */ 8639 8640 # if NETINET6 8641 case AF_INET6: 8642 memmove(&addr.sin6.sin6_addr, 8643 hp->h_addr_list[addrno++], 8644 IN6ADDRSZ); 8645 break; 8646 # endif /* NETINET6 */ 8647 8648 default: 8649 if (tTd(38, 5)) 8650 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n", 8651 map->map_mname, at, 8652 hp->h_addrtype); 8653 # if NETINET6 8654 freehostent(hp); 8655 # endif 8656 return false; 8657 } 8658 continue; 8659 } 8660 p = CurHostName; 8661 CurHostName = at; 8662 if (tTd(38, 5)) 8663 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n", 8664 map->map_mname, sm_errstring(save_errno)); 8665 CurHostName = p; 8666 # if NETINET6 8667 if (hp != NULL) 8668 freehostent(hp); 8669 # endif 8670 return false; 8671 } 8672 # if NETINET6 8673 if (hp != NULL) 8674 { 8675 freehostent(hp); 8676 hp = NULL; 8677 } 8678 # endif /* NETINET6 */ 8679 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd, 8680 SM_TIME_DEFAULT, 8681 (void *) &sock, 8682 SM_IO_RDWR, 8683 NULL)) == NULL) 8684 { 8685 close(sock); 8686 if (tTd(38, 2)) 8687 sm_dprintf("socket_open (%s): failed to create stream: %s\n", 8688 map->map_mname, sm_errstring(errno)); 8689 return false; 8690 } 8691 8692 tmo = map->map_timeout; 8693 if (tmo == 0) 8694 tmo = 30000; /* default: 30s */ 8695 else 8696 tmo *= 1000; /* s -> ms */ 8697 sm_io_setinfo(map->map_db1, SM_IO_WHAT_TIMEOUT, &tmo); 8698 8699 /* Save connection for reuse */ 8700 s->s_socketmap = map; 8701 return true; 8702 } 8703 8704 /* 8705 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server 8706 ** 8707 ** Cache SOCKET connections based on the connection specifier 8708 ** and PID so we don't have multiple connections open to 8709 ** the same server for different maps. Need a separate connection 8710 ** per PID since a parent process may close the map before the 8711 ** child is done with it. 8712 ** 8713 ** Parameters: 8714 ** conn -- SOCKET map connection specifier 8715 ** 8716 ** Returns: 8717 ** Symbol table entry for the SOCKET connection. 8718 */ 8719 8720 static STAB * 8721 socket_map_findconn(conn) 8722 const char *conn; 8723 { 8724 char *nbuf; 8725 STAB *SM_NONVOLATILE s = NULL; 8726 8727 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid); 8728 SM_TRY 8729 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER); 8730 SM_FINALLY 8731 sm_free(nbuf); 8732 SM_END_TRY 8733 return s; 8734 } 8735 8736 /* 8737 ** SOCKET_MAP_CLOSE -- close the socket 8738 */ 8739 8740 void 8741 socket_map_close(map) 8742 MAP *map; 8743 { 8744 STAB *s; 8745 MAP *smap; 8746 8747 if (tTd(38, 20)) 8748 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file, 8749 (long) CurrentPid); 8750 8751 /* Check if already closed */ 8752 if (map->map_db1 == NULL) 8753 { 8754 if (tTd(38, 20)) 8755 sm_dprintf("socket_map_close(%s) already closed\n", 8756 map->map_file); 8757 return; 8758 } 8759 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT); 8760 8761 /* Mark all the maps that share the connection as closed */ 8762 s = socket_map_findconn(map->map_file); 8763 smap = s->s_socketmap; 8764 while (smap != NULL) 8765 { 8766 MAP *next; 8767 8768 if (tTd(38, 2) && smap != map) 8769 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n", 8770 map->map_mname, smap->map_mname); 8771 8772 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 8773 smap->map_db1 = NULL; 8774 next = smap->socket_map_next; 8775 smap->socket_map_next = NULL; 8776 smap = next; 8777 } 8778 s->s_socketmap = NULL; 8779 } 8780 8781 /* 8782 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table 8783 */ 8784 8785 char * 8786 socket_map_lookup(map, name, av, statp) 8787 MAP *map; 8788 char *name; 8789 char **av; 8790 int *statp; 8791 { 8792 unsigned int nettolen, replylen, recvlen; 8793 int ret; 8794 char *replybuf, *rval, *value, *status, *key; 8795 SM_FILE_T *f; 8796 char keybuf[MAXNAME + 1]; /* EAI:ok */ 8797 8798 replybuf = NULL; 8799 rval = NULL; 8800 f = (SM_FILE_T *)map->map_db1; 8801 if (tTd(38, 20)) 8802 sm_dprintf("socket_map_lookup(%s, %s) %s\n", 8803 map->map_mname, name, map->map_file); 8804 8805 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 8806 { 8807 nettolen = strlen(name); 8808 if (nettolen > sizeof(keybuf) - 1) 8809 nettolen = sizeof(keybuf) - 1; 8810 memmove(keybuf, name, nettolen); 8811 keybuf[nettolen] = '\0'; 8812 makelower_buf(keybuf, keybuf, sizeof(keybuf)); 8813 key = keybuf; 8814 } 8815 else 8816 key = name; 8817 8818 nettolen = strlen(map->map_mname) + 1 + strlen(key); 8819 SM_ASSERT(nettolen > strlen(map->map_mname)); 8820 SM_ASSERT(nettolen > strlen(key)); 8821 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,", 8822 nettolen, map->map_mname, key) == SM_IO_EOF) || 8823 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) || 8824 (sm_io_error(f))) 8825 { 8826 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request", 8827 map->map_mname); 8828 *statp = EX_TEMPFAIL; 8829 goto errcl; 8830 } 8831 8832 if ((ret = sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen)) != 1) 8833 { 8834 if (errno == EAGAIN) 8835 { 8836 syserr("451 4.3.0 socket_map_lookup(%s): read timeout", 8837 map->map_mname); 8838 } 8839 else if (SM_IO_EOF == ret) 8840 { 8841 if (LogLevel > 9) 8842 sm_syslog(LOG_INFO, CurEnv->e_id, 8843 "socket_map_lookup(%s): EOF", 8844 map->map_mname); 8845 } 8846 else 8847 { 8848 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply %d", 8849 map->map_mname, errno); 8850 } 8851 *statp = EX_TEMPFAIL; 8852 goto errcl; 8853 } 8854 if (replylen > SOCKETMAP_MAXL) 8855 { 8856 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u", 8857 map->map_mname, replylen); 8858 *statp = EX_TEMPFAIL; 8859 goto errcl; 8860 } 8861 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':') 8862 { 8863 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply", 8864 map->map_mname); 8865 *statp = EX_TEMPFAIL; 8866 goto error; 8867 } 8868 8869 replybuf = (char *) sm_malloc(replylen + 1); 8870 if (replybuf == NULL) 8871 { 8872 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes", 8873 map->map_mname, replylen + 1); 8874 *statp = EX_OSERR; 8875 goto error; 8876 } 8877 8878 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen); 8879 if (recvlen < replylen) 8880 { 8881 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters", 8882 map->map_mname, recvlen, replylen); 8883 *statp = EX_TEMPFAIL; 8884 goto errcl; 8885 } 8886 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',') 8887 { 8888 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply", 8889 map->map_mname); 8890 *statp = EX_TEMPFAIL; 8891 goto errcl; 8892 } 8893 status = replybuf; 8894 replybuf[recvlen] = '\0'; 8895 value = strchr(replybuf, ' '); 8896 if (value != NULL) 8897 { 8898 *value = '\0'; 8899 value++; 8900 } 8901 if (strcmp(status, "OK") == 0) 8902 { 8903 *statp = EX_OK; 8904 8905 /* collect the return value */ 8906 if (bitset(MF_MATCHONLY, map->map_mflags)) 8907 rval = map_rewrite(map, key, strlen(key), NULL); 8908 else 8909 rval = map_rewrite(map, value, strlen(value), av); 8910 } 8911 else if (strcmp(status, "NOTFOUND") == 0) 8912 { 8913 *statp = EX_NOTFOUND; 8914 if (tTd(38, 20)) 8915 sm_dprintf("socket_map_lookup(%s): %s not found\n", 8916 map->map_mname, key); 8917 } 8918 else 8919 { 8920 if (tTd(38, 5)) 8921 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n", 8922 map->map_mname, key, status, 8923 value ? value : ""); 8924 if ((strcmp(status, "TEMP") == 0) || 8925 (strcmp(status, "TIMEOUT") == 0)) 8926 *statp = EX_TEMPFAIL; 8927 else if(strcmp(status, "PERM") == 0) 8928 *statp = EX_UNAVAILABLE; 8929 else 8930 *statp = EX_PROTOCOL; 8931 } 8932 8933 SM_FREE(replybuf); 8934 return rval; 8935 8936 errcl: 8937 socket_map_close(map); 8938 error: 8939 SM_FREE(replybuf); 8940 return rval; 8941 } 8942 #endif /* SOCKETMAP */ 8943