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
map_has_chged(map,fn,fd)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
map_reset_chged(s,unused)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
maps_reset_chged(msg)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
smdb_add_extension(full_name,max_full_name_len,db_name,extension)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
map_parseargs(map,ap)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 *
map_rewrite(map,s,slen,av)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
mapchown(mapname,fd0,fd1,filename)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
initmaps()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
map_init(s,unused)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
openmap(map)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
closemaps(bogus)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
map_close(s,bogus)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 *
sun_init_domain()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
getcanonname(host,hbsize,trymx,pttl)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
extract_canonname(name,dot,line,cbuf,cbuflen)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
dns_map_open(map,mode)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
dns_map_parseargs(map,args)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 *
dns_map_lookup(map,name,av,statp)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
ndbm_map_open(map,mode)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 *
ndbm_map_lookup(map,name,av,statp)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
ndbm_map_store(map,lhs,rhs)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
ndbm_map_close(map)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
bt_map_open(map,mode)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
hash_map_open(map,mode)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 *
db_map_lookup(map,name,av,statp)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
db_map_store(map,lhs,rhs)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
db_map_close(map)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
cdb_map_open(map,mode)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 *
cdb_map_lookup(map,name,av,statp)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
cdb_map_store(map,lhs,rhs)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
cdb_map_close(map)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
nis_map_open(map,mode)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 *
nis_map_lookup(map,name,av,statp)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
nis_getcanonname(name,hbsize,statp)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
nisplus_map_open(map,mode)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 *
nisplus_map_lookup(map,name,av,statp)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
nisplus_getcanonname(name,hbsize,statp)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 *
nisplus_default_domain()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 *
ldapmap_dequote(str)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
ldapmap_open(map,mode)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
ldapmap_close(map)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 *
sunet_id_hash(str)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 *
ldapmap_lookup(map,name,av,statp)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 *
ldapmap_findconn(lmap)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
ldapmap_parseargs(map,args)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
ldapmap_set_defaults(spec)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
ph_map_parseargs(map,args)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
ph_map_close(map)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
ph_timeout(unused)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
ph_map_send_debug(appdata,text)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
ph_map_recv_debug(appdata,text)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
ph_map_open(map,mode)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 *
ph_map_lookup(map,key,args,pstat)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
syslog_map_parseargs(map,args)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 *
syslog_map_lookup(map,string,args,statp)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
dprintf_map_parseargs(map,args)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 *
dprintf_map_lookup(map,string,args,statp)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
hes_map_open(map,mode)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 *
hes_map_lookup(map,name,av,statp)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
hes_map_close(map)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
ni_map_open(map,mode)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 *
ni_map_lookup(map,name,av,statp)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
ni_getcanonname(name,hbsize,statp)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
text_map_open(map,mode)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 *
text_map_lookup(map,name,av,statp)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
text_getcanonname(name,hbsize,statp)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 *
stab_map_lookup(map,name,av,pstat)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
stab_map_store(map,lhs,rhs)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
stab_map_open(map,mode)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 *
impl_map_lookup(map,name,av,pstat)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
impl_map_store(map,lhs,rhs)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
impl_map_open(map,mode)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
impl_map_close(map)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
user_map_open(map,mode)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 *
user_map_lookup(map,key,av,statp)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 *
prog_map_lookup(map,name,av,statp)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
seq_map_parse(map,ap)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
switch_map_open(map,mode)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 *
seq_map_lookup(map,key,args,pstat)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
seq_map_store(map,key,val)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
null_map_open(map,mode)7413 null_map_open(map, mode)
7414 MAP *map;
7415 int mode;
7416 {
7417 return true;
7418 }
7419
7420 /* ARGSUSED */
7421 void
null_map_close(map)7422 null_map_close(map)
7423 MAP *map;
7424 {
7425 return;
7426 }
7427
7428 char *
null_map_lookup(map,key,args,pstat)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
null_map_store(map,key,val)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 *
bogus_map_lookup(map,key,args,pstat)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 *
macro_map_lookup(map,name,av,statp)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
parse_fields(s,ibuf,blen,nr_substrings)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
regex_map_init(map,ap)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 *
regex_map_rewrite(map,s,slen,av)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 *
regex_map_lookup(map,name,av,statp)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 *
ns_map_t_find(mapname)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 *
nsd_map_lookup(map,name,av,statp)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 *
arith_map_lookup(map,name,av,statp)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 *
arpa_map_lookup(map,name,av,statp)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 *
setdebug_map_lookup(map,name,av,statp)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 *
setopt_map_lookup(map,name,av,statp)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
socket_map_open(map,mode)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 *
socket_map_findconn(conn)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
socket_map_close(map)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 *
socket_map_lookup(map,name,av,statp)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