2 * Copyright (c) 1998-2007 Sendmail, Inc. and its suppliers.
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.
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.
16 SM_RCSID("@(#)$Id: map.c,v 8.699 2007/10/10 00:06:45 ca Exp $")
25 ERROR README
: You are running the Berkeley DB version of ndbm
.h
. See
26 ERROR README
: the README file about tweaking Berkeley DB so it can
27 ERROR README
: coexist with NDBM
, or delete -DNDBM from the Makefile
28 ERROR README
: and use
-DNEWDB instead
.
35 struct dom_binding
; /* forward reference needed on IRIX */
36 # include <rpcsvc/ypclnt.h>
38 # define NDBM_YP_COMPAT /* create YP-compatible NDBM files */
45 # if DB_VERSION_MAJOR < 2
46 static bool db_map_open
__P((MAP
*, int, char *, DBTYPE
, const void *));
47 # endif /* DB_VERSION_MAJOR < 2 */
48 # if DB_VERSION_MAJOR == 2
49 static bool db_map_open
__P((MAP
*, int, char *, DBTYPE
, DB_INFO
*));
50 # endif /* DB_VERSION_MAJOR == 2 */
51 # if DB_VERSION_MAJOR > 2
52 static bool db_map_open
__P((MAP
*, int, char *, DBTYPE
, void **));
53 # endif /* DB_VERSION_MAJOR > 2 */
55 static bool extract_canonname
__P((char *, char *, char *, char[], int));
56 static void map_close
__P((STAB
*, int));
57 static void map_init
__P((STAB
*, int));
59 static STAB
* ldapmap_findconn
__P((SM_LDAP_STRUCT
*));
62 static bool nisplus_getcanonname
__P((char *, int, int *));
65 static bool nis_getcanonname
__P((char *, int, int *));
68 static bool ni_getcanonname
__P((char *, int, int *));
70 static bool text_getcanonname
__P((char *, int, int *));
72 static STAB
*socket_map_findconn
__P((const char*));
74 /* XXX arbitrary limit for sanity */
75 # define SOCKETMAP_MAXL 1000000
76 #endif /* SOCKETMAP */
78 /* default error message for trying to open a map in write mode */
80 # define SM_EMAPCANTWRITE ENOSYS
83 # define SM_EMAPCANTWRITE EFTYPE
85 # define SM_EMAPCANTWRITE ENXIO
90 ** MAP.C -- implementations for various map classes.
92 ** Each map class implements a series of functions:
94 ** bool map_parse(MAP *map, char *args)
95 ** Parse the arguments from the config file. Return true
96 ** if they were ok, false otherwise. Fill in map with the
99 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
100 ** Look up the key in the given map. If found, do any
101 ** rewriting the map wants (including "args" if desired)
102 ** and return the value. Set *pstat to the appropriate status
103 ** on error and return NULL. Args will be NULL if called
104 ** from the alias routines, although this should probably
105 ** not be relied upon. It is suggested you call map_rewrite
106 ** to return the results -- it takes care of null termination
107 ** and uses a dynamically expanded buffer as needed.
109 ** void map_store(MAP *map, char *key, char *value)
110 ** Store the key:value pair in the map.
112 ** bool map_open(MAP *map, int mode)
113 ** Open the map for the indicated mode. Mode should
114 ** be either O_RDONLY or O_RDWR. Return true if it
115 ** was opened successfully, false otherwise. If the open
116 ** failed and the MF_OPTIONAL flag is not set, it should
117 ** also print an error. If the MF_ALIAS bit is set
118 ** and this map class understands the @:@ convention, it
119 ** should call aliaswait() before returning.
121 ** void map_close(MAP *map)
124 ** This file also includes the implementation for getcanonname.
125 ** It is currently implemented in a pretty ad-hoc manner; it ought
126 ** to be more properly integrated into the map structure.
129 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
130 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */
131 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
132 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
133 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
136 ** MAP_PARSEARGS -- parse config line arguments for database lookup
138 ** This is a generic version of the map_parse method.
141 ** map -- the map being initialized.
142 ** ap -- a pointer to the args on the config line.
145 ** true -- if everything parsed OK.
146 ** false -- otherwise.
149 ** null terminates the filename; stores it in map
153 map_parseargs(map
, ap
)
157 register char *p
= ap
;
160 ** There is no check whether there is really an argument,
161 ** but that's not important enough to warrant extra code.
164 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
165 map
->map_spacesub
= SpaceSub
; /* default value */
168 while (isascii(*p
) && isspace(*p
))
175 map
->map_mflags
|= MF_INCLNULL
;
176 map
->map_mflags
&= ~MF_TRY0NULL
;
180 map
->map_mflags
&= ~MF_TRY1NULL
;
184 map
->map_mflags
|= MF_OPTIONAL
;
188 map
->map_mflags
|= MF_NOFOLDCASE
;
192 map
->map_mflags
|= MF_MATCHONLY
;
196 map
->map_mflags
|= MF_APPEND
;
200 map
->map_mflags
|= MF_KEEPQUOTES
;
212 while (isascii(*++p
) && isspace(*p
))
214 map
->map_keycolnm
= p
;
218 while (isascii(*++p
) && isspace(*p
))
220 map
->map_valcolnm
= p
;
225 map
->map_coldelim
= *p
;
231 map
->map_coldelim
= '\n';
235 map
->map_coldelim
= '\t';
239 map
->map_coldelim
= '\\';
245 map
->map_mflags
|= MF_NODEFER
;
250 map
->map_spacesub
= *++p
;
254 map
->map_mflags
|= MF_DEFER
;
258 syserr("Illegal option %c map %s", *p
, map
->map_mname
);
261 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
266 if (map
->map_app
!= NULL
)
267 map
->map_app
= newstr(map
->map_app
);
268 if (map
->map_tapp
!= NULL
)
269 map
->map_tapp
= newstr(map
->map_tapp
);
270 if (map
->map_keycolnm
!= NULL
)
271 map
->map_keycolnm
= newstr(map
->map_keycolnm
);
272 if (map
->map_valcolnm
!= NULL
)
273 map
->map_valcolnm
= newstr(map
->map_valcolnm
);
278 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
282 map
->map_file
= newstr(map
->map_file
);
285 while (*p
!= '\0' && isascii(*p
) && isspace(*p
))
288 map
->map_rebuild
= newstr(p
);
290 if (map
->map_file
== NULL
&&
291 !bitset(MCF_OPTFILE
, map
->map_class
->map_cflags
))
293 syserr("No file name for %s map %s",
294 map
->map_class
->map_cname
, map
->map_mname
);
300 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
302 ** It also adds the map_app string. It can be used as a utility
303 ** in the map_lookup method.
306 ** map -- the map that causes this.
307 ** s -- the string to rewrite, NOT necessarily null terminated.
308 ** slen -- the length of s.
309 ** av -- arguments to interpolate into buf.
312 ** Pointer to rewritten result. This is static data that
313 ** should be copied if it is to be saved!
317 map_rewrite(map
, s
, slen
, av
)
319 register const char *s
;
329 static size_t buflen
= 0;
330 static char *buf
= NULL
;
334 sm_dprintf("map_rewrite(%.*s), av =", (int) slen
, s
);
336 sm_dprintf(" (nullv)");
339 for (avp
= av
; *avp
!= NULL
; avp
++)
340 sm_dprintf("\n\t%s", *avp
);
345 /* count expected size of output (can safely overestimate) */
351 while (l
-- > 0 && (c
= *sp
++) != '\0')
358 if (!(isascii(c
) && isdigit(c
)))
360 for (avp
= av
; --c
>= '0' && *avp
!= NULL
; avp
++)
367 if (map
->map_app
!= NULL
)
368 len
+= strlen(map
->map_app
);
371 /* need to malloc additional space */
375 buf
= sm_pmalloc_x(buflen
);
381 memmove(bp
, s
, slen
);
384 /* assert(len > slen); */
389 while (slen
-- > 0 && (c
= *s
++) != '\0')
399 if (slen
-- <= 0 || (c
= *s
++) == '\0')
403 if (!(isascii(c
) && isdigit(c
)))
410 for (avp
= av
; --c
>= '0' && *avp
!= NULL
; avp
++)
415 /* transliterate argument into output string */
416 for (ap
= *avp
; (c
= *ap
++) != '\0' && len
> 0; --len
)
420 if (map
->map_app
!= NULL
&& len
> 0)
421 (void) sm_strlcpy(bp
, map
->map_app
, len
);
425 sm_dprintf("map_rewrite => %s\n", buf
);
429 ** INITMAPS -- rebuild alias maps
442 checkfd012("entering initmaps");
444 stabapply(map_init
, 0);
446 checkfd012("exiting initmaps");
450 ** MAP_INIT -- rebuild a map
453 ** s -- STAB entry: if map: try to rebuild
454 ** unused -- unused variable
460 ** will close already open rebuildable map.
471 /* has to be a map */
472 if (s
->s_symtype
!= ST_MAP
)
476 if (!bitset(MF_VALID
, map
->map_mflags
))
480 sm_dprintf("map_init(%s:%s, %s)\n",
481 map
->map_class
->map_cname
== NULL
? "NULL" :
482 map
->map_class
->map_cname
,
483 map
->map_mname
== NULL
? "NULL" : map
->map_mname
,
484 map
->map_file
== NULL
? "NULL" : map
->map_file
);
486 if (!bitset(MF_ALIAS
, map
->map_mflags
) ||
487 !bitset(MCF_REBUILDABLE
, map
->map_class
->map_cflags
))
490 sm_dprintf("\tnot rebuildable\n");
494 /* if already open, close it (for nested open) */
495 if (bitset(MF_OPEN
, map
->map_mflags
))
497 map
->map_mflags
|= MF_CLOSING
;
498 map
->map_class
->map_close(map
);
499 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
502 (void) rebuildaliases(map
, false);
506 ** OPENMAP -- open a map
509 ** map -- map to open (it must not be open).
512 ** whether open succeeded.
519 bool restore
= false;
520 bool savehold
= HoldErrs
;
521 bool savequick
= QuickAbort
;
522 int saveerrors
= Errors
;
524 if (!bitset(MF_VALID
, map
->map_mflags
))
527 /* better safe than sorry... */
528 if (bitset(MF_OPEN
, map
->map_mflags
))
531 /* Don't send a map open error out via SMTP */
532 if ((OnlyOneError
|| QuickAbort
) &&
533 (OpMode
== MD_SMTP
|| OpMode
== MD_DAEMON
))
541 if (map
->map_class
->map_open(map
, O_RDONLY
))
544 sm_dprintf("openmap()\t%s:%s %s: valid\n",
545 map
->map_class
->map_cname
== NULL
? "NULL" :
546 map
->map_class
->map_cname
,
547 map
->map_mname
== NULL
? "NULL" :
549 map
->map_file
== NULL
? "NULL" :
551 map
->map_mflags
|= MF_OPEN
;
552 map
->map_pid
= CurrentPid
;
557 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
558 map
->map_class
->map_cname
== NULL
? "NULL" :
559 map
->map_class
->map_cname
,
560 map
->map_mname
== NULL
? "NULL" :
562 map
->map_file
== NULL
? "NULL" :
564 errno
== 0 ? "" : ": ",
565 errno
== 0 ? "" : sm_errstring(errno
));
566 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
568 extern MAPCLASS BogusMapClass
;
570 map
->map_orgclass
= map
->map_class
;
571 map
->map_class
= &BogusMapClass
;
572 map
->map_mflags
|= MF_OPEN
|MF_OPENBOGUS
;
573 map
->map_pid
= CurrentPid
;
577 /* don't try again */
578 map
->map_mflags
&= ~MF_VALID
;
586 QuickAbort
= savequick
;
589 return bitset(MF_OPEN
, map
->map_mflags
);
592 ** CLOSEMAPS -- close all open maps opened by the current pid.
595 ** bogus -- only close bogus maps.
605 stabapply(map_close
, bogus
);
608 ** MAP_CLOSE -- close a map opened by the current pid.
611 ** s -- STAB entry: if map: try to close
612 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
622 int bogus
; /* int because of stabapply(), used as bool */
625 extern MAPCLASS BogusMapClass
;
627 if (s
->s_symtype
!= ST_MAP
)
633 ** close the map iff:
634 ** it is valid and open and opened by this process
635 ** and (!bogus or it's a bogus map or it is not persistent)
636 ** negate this: return iff
637 ** it is not valid or it is not open or not opened by this process
638 ** or (bogus and it's not a bogus map and it's not not-persistent)
641 if (!bitset(MF_VALID
, map
->map_mflags
) ||
642 !bitset(MF_OPEN
, map
->map_mflags
) ||
643 bitset(MF_CLOSING
, map
->map_mflags
) ||
644 map
->map_pid
!= CurrentPid
||
645 (bogus
&& map
->map_class
!= &BogusMapClass
&&
646 !bitset(MCF_NOTPERSIST
, map
->map_class
->map_cflags
)))
649 if (map
->map_class
== &BogusMapClass
&& map
->map_orgclass
!= NULL
&&
650 map
->map_orgclass
!= &BogusMapClass
)
651 map
->map_class
= map
->map_orgclass
;
653 sm_dprintf("closemaps: closing %s (%s)\n",
654 map
->map_mname
== NULL
? "NULL" : map
->map_mname
,
655 map
->map_file
== NULL
? "NULL" : map
->map_file
);
657 if (!bitset(MF_OPENBOGUS
, map
->map_mflags
))
659 map
->map_mflags
|= MF_CLOSING
;
660 map
->map_class
->map_close(map
);
662 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_OPENBOGUS
|MF_CLOSING
);
665 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
666 extern int getdomainname();
668 /* this is mainly for backward compatibility in Sun environment */
673 ** Get the domain name from the kernel.
674 ** If it does not start with a leading dot, then remove
675 ** the first component. Since leading dots are funny Unix
676 ** files, we treat a leading "+" the same as a leading dot.
677 ** Finally, force there to be at least one dot in the domain name
678 ** (i.e. top-level domains are not allowed, like "com", must be
679 ** something like "sun.com").
683 char *period
, *autodomain
;
685 if (getdomainname(buf
, sizeof buf
) < 0)
692 printf("domainname = %s\n", buf
);
696 period
= strchr(buf
, '.');
700 autodomain
= period
+ 1;
701 if (strchr(autodomain
, '.') == NULL
)
704 return newstr(autodomain
);
706 #endif /* SUN_EXTENSIONS && SUN_INIT_DOMAIN */
709 ** GETCANONNAME -- look up name using service switch
712 ** host -- the host name to look up.
713 ** hbsize -- the size of the host buffer.
714 ** trymx -- if set, try MX records.
715 ** pttl -- pointer to return TTL (can be NULL).
718 ** true -- if the host was found.
719 ** false -- otherwise.
723 getcanonname(host
, hbsize
, trymx
, pttl
)
732 bool got_tempfail
= false;
734 char *maptype
[MAXMAPSTACK
];
735 short mapreturn
[MAXMAPACTIONS
];
736 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
737 bool should_try_nis_domain
= false;
738 static char *nis_domain
= NULL
;
741 nmaps
= switch_map_find("hosts", maptype
, mapreturn
);
743 *pttl
= SM_DEFAULT_TTL
;
744 for (mapno
= 0; mapno
< nmaps
; mapno
++)
749 sm_dprintf("getcanonname(%s), trying %s\n",
750 host
, maptype
[mapno
]);
751 if (strcmp("files", maptype
[mapno
]) == 0)
753 found
= text_getcanonname(host
, hbsize
, &status
);
756 else if (strcmp("nis", maptype
[mapno
]) == 0)
758 found
= nis_getcanonname(host
, hbsize
, &status
);
759 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
760 if (nis_domain
== NULL
)
761 nis_domain
= sun_init_domain();
762 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
766 else if (strcmp("nisplus", maptype
[mapno
]) == 0)
768 found
= nisplus_getcanonname(host
, hbsize
, &status
);
769 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
770 if (nis_domain
== NULL
)
771 nis_domain
= sun_init_domain();
772 # endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
776 else if (strcmp("dns", maptype
[mapno
]) == 0)
778 found
= dns_getcanonname(host
, hbsize
, trymx
, &status
, pttl
);
780 #endif /* NAMED_BIND */
782 else if (strcmp("netinfo", maptype
[mapno
]) == 0)
784 found
= ni_getcanonname(host
, hbsize
, &status
);
790 status
= EX_UNAVAILABLE
;
794 ** Heuristic: if $m is not set, we are running during system
795 ** startup. In this case, when a name is apparently found
796 ** but has no dot, treat is as not found. This avoids
797 ** problems if /etc/hosts has no FQDN but is listed first
798 ** in the service switch.
802 (macvalue('m', CurEnv
) != NULL
|| strchr(host
, '.') != NULL
))
805 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
807 should_try_nis_domain
= true;
808 /* but don't break, as we need to try all methods first */
809 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
811 /* see if we should continue */
812 if (status
== EX_TEMPFAIL
)
817 else if (status
== EX_NOTFOUND
)
821 if (bitset(1 << mapno
, mapreturn
[i
]))
830 sm_dprintf("getcanonname(%s), found\n", host
);
833 ** If returned name is still single token, compensate
834 ** by tagging on $m. This is because some sites set
835 ** up their DNS or NIS databases wrong.
838 if ((d
= strchr(host
, '.')) == NULL
|| d
[1] == '\0')
840 d
= macvalue('m', CurEnv
);
842 hbsize
> (int) (strlen(host
) + strlen(d
) + 1))
844 if (host
[strlen(host
) - 1] != '.')
845 (void) sm_strlcat2(host
, ".", d
,
848 (void) sm_strlcat(host
, d
, hbsize
);
852 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
853 if (VendorCode
== VENDOR_SUN
&&
854 should_try_nis_domain
)
858 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
865 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN)
866 if (VendorCode
== VENDOR_SUN
&& should_try_nis_domain
)
869 if (nis_domain
!= NULL
&&
870 strlen(nis_domain
) + strlen(host
) + 1 < hbsize
)
872 (void) sm_strlcat2(host
, ".", nis_domain
, hbsize
);
876 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) */
879 sm_dprintf("getcanonname(%s), failed, status=%d\n", host
,
883 SM_SET_H_ERRNO(TRY_AGAIN
);
885 SM_SET_H_ERRNO(HOST_NOT_FOUND
);
890 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
893 ** name -- the name against which to match.
894 ** dot -- where to reinsert '.' to get FQDN
895 ** line -- the /etc/hosts line.
896 ** cbuf -- the location to store the result.
897 ** cbuflen -- the size of cbuf.
900 ** true -- if the line matched the desired name.
901 ** false -- otherwise.
905 extract_canonname(name
, dot
, line
, cbuf
, cbuflen
)
922 char nbuf
[MAXNAME
+ 1];
924 p
= get_column(line
, i
, '\0', nbuf
, sizeof(nbuf
));
929 if (cbuf
[0] == '\0' ||
930 (strchr(cbuf
, '.') == NULL
&& strchr(p
, '.') != NULL
))
932 (void) sm_strlcpy(cbuf
, p
, cbuflen
);
934 if (sm_strcasecmp(name
, p
) == 0)
936 else if (dot
!= NULL
)
938 /* try looking for the FQDN as well */
940 if (sm_strcasecmp(name
, p
) == 0)
945 if (found
&& strchr(cbuf
, '.') == NULL
)
947 /* try to add a domain on the end of the name */
948 char *domain
= macvalue('m', CurEnv
);
950 if (domain
!= NULL
&&
951 strlen(domain
) + (i
= strlen(cbuf
)) + 1 < (size_t) cbuflen
)
955 (void) sm_strlcpy(p
, domain
, cbuflen
- i
- 1);
968 # include "sm_resolve.h"
969 # if NETINET || NETINET6
970 # include <arpa/inet.h>
971 # endif /* NETINET || NETINET6 */
974 ** DNS_MAP_OPEN -- stub to check proper value for dns map type
978 dns_map_open(map
, mode
)
983 sm_dprintf("dns_map_open(%s, %d)\n", map
->map_mname
, mode
);
986 if (mode
!= O_RDONLY
)
988 /* issue a pseudo-error message */
989 errno
= SM_EMAPCANTWRITE
;
996 ** DNS_MAP_PARSEARGS -- parse dns map definition args.
999 ** map -- pointer to MAP
1000 ** args -- pointer to the args on the config line.
1003 ** true -- if everything parsed OK.
1004 ** false -- otherwise.
1007 #define map_sizelimit map_lockfd /* overload field */
1015 dns_map_parseargs(map
,args
)
1019 register char *p
= args
;
1020 struct dns_map
*map_p
;
1022 map_p
= (struct dns_map
*) xalloc(sizeof(*map_p
));
1023 map_p
->dns_m_type
= -1;
1024 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
1028 while (isascii(*p
) && isspace(*p
))
1035 map
->map_mflags
|= MF_INCLNULL
;
1036 map
->map_mflags
&= ~MF_TRY0NULL
;
1040 map
->map_mflags
&= ~MF_TRY1NULL
;
1044 map
->map_mflags
|= MF_OPTIONAL
;
1048 map
->map_mflags
|= MF_NOFOLDCASE
;
1052 map
->map_mflags
|= MF_MATCHONLY
;
1056 map
->map_mflags
|= MF_APPEND
;
1060 map
->map_mflags
|= MF_KEEPQUOTES
;
1064 map
->map_mflags
|= MF_NODEFER
;
1072 map
->map_tapp
= ++p
;
1083 map
->map_timeout
= convtime(p
, 's');
1090 while (isascii(*++p
) && isspace(*p
))
1092 map
->map_retry
= atoi(p
);
1097 map
->map_coldelim
= *p
;
1103 map
->map_coldelim
= '\n';
1107 map
->map_coldelim
= '\t';
1111 map
->map_coldelim
= '\\';
1117 while (isascii(*++p
) && isspace(*p
))
1119 map
->map_sizelimit
= atoi(p
);
1122 /* Start of dns_map specific args */
1123 case 'R': /* search field */
1127 while (isascii(*++p
) && isspace(*p
))
1132 map_p
->dns_m_type
= dns_string_to_type(p
);
1135 if (map_p
->dns_m_type
< 0)
1136 syserr("dns map %s: wrong type %s",
1141 case 'B': /* base domain */
1145 while (isascii(*++p
) && isspace(*p
))
1152 ** slight abuse of map->map_file; it isn't
1153 ** used otherwise in this map type.
1156 map
->map_file
= newstr(p
);
1162 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
1167 if (map_p
->dns_m_type
< 0)
1168 syserr("dns map %s: missing -R type", map
->map_mname
);
1169 if (map
->map_app
!= NULL
)
1170 map
->map_app
= newstr(map
->map_app
);
1171 if (map
->map_tapp
!= NULL
)
1172 map
->map_tapp
= newstr(map
->map_tapp
);
1175 ** Assumption: assert(sizeof(int) <= sizeof(ARBPTR_T));
1176 ** Even if this assumption is wrong, we use only one byte,
1177 ** so it doesn't really matter.
1180 map
->map_db1
= (ARBPTR_T
) map_p
;
1185 ** DNS_MAP_LOOKUP -- perform dns map lookup.
1188 ** map -- pointer to MAP
1189 ** name -- name to lookup
1190 ** av -- arguments to interpolate into buf.
1191 ** statp -- pointer to status (EX_)
1194 ** result of lookup if succeeded.
1195 ** NULL -- otherwise.
1199 dns_map_lookup(map
, name
, av
, statp
)
1206 char *vp
= NULL
, *result
= NULL
;
1208 struct dns_map
*map_p
;
1209 RESOURCE_RECORD_T
*rr
= NULL
;
1210 DNS_REPLY_T
*r
= NULL
;
1212 static char buf6
[INET6_ADDRSTRLEN
];
1213 # endif /* NETINET6 */
1216 sm_dprintf("dns_map_lookup(%s, %s)\n",
1217 map
->map_mname
, name
);
1219 map_p
= (struct dns_map
*)(map
->map_db1
);
1220 if (map
->map_file
!= NULL
&& *map
->map_file
!= '\0')
1225 len
= strlen(map
->map_file
) + strlen(name
) + 2;
1226 appdomain
= (char *) sm_malloc(len
);
1227 if (appdomain
== NULL
)
1229 *statp
= EX_UNAVAILABLE
;
1232 (void) sm_strlcpyn(appdomain
, len
, 3, name
, ".", map
->map_file
);
1233 r
= dns_lookup_int(appdomain
, C_IN
, map_p
->dns_m_type
,
1234 map
->map_timeout
, map
->map_retry
);
1239 r
= dns_lookup_int(name
, C_IN
, map_p
->dns_m_type
,
1240 map
->map_timeout
, map
->map_retry
);
1246 if (h_errno
== TRY_AGAIN
|| transienterror(errno
))
1247 *statp
= EX_TEMPFAIL
;
1249 *statp
= EX_NOTFOUND
;
1253 for (rr
= r
->dns_r_head
; rr
!= NULL
; rr
= rr
->rr_next
)
1258 switch (rr
->rr_type
)
1262 value
= rr
->rr_u
.rr_txt
;
1266 value
= rr
->rr_u
.rr_txt
;
1270 value
= rr
->rr_u
.rr_mx
->mx_r_domain
;
1274 value
= rr
->rr_u
.rr_srv
->srv_r_target
;
1278 value
= rr
->rr_u
.rr_txt
;
1282 value
= rr
->rr_u
.rr_txt
;
1286 value
= rr
->rr_u
.rr_mx
->mx_r_domain
;
1291 value
= inet_ntoa(*(rr
->rr_u
.rr_a
));
1293 # endif /* NETINET */
1297 value
= anynet_ntop(rr
->rr_u
.rr_aaaa
, buf6
,
1300 # endif /* NETINET6 */
1303 (void) strreplnonprt(value
, 'X');
1304 if (map_p
->dns_m_type
!= rr
->rr_type
)
1307 sm_dprintf("\tskipping type %s (%d) value %s\n",
1308 type
!= NULL
? type
: "<UNKNOWN>",
1310 value
!= NULL
? value
: "<NO VALUE>");
1315 if (rr
->rr_type
== T_AAAA
&& value
== NULL
)
1318 *statp
= EX_DATAERR
;
1320 sm_dprintf("\tbad T_AAAA conversion\n");
1323 # endif /* NETINET6 */
1325 sm_dprintf("\tfound type %s (%d) value %s\n",
1326 type
!= NULL
? type
: "<UNKNOWN>",
1328 value
!= NULL
? value
: "<NO VALUE>");
1329 if (value
!= NULL
&&
1330 (map
->map_coldelim
== '\0' ||
1331 map
->map_sizelimit
== 1 ||
1332 bitset(MF_MATCHONLY
, map
->map_mflags
)))
1334 /* Only care about the first match */
1338 else if (vp
== NULL
)
1345 /* concatenate the results */
1349 sz
= strlen(vp
) + strlen(value
) + 2;
1351 (void) sm_snprintf(new, sz
, "%s%c%s",
1352 vp
, map
->map_coldelim
, value
);
1355 if (map
->map_sizelimit
> 0 &&
1356 ++resnum
>= map
->map_sizelimit
)
1363 *statp
= EX_NOTFOUND
;
1365 sm_dprintf("\tno match found\n");
1369 /* Cleanly truncate for rulesets */
1370 truncate_at_delim(vp
, PSBUFSIZE
/ 2, map
->map_coldelim
);
1375 sm_syslog(LOG_INFO
, CurEnv
->e_id
, "dns %.100s => %s",
1377 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
1378 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
1380 result
= map_rewrite(map
, vp
, vsize
, av
);
1389 # endif /* DNSMAP */
1390 #endif /* NAMED_BIND */
1399 ** NDBM_MAP_OPEN -- DBM-style map open
1403 ndbm_map_open(map
, mode
)
1413 int smode
= S_IREAD
;
1414 char dirfile
[MAXPATHLEN
];
1415 char pagfile
[MAXPATHLEN
];
1417 struct stat std
, stp
;
1420 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1421 map
->map_mname
, map
->map_file
, mode
);
1422 map
->map_lockfd
= -1;
1425 /* do initial file and directory checks */
1426 if (sm_strlcpyn(dirfile
, sizeof(dirfile
), 2,
1427 map
->map_file
, ".dir") >= sizeof(dirfile
) ||
1428 sm_strlcpyn(pagfile
, sizeof(pagfile
), 2,
1429 map
->map_file
, ".pag") >= sizeof(pagfile
))
1432 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1433 syserr("dbm map \"%s\": map file %s name too long",
1434 map
->map_mname
, map
->map_file
);
1437 sff
= SFF_ROOTOK
|SFF_REGONLY
;
1441 if (!bitnset(DBS_WRITEMAPTOSYMLINK
, DontBlameSendmail
))
1443 if (!bitnset(DBS_WRITEMAPTOHARDLINK
, DontBlameSendmail
))
1449 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
1452 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
1453 sff
|= SFF_SAFEDIRPATH
;
1454 ret
= safefile(dirfile
, RunAsUid
, RunAsGid
, RunAsUserName
,
1457 ret
= safefile(pagfile
, RunAsUid
, RunAsGid
, RunAsUserName
,
1462 char *prob
= "unsafe";
1464 /* cannot open this map */
1468 sm_dprintf("\t%s map file: %d\n", prob
, ret
);
1469 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1470 syserr("dbm map \"%s\": %s map file %s",
1471 map
->map_mname
, prob
, map
->map_file
);
1474 if (std
.st_mode
== ST_MODE_NOFILE
)
1475 mode
|= O_CREAT
|O_EXCL
;
1478 if (mode
== O_RDONLY
)
1481 mode
|= O_TRUNC
|O_EXLOCK
;
1482 # else /* LOCK_ON_OPEN */
1483 if ((mode
& O_ACCMODE
) == O_RDWR
)
1487 ** Warning: race condition. Try to lock the file as
1488 ** quickly as possible after opening it.
1489 ** This may also have security problems on some systems,
1490 ** but there isn't anything we can do about it.
1494 # else /* NOFTRUNCATE */
1496 ** This ugly code opens the map without truncating it,
1497 ** locks the file, then truncates it. Necessary to
1498 ** avoid race conditions.
1503 long sff
= SFF_CREAT
|SFF_OPENASROOT
;
1505 if (!bitnset(DBS_WRITEMAPTOSYMLINK
, DontBlameSendmail
))
1507 if (!bitnset(DBS_WRITEMAPTOHARDLINK
, DontBlameSendmail
))
1510 dirfd
= safeopen(dirfile
, mode
, DBMMODE
, sff
);
1511 pagfd
= safeopen(pagfile
, mode
, DBMMODE
, sff
);
1513 if (dirfd
< 0 || pagfd
< 0)
1517 (void) close(dirfd
);
1519 (void) close(pagfd
);
1521 syserr("ndbm_map_open: cannot create database %s",
1525 if (ftruncate(dirfd
, (off_t
) 0) < 0 ||
1526 ftruncate(pagfd
, (off_t
) 0) < 0)
1529 (void) close(dirfd
);
1530 (void) close(pagfd
);
1532 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1537 /* if new file, get "before" bits for later filechanged check */
1538 if (std
.st_mode
== ST_MODE_NOFILE
&&
1539 (fstat(dirfd
, &std
) < 0 || fstat(pagfd
, &stp
) < 0))
1542 (void) close(dirfd
);
1543 (void) close(pagfd
);
1545 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1550 /* have to save the lock for the duration (bletch) */
1551 map
->map_lockfd
= dirfd
;
1552 (void) close(pagfd
);
1554 /* twiddle bits for dbm_open */
1555 mode
&= ~(O_CREAT
|O_EXCL
);
1556 # endif /* NOFTRUNCATE */
1558 # endif /* LOCK_ON_OPEN */
1560 /* open the database */
1561 dbm
= dbm_open(map
->map_file
, mode
, DBMMODE
);
1565 if (bitset(MF_ALIAS
, map
->map_mflags
) &&
1566 aliaswait(map
, ".pag", false))
1568 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1569 if (map
->map_lockfd
>= 0)
1570 (void) close(map
->map_lockfd
);
1571 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1573 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1574 syserr("Cannot open DBM database %s", map
->map_file
);
1577 dfd
= dbm_dirfno(dbm
);
1578 pfd
= dbm_pagfno(dbm
);
1581 /* heuristic: if files are linked, this is actually gdbm */
1583 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1584 if (map
->map_lockfd
>= 0)
1585 (void) close(map
->map_lockfd
);
1586 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1588 syserr("dbm map \"%s\": cannot support GDBM",
1593 if (filechanged(dirfile
, dfd
, &std
) ||
1594 filechanged(pagfile
, pfd
, &stp
))
1598 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1599 if (map
->map_lockfd
>= 0)
1600 (void) close(map
->map_lockfd
);
1601 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1603 syserr("ndbm_map_open(%s): file changed after open",
1608 map
->map_db1
= (ARBPTR_T
) dbm
;
1611 ** Need to set map_mtime before the call to aliaswait()
1612 ** as aliaswait() will call map_lookup() which requires
1613 ** map_mtime to be set
1616 if (fstat(pfd
, &st
) >= 0)
1617 map
->map_mtime
= st
.st_mtime
;
1619 if (mode
== O_RDONLY
)
1623 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_UN
);
1625 (void) lockfile(pfd
, map
->map_file
, ".pag", LOCK_UN
);
1626 # endif /* LOCK_ON_OPEN */
1627 if (bitset(MF_ALIAS
, map
->map_mflags
) &&
1628 !aliaswait(map
, ".pag", true))
1633 map
->map_mflags
|= MF_LOCKED
;
1634 if (geteuid() == 0 && TrustedUid
!= 0)
1637 if (fchown(dfd
, TrustedUid
, -1) < 0 ||
1638 fchown(pfd
, TrustedUid
, -1) < 0)
1642 sm_syslog(LOG_ALERT
, NOQID
,
1643 "ownership change on %s failed: %s",
1644 map
->map_file
, sm_errstring(err
));
1645 message("050 ownership change on %s failed: %s",
1646 map
->map_file
, sm_errstring(err
));
1648 # else /* HASFCHOWN */
1649 sm_syslog(LOG_ALERT
, NOQID
,
1650 "no fchown(): cannot change ownership on %s",
1652 message("050 no fchown(): cannot change ownership on %s",
1654 # endif /* HASFCHOWN */
1662 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1666 ndbm_map_lookup(map
, name
, av
, statp
)
1674 char keybuf
[MAXNAME
+ 1];
1678 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1679 map
->map_mname
, name
);
1682 key
.dsize
= strlen(name
);
1683 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
1685 if (key
.dsize
> sizeof(keybuf
) - 1)
1686 key
.dsize
= sizeof(keybuf
) - 1;
1687 memmove(keybuf
, key
.dptr
, key
.dsize
);
1688 keybuf
[key
.dsize
] = '\0';
1693 dfd
= dbm_dirfno((DBM
*) map
->map_db1
);
1694 if (dfd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
1695 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_SH
);
1696 pfd
= dbm_pagfno((DBM
*) map
->map_db1
);
1697 if (pfd
< 0 || fstat(pfd
, &stbuf
) < 0 ||
1698 stbuf
.st_mtime
> map
->map_mtime
)
1700 /* Reopen the database to sync the cache */
1701 int omode
= bitset(map
->map_mflags
, MF_WRITABLE
) ? O_RDWR
1704 if (dfd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
1705 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_UN
);
1706 map
->map_mflags
|= MF_CLOSING
;
1707 map
->map_class
->map_close(map
);
1708 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
1709 if (map
->map_class
->map_open(map
, omode
))
1711 map
->map_mflags
|= MF_OPEN
;
1712 map
->map_pid
= CurrentPid
;
1713 if ((omode
&& O_ACCMODE
) == O_RDWR
)
1714 map
->map_mflags
|= MF_WRITABLE
;
1719 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1721 extern MAPCLASS BogusMapClass
;
1723 *statp
= EX_TEMPFAIL
;
1724 map
->map_orgclass
= map
->map_class
;
1725 map
->map_class
= &BogusMapClass
;
1726 map
->map_mflags
|= MF_OPEN
;
1727 map
->map_pid
= CurrentPid
;
1728 syserr("Cannot reopen NDBM database %s",
1735 if (bitset(MF_TRY0NULL
, map
->map_mflags
))
1737 val
= dbm_fetch((DBM
*) map
->map_db1
, key
);
1738 if (val
.dptr
!= NULL
)
1739 map
->map_mflags
&= ~MF_TRY1NULL
;
1741 if (val
.dptr
== NULL
&& bitset(MF_TRY1NULL
, map
->map_mflags
))
1744 val
= dbm_fetch((DBM
*) map
->map_db1
, key
);
1745 if (val
.dptr
!= NULL
)
1746 map
->map_mflags
&= ~MF_TRY0NULL
;
1748 if (dfd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
1749 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_UN
);
1750 if (val
.dptr
== NULL
)
1752 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
1753 return map_rewrite(map
, name
, strlen(name
), NULL
);
1755 return map_rewrite(map
, val
.dptr
, val
.dsize
, av
);
1760 ** NDBM_MAP_STORE -- store a datum in the database
1764 ndbm_map_store(map
, lhs
, rhs
)
1772 char keybuf
[MAXNAME
+ 1];
1775 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1776 map
->map_mname
, lhs
, rhs
);
1778 key
.dsize
= strlen(lhs
);
1780 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
1782 if (key
.dsize
> sizeof(keybuf
) - 1)
1783 key
.dsize
= sizeof(keybuf
) - 1;
1784 memmove(keybuf
, key
.dptr
, key
.dsize
);
1785 keybuf
[key
.dsize
] = '\0';
1790 data
.dsize
= strlen(rhs
);
1793 if (bitset(MF_INCLNULL
, map
->map_mflags
))
1799 status
= dbm_store((DBM
*) map
->map_db1
, key
, data
, DBM_INSERT
);
1802 if (!bitset(MF_APPEND
, map
->map_mflags
))
1803 message("050 Warning: duplicate alias name %s", lhs
);
1806 static char *buf
= NULL
;
1807 static int bufsiz
= 0;
1811 old
.dptr
= ndbm_map_lookup(map
, key
.dptr
,
1812 (char **) NULL
, &xstat
);
1813 if (old
.dptr
!= NULL
&& *(char *) old
.dptr
!= '\0')
1815 old
.dsize
= strlen(old
.dptr
);
1816 if (data
.dsize
+ old
.dsize
+ 2 > bufsiz
)
1819 (void) sm_free(buf
);
1820 bufsiz
= data
.dsize
+ old
.dsize
+ 2;
1821 buf
= sm_pmalloc_x(bufsiz
);
1823 (void) sm_strlcpyn(buf
, bufsiz
, 3,
1824 data
.dptr
, ",", old
.dptr
);
1825 data
.dsize
= data
.dsize
+ old
.dsize
+ 1;
1828 sm_dprintf("ndbm_map_store append=%s\n",
1832 status
= dbm_store((DBM
*) map
->map_db1
,
1833 key
, data
, DBM_REPLACE
);
1836 syserr("readaliases: dbm put (%s): %d", lhs
, status
);
1841 ** NDBM_MAP_CLOSE -- close the database
1849 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1850 map
->map_mname
, map
->map_file
, map
->map_mflags
);
1852 if (bitset(MF_WRITABLE
, map
->map_mflags
))
1854 # ifdef NDBM_YP_COMPAT
1856 char buf
[MAXHOSTNAMELEN
];
1858 inclnull
= bitset(MF_INCLNULL
, map
->map_mflags
);
1859 map
->map_mflags
&= ~MF_INCLNULL
;
1861 if (strstr(map
->map_file
, "/yp/") != NULL
)
1863 long save_mflags
= map
->map_mflags
;
1865 map
->map_mflags
|= MF_NOFOLDCASE
;
1867 (void) sm_snprintf(buf
, sizeof(buf
), "%010ld", curtime());
1868 ndbm_map_store(map
, "YP_LAST_MODIFIED", buf
);
1870 (void) gethostname(buf
, sizeof(buf
));
1871 ndbm_map_store(map
, "YP_MASTER_NAME", buf
);
1873 map
->map_mflags
= save_mflags
;
1877 map
->map_mflags
|= MF_INCLNULL
;
1878 # endif /* NDBM_YP_COMPAT */
1880 /* write out the distinguished alias */
1881 ndbm_map_store(map
, "@", "@");
1883 dbm_close((DBM
*) map
->map_db1
);
1885 /* release lock (if needed) */
1887 if (map
->map_lockfd
>= 0)
1888 (void) close(map
->map_lockfd
);
1889 # endif /* !LOCK_ON_OPEN */
1894 ** NEWDB (Hash and BTree) Modules
1900 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1902 ** These do rather bizarre locking. If you can lock on open,
1903 ** do that to avoid the condition of opening a database that
1904 ** is being rebuilt. If you don't, we'll try to fake it, but
1905 ** there will be a race condition. If opening for read-only,
1906 ** we immediately release the lock to avoid freezing things up.
1907 ** We really ought to hold the lock, but guarantee that we won't
1908 ** be pokey about it. That's hard to do.
1911 /* these should be K line arguments */
1912 # if DB_VERSION_MAJOR < 2
1913 # define db_cachesize cachesize
1914 # define h_nelem nelem
1915 # ifndef DB_CACHE_SIZE
1916 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1917 # endif /* ! DB_CACHE_SIZE */
1918 # ifndef DB_HASH_NELEM
1919 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1920 # endif /* ! DB_HASH_NELEM */
1921 # endif /* DB_VERSION_MAJOR < 2 */
1924 bt_map_open(map
, mode
)
1928 # if DB_VERSION_MAJOR < 2
1930 # endif /* DB_VERSION_MAJOR < 2 */
1931 # if DB_VERSION_MAJOR == 2
1933 # endif /* DB_VERSION_MAJOR == 2 */
1934 # if DB_VERSION_MAJOR > 2
1935 void *btinfo
= NULL
;
1936 # endif /* DB_VERSION_MAJOR > 2 */
1939 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1940 map
->map_mname
, map
->map_file
, mode
);
1942 # if DB_VERSION_MAJOR < 3
1943 memset(&btinfo
, '\0', sizeof(btinfo
));
1944 # ifdef DB_CACHE_SIZE
1945 btinfo
.db_cachesize
= DB_CACHE_SIZE
;
1946 # endif /* DB_CACHE_SIZE */
1947 # endif /* DB_VERSION_MAJOR < 3 */
1949 return db_map_open(map
, mode
, "btree", DB_BTREE
, &btinfo
);
1953 hash_map_open(map
, mode
)
1957 # if DB_VERSION_MAJOR < 2
1959 # endif /* DB_VERSION_MAJOR < 2 */
1960 # if DB_VERSION_MAJOR == 2
1962 # endif /* DB_VERSION_MAJOR == 2 */
1963 # if DB_VERSION_MAJOR > 2
1965 # endif /* DB_VERSION_MAJOR > 2 */
1968 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1969 map
->map_mname
, map
->map_file
, mode
);
1971 # if DB_VERSION_MAJOR < 3
1972 memset(&hinfo
, '\0', sizeof(hinfo
));
1973 # ifdef DB_HASH_NELEM
1974 hinfo
.h_nelem
= DB_HASH_NELEM
;
1975 # endif /* DB_HASH_NELEM */
1976 # ifdef DB_CACHE_SIZE
1977 hinfo
.db_cachesize
= DB_CACHE_SIZE
;
1978 # endif /* DB_CACHE_SIZE */
1979 # endif /* DB_VERSION_MAJOR < 3 */
1981 return db_map_open(map
, mode
, "hash", DB_HASH
, &hinfo
);
1985 db_map_open(map
, mode
, mapclassname
, dbtype
, openinfo
)
1990 # if DB_VERSION_MAJOR < 2
1991 const void *openinfo
;
1992 # endif /* DB_VERSION_MAJOR < 2 */
1993 # if DB_VERSION_MAJOR == 2
1995 # endif /* DB_VERSION_MAJOR == 2 */
1996 # if DB_VERSION_MAJOR > 2
1998 # endif /* DB_VERSION_MAJOR > 2 */
2003 int smode
= S_IREAD
;
2008 char buf
[MAXPATHLEN
];
2010 /* do initial file and directory checks */
2011 if (sm_strlcpy(buf
, map
->map_file
, sizeof(buf
)) >= sizeof(buf
))
2014 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2015 syserr("map \"%s\": map file %s name too long",
2016 map
->map_mname
, map
->map_file
);
2020 if (i
< 3 || strcmp(&buf
[i
- 3], ".db") != 0)
2022 if (sm_strlcat(buf
, ".db", sizeof(buf
)) >= sizeof(buf
))
2025 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2026 syserr("map \"%s\": map file %s name too long",
2027 map
->map_mname
, map
->map_file
);
2035 sff
= SFF_ROOTOK
|SFF_REGONLY
;
2039 if (!bitnset(DBS_WRITEMAPTOSYMLINK
, DontBlameSendmail
))
2041 if (!bitnset(DBS_WRITEMAPTOHARDLINK
, DontBlameSendmail
))
2047 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
2050 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
2051 sff
|= SFF_SAFEDIRPATH
;
2052 i
= safefile(buf
, RunAsUid
, RunAsGid
, RunAsUserName
, sff
, smode
, &st
);
2056 char *prob
= "unsafe";
2058 /* cannot open this map */
2062 sm_dprintf("\t%s map file: %s\n", prob
, sm_errstring(i
));
2064 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2065 syserr("%s map \"%s\": %s map file %s",
2066 mapclassname
, map
->map_mname
, prob
, buf
);
2069 if (st
.st_mode
== ST_MODE_NOFILE
)
2070 omode
|= O_CREAT
|O_EXCL
;
2072 map
->map_lockfd
= -1;
2076 omode
|= O_TRUNC
|O_EXLOCK
;
2079 # else /* LOCK_ON_OPEN */
2081 ** Pre-lock the file to avoid race conditions. In particular,
2082 ** since dbopen returns NULL if the file is zero length, we
2083 ** must have a locked instance around the dbopen.
2086 fd
= open(buf
, omode
, DBMMODE
);
2089 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2090 syserr("db_map_open: cannot pre-open database %s", buf
);
2094 /* make sure no baddies slipped in just before the open... */
2095 if (filechanged(buf
, fd
, &st
))
2100 syserr("db_map_open(%s): file changed after pre-open", buf
);
2104 /* if new file, get the "before" bits for later filechanged check */
2105 if (st
.st_mode
== ST_MODE_NOFILE
&& fstat(fd
, &st
) < 0)
2110 syserr("db_map_open(%s): cannot fstat pre-opened file",
2115 /* actually lock the pre-opened file */
2116 if (!lockfile(fd
, buf
, NULL
, mode
== O_RDONLY
? LOCK_SH
: LOCK_EX
))
2117 syserr("db_map_open: cannot lock %s", buf
);
2119 /* set up mode bits for dbopen */
2122 omode
&= ~(O_EXCL
|O_CREAT
);
2123 # endif /* LOCK_ON_OPEN */
2125 # if DB_VERSION_MAJOR < 2
2126 db
= dbopen(buf
, omode
, DBMMODE
, dbtype
, openinfo
);
2127 # else /* DB_VERSION_MAJOR < 2 */
2130 # if DB_VERSION_MAJOR > 2
2132 # endif /* DB_VERSION_MAJOR > 2 */
2134 if (mode
== O_RDONLY
)
2136 if (bitset(O_CREAT
, omode
))
2138 if (bitset(O_TRUNC
, omode
))
2139 flags
|= DB_TRUNCATE
;
2140 SM_DB_FLAG_ADD(flags
);
2142 # if DB_VERSION_MAJOR > 2
2143 ret
= db_create(&db
, NULL
, 0);
2144 # ifdef DB_CACHE_SIZE
2145 if (ret
== 0 && db
!= NULL
)
2147 ret
= db
->set_cachesize(db
, 0, DB_CACHE_SIZE
, 0);
2150 (void) db
->close(db
, 0);
2154 # endif /* DB_CACHE_SIZE */
2155 # ifdef DB_HASH_NELEM
2156 if (dbtype
== DB_HASH
&& ret
== 0 && db
!= NULL
)
2158 ret
= db
->set_h_nelem(db
, DB_HASH_NELEM
);
2161 (void) db
->close(db
, 0);
2165 # endif /* DB_HASH_NELEM */
2166 if (ret
== 0 && db
!= NULL
)
2169 DBTXN
/* transaction for DB 4.1 */
2170 buf
, NULL
, dbtype
, flags
, DBMMODE
);
2173 #ifdef DB_OLD_VERSION
2174 if (ret
== DB_OLD_VERSION
)
2176 #endif /* DB_OLD_VERSION */
2177 (void) db
->close(db
, 0);
2182 # else /* DB_VERSION_MAJOR > 2 */
2183 errno
= db_open(buf
, dbtype
, flags
, DBMMODE
,
2184 NULL
, openinfo
, &db
);
2185 # endif /* DB_VERSION_MAJOR > 2 */
2187 # endif /* DB_VERSION_MAJOR < 2 */
2192 map
->map_lockfd
= fd
;
2195 # endif /* !LOCK_ON_OPEN */
2199 if (mode
== O_RDONLY
&& bitset(MF_ALIAS
, map
->map_mflags
) &&
2200 aliaswait(map
, ".db", false))
2203 if (map
->map_lockfd
>= 0)
2204 (void) close(map
->map_lockfd
);
2205 # endif /* !LOCK_ON_OPEN */
2207 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2208 syserr("Cannot open %s database %s",
2213 # if DB_VERSION_MAJOR < 2
2215 # else /* DB_VERSION_MAJOR < 2 */
2217 errno
= db
->fd(db
, &fd
);
2218 # endif /* DB_VERSION_MAJOR < 2 */
2219 if (filechanged(buf
, fd
, &st
))
2222 # if DB_VERSION_MAJOR < 2
2223 (void) db
->close(db
);
2224 # else /* DB_VERSION_MAJOR < 2 */
2225 errno
= db
->close(db
, 0);
2226 # endif /* DB_VERSION_MAJOR < 2 */
2228 if (map
->map_lockfd
>= 0)
2229 (void) close(map
->map_lockfd
);
2230 # endif /* !LOCK_ON_OPEN */
2232 syserr("db_map_open(%s): file changed after open", buf
);
2237 map
->map_mflags
|= MF_LOCKED
;
2239 if (fd
>= 0 && mode
== O_RDONLY
)
2241 (void) lockfile(fd
, buf
, NULL
, LOCK_UN
);
2243 # endif /* LOCK_ON_OPEN */
2245 /* try to make sure that at least the database header is on disk */
2248 (void) db
->sync(db
, 0);
2249 if (geteuid() == 0 && TrustedUid
!= 0)
2252 if (fchown(fd
, TrustedUid
, -1) < 0)
2256 sm_syslog(LOG_ALERT
, NOQID
,
2257 "ownership change on %s failed: %s",
2258 buf
, sm_errstring(err
));
2259 message("050 ownership change on %s failed: %s",
2260 buf
, sm_errstring(err
));
2262 # else /* HASFCHOWN */
2263 sm_syslog(LOG_ALERT
, NOQID
,
2264 "no fchown(): cannot change ownership on %s",
2266 message("050 no fchown(): cannot change ownership on %s",
2268 # endif /* HASFCHOWN */
2272 map
->map_db2
= (ARBPTR_T
) db
;
2275 ** Need to set map_mtime before the call to aliaswait()
2276 ** as aliaswait() will call map_lookup() which requires
2277 ** map_mtime to be set
2280 if (fd
>= 0 && fstat(fd
, &st
) >= 0)
2281 map
->map_mtime
= st
.st_mtime
;
2283 if (mode
== O_RDONLY
&& bitset(MF_ALIAS
, map
->map_mflags
) &&
2284 !aliaswait(map
, ".db", true))
2291 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2295 db_map_lookup(map
, name
, av
, statp
)
2302 register DB
*db
= (DB
*) map
->map_db2
;
2308 char keybuf
[MAXNAME
+ 1];
2309 char buf
[MAXPATHLEN
];
2311 memset(&key
, '\0', sizeof(key
));
2312 memset(&val
, '\0', sizeof(val
));
2315 sm_dprintf("db_map_lookup(%s, %s)\n",
2316 map
->map_mname
, name
);
2318 if (sm_strlcpy(buf
, map
->map_file
, sizeof(buf
)) >= sizeof(buf
))
2321 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2322 syserr("map \"%s\": map file %s name too long",
2323 map
->map_mname
, map
->map_file
);
2327 if (i
> 3 && strcmp(&buf
[i
- 3], ".db") == 0)
2330 key
.size
= strlen(name
);
2331 if (key
.size
> sizeof(keybuf
) - 1)
2332 key
.size
= sizeof(keybuf
) - 1;
2334 memmove(keybuf
, name
, key
.size
);
2335 keybuf
[key
.size
] = '\0';
2336 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
2339 # if DB_VERSION_MAJOR < 2
2341 # else /* DB_VERSION_MAJOR < 2 */
2343 errno
= db
->fd(db
, &fd
);
2344 # endif /* DB_VERSION_MAJOR < 2 */
2345 if (fd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
2346 (void) lockfile(fd
, buf
, ".db", LOCK_SH
);
2347 if (fd
< 0 || fstat(fd
, &stbuf
) < 0 || stbuf
.st_mtime
> map
->map_mtime
)
2349 /* Reopen the database to sync the cache */
2350 int omode
= bitset(map
->map_mflags
, MF_WRITABLE
) ? O_RDWR
2353 if (fd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
2354 (void) lockfile(fd
, buf
, ".db", LOCK_UN
);
2355 map
->map_mflags
|= MF_CLOSING
;
2356 map
->map_class
->map_close(map
);
2357 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
2358 if (map
->map_class
->map_open(map
, omode
))
2360 map
->map_mflags
|= MF_OPEN
;
2361 map
->map_pid
= CurrentPid
;
2362 if ((omode
&& O_ACCMODE
) == O_RDWR
)
2363 map
->map_mflags
|= MF_WRITABLE
;
2364 db
= (DB
*) map
->map_db2
;
2369 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2371 extern MAPCLASS BogusMapClass
;
2373 *statp
= EX_TEMPFAIL
;
2374 map
->map_orgclass
= map
->map_class
;
2375 map
->map_class
= &BogusMapClass
;
2376 map
->map_mflags
|= MF_OPEN
;
2377 map
->map_pid
= CurrentPid
;
2378 syserr("Cannot reopen DB database %s",
2386 if (bitset(MF_TRY0NULL
, map
->map_mflags
))
2388 # if DB_VERSION_MAJOR < 2
2389 st
= db
->get(db
, &key
, &val
, 0);
2390 # else /* DB_VERSION_MAJOR < 2 */
2391 errno
= db
->get(db
, NULL
, &key
, &val
, 0);
2407 # endif /* DB_VERSION_MAJOR < 2 */
2409 map
->map_mflags
&= ~MF_TRY1NULL
;
2411 if (st
!= 0 && bitset(MF_TRY1NULL
, map
->map_mflags
))
2414 # if DB_VERSION_MAJOR < 2
2415 st
= db
->get(db
, &key
, &val
, 0);
2416 # else /* DB_VERSION_MAJOR < 2 */
2417 errno
= db
->get(db
, NULL
, &key
, &val
, 0);
2433 # endif /* DB_VERSION_MAJOR < 2 */
2435 map
->map_mflags
&= ~MF_TRY0NULL
;
2438 if (fd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
2439 (void) lockfile(fd
, buf
, ".db", LOCK_UN
);
2444 syserr("db_map_lookup: get (%s)", name
);
2447 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
2448 return map_rewrite(map
, name
, strlen(name
), NULL
);
2450 return map_rewrite(map
, val
.data
, val
.size
, av
);
2455 ** DB_MAP_STORE -- store a datum in the NEWDB database
2459 db_map_store(map
, lhs
, rhs
)
2467 register DB
*db
= map
->map_db2
;
2468 char keybuf
[MAXNAME
+ 1];
2470 memset(&key
, '\0', sizeof(key
));
2471 memset(&data
, '\0', sizeof(data
));
2474 sm_dprintf("db_map_store(%s, %s, %s)\n",
2475 map
->map_mname
, lhs
, rhs
);
2477 key
.size
= strlen(lhs
);
2479 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
2481 if (key
.size
> sizeof(keybuf
) - 1)
2482 key
.size
= sizeof(keybuf
) - 1;
2483 memmove(keybuf
, key
.data
, key
.size
);
2484 keybuf
[key
.size
] = '\0';
2489 data
.size
= strlen(rhs
);
2492 if (bitset(MF_INCLNULL
, map
->map_mflags
))
2498 # if DB_VERSION_MAJOR < 2
2499 status
= db
->put(db
, &key
, &data
, R_NOOVERWRITE
);
2500 # else /* DB_VERSION_MAJOR < 2 */
2501 errno
= db
->put(db
, NULL
, &key
, &data
, DB_NOOVERWRITE
);
2516 # endif /* DB_VERSION_MAJOR < 2 */
2519 if (!bitset(MF_APPEND
, map
->map_mflags
))
2520 message("050 Warning: duplicate alias name %s", lhs
);
2523 static char *buf
= NULL
;
2524 static int bufsiz
= 0;
2527 memset(&old
, '\0', sizeof(old
));
2529 old
.data
= db_map_lookup(map
, key
.data
,
2530 (char **) NULL
, &status
);
2531 if (old
.data
!= NULL
)
2533 old
.size
= strlen(old
.data
);
2534 if (data
.size
+ old
.size
+ 2 > (size_t) bufsiz
)
2538 bufsiz
= data
.size
+ old
.size
+ 2;
2539 buf
= sm_pmalloc_x(bufsiz
);
2541 (void) sm_strlcpyn(buf
, bufsiz
, 3,
2542 (char *) data
.data
, ",",
2544 data
.size
= data
.size
+ old
.size
+ 1;
2547 sm_dprintf("db_map_store append=%s\n",
2548 (char *) data
.data
);
2551 # if DB_VERSION_MAJOR < 2
2552 status
= db
->put(db
, &key
, &data
, 0);
2553 # else /* DB_VERSION_MAJOR < 2 */
2554 status
= errno
= db
->put(db
, NULL
, &key
, &data
, 0);
2555 # endif /* DB_VERSION_MAJOR < 2 */
2558 syserr("readaliases: db put (%s)", lhs
);
2563 ** DB_MAP_CLOSE -- add distinguished entries and close the database
2570 register DB
*db
= map
->map_db2
;
2573 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2574 map
->map_mname
, map
->map_file
, map
->map_mflags
);
2576 if (bitset(MF_WRITABLE
, map
->map_mflags
))
2578 /* write out the distinguished alias */
2579 db_map_store(map
, "@", "@");
2582 (void) db
->sync(db
, 0);
2585 if (map
->map_lockfd
>= 0)
2586 (void) close(map
->map_lockfd
);
2587 # endif /* !LOCK_ON_OPEN */
2589 # if DB_VERSION_MAJOR < 2
2590 if (db
->close(db
) != 0)
2591 # else /* DB_VERSION_MAJOR < 2 */
2593 ** Berkeley DB can use internal shared memory
2594 ** locking for its memory pool. Closing a map
2595 ** opened by another process will interfere
2596 ** with the shared memory and locks of the parent
2597 ** process leaving things in a bad state.
2601 ** If this map was not opened by the current
2602 ** process, do not close the map but recover
2603 ** the file descriptor.
2606 if (map
->map_pid
!= CurrentPid
)
2610 errno
= db
->fd(db
, &fd
);
2616 if ((errno
= db
->close(db
, 0)) != 0)
2617 # endif /* DB_VERSION_MAJOR < 2 */
2618 syserr("db_map_close(%s, %s, %lx): db close failure",
2619 map
->map_mname
, map
->map_file
, map
->map_mflags
);
2629 # define YPERR_BUSY 16
2630 # endif /* ! YPERR_BUSY */
2633 ** NIS_MAP_OPEN -- open DBM map
2637 nis_map_open(map
, mode
)
2647 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2648 map
->map_mname
, map
->map_file
, mode
);
2651 if (mode
!= O_RDONLY
)
2653 /* issue a pseudo-error message */
2654 errno
= SM_EMAPCANTWRITE
;
2658 p
= strchr(map
->map_file
, '@');
2663 map
->map_domain
= p
;
2666 if (*map
->map_file
== '\0')
2667 map
->map_file
= "mail.aliases";
2669 if (map
->map_domain
== NULL
)
2671 yperr
= yp_get_default_domain(&map
->map_domain
);
2674 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2675 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2681 /* check to see if this map actually exists */
2683 yperr
= yp_match(map
->map_domain
, map
->map_file
, "@", 1,
2686 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2687 map
->map_domain
, map
->map_file
, yperr_string(yperr
));
2691 if (yperr
== 0 || yperr
== YPERR_KEY
|| yperr
== YPERR_BUSY
)
2694 ** We ought to be calling aliaswait() here if this is an
2695 ** alias file, but powerful HP-UX NIS servers apparently
2696 ** don't insert the @:@ token into the alias map when it
2697 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2701 if (!bitset(MF_ALIAS
, map
->map_mflags
) ||
2702 aliaswait(map
, NULL
, true))
2707 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2709 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2710 map
->map_file
, map
->map_domain
, yperr_string(yperr
));
2718 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2723 nis_map_lookup(map
, name
, av
, statp
)
2733 char keybuf
[MAXNAME
+ 1];
2734 char *SM_NONVOLATILE result
= NULL
;
2737 sm_dprintf("nis_map_lookup(%s, %s)\n",
2738 map
->map_mname
, name
);
2740 buflen
= strlen(name
);
2741 if (buflen
> sizeof(keybuf
) - 1)
2742 buflen
= sizeof(keybuf
) - 1;
2743 memmove(keybuf
, name
, buflen
);
2744 keybuf
[buflen
] = '\0';
2745 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
2749 if (bitset(MF_TRY0NULL
, map
->map_mflags
))
2751 yperr
= yp_match(map
->map_domain
, map
->map_file
, keybuf
, buflen
,
2754 map
->map_mflags
&= ~MF_TRY1NULL
;
2756 if (yperr
== YPERR_KEY
&& bitset(MF_TRY1NULL
, map
->map_mflags
))
2760 yperr
= yp_match(map
->map_domain
, map
->map_file
, keybuf
, buflen
,
2763 map
->map_mflags
&= ~MF_TRY0NULL
;
2767 if (yperr
!= YPERR_KEY
&& yperr
!= YPERR_BUSY
)
2768 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
2774 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
2775 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
2777 result
= map_rewrite(map
, vp
, vsize
, av
);
2787 ** NIS_GETCANONNAME -- look up canonical name in NIS
2791 nis_getcanonname(name
, hbsize
, statp
)
2800 static bool try0null
= true;
2801 static bool try1null
= true;
2802 static char *yp_domain
= NULL
;
2803 char host_record
[MAXLINE
];
2805 char nbuf
[MAXNAME
+ 1];
2808 sm_dprintf("nis_getcanonname(%s)\n", name
);
2810 if (sm_strlcpy(nbuf
, name
, sizeof(nbuf
)) >= sizeof(nbuf
))
2812 *statp
= EX_UNAVAILABLE
;
2815 (void) shorten_hostname(nbuf
);
2816 keylen
= strlen(nbuf
);
2818 if (yp_domain
== NULL
)
2819 (void) yp_get_default_domain(&yp_domain
);
2825 yperr
= yp_match(yp_domain
, "hosts.byname", nbuf
, keylen
,
2830 if (yperr
== YPERR_KEY
&& try1null
)
2834 yperr
= yp_match(yp_domain
, "hosts.byname", nbuf
, keylen
,
2841 if (yperr
== YPERR_KEY
)
2843 else if (yperr
== YPERR_BUSY
)
2844 *statp
= EX_TEMPFAIL
;
2846 *statp
= EX_UNAVAILABLE
;
2851 (void) sm_strlcpy(host_record
, vp
, sizeof(host_record
));
2854 sm_dprintf("got record `%s'\n", host_record
);
2855 vp
= strpbrk(host_record
, "#\n");
2858 if (!extract_canonname(nbuf
, NULL
, host_record
, cbuf
, sizeof(cbuf
)))
2860 /* this should not happen, but.... */
2864 if (sm_strlcpy(name
, cbuf
, hbsize
) >= hbsize
)
2866 *statp
= EX_UNAVAILABLE
;
2877 ** This code donated by Sun Microsystems.
2882 # undef NIS /* symbol conflict in nis.h */
2883 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2884 # include <rpcsvc/nis.h>
2885 # include <rpcsvc/nislib.h>
2887 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2888 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2889 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2890 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2893 ** NISPLUS_MAP_OPEN -- open nisplus table
2897 nisplus_map_open(map
, mode
)
2901 nis_result
*res
= NULL
;
2902 int retry_cnt
, max_col
, i
;
2903 char qbuf
[MAXLINE
+ NIS_MAXNAMELEN
];
2906 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2907 map
->map_mname
, map
->map_file
, mode
);
2910 if (mode
!= O_RDONLY
)
2916 if (*map
->map_file
== '\0')
2917 map
->map_file
= "mail_aliases.org_dir";
2919 if (PARTIAL_NAME(map
->map_file
) && map
->map_domain
== NULL
)
2921 /* set default NISPLUS Domain to $m */
2922 map
->map_domain
= newstr(nisplus_default_domain());
2924 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2925 map
->map_file
, map
->map_domain
);
2927 if (!PARTIAL_NAME(map
->map_file
))
2929 map
->map_domain
= newstr("");
2930 (void) sm_strlcpy(qbuf
, map
->map_file
, sizeof(qbuf
));
2934 /* check to see if this map actually exists */
2935 (void) sm_strlcpyn(qbuf
, sizeof(qbuf
), 3,
2936 map
->map_file
, ".", map
->map_domain
);
2940 while (res
== NULL
|| res
->status
!= NIS_SUCCESS
)
2942 res
= nis_lookup(qbuf
, FOLLOW_LINKS
);
2943 switch (res
->status
)
2950 case NIS_NAMEUNREACHABLE
:
2951 if (retry_cnt
++ > 4)
2956 /* try not to overwhelm hosed server */
2960 default: /* all other nisplus errors */
2962 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2963 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2964 map
->map_file
, map
->map_domain
,
2965 nis_sperrno(res
->status
));
2972 if (NIS_RES_NUMOBJ(res
) != 1 ||
2973 (NIS_RES_OBJECT(res
)->zo_data
.zo_type
!= TABLE_OBJ
))
2976 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf
);
2978 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2979 syserr("451 4.3.5 %s.%s: %s is not a table",
2980 map
->map_file
, map
->map_domain
,
2981 nis_sperrno(res
->status
));
2986 /* default key column is column 0 */
2987 if (map
->map_keycolnm
== NULL
)
2988 map
->map_keycolnm
= newstr(COL_NAME(res
,0));
2990 max_col
= COL_MAX(res
);
2992 /* verify the key column exist */
2993 for (i
= 0; i
< max_col
; i
++)
2995 if (strcmp(map
->map_keycolnm
, COL_NAME(res
,i
)) == 0)
3001 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
3002 map
->map_file
, map
->map_keycolnm
);
3007 /* default value column is the last column */
3008 if (map
->map_valcolnm
== NULL
)
3010 map
->map_valcolno
= max_col
- 1;
3014 for (i
= 0; i
< max_col
; i
++)
3016 if (strcmp(map
->map_valcolnm
, COL_NAME(res
,i
)) == 0)
3018 map
->map_valcolno
= i
;
3024 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
3025 map
->map_file
, map
->map_keycolnm
);
3032 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
3036 nisplus_map_lookup(map
, name
, av
, statp
)
3046 char search_key
[MAXNAME
+ 4];
3047 char qbuf
[MAXLINE
+ NIS_MAXNAMELEN
];
3051 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3052 map
->map_mname
, name
);
3054 if (!bitset(MF_OPEN
, map
->map_mflags
))
3056 if (nisplus_map_open(map
, O_RDONLY
))
3058 map
->map_mflags
|= MF_OPEN
;
3059 map
->map_pid
= CurrentPid
;
3063 *statp
= EX_UNAVAILABLE
;
3069 ** Copy the name to the key buffer, escaping double quote characters
3070 ** by doubling them and quoting "]" and "," to avoid having the
3071 ** NIS+ parser choke on them.
3074 skleft
= sizeof(search_key
) - 4;
3076 for (p
= name
; *p
!= '\0' && skleft
> 0; p
++)
3082 /* quote the character */
3090 /* double the quote */
3102 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
3103 makelower(search_key
);
3105 /* construct the query */
3106 if (PARTIAL_NAME(map
->map_file
))
3107 (void) sm_snprintf(qbuf
, sizeof(qbuf
), "[%s=%s],%s.%s",
3108 map
->map_keycolnm
, search_key
, map
->map_file
,
3111 (void) sm_snprintf(qbuf
, sizeof(qbuf
), "[%s=%s],%s",
3112 map
->map_keycolnm
, search_key
, map
->map_file
);
3115 sm_dprintf("qbuf=%s\n", qbuf
);
3116 result
= nis_list(qbuf
, FOLLOW_LINKS
| FOLLOW_PATH
, NULL
, NULL
);
3117 if (result
->status
== NIS_SUCCESS
)
3122 if ((count
= NIS_RES_NUMOBJ(result
)) != 1)
3125 sm_syslog(LOG_WARNING
, CurEnv
->e_id
,
3126 "%s: lookup error, expected 1 entry, got %d",
3127 map
->map_file
, count
);
3129 /* ignore second entry */
3131 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3135 p
= ((NIS_RES_OBJECT(result
))->EN_col(map
->map_valcolno
));
3136 /* set the length of the result */
3141 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3143 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3144 str
= map_rewrite(map
, name
, strlen(name
), NULL
);
3146 str
= map_rewrite(map
, p
, vsize
, av
);
3147 nis_freeresult(result
);
3153 if (result
->status
== NIS_NOTFOUND
)
3154 *statp
= EX_NOTFOUND
;
3155 else if (result
->status
== NIS_TRYAGAIN
)
3156 *statp
= EX_TEMPFAIL
;
3159 *statp
= EX_UNAVAILABLE
;
3160 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
3164 sm_dprintf("nisplus_map_lookup(%s), failed\n", name
);
3165 nis_freeresult(result
);
3172 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3176 nisplus_getcanonname(name
, hbsize
, statp
)
3185 char nbuf
[MAXNAME
+ 1];
3186 char qbuf
[MAXLINE
+ NIS_MAXNAMELEN
];
3188 if (sm_strlcpy(nbuf
, name
, sizeof(nbuf
)) >= sizeof(nbuf
))
3190 *statp
= EX_UNAVAILABLE
;
3193 (void) shorten_hostname(nbuf
);
3195 p
= strchr(nbuf
, '.');
3199 (void) sm_snprintf(qbuf
, sizeof(qbuf
),
3200 "[name=%s],hosts.org_dir", nbuf
);
3202 else if (p
[1] != '\0')
3204 /* multi token -- take only first token in nbuf */
3206 (void) sm_snprintf(qbuf
, sizeof(qbuf
),
3207 "[name=%s],hosts.org_dir.%s", nbuf
, &p
[1]);
3216 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3219 result
= nis_list(qbuf
, EXPAND_NAME
|FOLLOW_LINKS
|FOLLOW_PATH
,
3222 if (result
->status
== NIS_SUCCESS
)
3227 if ((count
= NIS_RES_NUMOBJ(result
)) != 1)
3230 sm_syslog(LOG_WARNING
, CurEnv
->e_id
,
3231 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3234 /* ignore second entry */
3236 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3241 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3242 name
, (NIS_RES_OBJECT(result
))->zo_domain
);
3245 vp
= ((NIS_RES_OBJECT(result
))->EN_col(0));
3248 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3250 if (strchr(vp
, '.') != NULL
)
3256 domain
= macvalue('m', CurEnv
);
3260 if (hbsize
> vsize
+ (int) strlen(domain
) + 1)
3262 if (domain
[0] == '\0')
3263 (void) sm_strlcpy(name
, vp
, hbsize
);
3265 (void) sm_snprintf(name
, hbsize
,
3266 "%s.%s", vp
, domain
);
3271 nis_freeresult(result
);
3276 if (result
->status
== NIS_NOTFOUND
)
3278 else if (result
->status
== NIS_TRYAGAIN
)
3279 *statp
= EX_TEMPFAIL
;
3281 *statp
= EX_UNAVAILABLE
;
3284 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3285 name
, result
->status
, *statp
);
3286 nis_freeresult(result
);
3291 nisplus_default_domain()
3293 static char default_domain
[MAXNAME
+ 1] = "";
3296 if (default_domain
[0] != '\0')
3297 return default_domain
;
3299 p
= nis_local_directory();
3300 (void) sm_strlcpy(default_domain
, p
, sizeof(default_domain
));
3301 return default_domain
;
3304 #endif /* NISPLUS */
3310 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3313 #if defined(LDAPMAP) || defined(PH_MAP)
3316 # define ph_map_dequote ldapmap_dequote
3317 # endif /* PH_MAP */
3319 static char *ldapmap_dequote
__P((char *));
3322 ldapmap_dequote(str
)
3334 /* Should probably swallow initial whitespace here */
3339 while (*p
!= '"' && *p
!= '\0')
3345 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3349 static SM_LDAP_STRUCT
*LDAPDefaults
= NULL
;
3352 ** LDAPMAP_OPEN -- open LDAP map
3354 ** Connect to the LDAP server. Re-use existing connections since a
3355 ** single server connection to a host (with the same host, port,
3356 ** bind DN, and secret) can answer queries for multiple maps.
3360 ldapmap_open(map
, mode
)
3364 SM_LDAP_STRUCT
*lmap
;
3369 sm_dprintf("ldapmap_open(%s, %d): ", map
->map_mname
, mode
);
3371 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3372 HASLDAPGETALIASBYNAME
3373 if (VendorCode
== VENDOR_SUN
&&
3374 strcmp(map
->map_mname
, "aliases.ldap") == 0)
3378 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3382 /* sendmail doesn't have the ability to write to LDAP (yet) */
3383 if (mode
!= O_RDONLY
)
3385 /* issue a pseudo-error message */
3386 errno
= SM_EMAPCANTWRITE
;
3390 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3392 s
= ldapmap_findconn(lmap
);
3393 if (s
->s_lmap
!= NULL
)
3395 /* Already have a connection open to this LDAP server */
3396 lmap
->ldap_ld
= ((SM_LDAP_STRUCT
*)s
->s_lmap
->map_db1
)->ldap_ld
;
3397 lmap
->ldap_pid
= ((SM_LDAP_STRUCT
*)s
->s_lmap
->map_db1
)->ldap_pid
;
3399 /* Add this map as head of linked list */
3400 lmap
->ldap_next
= s
->s_lmap
;
3404 sm_dprintf("using cached connection\n");
3409 sm_dprintf("opening new connection\n");
3411 if (lmap
->ldap_host
!= NULL
)
3412 id
= lmap
->ldap_host
;
3413 else if (lmap
->ldap_uri
!= NULL
)
3414 id
= lmap
->ldap_uri
;
3418 /* No connection yet, connect */
3419 if (!sm_ldap_start(map
->map_mname
, lmap
))
3421 if (errno
== ETIMEDOUT
)
3424 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
3425 "timeout conning to LDAP server %.100s",
3429 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3431 if (bitset(MF_NODEFER
, map
->map_mflags
))
3433 syserr("%s failed to %s in map %s",
3435 "ldap_init/ldap_bind",
3436 # else /* USE_LDAP_INIT */
3438 # endif /* USE_LDAP_INIT */
3439 id
, map
->map_mname
);
3443 syserr("451 4.3.5 %s failed to %s in map %s",
3445 "ldap_init/ldap_bind",
3446 # else /* USE_LDAP_INIT */
3448 # endif /* USE_LDAP_INIT */
3449 id
, map
->map_mname
);
3455 /* Save connection for reuse */
3461 ** LDAPMAP_CLOSE -- close ldap map
3468 SM_LDAP_STRUCT
*lmap
;
3472 sm_dprintf("ldapmap_close(%s)\n", map
->map_mname
);
3474 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3476 /* Check if already closed */
3477 if (lmap
->ldap_ld
== NULL
)
3480 /* Close the LDAP connection */
3481 sm_ldap_close(lmap
);
3483 /* Mark all the maps that share the connection as closed */
3484 s
= ldapmap_findconn(lmap
);
3486 while (s
->s_lmap
!= NULL
)
3488 MAP
*smap
= s
->s_lmap
;
3490 if (tTd(38, 2) && smap
!= map
)
3491 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3492 map
->map_mname
, smap
->map_mname
);
3493 smap
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
);
3494 lmap
= (SM_LDAP_STRUCT
*) smap
->map_db1
;
3495 lmap
->ldap_ld
= NULL
;
3496 s
->s_lmap
= lmap
->ldap_next
;
3497 lmap
->ldap_next
= NULL
;
3503 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3504 ** This only makes sense at Stanford University.
3517 if (islower(*p
) || isdigit(*p
))
3522 else if (isupper(*p
))
3524 *p_last
= tolower(*p
);
3529 if (*p_last
!= '\0')
3533 # define SM_CONVERT_ID(str) sunet_id_hash(str)
3534 # else /* SUNET_ID */
3535 # define SM_CONVERT_ID(str) makelower(str)
3536 # endif /* SUNET_ID */
3539 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3543 ldapmap_lookup(map
, name
, av
, statp
)
3556 char *result
= NULL
;
3558 SM_LDAP_STRUCT
*lmap
= NULL
;
3559 char *argv
[SM_LDAP_ARGS
];
3560 char keybuf
[MAXKEY
];
3561 #if SM_LDAP_ARGS != MAX_MAP_ARGS
3562 # ERROR _SM_LDAP_ARGS must be the same as _MAX_MAP_ARGS
3563 #endif /* SM_LDAP_ARGS != MAX_MAP_ARGS */
3565 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \
3566 HASLDAPGETALIASBYNAME
3567 if (VendorCode
== VENDOR_SUN
&&
3568 strcmp(map
->map_mname
, "aliases.ldap") == 0)
3571 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3572 extern char *__getldapaliasbyname();
3575 answer
= __getldapaliasbyname(name
, &rc
);
3577 char answer
[MAXNAME
+ 1];
3579 rc
= __getldapaliasbyname(name
, answer
, sizeof(answer
));
3584 sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n",
3586 *statp
= EX_NOTFOUND
;
3591 sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name
,
3593 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3594 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
3596 result
= map_rewrite(map
, answer
, strlen(answer
), av
);
3597 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2)
3602 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */
3604 /* Get ldap struct pointer from map */
3605 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3606 sm_ldap_setopts(lmap
->ldap_ld
, lmap
);
3608 if (lmap
->ldap_multi_args
)
3610 SM_REQUIRE(av
!= NULL
);
3611 memset(argv
, '\0', sizeof(argv
));
3612 for (i
= 0; i
< SM_LDAP_ARGS
&& av
[i
] != NULL
; i
++)
3614 argv
[i
] = sm_strdup(av
[i
]);
3615 if (argv
[i
] == NULL
)
3620 for (j
= 0; j
< i
&& argv
[j
] != NULL
; j
++)
3622 *statp
= EX_TEMPFAIL
;
3627 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
3628 SM_CONVERT_ID(av
[i
]);
3633 (void) sm_strlcpy(keybuf
, name
, sizeof(keybuf
));
3635 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
3636 SM_CONVERT_ID(keybuf
);
3641 if (lmap
->ldap_multi_args
)
3643 sm_dprintf("ldapmap_lookup(%s, argv)\n",
3645 for (i
= 0; i
< SM_LDAP_ARGS
; i
++)
3647 sm_dprintf(" argv[%d] = %s\n", i
,
3648 argv
[i
] == NULL
? "NULL" : argv
[i
]);
3653 sm_dprintf("ldapmap_lookup(%s, %s)\n",
3654 map
->map_mname
, name
);
3658 if (lmap
->ldap_multi_args
)
3660 msgid
= sm_ldap_search_m(lmap
, argv
);
3662 /* free the argv array and its content, no longer needed */
3663 for (i
= 0; i
< SM_LDAP_ARGS
&& argv
[i
] != NULL
; i
++)
3667 msgid
= sm_ldap_search(lmap
, keybuf
);
3668 if (msgid
== SM_LDAP_ERR
)
3670 errno
= sm_ldap_geterrno(lmap
->ldap_ld
) + E_LDAPBASE
;
3672 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3675 ** Do not include keybuf as this error may be shown
3679 if (bitset(MF_NODEFER
, map
->map_mflags
))
3680 syserr("Error in ldap_search in map %s",
3683 syserr("451 4.3.5 Error in ldap_search in map %s",
3686 *statp
= EX_TEMPFAIL
;
3687 switch (save_errno
- E_LDAPBASE
)
3689 # ifdef LDAP_SERVER_DOWN
3690 case LDAP_SERVER_DOWN
:
3691 # endif /* LDAP_SERVER_DOWN */
3693 case LDAP_UNAVAILABLE
:
3694 /* server disappeared, try reopen on next search */
3701 #if SM_LDAP_ERROR_ON_MISSING_ARGS
3702 else if (msgid
== SM_LDAP_ERR_ARG_MISS
)
3704 if (bitset(MF_NODEFER
, map
->map_mflags
))
3705 syserr("Error in ldap_search in map %s, too few arguments",
3708 syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments",
3713 #endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */
3715 *statp
= EX_NOTFOUND
;
3719 if (bitset(MF_SINGLEMATCH
, map
->map_mflags
))
3720 flags
|= SM_LDAP_SINGLEMATCH
;
3721 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3722 flags
|= SM_LDAP_MATCHONLY
;
3723 # if _FFR_LDAP_SINGLEDN
3724 if (bitset(MF_SINGLEDN
, map
->map_mflags
))
3725 flags
|= SM_LDAP_SINGLEDN
;
3726 # endif /* _FFR_LDAP_SINGLEDN */
3728 /* Create an rpool for search related memory usage */
3729 rpool
= sm_rpool_new_x(NULL
);
3732 *statp
= sm_ldap_results(lmap
, msgid
, flags
, map
->map_coldelim
,
3733 rpool
, &p
, &plen
, &psize
, NULL
);
3736 /* Copy result so rpool can be freed */
3737 if (*statp
== EX_OK
&& p
!= NULL
)
3739 sm_rpool_free(rpool
);
3741 /* need to restart LDAP connection? */
3742 if (*statp
== EX_RESTART
)
3744 *statp
= EX_TEMPFAIL
;
3749 if (*statp
!= EX_OK
&& *statp
!= EX_NOTFOUND
)
3751 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3753 if (bitset(MF_NODEFER
, map
->map_mflags
))
3754 syserr("Error getting LDAP results in map %s",
3757 syserr("451 4.3.5 Error getting LDAP results in map %s",
3764 /* Did we match anything? */
3765 if (vp
== NULL
&& !bitset(MF_MATCHONLY
, map
->map_mflags
))
3768 if (*statp
== EX_OK
)
3771 sm_syslog(LOG_INFO
, CurEnv
->e_id
,
3772 "ldap %.100s => %s", name
,
3773 vp
== NULL
? "<NULL>" : vp
);
3774 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3775 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
3778 /* vp != NULL according to test above */
3779 result
= map_rewrite(map
, vp
, strlen(vp
), av
);
3782 sm_free(vp
); /* XXX */
3788 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3790 ** Cache LDAP connections based on the host, port, bind DN,
3791 ** secret, and PID so we don't have multiple connections open to
3792 ** the same server for different maps. Need a separate connection
3793 ** per PID since a parent process may close the map before the
3794 ** child is done with it.
3797 ** lmap -- LDAP map information
3800 ** Symbol table entry for the LDAP connection.
3804 ldapmap_findconn(lmap
)
3805 SM_LDAP_STRUCT
*lmap
;
3810 STAB
*SM_NONVOLATILE s
= NULL
;
3812 if (lmap
->ldap_host
!= NULL
)
3813 id
= lmap
->ldap_host
;
3814 else if (lmap
->ldap_uri
!= NULL
)
3815 id
= lmap
->ldap_uri
;
3819 format
= "%s%c%d%c%d%c%s%c%s%d";
3820 nbuf
= sm_stringf_x(format
,
3827 (lmap
->ldap_binddn
== NULL
? ""
3828 : lmap
->ldap_binddn
),
3830 (lmap
->ldap_secret
== NULL
? ""
3831 : lmap
->ldap_secret
),
3834 s
= stab(nbuf
, ST_LMAP
, ST_ENTER
);
3841 ** LDAPMAP_PARSEARGS -- parse ldap map definition args.
3844 static struct lamvalues LDAPAuthMethods
[] =
3846 { "none", LDAP_AUTH_NONE
},
3847 { "simple", LDAP_AUTH_SIMPLE
},
3848 # ifdef LDAP_AUTH_KRBV4
3849 { "krbv4", LDAP_AUTH_KRBV4
},
3850 # endif /* LDAP_AUTH_KRBV4 */
3854 static struct ladvalues LDAPAliasDereference
[] =
3856 { "never", LDAP_DEREF_NEVER
},
3857 { "always", LDAP_DEREF_ALWAYS
},
3858 { "search", LDAP_DEREF_SEARCHING
},
3859 { "find", LDAP_DEREF_FINDING
},
3863 static struct lssvalues LDAPSearchScope
[] =
3865 { "base", LDAP_SCOPE_BASE
},
3866 { "one", LDAP_SCOPE_ONELEVEL
},
3867 { "sub", LDAP_SCOPE_SUBTREE
},
3872 ldapmap_parseargs(map
, args
)
3876 bool secretread
= true;
3877 bool attrssetup
= false;
3879 register char *p
= args
;
3880 SM_LDAP_STRUCT
*lmap
;
3881 struct lamvalues
*lam
;
3882 struct ladvalues
*lad
;
3883 struct lssvalues
*lss
;
3884 char ldapfilt
[MAXLINE
];
3885 char m_tmp
[MAXPATHLEN
+ LDAPMAP_MAX_PASSWD
];
3887 /* Get ldap struct pointer from map */
3888 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3890 /* Check if setting the initial LDAP defaults */
3891 if (lmap
== NULL
|| lmap
!= LDAPDefaults
)
3893 /* We need to alloc an SM_LDAP_STRUCT struct */
3894 lmap
= (SM_LDAP_STRUCT
*) xalloc(sizeof(*lmap
));
3895 if (LDAPDefaults
== NULL
)
3896 sm_ldap_clear(lmap
);
3898 STRUCTCOPY(*LDAPDefaults
, *lmap
);
3901 /* there is no check whether there is really an argument */
3902 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
3903 map
->map_spacesub
= SpaceSub
; /* default value */
3905 /* Check if setting up an alias or file class LDAP map */
3906 if (bitset(MF_ALIAS
, map
->map_mflags
))
3908 /* Comma separate if used as an alias file */
3909 map
->map_coldelim
= ',';
3914 char jbuf
[MAXHOSTNAMELEN
];
3915 char lcbuf
[MAXLINE
];
3918 expand("\201j", jbuf
, sizeof(jbuf
), &BlankEnvelope
);
3919 if (jbuf
[0] == '\0')
3921 (void) sm_strlcpy(jbuf
, "localhost",
3925 lc
= macvalue(macid("{sendmailMTACluster}"), CurEnv
);
3930 expand(lc
, lcbuf
, sizeof(lcbuf
), CurEnv
);
3934 n
= sm_snprintf(ldapfilt
, sizeof(ldapfilt
),
3935 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3937 if (n
>= sizeof(ldapfilt
))
3939 syserr("%s: Default LDAP string too long",
3944 /* default args for an alias LDAP entry */
3945 lmap
->ldap_filter
= ldapfilt
;
3946 lmap
->ldap_attr
[0] = "objectClass";
3947 lmap
->ldap_attr_type
[0] = SM_LDAP_ATTR_OBJCLASS
;
3948 lmap
->ldap_attr_needobjclass
[0] = NULL
;
3949 lmap
->ldap_attr
[1] = "sendmailMTAAliasValue";
3950 lmap
->ldap_attr_type
[1] = SM_LDAP_ATTR_NORMAL
;
3951 lmap
->ldap_attr_needobjclass
[1] = NULL
;
3952 lmap
->ldap_attr
[2] = "sendmailMTAAliasSearch";
3953 lmap
->ldap_attr_type
[2] = SM_LDAP_ATTR_FILTER
;
3954 lmap
->ldap_attr_needobjclass
[2] = "sendmailMTAMapObject";
3955 lmap
->ldap_attr
[3] = "sendmailMTAAliasURL";
3956 lmap
->ldap_attr_type
[3] = SM_LDAP_ATTR_URL
;
3957 lmap
->ldap_attr_needobjclass
[3] = "sendmailMTAMapObject";
3958 lmap
->ldap_attr
[4] = NULL
;
3959 lmap
->ldap_attr_type
[4] = SM_LDAP_ATTR_NONE
;
3960 lmap
->ldap_attr_needobjclass
[4] = NULL
;
3964 else if (bitset(MF_FILECLASS
, map
->map_mflags
))
3966 /* Space separate if used as a file class file */
3967 map
->map_coldelim
= ' ';
3972 while (isascii(*p
) && isspace(*p
))
3979 map
->map_mflags
|= MF_APPEND
;
3987 map
->map_mflags
|= MF_DEFER
;
3991 map
->map_mflags
|= MF_NOFOLDCASE
;
3995 map
->map_mflags
|= MF_MATCHONLY
;
3999 map
->map_mflags
|= MF_INCLNULL
;
4000 map
->map_mflags
&= ~MF_TRY0NULL
;
4004 map
->map_mflags
&= ~MF_TRY1NULL
;
4008 map
->map_mflags
|= MF_OPTIONAL
;
4012 map
->map_mflags
|= MF_KEEPQUOTES
;
4016 map
->map_spacesub
= *++p
;
4020 map
->map_tapp
= ++p
;
4024 map
->map_mflags
|= MF_NODEFER
;
4029 map
->map_coldelim
= *p
;
4035 map
->map_coldelim
= '\n';
4039 map
->map_coldelim
= '\t';
4043 map
->map_coldelim
= '\\';
4048 /* Start of ldapmap specific args */
4050 map
->map_mflags
|= MF_SINGLEMATCH
;
4053 # if _FFR_LDAP_SINGLEDN
4055 map
->map_mflags
|= MF_SINGLEDN
;
4057 # endif /* _FFR_LDAP_SINGLEDN */
4059 case 'b': /* search base */
4060 while (isascii(*++p
) && isspace(*p
))
4062 lmap
->ldap_base
= p
;
4065 # if _FFR_LDAP_NETWORK_TIMEOUT
4066 case 'c': /* network (connect) timeout */
4067 while (isascii(*++p
) && isspace(*p
))
4069 lmap
->ldap_networktmo
.tv_sec
= atoi(p
);
4071 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */
4073 case 'd': /* Dn to bind to server as */
4074 while (isascii(*++p
) && isspace(*p
))
4076 lmap
->ldap_binddn
= p
;
4079 case 'H': /* Use LDAP URI */
4081 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4084 # else /* !USE_LDAP_INIT */
4085 if (lmap
->ldap_host
!= NULL
)
4087 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4091 while (isascii(*++p
) && isspace(*p
))
4095 # endif /* !USE_LDAP_INIT */
4097 case 'h': /* ldap host */
4098 while (isascii(*++p
) && isspace(*p
))
4100 if (lmap
->ldap_uri
!= NULL
)
4102 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4106 lmap
->ldap_host
= p
;
4110 lmap
->ldap_multi_args
= true;
4113 case 'k': /* search field */
4114 while (isascii(*++p
) && isspace(*p
))
4116 lmap
->ldap_filter
= p
;
4119 case 'l': /* time limit */
4120 while (isascii(*++p
) && isspace(*p
))
4122 lmap
->ldap_timelimit
= atoi(p
);
4123 lmap
->ldap_timeout
.tv_sec
= lmap
->ldap_timelimit
;
4126 case 'M': /* Method for binding */
4127 while (isascii(*++p
) && isspace(*p
))
4130 if (sm_strncasecmp(p
, "LDAP_AUTH_", 10) == 0)
4133 for (lam
= LDAPAuthMethods
;
4134 lam
!= NULL
&& lam
->lam_name
!= NULL
; lam
++)
4136 if (sm_strncasecmp(p
, lam
->lam_name
,
4137 strlen(lam
->lam_name
)) == 0)
4140 if (lam
->lam_name
!= NULL
)
4141 lmap
->ldap_method
= lam
->lam_code
;
4144 /* bad config line */
4145 if (!bitset(MCF_OPTFILE
,
4146 map
->map_class
->map_cflags
))
4150 if ((ptr
= strchr(p
, ' ')) != NULL
)
4152 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4161 case 'n': /* retrieve attribute names only */
4162 lmap
->ldap_attrsonly
= LDAPMAP_TRUE
;
4166 ** This is a string that is dependent on the
4167 ** method used defined by 'M'.
4170 case 'P': /* Secret password for binding */
4171 while (isascii(*++p
) && isspace(*p
))
4173 lmap
->ldap_secret
= p
;
4177 case 'p': /* ldap port */
4178 while (isascii(*++p
) && isspace(*p
))
4180 lmap
->ldap_port
= atoi(p
);
4183 /* args stolen from ldapsearch.c */
4184 case 'R': /* don't auto chase referrals */
4185 # ifdef LDAP_REFERRALS
4186 lmap
->ldap_options
&= ~LDAP_OPT_REFERRALS
;
4187 # else /* LDAP_REFERRALS */
4188 syserr("compile with -DLDAP_REFERRALS for referral support");
4189 # endif /* LDAP_REFERRALS */
4192 case 'r': /* alias dereferencing */
4193 while (isascii(*++p
) && isspace(*p
))
4196 if (sm_strncasecmp(p
, "LDAP_DEREF_", 11) == 0)
4199 for (lad
= LDAPAliasDereference
;
4200 lad
!= NULL
&& lad
->lad_name
!= NULL
; lad
++)
4202 if (sm_strncasecmp(p
, lad
->lad_name
,
4203 strlen(lad
->lad_name
)) == 0)
4206 if (lad
->lad_name
!= NULL
)
4207 lmap
->ldap_deref
= lad
->lad_code
;
4210 /* bad config line */
4211 if (!bitset(MCF_OPTFILE
,
4212 map
->map_class
->map_cflags
))
4216 if ((ptr
= strchr(p
, ' ')) != NULL
)
4218 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
4227 case 's': /* search scope */
4228 while (isascii(*++p
) && isspace(*p
))
4231 if (sm_strncasecmp(p
, "LDAP_SCOPE_", 11) == 0)
4234 for (lss
= LDAPSearchScope
;
4235 lss
!= NULL
&& lss
->lss_name
!= NULL
; lss
++)
4237 if (sm_strncasecmp(p
, lss
->lss_name
,
4238 strlen(lss
->lss_name
)) == 0)
4241 if (lss
->lss_name
!= NULL
)
4242 lmap
->ldap_scope
= lss
->lss_code
;
4245 /* bad config line */
4246 if (!bitset(MCF_OPTFILE
,
4247 map
->map_class
->map_cflags
))
4251 if ((ptr
= strchr(p
, ' ')) != NULL
)
4253 syserr("Scope must be [base|one|sub] (not %s) in map %s",
4264 lmap
->ldap_attrsep
= *p
;
4270 lmap
->ldap_attrsep
= '\n';
4274 lmap
->ldap_attrsep
= '\t';
4278 lmap
->ldap_attrsep
= '\\';
4283 case 'v': /* attr to return */
4284 while (isascii(*++p
) && isspace(*p
))
4286 lmap
->ldap_attr
[0] = p
;
4287 lmap
->ldap_attr
[1] = NULL
;
4291 /* -w should be for passwd, -P should be for version */
4292 while (isascii(*++p
) && isspace(*p
))
4294 lmap
->ldap_version
= atoi(p
);
4295 # ifdef LDAP_VERSION_MAX
4296 if (lmap
->ldap_version
> LDAP_VERSION_MAX
)
4298 syserr("LDAP version %d exceeds max of %d in map %s",
4299 lmap
->ldap_version
, LDAP_VERSION_MAX
,
4303 # endif /* LDAP_VERSION_MAX */
4304 # ifdef LDAP_VERSION_MIN
4305 if (lmap
->ldap_version
< LDAP_VERSION_MIN
)
4307 syserr("LDAP version %d is lower than min of %d in map %s",
4308 lmap
->ldap_version
, LDAP_VERSION_MIN
,
4312 # endif /* LDAP_VERSION_MIN */
4316 while (isascii(*++p
) && isspace(*p
))
4318 lmap
->ldap_sizelimit
= atoi(p
);
4322 syserr("Illegal option %c map %s", *p
, map
->map_mname
);
4326 /* need to account for quoted strings here */
4327 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
4331 while (*++p
!= '"' && *p
!= '\0')
4344 if (map
->map_app
!= NULL
)
4345 map
->map_app
= newstr(ldapmap_dequote(map
->map_app
));
4346 if (map
->map_tapp
!= NULL
)
4347 map
->map_tapp
= newstr(ldapmap_dequote(map
->map_tapp
));
4350 ** We need to swallow up all the stuff into a struct
4351 ** and dump it into map->map_dbptr1
4354 if (lmap
->ldap_host
!= NULL
&&
4355 (LDAPDefaults
== NULL
||
4356 LDAPDefaults
== lmap
||
4357 LDAPDefaults
->ldap_host
!= lmap
->ldap_host
))
4358 lmap
->ldap_host
= newstr(ldapmap_dequote(lmap
->ldap_host
));
4359 map
->map_domain
= lmap
->ldap_host
;
4361 if (lmap
->ldap_uri
!= NULL
&&
4362 (LDAPDefaults
== NULL
||
4363 LDAPDefaults
== lmap
||
4364 LDAPDefaults
->ldap_uri
!= lmap
->ldap_uri
))
4365 lmap
->ldap_uri
= newstr(ldapmap_dequote(lmap
->ldap_uri
));
4366 map
->map_domain
= lmap
->ldap_uri
;
4368 if (lmap
->ldap_binddn
!= NULL
&&
4369 (LDAPDefaults
== NULL
||
4370 LDAPDefaults
== lmap
||
4371 LDAPDefaults
->ldap_binddn
!= lmap
->ldap_binddn
))
4372 lmap
->ldap_binddn
= newstr(ldapmap_dequote(lmap
->ldap_binddn
));
4374 if (lmap
->ldap_secret
!= NULL
&&
4375 (LDAPDefaults
== NULL
||
4376 LDAPDefaults
== lmap
||
4377 LDAPDefaults
->ldap_secret
!= lmap
->ldap_secret
))
4380 long sff
= SFF_OPENASROOT
|SFF_ROOTOK
|SFF_NOWLINK
|SFF_NOWWFILES
|SFF_NOGWFILES
;
4382 if (DontLockReadFiles
)
4385 /* need to use method to map secret to passwd string */
4386 switch (lmap
->ldap_method
)
4388 case LDAP_AUTH_NONE
:
4392 case LDAP_AUTH_SIMPLE
:
4395 ** Secret is the name of a file with
4396 ** the first line as the password.
4399 /* Already read in the secret? */
4403 sfd
= safefopen(ldapmap_dequote(lmap
->ldap_secret
),
4407 syserr("LDAP map: cannot open secret %s",
4408 ldapmap_dequote(lmap
->ldap_secret
));
4411 lmap
->ldap_secret
= sfgets(m_tmp
, sizeof(m_tmp
),
4412 sfd
, TimeOuts
.to_fileopen
,
4413 "ldapmap_parseargs");
4414 (void) sm_io_close(sfd
, SM_TIME_DEFAULT
);
4415 if (strlen(m_tmp
) > LDAPMAP_MAX_PASSWD
)
4417 syserr("LDAP map: secret in %s too long",
4418 ldapmap_dequote(lmap
->ldap_secret
));
4421 if (lmap
->ldap_secret
!= NULL
&&
4425 if (m_tmp
[strlen(m_tmp
) - 1] == '\n')
4426 m_tmp
[strlen(m_tmp
) - 1] = '\0';
4428 lmap
->ldap_secret
= m_tmp
;
4432 # ifdef LDAP_AUTH_KRBV4
4433 case LDAP_AUTH_KRBV4
:
4436 ** Secret is where the ticket file is
4440 (void) sm_snprintf(m_tmp
, sizeof(m_tmp
),
4442 ldapmap_dequote(lmap
->ldap_secret
));
4443 lmap
->ldap_secret
= m_tmp
;
4445 # endif /* LDAP_AUTH_KRBV4 */
4447 default: /* Should NEVER get here */
4448 syserr("LDAP map: Illegal value in lmap method");
4455 if (lmap
->ldap_secret
!= NULL
&&
4456 (LDAPDefaults
== NULL
||
4457 LDAPDefaults
== lmap
||
4458 LDAPDefaults
->ldap_secret
!= lmap
->ldap_secret
))
4459 lmap
->ldap_secret
= newstr(ldapmap_dequote(lmap
->ldap_secret
));
4461 if (lmap
->ldap_base
!= NULL
&&
4462 (LDAPDefaults
== NULL
||
4463 LDAPDefaults
== lmap
||
4464 LDAPDefaults
->ldap_base
!= lmap
->ldap_base
))
4465 lmap
->ldap_base
= newstr(ldapmap_dequote(lmap
->ldap_base
));
4468 ** Save the server from extra work. If request is for a single
4469 ** match, tell the server to only return enough records to
4470 ** determine if there is a single match or not. This can not
4471 ** be one since the server would only return one and we wouldn't
4472 ** know if there were others available.
4475 if (bitset(MF_SINGLEMATCH
, map
->map_mflags
))
4476 lmap
->ldap_sizelimit
= 2;
4478 /* If setting defaults, don't process ldap_filter and ldap_attr */
4479 if (lmap
== LDAPDefaults
)
4482 if (lmap
->ldap_filter
!= NULL
)
4483 lmap
->ldap_filter
= newstr(ldapmap_dequote(lmap
->ldap_filter
));
4486 if (!bitset(MCF_OPTFILE
, map
->map_class
->map_cflags
))
4488 syserr("No filter given in map %s", map
->map_mname
);
4493 if (!attrssetup
&& lmap
->ldap_attr
[0] != NULL
)
4495 bool recurse
= false;
4496 bool normalseen
= false;
4499 p
= ldapmap_dequote(lmap
->ldap_attr
[0]);
4500 lmap
->ldap_attr
[0] = NULL
;
4502 /* Prime the attr list with the objectClass attribute */
4503 lmap
->ldap_attr
[i
] = "objectClass";
4504 lmap
->ldap_attr_type
[i
] = SM_LDAP_ATTR_OBJCLASS
;
4505 lmap
->ldap_attr_needobjclass
[i
] = NULL
;
4512 while (isascii(*p
) && isspace(*p
))
4521 if (i
>= LDAPMAP_MAX_ATTR
)
4523 syserr("Too many return attributes in %s (max %d)",
4524 map
->map_mname
, LDAPMAP_MAX_ATTR
);
4534 type
= strchr(v
, ':');
4538 needobjclass
= strchr(type
, ':');
4539 if (needobjclass
!= NULL
)
4540 *needobjclass
++ = '\0';
4544 needobjclass
= NULL
;
4549 /* allow override on "objectClass" type */
4550 if (sm_strcasecmp(v
, "objectClass") == 0 &&
4551 lmap
->ldap_attr_type
[0] == SM_LDAP_ATTR_OBJCLASS
)
4558 ** Don't add something to attribute
4562 for (j
= 1; j
< i
; j
++)
4564 if (sm_strcasecmp(v
, lmap
->ldap_attr
[j
]) == 0)
4566 syserr("Duplicate attribute (%s) in %s",
4572 lmap
->ldap_attr
[use
] = newstr(v
);
4573 if (needobjclass
!= NULL
&&
4574 *needobjclass
!= '\0' &&
4575 *needobjclass
!= '*')
4577 lmap
->ldap_attr_needobjclass
[use
] = newstr(needobjclass
);
4581 lmap
->ldap_attr_needobjclass
[use
] = NULL
;
4586 if (type
!= NULL
&& *type
!= '\0')
4588 if (sm_strcasecmp(type
, "dn") == 0)
4591 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_DN
;
4593 else if (sm_strcasecmp(type
, "filter") == 0)
4596 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_FILTER
;
4598 else if (sm_strcasecmp(type
, "url") == 0)
4601 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_URL
;
4603 else if (sm_strcasecmp(type
, "normal") == 0)
4605 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_NORMAL
;
4610 syserr("Unknown attribute type (%s) in %s",
4611 type
, map
->map_mname
);
4617 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_NORMAL
;
4623 lmap
->ldap_attr
[i
] = NULL
;
4625 /* Set in case needed in future code */
4628 if (recurse
&& !normalseen
)
4630 syserr("LDAP recursion requested in %s but no returnable attribute given",
4634 if (recurse
&& lmap
->ldap_attrsonly
== LDAPMAP_TRUE
)
4636 syserr("LDAP recursion requested in %s can not be used with -n",
4641 map
->map_db1
= (ARBPTR_T
) lmap
;
4646 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4649 ** spec -- map argument string from LDAPDefaults option
4656 ldapmap_set_defaults(spec
)
4662 /* Allocate and set the default values */
4663 if (LDAPDefaults
== NULL
)
4664 LDAPDefaults
= (SM_LDAP_STRUCT
*) xalloc(sizeof(*LDAPDefaults
));
4665 sm_ldap_clear(LDAPDefaults
);
4667 memset(&map
, '\0', sizeof(map
));
4669 /* look up the class */
4670 class = stab("ldap", ST_MAPCLASS
, ST_FIND
);
4673 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4676 map
.map_class
= &class->s_mapclass
;
4677 map
.map_db1
= (ARBPTR_T
) LDAPDefaults
;
4678 map
.map_mname
= "O LDAPDefaultSpec";
4680 (void) ldapmap_parseargs(&map
, spec
);
4682 /* These should never be set in LDAPDefaults */
4683 if (map
.map_mflags
!= (MF_TRY0NULL
|MF_TRY1NULL
) ||
4684 map
.map_spacesub
!= SpaceSub
||
4685 map
.map_app
!= NULL
||
4686 map
.map_tapp
!= NULL
)
4688 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4689 SM_FREE_CLR(map
.map_app
);
4690 SM_FREE_CLR(map
.map_tapp
);
4693 if (LDAPDefaults
->ldap_filter
!= NULL
)
4695 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4697 /* don't free, it isn't malloc'ed in parseargs */
4698 LDAPDefaults
->ldap_filter
= NULL
;
4701 if (LDAPDefaults
->ldap_attr
[0] != NULL
)
4703 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4704 /* don't free, they aren't malloc'ed in parseargs */
4705 LDAPDefaults
->ldap_attr
[0] = NULL
;
4708 #endif /* LDAPMAP */
4716 ** Support for the CCSO Nameserver (ph/qi).
4717 ** This code is intended to replace the so-called "ph mailer".
4718 ** Contributed by Mark D. Roth. Contact him for support.
4721 /* what version of the ph map code we're running */
4722 static char phmap_id
[128];
4724 /* sendmail version for phmap id string */
4725 extern const char Version
[];
4727 /* assume we're using nph-1.2.x if not specified */
4728 # ifndef NPH_VERSION
4729 # define NPH_VERSION 10200
4732 /* compatibility for versions older than nph-1.2.0 */
4733 # if NPH_VERSION < 10200
4734 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4735 # define PH_OPEN_DONTID PH_DONTID
4736 # define PH_CLOSE_FAST PH_FASTCLOSE
4737 # define PH_ERR_DATAERR PH_DATAERR
4738 # define PH_ERR_NOMATCH PH_NOMATCH
4739 # endif /* NPH_VERSION < 10200 */
4742 ** PH_MAP_PARSEARGS -- parse ph map definition args.
4746 ph_map_parseargs(map
, args
)
4751 register char *p
= args
;
4752 PH_MAP_STRUCT
*pmap
= NULL
;
4754 /* initialize version string */
4755 (void) sm_snprintf(phmap_id
, sizeof(phmap_id
),
4756 "sendmail-%s phmap-20010529 libphclient-%s",
4757 Version
, libphclient_version
);
4759 pmap
= (PH_MAP_STRUCT
*) xalloc(sizeof(*pmap
));
4762 pmap
->ph_servers
= NULL
;
4763 pmap
->ph_field_list
= NULL
;
4765 pmap
->ph_timeout
= 0;
4766 pmap
->ph_fastclose
= 0;
4768 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
4771 while (isascii(*p
) && isspace(*p
))
4778 map
->map_mflags
|= MF_INCLNULL
;
4779 map
->map_mflags
&= ~MF_TRY0NULL
;
4783 map
->map_mflags
&= ~MF_TRY1NULL
;
4787 map
->map_mflags
|= MF_OPTIONAL
;
4791 map
->map_mflags
|= MF_NOFOLDCASE
;
4795 map
->map_mflags
|= MF_MATCHONLY
;
4799 map
->map_mflags
|= MF_APPEND
;
4803 map
->map_mflags
|= MF_KEEPQUOTES
;
4807 map
->map_mflags
|= MF_NODEFER
;
4815 map
->map_tapp
= ++p
;
4819 while (isascii(*++p
) && isspace(*p
))
4821 pmap
->ph_timeout
= atoi(p
);
4825 map
->map_spacesub
= *++p
;
4829 map
->map_mflags
|= MF_DEFER
;
4832 case 'h': /* PH server list */
4833 while (isascii(*++p
) && isspace(*p
))
4835 pmap
->ph_servers
= p
;
4838 case 'k': /* fields to search for */
4839 while (isascii(*++p
) && isspace(*p
))
4841 pmap
->ph_field_list
= p
;
4845 syserr("ph_map_parseargs: unknown option -%c", *p
);
4848 /* try to account for quoted strings */
4849 done
= isascii(*p
) && isspace(*p
);
4850 while (*p
!= '\0' && !done
)
4854 while (*++p
!= '"' && *p
!= '\0')
4861 done
= isascii(*p
) && isspace(*p
);
4868 if (map
->map_app
!= NULL
)
4869 map
->map_app
= newstr(ph_map_dequote(map
->map_app
));
4870 if (map
->map_tapp
!= NULL
)
4871 map
->map_tapp
= newstr(ph_map_dequote(map
->map_tapp
));
4873 if (pmap
->ph_field_list
!= NULL
)
4874 pmap
->ph_field_list
= newstr(ph_map_dequote(pmap
->ph_field_list
));
4876 if (pmap
->ph_servers
!= NULL
)
4877 pmap
->ph_servers
= newstr(ph_map_dequote(pmap
->ph_servers
));
4880 syserr("ph_map_parseargs: -h flag is required");
4884 map
->map_db1
= (ARBPTR_T
) pmap
;
4889 ** PH_MAP_CLOSE -- close the connection to the ph server
4896 PH_MAP_STRUCT
*pmap
;
4898 pmap
= (PH_MAP_STRUCT
*)map
->map_db1
;
4900 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4901 map
->map_mname
, pmap
->ph_fastclose
);
4904 if (pmap
->ph
!= NULL
)
4906 ph_set_sendhook(pmap
->ph
, NULL
);
4907 ph_set_recvhook(pmap
->ph
, NULL
);
4908 ph_close(pmap
->ph
, pmap
->ph_fastclose
);
4911 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
);
4914 static jmp_buf PHTimeout
;
4922 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
4923 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4928 longjmp(PHTimeout
, 1);
4932 #if NPH_VERSION >= 10200
4933 ph_map_send_debug(appdata
, text
)
4936 ph_map_send_debug(text
)
4941 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
4942 "ph_map_send_debug: ==> %s", text
);
4944 sm_dprintf("ph_map_send_debug: ==> %s\n", text
);
4948 #if NPH_VERSION >= 10200
4949 ph_map_recv_debug(appdata
, text
)
4952 ph_map_recv_debug(text
)
4957 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
4958 "ph_map_recv_debug: <== %s", text
);
4960 sm_dprintf("ph_map_recv_debug: <== %s\n", text
);
4964 ** PH_MAP_OPEN -- sub for opening PH map
4967 ph_map_open(map
, mode
)
4971 PH_MAP_STRUCT
*pmap
;
4972 register SM_EVENT
*ev
= NULL
;
4974 char *hostlist
, *host
;
4977 sm_dprintf("ph_map_open(%s)\n", map
->map_mname
);
4980 if (mode
!= O_RDONLY
)
4982 /* issue a pseudo-error message */
4983 errno
= SM_EMAPCANTWRITE
;
4987 if (CurEnv
!= NULL
&& CurEnv
->e_sendmode
== SM_DEFER
&&
4988 bitset(MF_DEFER
, map
->map_mflags
))
4991 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
4995 ** Unset MF_DEFER here so that map_lookup() returns
4996 ** a temporary failure using the bogus map and
4997 ** map->map_tapp instead of the default permanent error.
5000 map
->map_mflags
&= ~MF_DEFER
;
5004 pmap
= (PH_MAP_STRUCT
*)map
->map_db1
;
5005 pmap
->ph_fastclose
= 0; /* refresh field for reopen */
5007 /* try each host in the list */
5008 hostlist
= newstr(pmap
->ph_servers
);
5009 for (host
= strtok(hostlist
, " ");
5011 host
= strtok(NULL
, " "))
5014 if (pmap
->ph_timeout
!= 0)
5016 if (setjmp(PHTimeout
) != 0)
5020 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
5021 "timeout connecting to PH server %.100s",
5024 goto ph_map_open_abort
;
5026 ev
= sm_setevent(pmap
->ph_timeout
, ph_timeout
, 0);
5029 /* open connection to server */
5030 if (ph_open(&(pmap
->ph
), host
,
5031 PH_OPEN_ROUNDROBIN
|PH_OPEN_DONTID
,
5032 ph_map_send_debug
, ph_map_recv_debug
5033 #if NPH_VERSION >= 10200
5037 && ph_id(pmap
->ph
, phmap_id
) == 0)
5041 sm_free(hostlist
); /* XXX */
5049 pmap
->ph_fastclose
= PH_CLOSE_FAST
;
5054 if (bitset(MF_NODEFER
, map
->map_mflags
))
5058 syserr("ph_map_open: %s: cannot connect to PH server",
5061 else if (!bitset(MF_OPTIONAL
, map
->map_mflags
) && LogLevel
> 1)
5062 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
5063 "ph_map_open: %s: cannot connect to PH server",
5065 sm_free(hostlist
); /* XXX */
5070 ** PH_MAP_LOOKUP -- look up key from ph server
5074 ph_map_lookup(map
, key
, args
, pstat
)
5080 int i
, save_errno
= 0;
5081 register SM_EVENT
*ev
= NULL
;
5082 PH_MAP_STRUCT
*pmap
;
5085 pmap
= (PH_MAP_STRUCT
*)map
->map_db1
;
5090 if (pmap
->ph_timeout
!= 0)
5092 if (setjmp(PHTimeout
) != 0)
5096 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
5097 "timeout during PH lookup of %.100s",
5100 *pstat
= EX_TEMPFAIL
;
5101 goto ph_map_lookup_abort
;
5103 ev
= sm_setevent(pmap
->ph_timeout
, ph_timeout
, 0);
5106 /* perform lookup */
5107 i
= ph_email_resolve(pmap
->ph
, key
, pmap
->ph_field_list
, &value
);
5109 *pstat
= EX_TEMPFAIL
;
5110 else if (i
== PH_ERR_NOMATCH
|| i
== PH_ERR_DATAERR
)
5111 *pstat
= EX_UNAVAILABLE
;
5113 ph_map_lookup_abort
:
5118 ** Close the connection if the timer popped
5119 ** or we got a temporary PH error
5122 if (*pstat
== EX_TEMPFAIL
)
5125 pmap
->ph_fastclose
= PH_CLOSE_FAST
;
5130 if (*pstat
== EX_OK
)
5133 sm_dprintf("ph_map_lookup: %s => %s\n", key
, value
);
5135 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5136 return map_rewrite(map
, key
, strlen(key
), NULL
);
5138 return map_rewrite(map
, value
, strlen(value
), args
);
5149 #define map_prio map_lockfd /* overload field */
5152 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5156 syslog_map_parseargs(map
, args
)
5161 char *priority
= NULL
;
5163 /* there is no check whether there is really an argument */
5166 while (isascii(*p
) && isspace(*p
))
5173 map
->map_mflags
|= MF_DEFER
;
5178 map
->map_spacesub
= *++p
;
5184 while (*++p
!= '\0' && isascii(*p
) && isspace(*p
))
5189 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
5196 syserr("Illegal option %c map syslog", *p
);
5201 if (priority
== NULL
)
5202 map
->map_prio
= LOG_INFO
;
5205 if (sm_strncasecmp("LOG_", priority
, 4) == 0)
5209 if (sm_strcasecmp("EMERG", priority
) == 0)
5210 map
->map_prio
= LOG_EMERG
;
5212 #endif /* LOG_EMERG */
5214 if (sm_strcasecmp("ALERT", priority
) == 0)
5215 map
->map_prio
= LOG_ALERT
;
5217 #endif /* LOG_ALERT */
5219 if (sm_strcasecmp("CRIT", priority
) == 0)
5220 map
->map_prio
= LOG_CRIT
;
5222 #endif /* LOG_CRIT */
5224 if (sm_strcasecmp("ERR", priority
) == 0)
5225 map
->map_prio
= LOG_ERR
;
5227 #endif /* LOG_ERR */
5229 if (sm_strcasecmp("WARNING", priority
) == 0)
5230 map
->map_prio
= LOG_WARNING
;
5232 #endif /* LOG_WARNING */
5234 if (sm_strcasecmp("NOTICE", priority
) == 0)
5235 map
->map_prio
= LOG_NOTICE
;
5237 #endif /* LOG_NOTICE */
5239 if (sm_strcasecmp("INFO", priority
) == 0)
5240 map
->map_prio
= LOG_INFO
;
5242 #endif /* LOG_INFO */
5244 if (sm_strcasecmp("DEBUG", priority
) == 0)
5245 map
->map_prio
= LOG_DEBUG
;
5247 #endif /* LOG_DEBUG */
5249 syserr("syslog_map_parseargs: Unknown priority %s",
5258 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5262 syslog_map_lookup(map
, string
, args
, statp
)
5268 char *ptr
= map_rewrite(map
, string
, strlen(string
), args
);
5273 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5274 map
->map_mname
, map
->map_prio
, ptr
);
5276 sm_syslog(map
->map_prio
, CurEnv
->e_id
, "%s", ptr
);
5283 #if _FFR_DPRINTF_MAP
5288 #define map_dbg_level map_lockfd /* overload field */
5291 ** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages.
5295 dprintf_map_parseargs(map
, args
)
5300 char *dbg_level
= NULL
;
5302 /* there is no check whether there is really an argument */
5305 while (isascii(*p
) && isspace(*p
))
5312 map
->map_mflags
|= MF_DEFER
;
5317 map
->map_spacesub
= *++p
;
5323 while (*++p
!= '\0' && isascii(*p
) && isspace(*p
))
5328 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
5335 syserr("Illegal option %c map dprintf", *p
);
5340 if (dbg_level
== NULL
)
5341 map
->map_dbg_level
= 0;
5344 if (!(isascii(*dbg_level
) && isdigit(*dbg_level
)))
5346 syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s",
5347 map
->map_mname
, map
->map_file
,
5351 map
->map_dbg_level
= atoi(dbg_level
);
5357 ** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string
5361 dprintf_map_lookup(map
, string
, args
, statp
)
5367 char *ptr
= map_rewrite(map
, string
, strlen(string
), args
);
5369 if (ptr
!= NULL
&& tTd(85, map
->map_dbg_level
))
5370 sm_dprintf("%s\n", ptr
);
5374 #endif /* _FFR_DPRINTF_MAP */
5383 hes_map_open(map
, mode
)
5388 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5389 map
->map_mname
, map
->map_file
, mode
);
5391 if (mode
!= O_RDONLY
)
5393 /* issue a pseudo-error message */
5394 errno
= SM_EMAPCANTWRITE
;
5399 if (HesiodContext
!= NULL
|| hesiod_init(&HesiodContext
) == 0)
5402 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
5403 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5404 sm_errstring(errno
));
5406 # else /* HESIOD_INIT */
5407 if (hes_error() == HES_ER_UNINIT
)
5409 switch (hes_error())
5412 case HES_ER_NOTFOUND
:
5416 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
5417 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5420 # endif /* HESIOD_INIT */
5424 hes_map_lookup(map
, name
, av
, statp
)
5433 sm_dprintf("hes_map_lookup(%s, %s)\n", map
->map_file
, name
);
5435 if (name
[0] == '\\')
5443 if (nl
< sizeof(nbuf
) - 1)
5446 np
= xalloc(strlen(name
) + 2);
5448 (void) sm_strlcpy(&np
[1], name
, (sizeof(nbuf
)) - 1);
5450 hp
= hesiod_resolve(HesiodContext
, np
, map
->map_file
);
5451 # else /* HESIOD_INIT */
5452 hp
= hes_resolve(np
, map
->map_file
);
5453 # endif /* HESIOD_INIT */
5456 sm_free(np
); /* XXX */
5462 hp
= hesiod_resolve(HesiodContext
, name
, map
->map_file
);
5463 # else /* HESIOD_INIT */
5464 hp
= hes_resolve(name
, map
->map_file
);
5465 # endif /* HESIOD_INIT */
5468 if (hp
== NULL
|| *hp
== NULL
)
5473 *statp
= EX_NOTFOUND
;
5476 *statp
= EX_TEMPFAIL
;
5481 *statp
= EX_UNAVAILABLE
;
5485 hesiod_free_list(HesiodContext
, hp
);
5488 # else /* HESIOD_INIT */
5489 if (hp
== NULL
|| hp
[0] == NULL
)
5491 switch (hes_error())
5497 case HES_ER_NOTFOUND
:
5498 *statp
= EX_NOTFOUND
;
5502 *statp
= EX_UNAVAILABLE
;
5506 *statp
= EX_TEMPFAIL
;
5511 # endif /* HESIOD_INIT */
5513 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5514 return map_rewrite(map
, name
, strlen(name
), NULL
);
5516 return map_rewrite(map
, hp
[0], strlen(hp
[0]), av
);
5520 ** HES_MAP_CLOSE -- free the Hesiod context
5528 sm_dprintf("hes_map_close(%s)\n", map
->map_file
);
5531 /* Free the hesiod context */
5532 if (HesiodContext
!= NULL
)
5534 hesiod_end(HesiodContext
);
5535 HesiodContext
= NULL
;
5537 # endif /* HESIOD_INIT */
5542 ** NeXT NETINFO Modules
5547 # define NETINFO_DEFAULT_DIR "/aliases"
5548 # define NETINFO_DEFAULT_PROPERTY "members"
5551 ** NI_MAP_OPEN -- open NetInfo Aliases
5555 ni_map_open(map
, mode
)
5560 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5561 map
->map_mname
, map
->map_file
, mode
);
5564 if (*map
->map_file
== '\0')
5565 map
->map_file
= NETINFO_DEFAULT_DIR
;
5567 if (map
->map_valcolnm
== NULL
)
5568 map
->map_valcolnm
= NETINFO_DEFAULT_PROPERTY
;
5570 if (map
->map_coldelim
== '\0')
5572 if (bitset(MF_ALIAS
, map
->map_mflags
))
5573 map
->map_coldelim
= ',';
5574 else if (bitset(MF_FILECLASS
, map
->map_mflags
))
5575 map
->map_coldelim
= ' ';
5582 ** NI_MAP_LOOKUP -- look up a datum in NetInfo
5586 ni_map_lookup(map
, name
, av
, statp
)
5596 sm_dprintf("ni_map_lookup(%s, %s)\n", map
->map_mname
, name
);
5598 propval
= ni_propval(map
->map_file
, map
->map_keycolnm
, name
,
5599 map
->map_valcolnm
, map
->map_coldelim
);
5601 if (propval
== NULL
)
5605 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5606 res
= map_rewrite(map
, name
, strlen(name
), NULL
);
5608 res
= map_rewrite(map
, propval
, strlen(propval
), av
);
5617 ni_getcanonname(name
, hbsize
, statp
)
5624 char nbuf
[MAXNAME
+ 1];
5627 sm_dprintf("ni_getcanonname(%s)\n", name
);
5629 if (sm_strlcpy(nbuf
, name
, sizeof(nbuf
)) >= sizeof(nbuf
))
5631 *statp
= EX_UNAVAILABLE
;
5634 (void) shorten_hostname(nbuf
);
5636 /* we only accept single token search key */
5637 if (strchr(nbuf
, '.'))
5644 vptr
= ni_propval("/machines", NULL
, nbuf
, "name", '\n');
5652 /* Only want the first machine name */
5653 if ((ptr
= strchr(vptr
, '\n')) != NULL
)
5656 if (sm_strlcpy(name
, vptr
, hbsize
) >= hbsize
)
5659 *statp
= EX_UNAVAILABLE
;
5666 #endif /* NETINFO */
5668 ** TEXT (unindexed text file) Modules
5670 ** This code donated by Sun Microsystems.
5673 #define map_sff map_lockfd /* overload field */
5677 ** TEXT_MAP_OPEN -- open text table
5681 text_map_open(map
, mode
)
5689 sm_dprintf("text_map_open(%s, %s, %d)\n",
5690 map
->map_mname
, map
->map_file
, mode
);
5693 if (mode
!= O_RDONLY
)
5699 if (*map
->map_file
== '\0')
5701 syserr("text map \"%s\": file name required",
5706 if (map
->map_file
[0] != '/')
5708 syserr("text map \"%s\": file name must be fully qualified",
5713 sff
= SFF_ROOTOK
|SFF_REGONLY
;
5714 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
5716 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
5717 sff
|= SFF_SAFEDIRPATH
;
5718 if ((i
= safefile(map
->map_file
, RunAsUid
, RunAsGid
, RunAsUserName
,
5719 sff
, S_IRUSR
, NULL
)) != 0)
5721 int save_errno
= errno
;
5723 /* cannot open this map */
5725 sm_dprintf("\tunsafe map file: %d\n", i
);
5727 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
5728 syserr("text map \"%s\": unsafe map file %s",
5729 map
->map_mname
, map
->map_file
);
5733 if (map
->map_keycolnm
== NULL
)
5734 map
->map_keycolno
= 0;
5737 if (!(isascii(*map
->map_keycolnm
) && isdigit(*map
->map_keycolnm
)))
5739 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5740 map
->map_mname
, map
->map_file
,
5744 map
->map_keycolno
= atoi(map
->map_keycolnm
);
5747 if (map
->map_valcolnm
== NULL
)
5748 map
->map_valcolno
= 0;
5751 if (!(isascii(*map
->map_valcolnm
) && isdigit(*map
->map_valcolnm
)))
5753 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5754 map
->map_mname
, map
->map_file
,
5758 map
->map_valcolno
= atoi(map
->map_valcolnm
);
5763 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5764 map
->map_mname
, map
->map_file
);
5765 if (map
->map_coldelim
== '\0')
5766 sm_dprintf("(white space)\n");
5768 sm_dprintf("%c\n", map
->map_coldelim
);
5777 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5781 text_map_lookup(map
, name
, av
, statp
)
5794 long sff
= map
->map_sff
;
5795 char search_key
[MAXNAME
+ 1];
5796 char linebuf
[MAXLINE
];
5797 char buf
[MAXNAME
+ 1];
5801 sm_dprintf("text_map_lookup(%s, %s)\n", map
->map_mname
, name
);
5803 buflen
= strlen(name
);
5804 if (buflen
> sizeof(search_key
) - 1)
5805 buflen
= sizeof(search_key
) - 1; /* XXX just cut if off? */
5806 memmove(search_key
, name
, buflen
);
5807 search_key
[buflen
] = '\0';
5808 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
5809 makelower(search_key
);
5811 f
= safefopen(map
->map_file
, O_RDONLY
, FileMode
, sff
);
5814 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
5815 *statp
= EX_UNAVAILABLE
;
5818 key_idx
= map
->map_keycolno
;
5819 delim
= map
->map_coldelim
;
5820 while (sm_io_fgets(f
, SM_TIME_DEFAULT
,
5821 linebuf
, sizeof(linebuf
)) != NULL
)
5825 /* skip comment line */
5826 if (linebuf
[0] == '#')
5828 p
= strchr(linebuf
, '\n');
5831 p
= get_column(linebuf
, key_idx
, delim
, buf
, sizeof(buf
));
5832 if (p
!= NULL
&& sm_strcasecmp(search_key
, p
) == 0)
5838 (void) sm_io_close(f
, SM_TIME_DEFAULT
);
5841 *statp
= EX_NOTFOUND
;
5844 vp
= get_column(linebuf
, map
->map_valcolno
, delim
, buf
, sizeof(buf
));
5847 *statp
= EX_NOTFOUND
;
5852 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5853 return map_rewrite(map
, name
, strlen(name
), NULL
);
5855 return map_rewrite(map
, vp
, vsize
, av
);
5859 ** TEXT_GETCANONNAME -- look up canonical name in hosts file
5863 text_getcanonname(name
, hbsize
, statp
)
5871 char linebuf
[MAXLINE
];
5872 char cbuf
[MAXNAME
+ 1];
5873 char nbuf
[MAXNAME
+ 1];
5876 sm_dprintf("text_getcanonname(%s)\n", name
);
5878 if (sm_strlcpy(nbuf
, name
, sizeof(nbuf
)) >= sizeof(nbuf
))
5880 *statp
= EX_UNAVAILABLE
;
5883 dot
= shorten_hostname(nbuf
);
5885 f
= sm_io_open(SmFtStdio
, SM_TIME_DEFAULT
, HostsFile
, SM_IO_RDONLY
,
5889 *statp
= EX_UNAVAILABLE
;
5894 sm_io_fgets(f
, SM_TIME_DEFAULT
,
5895 linebuf
, sizeof(linebuf
)) != NULL
)
5897 char *p
= strpbrk(linebuf
, "#\n");
5901 if (linebuf
[0] != '\0')
5902 found
= extract_canonname(nbuf
, dot
, linebuf
,
5903 cbuf
, sizeof(cbuf
));
5905 (void) sm_io_close(f
, SM_TIME_DEFAULT
);
5912 if (sm_strlcpy(name
, cbuf
, hbsize
) >= hbsize
)
5914 *statp
= EX_UNAVAILABLE
;
5921 ** STAB (Symbol Table) Modules
5926 ** STAB_MAP_LOOKUP -- look up alias in symbol table
5931 stab_map_lookup(map
, name
, av
, pstat
)
5940 sm_dprintf("stab_lookup(%s, %s)\n",
5941 map
->map_mname
, name
);
5943 s
= stab(name
, ST_ALIAS
, ST_FIND
);
5946 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5947 return map_rewrite(map
, name
, strlen(name
), NULL
);
5949 return map_rewrite(map
, s
->s_alias
, strlen(s
->s_alias
), av
);
5953 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5957 stab_map_store(map
, lhs
, rhs
)
5964 s
= stab(lhs
, ST_ALIAS
, ST_ENTER
);
5965 s
->s_alias
= newstr(rhs
);
5970 ** STAB_MAP_OPEN -- initialize (reads data file)
5972 ** This is a wierd case -- it is only intended as a fallback for
5973 ** aliases. For this reason, opens for write (only during a
5974 ** "newaliases") always fails, and opens for read open the
5975 ** actual underlying text file instead of the database.
5979 stab_map_open(map
, mode
)
5988 sm_dprintf("stab_map_open(%s, %s, %d)\n",
5989 map
->map_mname
, map
->map_file
, mode
);
5992 if (mode
!= O_RDONLY
)
5998 sff
= SFF_ROOTOK
|SFF_REGONLY
;
5999 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
6001 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
6002 sff
|= SFF_SAFEDIRPATH
;
6003 af
= safefopen(map
->map_file
, O_RDONLY
, 0444, sff
);
6006 readaliases(map
, af
, false, false);
6008 if (fstat(sm_io_getinfo(af
, SM_IO_WHAT_FD
, NULL
), &st
) >= 0)
6009 map
->map_mtime
= st
.st_mtime
;
6010 (void) sm_io_close(af
, SM_TIME_DEFAULT
);
6017 ** Tries several types. For back compatibility of aliases.
6022 ** IMPL_MAP_LOOKUP -- lookup in best open database
6026 impl_map_lookup(map
, name
, av
, pstat
)
6033 sm_dprintf("impl_map_lookup(%s, %s)\n",
6034 map
->map_mname
, name
);
6037 if (bitset(MF_IMPL_HASH
, map
->map_mflags
))
6038 return db_map_lookup(map
, name
, av
, pstat
);
6041 if (bitset(MF_IMPL_NDBM
, map
->map_mflags
))
6042 return ndbm_map_lookup(map
, name
, av
, pstat
);
6044 return stab_map_lookup(map
, name
, av
, pstat
);
6048 ** IMPL_MAP_STORE -- store in open databases
6052 impl_map_store(map
, lhs
, rhs
)
6058 sm_dprintf("impl_map_store(%s, %s, %s)\n",
6059 map
->map_mname
, lhs
, rhs
);
6061 if (bitset(MF_IMPL_HASH
, map
->map_mflags
))
6062 db_map_store(map
, lhs
, rhs
);
6065 if (bitset(MF_IMPL_NDBM
, map
->map_mflags
))
6066 ndbm_map_store(map
, lhs
, rhs
);
6068 stab_map_store(map
, lhs
, rhs
);
6072 ** IMPL_MAP_OPEN -- implicit database open
6076 impl_map_open(map
, mode
)
6081 sm_dprintf("impl_map_open(%s, %s, %d)\n",
6082 map
->map_mname
, map
->map_file
, mode
);
6086 map
->map_mflags
|= MF_IMPL_HASH
;
6087 if (hash_map_open(map
, mode
))
6089 # ifdef NDBM_YP_COMPAT
6090 if (mode
== O_RDONLY
|| strstr(map
->map_file
, "/yp/") == NULL
)
6091 # endif /* NDBM_YP_COMPAT */
6095 map
->map_mflags
&= ~MF_IMPL_HASH
;
6098 map
->map_mflags
|= MF_IMPL_NDBM
;
6099 if (ndbm_map_open(map
, mode
))
6104 map
->map_mflags
&= ~MF_IMPL_NDBM
;
6107 #if defined(NEWDB) || defined(NDBM)
6109 message("WARNING: cannot open alias database %s%s",
6111 mode
== O_RDONLY
? "; reading text version" : "");
6112 #else /* defined(NEWDB) || defined(NDBM) */
6113 if (mode
!= O_RDONLY
)
6114 usrerr("Cannot rebuild aliases: no database format defined");
6115 #endif /* defined(NEWDB) || defined(NDBM) */
6117 if (mode
== O_RDONLY
)
6118 return stab_map_open(map
, mode
);
6125 ** IMPL_MAP_CLOSE -- close any open database(s)
6133 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
6134 map
->map_mname
, map
->map_file
, map
->map_mflags
);
6136 if (bitset(MF_IMPL_HASH
, map
->map_mflags
))
6139 map
->map_mflags
&= ~MF_IMPL_HASH
;
6144 if (bitset(MF_IMPL_NDBM
, map
->map_mflags
))
6146 ndbm_map_close(map
);
6147 map
->map_mflags
&= ~MF_IMPL_NDBM
;
6154 ** Provides access to the system password file.
6158 ** USER_MAP_OPEN -- open user map
6160 ** Really just binds field names to field numbers.
6164 user_map_open(map
, mode
)
6169 sm_dprintf("user_map_open(%s, %d)\n",
6170 map
->map_mname
, mode
);
6173 if (mode
!= O_RDONLY
)
6175 /* issue a pseudo-error message */
6176 errno
= SM_EMAPCANTWRITE
;
6179 if (map
->map_valcolnm
== NULL
)
6182 else if (sm_strcasecmp(map
->map_valcolnm
, "name") == 0)
6183 map
->map_valcolno
= 1;
6184 else if (sm_strcasecmp(map
->map_valcolnm
, "passwd") == 0)
6185 map
->map_valcolno
= 2;
6186 else if (sm_strcasecmp(map
->map_valcolnm
, "uid") == 0)
6187 map
->map_valcolno
= 3;
6188 else if (sm_strcasecmp(map
->map_valcolnm
, "gid") == 0)
6189 map
->map_valcolno
= 4;
6190 else if (sm_strcasecmp(map
->map_valcolnm
, "gecos") == 0)
6191 map
->map_valcolno
= 5;
6192 else if (sm_strcasecmp(map
->map_valcolnm
, "dir") == 0)
6193 map
->map_valcolno
= 6;
6194 else if (sm_strcasecmp(map
->map_valcolnm
, "shell") == 0)
6195 map
->map_valcolno
= 7;
6198 syserr("User map %s: unknown column name %s",
6199 map
->map_mname
, map
->map_valcolnm
);
6207 ** USER_MAP_LOOKUP -- look up a user in the passwd file.
6212 user_map_lookup(map
, key
, av
, statp
)
6222 sm_dprintf("user_map_lookup(%s, %s)\n",
6223 map
->map_mname
, key
);
6225 *statp
= finduser(key
, &fuzzy
, &user
);
6226 if (*statp
!= EX_OK
)
6228 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
6229 return map_rewrite(map
, key
, strlen(key
), NULL
);
6235 switch (map
->map_valcolno
)
6239 rwval
= user
.mbdb_name
;
6243 rwval
= "x"; /* passwd no longer supported */
6247 (void) sm_snprintf(buf
, sizeof(buf
), "%d",
6248 (int) user
.mbdb_uid
);
6253 (void) sm_snprintf(buf
, sizeof(buf
), "%d",
6254 (int) user
.mbdb_gid
);
6259 rwval
= user
.mbdb_fullname
;
6263 rwval
= user
.mbdb_homedir
;
6267 rwval
= user
.mbdb_shell
;
6270 syserr("user_map %s: bogus field %d",
6271 map
->map_mname
, map
->map_valcolno
);
6274 return map_rewrite(map
, rwval
, strlen(rwval
), av
);
6278 ** Program map type.
6280 ** This provides access to arbitrary programs. It should be used
6281 ** only very sparingly, since there is no way to bound the cost
6282 ** of invoking an arbitrary program.
6286 prog_map_lookup(map
, name
, av
, statp
)
6299 char *argv
[MAXPV
+ 1];
6303 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6304 map
->map_mname
, name
, map
->map_file
);
6307 argv
[i
++] = map
->map_file
;
6308 if (map
->map_rebuild
!= NULL
)
6310 (void) sm_strlcpy(buf
, map
->map_rebuild
, sizeof(buf
));
6311 for (p
= strtok(buf
, " \t"); p
!= NULL
; p
= strtok(NULL
, " \t"))
6322 sm_dprintf("prog_open:");
6323 for (i
= 0; argv
[i
] != NULL
; i
++)
6324 sm_dprintf(" %s", argv
[i
]);
6327 (void) sm_blocksignal(SIGCHLD
);
6328 pid
= prog_open(argv
, &fd
, CurEnv
);
6331 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
6332 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6333 map
->map_mname
, sm_errstring(errno
));
6334 else if (tTd(38, 9))
6335 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6336 map
->map_mname
, sm_errstring(errno
));
6337 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
6341 i
= read(fd
, buf
, sizeof(buf
) - 1);
6344 syserr("prog_map_lookup(%s): read error %s",
6345 map
->map_mname
, sm_errstring(errno
));
6351 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6358 p
= strchr(buf
, '\n');
6362 /* collect the return value */
6363 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
6364 rval
= map_rewrite(map
, name
, strlen(name
), NULL
);
6366 rval
= map_rewrite(map
, buf
, strlen(buf
), av
);
6368 /* now flush any additional output */
6369 while ((i
= read(fd
, buf
, sizeof(buf
))) > 0)
6373 /* wait for the process to terminate */
6375 status
= waitfor(pid
);
6377 (void) sm_releasesignal(SIGCHLD
);
6382 syserr("prog_map_lookup(%s): wait error %s",
6383 map
->map_mname
, sm_errstring(errno
));
6384 *statp
= EX_SOFTWARE
;
6387 else if (WIFEXITED(status
))
6389 if ((*statp
= WEXITSTATUS(status
)) != EX_OK
)
6394 syserr("prog_map_lookup(%s): child died on signal %d",
6395 map
->map_mname
, status
);
6396 *statp
= EX_UNAVAILABLE
;
6402 ** Sequenced map type.
6404 ** Tries each map in order until something matches, much like
6405 ** implicit. Stores go to the first map in the list that can
6408 ** This is slightly unusual in that there are two interfaces.
6409 ** The "sequence" interface lets you stack maps arbitrarily.
6410 ** The "switch" interface builds a sequence map by looking
6411 ** at a system-dependent configuration file such as
6412 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6414 ** We don't need an explicit open, since all maps are
6415 ** opened on demand.
6419 ** SEQ_MAP_PARSE -- Sequenced map parsing
6423 seq_map_parse(map
, ap
)
6430 sm_dprintf("seq_map_parse(%s, %s)\n", map
->map_mname
, ap
);
6437 /* find beginning of map name */
6438 while (isascii(*ap
) && isspace(*ap
))
6441 (isascii(*p
) && isalnum(*p
)) || *p
== '_' || *p
== '.';
6446 while (*p
!= '\0' && (!isascii(*p
) || !isalnum(*p
)))
6453 s
= stab(ap
, ST_MAP
, ST_FIND
);
6456 syserr("Sequence map %s: unknown member map %s",
6457 map
->map_mname
, ap
);
6459 else if (maxmap
>= MAXMAPSTACK
)
6461 syserr("Sequence map %s: too many member maps (%d max)",
6462 map
->map_mname
, MAXMAPSTACK
);
6465 else if (maxmap
< MAXMAPSTACK
)
6467 map
->map_stack
[maxmap
++] = &s
->s_map
;
6475 ** SWITCH_MAP_OPEN -- open a switched map
6477 ** This looks at the system-dependent configuration and builds
6478 ** a sequence map that does the same thing.
6480 ** Every system must define a switch_map_find routine in conf.c
6481 ** that will return the list of service types associated with a
6482 ** given service class.
6486 switch_map_open(map
, mode
)
6492 char *maptype
[MAXMAPSTACK
];
6495 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6496 map
->map_mname
, map
->map_file
, mode
);
6499 nmaps
= switch_map_find(map
->map_file
, maptype
, map
->map_return
);
6502 sm_dprintf("\tswitch_map_find => %d\n", nmaps
);
6503 for (mapno
= 0; mapno
< nmaps
; mapno
++)
6504 sm_dprintf("\t\t%s\n", maptype
[mapno
]);
6506 if (nmaps
<= 0 || nmaps
> MAXMAPSTACK
)
6509 for (mapno
= 0; mapno
< nmaps
; mapno
++)
6512 char nbuf
[MAXNAME
+ 1];
6514 if (maptype
[mapno
] == NULL
)
6516 (void) sm_strlcpyn(nbuf
, sizeof(nbuf
), 3,
6517 map
->map_mname
, ".", maptype
[mapno
]);
6518 s
= stab(nbuf
, ST_MAP
, ST_FIND
);
6521 syserr("Switch map %s: unknown member map %s",
6522 map
->map_mname
, nbuf
);
6526 map
->map_stack
[mapno
] = &s
->s_map
;
6528 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6530 s
->s_map
.map_class
->map_cname
,
6539 ** SEQ_MAP_CLOSE -- close all underlying maps
6549 sm_dprintf("seq_map_close(%s)\n", map
->map_mname
);
6551 for (mapno
= 0; mapno
< MAXMAPSTACK
; mapno
++)
6553 MAP
*mm
= map
->map_stack
[mapno
];
6555 if (mm
== NULL
|| !bitset(MF_OPEN
, mm
->map_mflags
))
6557 mm
->map_mflags
|= MF_CLOSING
;
6558 mm
->map_class
->map_close(mm
);
6559 mm
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
6565 ** SEQ_MAP_LOOKUP -- sequenced map lookup
6569 seq_map_lookup(map
, key
, args
, pstat
)
6577 bool tempfail
= false;
6580 sm_dprintf("seq_map_lookup(%s, %s)\n", map
->map_mname
, key
);
6582 for (mapno
= 0; mapno
< MAXMAPSTACK
; mapbit
<<= 1, mapno
++)
6584 MAP
*mm
= map
->map_stack
[mapno
];
6589 if (!bitset(MF_OPEN
, mm
->map_mflags
) &&
6592 if (bitset(mapbit
, map
->map_return
[MA_UNAVAIL
]))
6594 *pstat
= EX_UNAVAILABLE
;
6600 rv
= mm
->map_class
->map_lookup(mm
, key
, args
, pstat
);
6603 if (*pstat
== EX_TEMPFAIL
)
6605 if (bitset(mapbit
, map
->map_return
[MA_TRYAGAIN
]))
6609 else if (bitset(mapbit
, map
->map_return
[MA_NOTFOUND
]))
6613 *pstat
= EX_TEMPFAIL
;
6614 else if (*pstat
== EX_OK
)
6615 *pstat
= EX_NOTFOUND
;
6620 ** SEQ_MAP_STORE -- sequenced map store
6624 seq_map_store(map
, key
, val
)
6632 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6633 map
->map_mname
, key
, val
);
6635 for (mapno
= 0; mapno
< MAXMAPSTACK
; mapno
++)
6637 MAP
*mm
= map
->map_stack
[mapno
];
6639 if (mm
== NULL
|| !bitset(MF_WRITABLE
, mm
->map_mflags
))
6642 mm
->map_class
->map_store(mm
, key
, val
);
6645 syserr("seq_map_store(%s, %s, %s): no writable map",
6646 map
->map_mname
, key
, val
);
6654 null_map_open(map
, mode
)
6670 null_map_lookup(map
, key
, args
, pstat
)
6676 *pstat
= EX_NOTFOUND
;
6682 null_map_store(map
, key
, val
)
6695 bogus_map_lookup(map
, key
, args
, pstat
)
6701 *pstat
= EX_TEMPFAIL
;
6705 MAPCLASS BogusMapClass
=
6707 "bogus-map", NULL
, 0,
6708 NULL
, bogus_map_lookup
, null_map_store
,
6709 null_map_open
, null_map_close
,
6716 macro_map_lookup(map
, name
, av
, statp
)
6725 sm_dprintf("macro_map_lookup(%s, %s)\n", map
->map_mname
,
6726 name
== NULL
? "NULL" : name
);
6730 (mid
= macid(name
)) == 0)
6737 macdefine(&CurEnv
->e_macro
, A_PERM
, mid
, NULL
);
6739 macdefine(&CurEnv
->e_macro
, A_TEMP
, mid
, av
[1]);
6752 # define DEFAULT_DELIM CONDELSE
6753 # define END_OF_FIELDS -1
6754 # define ERRBUF_SIZE 80
6755 # define MAX_MATCH 32
6757 # define xnalloc(s) memset(xalloc(s), '\0', s);
6761 regex_t
*regex_pattern_buf
; /* xalloc it */
6762 int *regex_subfields
; /* move to type MAP */
6763 char *regex_delim
; /* move to type MAP */
6766 static int parse_fields
__P((char *, int *, int, int));
6767 static char *regex_map_rewrite
__P((MAP
*, const char*, size_t, char **));
6770 parse_fields(s
, ibuf
, blen
, nr_substrings
)
6772 int *ibuf
; /* array */
6773 int blen
; /* number of elements in ibuf */
6774 int nr_substrings
; /* number of substrings in the pattern */
6778 bool lastone
= false;
6780 blen
--; /* for terminating END_OF_FIELDS */
6801 if (val
< 0 || val
>= nr_substrings
)
6803 syserr("field (%d) out of range, only %d substrings in pattern",
6804 val
, nr_substrings
);
6811 syserr("too many fields, %d max", blen
);
6816 ibuf
[i
] = END_OF_FIELDS
;
6821 regex_map_init(map
, ap
)
6826 struct regex_map
*map_p
;
6828 char *sub_param
= NULL
;
6830 static char defdstr
[] = { (char) DEFAULT_DELIM
, '\0' };
6833 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6834 map
->map_mname
, ap
);
6836 pflags
= REG_ICASE
| REG_EXTENDED
| REG_NOSUB
;
6838 map_p
= (struct regex_map
*) xnalloc(sizeof(*map_p
));
6839 map_p
->regex_pattern_buf
= (regex_t
*)xnalloc(sizeof(regex_t
));
6843 while (isascii(*p
) && isspace(*p
))
6850 map
->map_mflags
|= MF_REGEX_NOT
;
6853 case 'f': /* case sensitive */
6854 map
->map_mflags
|= MF_NOFOLDCASE
;
6855 pflags
&= ~REG_ICASE
;
6858 case 'b': /* basic regular expressions */
6859 pflags
&= ~REG_EXTENDED
;
6862 case 's': /* substring match () syntax */
6864 pflags
&= ~REG_NOSUB
;
6867 case 'd': /* delimiter */
6868 map_p
->regex_delim
= ++p
;
6871 case 'a': /* map append */
6875 case 'm': /* matchonly */
6876 map
->map_mflags
|= MF_MATCHONLY
;
6880 map
->map_mflags
|= MF_KEEPQUOTES
;
6884 map
->map_spacesub
= *++p
;
6888 map
->map_mflags
|= MF_DEFER
;
6892 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
6898 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p
, pflags
);
6900 if ((regerr
= regcomp(map_p
->regex_pattern_buf
, p
, pflags
)) != 0)
6903 char errbuf
[ERRBUF_SIZE
];
6905 (void) regerror(regerr
, map_p
->regex_pattern_buf
,
6906 errbuf
, sizeof(errbuf
));
6907 syserr("pattern-compile-error: %s", errbuf
);
6908 sm_free(map_p
->regex_pattern_buf
); /* XXX */
6909 sm_free(map_p
); /* XXX */
6913 if (map
->map_app
!= NULL
)
6914 map
->map_app
= newstr(map
->map_app
);
6915 if (map_p
->regex_delim
!= NULL
)
6916 map_p
->regex_delim
= newstr(map_p
->regex_delim
);
6918 map_p
->regex_delim
= defdstr
;
6920 if (!bitset(REG_NOSUB
, pflags
))
6922 /* substring matching */
6924 int *fields
= (int *) xalloc(sizeof(int) * (MAX_MATCH
+ 1));
6926 substrings
= map_p
->regex_pattern_buf
->re_nsub
+ 1;
6929 sm_dprintf("regex_map_init: nr of substrings %d\n",
6932 if (substrings
>= MAX_MATCH
)
6934 syserr("too many substrings, %d max", MAX_MATCH
);
6935 sm_free(map_p
->regex_pattern_buf
); /* XXX */
6936 sm_free(map_p
); /* XXX */
6939 if (sub_param
!= NULL
&& sub_param
[0] != '\0')
6941 /* optional parameter -sfields */
6942 if (parse_fields(sub_param
, fields
,
6943 MAX_MATCH
+ 1, substrings
) == -1)
6950 /* set default fields */
6951 for (i
= 0; i
< substrings
; i
++)
6953 fields
[i
] = END_OF_FIELDS
;
6955 map_p
->regex_subfields
= fields
;
6960 sm_dprintf("regex_map_init: subfields");
6961 for (ip
= fields
; *ip
!= END_OF_FIELDS
; ip
++)
6962 sm_dprintf(" %d", *ip
);
6966 map
->map_db1
= (ARBPTR_T
) map_p
; /* dirty hack */
6971 regex_map_rewrite(map
, s
, slen
, av
)
6977 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
6978 return map_rewrite(map
, av
[0], strlen(av
[0]), NULL
);
6980 return map_rewrite(map
, s
, slen
, av
);
6984 regex_map_lookup(map
, name
, av
, statp
)
6991 struct regex_map
*map_p
;
6992 regmatch_t pmatch
[MAX_MATCH
];
6998 sm_dprintf("regex_map_lookup: key '%s'\n", name
);
6999 for (cpp
= av
; cpp
!= NULL
&& *cpp
!= NULL
; cpp
++)
7000 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp
);
7003 map_p
= (struct regex_map
*)(map
->map_db1
);
7004 reg_res
= regexec(map_p
->regex_pattern_buf
,
7005 name
, MAX_MATCH
, pmatch
, 0);
7007 if (bitset(MF_REGEX_NOT
, map
->map_mflags
))
7010 if (reg_res
== REG_NOMATCH
)
7011 return regex_map_rewrite(map
, "", (size_t) 0, av
);
7015 if (reg_res
== REG_NOMATCH
)
7018 if (map_p
->regex_subfields
!= NULL
)
7021 static char retbuf
[MAXNAME
];
7022 int fields
[MAX_MATCH
+ 1];
7024 int anglecnt
= 0, cmntcnt
= 0, spacecnt
= 0;
7025 bool quotemode
= false, bslashmode
= false;
7026 register char *dp
, *sp
;
7031 ldp
= retbuf
+ sizeof(retbuf
) - 1;
7035 if (parse_fields(av
[1], fields
, MAX_MATCH
+ 1,
7036 (int) map_p
->regex_pattern_buf
->re_nsub
+ 1) == -1)
7044 ip
= map_p
->regex_subfields
;
7046 for ( ; *ip
!= END_OF_FIELDS
; ip
++)
7050 for (sp
= map_p
->regex_delim
; *sp
; sp
++)
7059 if (*ip
>= MAX_MATCH
||
7060 pmatch
[*ip
].rm_so
< 0 || pmatch
[*ip
].rm_eo
< 0)
7063 sp
= name
+ pmatch
[*ip
].rm_so
;
7064 endp
= name
+ pmatch
[*ip
].rm_eo
;
7065 for (; endp
> sp
; sp
++)
7074 else if (quotemode
&& *sp
!= '"' &&
7079 else switch (*dp
++ = *sp
)
7106 quotemode
= !quotemode
;
7112 if (anglecnt
!= 0 || cmntcnt
!= 0 || quotemode
||
7113 bslashmode
|| spacecnt
!= 0)
7115 sm_syslog(LOG_WARNING
, NOQID
,
7116 "Warning: regex may cause prescan() failure map=%s lookup=%s",
7117 map
->map_mname
, name
);
7123 return regex_map_rewrite(map
, retbuf
, strlen(retbuf
), av
);
7125 return regex_map_rewrite(map
, "", (size_t)0, av
);
7127 #endif /* MAP_REGEX */
7134 # define _DATUM_DEFINED
7135 # include <ns_api.h>
7137 typedef struct ns_map_list
7139 ns_map_t
*map
; /* XXX ns_ ? */
7141 struct ns_map_list
*next
;
7145 ns_map_t_find(mapname
)
7148 static ns_map_list_t
*ns_maps
= NULL
;
7149 ns_map_list_t
*ns_map
;
7151 /* walk the list of maps looking for the correctly named map */
7152 for (ns_map
= ns_maps
; ns_map
!= NULL
; ns_map
= ns_map
->next
)
7154 if (strcmp(ns_map
->mapname
, mapname
) == 0)
7158 /* if we are looking at a NULL ns_map_list_t, then create a new one */
7161 ns_map
= (ns_map_list_t
*) xalloc(sizeof(*ns_map
));
7162 ns_map
->mapname
= newstr(mapname
);
7163 ns_map
->map
= (ns_map_t
*) xalloc(sizeof(*ns_map
->map
));
7164 memset(ns_map
->map
, '\0', sizeof(*ns_map
->map
));
7165 ns_map
->next
= ns_maps
;
7172 nsd_map_lookup(map
, name
, av
, statp
)
7181 char keybuf
[MAXNAME
+ 1];
7185 sm_dprintf("nsd_map_lookup(%s, %s)\n", map
->map_mname
, name
);
7187 buflen
= strlen(name
);
7188 if (buflen
> sizeof(keybuf
) - 1)
7189 buflen
= sizeof(keybuf
) - 1; /* XXX simply cut off? */
7190 memmove(keybuf
, name
, buflen
);
7191 keybuf
[buflen
] = '\0';
7192 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
7195 ns_map
= ns_map_t_find(map
->map_file
);
7199 sm_dprintf("nsd_map_t_find failed\n");
7200 *statp
= EX_UNAVAILABLE
;
7203 r
= ns_lookup(ns_map
, NULL
, map
->map_file
, keybuf
, NULL
,
7205 if (r
== NS_UNAVAIL
|| r
== NS_TRYAGAIN
)
7207 *statp
= EX_TEMPFAIL
;
7213 # endif /* NS_NOPERM */
7219 if (r
!= NS_SUCCESS
)
7221 *statp
= EX_NOTFOUND
;
7227 /* Null out trailing \n */
7228 if ((p
= strchr(buf
, '\n')) != NULL
)
7231 return map_rewrite(map
, buf
, strlen(buf
), av
);
7233 #endif /* MAP_NSD */
7236 arith_map_lookup(map
, name
, av
, statp
)
7246 static char result
[16];
7251 sm_dprintf("arith_map_lookup: key '%s'\n", name
);
7252 for (cpp
= av
; cpp
!= NULL
&& *cpp
!= NULL
; cpp
++)
7253 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp
);
7261 ** read arguments for arith map
7262 ** - no check is made whether they are really numbers
7263 ** - just ignores args after the second
7266 for (++cpp
; cpp
!= NULL
&& *cpp
!= NULL
&& r
< 2; cpp
++)
7267 v
[r
++] = strtol(*cpp
, NULL
, 0);
7269 /* operator and (at least) two operands given? */
7270 if (name
!= NULL
&& r
== 2)
7316 r
= v
[1] - v
[0] + 1;
7319 r
= get_random() % r
+ v
[0];
7326 sm_syslog(LOG_WARNING
, NOQID
,
7327 "arith_map: unknown operator %c",
7328 isprint(*name
) ? *name
: '?');
7332 (void) sm_snprintf(result
, sizeof(result
),
7333 res
? "TRUE" : "FALSE");
7335 (void) sm_snprintf(result
, sizeof(result
), "%ld", r
);
7344 # if NETINET || NETINET6
7345 # include <arpa/inet.h>
7346 # endif /* NETINET || NETINET6 */
7348 # define socket_map_next map_stack[0]
7351 ** SOCKET_MAP_OPEN -- open socket table
7355 socket_map_open(map
, mode
)
7361 SOCKADDR_LEN_T addrlen
= 0;
7367 struct hostent
*hp
= NULL
;
7371 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7372 map
->map_mname
, map
->map_file
, mode
);
7376 /* sendmail doesn't have the ability to write to SOCKET (yet) */
7377 if (mode
!= O_RDONLY
)
7379 /* issue a pseudo-error message */
7380 errno
= SM_EMAPCANTWRITE
;
7384 if (*map
->map_file
== '\0')
7386 syserr("socket map \"%s\": empty or missing socket information",
7391 s
= socket_map_findconn(map
->map_file
);
7392 if (s
->s_socketmap
!= NULL
)
7394 /* Copy open connection */
7395 map
->map_db1
= s
->s_socketmap
->map_db1
;
7397 /* Add this map as head of linked list */
7398 map
->socket_map_next
= s
->s_socketmap
;
7399 s
->s_socketmap
= map
;
7402 sm_dprintf("using cached connection\n");
7407 sm_dprintf("opening new connection\n");
7409 /* following code is ripped from milter.c */
7410 /* XXX It should be put in a library... */
7412 /* protocol:filename or protocol:port@host */
7413 memset(&addr
, '\0', sizeof(addr
));
7415 colon
= strchr(p
, ':');
7423 /* default to AF_UNIX */
7424 addr
.sa
.sa_family
= AF_UNIX
;
7425 # else /* NETUNIX */
7427 /* default to AF_INET */
7428 addr
.sa
.sa_family
= AF_INET
;
7429 # else /* NETINET */
7431 /* default to AF_INET6 */
7432 addr
.sa
.sa_family
= AF_INET6
;
7433 # else /* NETINET6 */
7434 /* no protocols available */
7435 syserr("socket map \"%s\": no valid socket protocols available",
7438 # endif /* NETINET6 */
7439 # endif /* NETINET */
7440 # endif /* NETUNIX */
7443 else if (sm_strcasecmp(p
, "unix") == 0 ||
7444 sm_strcasecmp(p
, "local") == 0)
7445 addr
.sa
.sa_family
= AF_UNIX
;
7446 # endif /* NETUNIX */
7448 else if (sm_strcasecmp(p
, "inet") == 0)
7449 addr
.sa
.sa_family
= AF_INET
;
7450 # endif /* NETINET */
7452 else if (sm_strcasecmp(p
, "inet6") == 0)
7453 addr
.sa
.sa_family
= AF_INET6
;
7454 # endif /* NETINET6 */
7457 # ifdef EPROTONOSUPPORT
7458 errno
= EPROTONOSUPPORT
;
7459 # else /* EPROTONOSUPPORT */
7461 # endif /* EPROTONOSUPPORT */
7462 syserr("socket map \"%s\": unknown socket type %s",
7472 /* default to AF_UNIX */
7473 addr
.sa
.sa_family
= AF_UNIX
;
7476 /* default to AF_INET */
7477 addr
.sa
.sa_family
= AF_INET
;
7478 # else /* NETINET */
7480 /* default to AF_INET6 */
7481 addr
.sa
.sa_family
= AF_INET6
;
7482 # else /* NETINET6 */
7483 syserr("socket map \"%s\": unknown socket type %s",
7486 # endif /* NETINET6 */
7487 # endif /* NETINET */
7488 #endif /* NETUNIX */
7492 if (addr
.sa
.sa_family
== AF_UNIX
)
7494 long sff
= SFF_SAFEDIRPATH
|SFF_OPENASROOT
|SFF_NOLINK
|SFF_EXECOK
;
7497 if (strlen(colon
) >= sizeof(addr
.sunix
.sun_path
))
7499 syserr("socket map \"%s\": local socket name %s too long",
7500 map
->map_mname
, colon
);
7503 errno
= safefile(colon
, RunAsUid
, RunAsGid
, RunAsUserName
, sff
,
7504 S_IRUSR
|S_IWUSR
, NULL
);
7508 /* if not safe, don't create */
7509 syserr("socket map \"%s\": local socket name %s unsafe",
7510 map
->map_mname
, colon
);
7514 (void) sm_strlcpy(addr
.sunix
.sun_path
, colon
,
7515 sizeof(addr
.sunix
.sun_path
));
7516 addrlen
= sizeof(struct sockaddr_un
);
7519 # endif /* NETUNIX */
7520 # if NETINET || NETINET6
7523 || addr
.sa
.sa_family
== AF_INET
7524 # endif /* NETINET */
7526 || addr
.sa
.sa_family
== AF_INET6
7527 # endif /* NETINET6 */
7530 unsigned short port
;
7532 /* Parse port@host */
7533 at
= strchr(colon
, '@');
7536 syserr("socket map \"%s\": bad address %s (expected port@host)",
7537 map
->map_mname
, colon
);
7541 if (isascii(*colon
) && isdigit(*colon
))
7542 port
= htons((unsigned short) atoi(colon
));
7545 # ifdef NO_GETSERVBYNAME
7546 syserr("socket map \"%s\": invalid port number %s",
7547 map
->map_mname
, colon
);
7549 # else /* NO_GETSERVBYNAME */
7550 register struct servent
*sp
;
7552 sp
= getservbyname(colon
, "tcp");
7555 syserr("socket map \"%s\": unknown port name %s",
7556 map
->map_mname
, colon
);
7560 # endif /* NO_GETSERVBYNAME */
7567 end
= strchr(at
, ']');
7572 unsigned long hid
= INADDR_NONE
;
7573 # endif /* NETINET */
7575 struct sockaddr_in6 hid6
;
7576 # endif /* NETINET6 */
7580 if (addr
.sa
.sa_family
== AF_INET
&&
7581 (hid
= inet_addr(&at
[1])) != INADDR_NONE
)
7583 addr
.sin
.sin_addr
.s_addr
= hid
;
7584 addr
.sin
.sin_port
= port
;
7587 # endif /* NETINET */
7589 (void) memset(&hid6
, '\0', sizeof(hid6
));
7590 if (addr
.sa
.sa_family
== AF_INET6
&&
7591 anynet_pton(AF_INET6
, &at
[1],
7592 &hid6
.sin6_addr
) == 1)
7594 addr
.sin6
.sin6_addr
= hid6
.sin6_addr
;
7595 addr
.sin6
.sin6_port
= port
;
7598 # endif /* NETINET6 */
7602 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7603 map
->map_mname
, at
);
7609 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7610 map
->map_mname
, at
);
7616 hp
= sm_gethostbyname(at
, addr
.sa
.sa_family
);
7619 syserr("socket map \"%s\": Unknown host name %s",
7620 map
->map_mname
, at
);
7623 addr
.sa
.sa_family
= hp
->h_addrtype
;
7624 switch (hp
->h_addrtype
)
7628 memmove(&addr
.sin
.sin_addr
,
7629 hp
->h_addr
, INADDRSZ
);
7630 addr
.sin
.sin_port
= port
;
7631 addrlen
= sizeof(struct sockaddr_in
);
7634 # endif /* NETINET */
7638 memmove(&addr
.sin6
.sin6_addr
,
7639 hp
->h_addr
, IN6ADDRSZ
);
7640 addr
.sin6
.sin6_port
= port
;
7641 addrlen
= sizeof(struct sockaddr_in6
);
7644 # endif /* NETINET6 */
7647 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7648 map
->map_mname
, at
, hp
->h_addrtype
);
7651 # endif /* NETINET6 */
7657 # endif /* NETINET || NETINET6 */
7659 syserr("socket map \"%s\": unknown socket protocol",
7664 /* nope, actually connecting */
7667 sock
= socket(addr
.sa
.sa_family
, SOCK_STREAM
, 0);
7672 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7674 sm_errstring(save_errno
));
7678 # endif /* NETINET6 */
7682 if (connect(sock
, (struct sockaddr
*) &addr
, addrlen
) >= 0)
7685 /* couldn't connect.... try next address */
7690 sm_dprintf("socket_open (%s): open %s failed: %s\n",
7691 map
->map_mname
, at
, sm_errstring(save_errno
));
7695 /* try next address */
7696 if (hp
!= NULL
&& hp
->h_addr_list
[addrno
] != NULL
)
7698 switch (addr
.sa
.sa_family
)
7702 memmove(&addr
.sin
.sin_addr
,
7703 hp
->h_addr_list
[addrno
++],
7706 # endif /* NETINET */
7710 memmove(&addr
.sin6
.sin6_addr
,
7711 hp
->h_addr_list
[addrno
++],
7714 # endif /* NETINET6 */
7718 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7723 # endif /* NETINET6 */
7731 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7732 map
->map_mname
, sm_errstring(save_errno
));
7737 # endif /* NETINET6 */
7746 # endif /* NETINET6 */
7747 if ((map
->map_db1
= (ARBPTR_T
) sm_io_open(SmFtStdiofd
,
7755 sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7756 map
->map_mname
, sm_errstring(errno
));
7760 /* Save connection for reuse */
7761 s
->s_socketmap
= map
;
7766 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7768 ** Cache SOCKET connections based on the connection specifier
7769 ** and PID so we don't have multiple connections open to
7770 ** the same server for different maps. Need a separate connection
7771 ** per PID since a parent process may close the map before the
7772 ** child is done with it.
7775 ** conn -- SOCKET map connection specifier
7778 ** Symbol table entry for the SOCKET connection.
7782 socket_map_findconn(conn
)
7786 STAB
*SM_NONVOLATILE s
= NULL
;
7788 nbuf
= sm_stringf_x("%s%c%d", conn
, CONDELSE
, (int) CurrentPid
);
7790 s
= stab(nbuf
, ST_SOCKETMAP
, ST_ENTER
);
7798 ** SOCKET_MAP_CLOSE -- close the socket
7802 socket_map_close(map
)
7809 sm_dprintf("socket_map_close(%s), pid=%ld\n", map
->map_file
,
7812 /* Check if already closed */
7813 if (map
->map_db1
== NULL
)
7816 sm_dprintf("socket_map_close(%s) already closed\n",
7820 sm_io_close((SM_FILE_T
*)map
->map_db1
, SM_TIME_DEFAULT
);
7822 /* Mark all the maps that share the connection as closed */
7823 s
= socket_map_findconn(map
->map_file
);
7824 smap
= s
->s_socketmap
;
7825 while (smap
!= NULL
)
7829 if (tTd(38, 2) && smap
!= map
)
7830 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7831 map
->map_mname
, smap
->map_mname
);
7833 smap
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
);
7834 smap
->map_db1
= NULL
;
7835 next
= smap
->socket_map_next
;
7836 smap
->socket_map_next
= NULL
;
7839 s
->s_socketmap
= NULL
;
7843 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7847 socket_map_lookup(map
, name
, av
, statp
)
7853 unsigned int nettolen
, replylen
, recvlen
;
7854 char *replybuf
, *rval
, *value
, *status
, *key
;
7856 char keybuf
[MAXNAME
+ 1];
7860 f
= (SM_FILE_T
*)map
->map_db1
;
7862 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7863 map
->map_mname
, name
, map
->map_file
);
7865 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
7867 nettolen
= strlen(name
);
7868 if (nettolen
> sizeof(keybuf
) - 1)
7869 nettolen
= sizeof(keybuf
) - 1;
7870 memmove(keybuf
, name
, nettolen
);
7871 keybuf
[nettolen
] = '\0';
7878 nettolen
= strlen(map
->map_mname
) + 1 + strlen(key
);
7879 SM_ASSERT(nettolen
> strlen(map
->map_mname
));
7880 SM_ASSERT(nettolen
> strlen(key
));
7881 if ((sm_io_fprintf(f
, SM_TIME_DEFAULT
, "%u:%s %s,",
7882 nettolen
, map
->map_mname
, key
) == SM_IO_EOF
) ||
7883 (sm_io_flush(f
, SM_TIME_DEFAULT
) != 0) ||
7886 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7888 *statp
= EX_TEMPFAIL
;
7892 if (sm_io_fscanf(f
, SM_TIME_DEFAULT
, "%9u", &replylen
) != 1)
7894 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7896 *statp
= EX_TEMPFAIL
;
7899 if (replylen
> SOCKETMAP_MAXL
)
7901 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7902 map
->map_mname
, replylen
);
7903 *statp
= EX_TEMPFAIL
;
7906 if (sm_io_getc(f
, SM_TIME_DEFAULT
) != ':')
7908 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7910 *statp
= EX_TEMPFAIL
;
7914 replybuf
= (char *) sm_malloc(replylen
+ 1);
7915 if (replybuf
== NULL
)
7917 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7918 map
->map_mname
, replylen
+ 1);
7923 recvlen
= sm_io_read(f
, SM_TIME_DEFAULT
, replybuf
, replylen
);
7924 if (recvlen
< replylen
)
7926 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7927 map
->map_mname
, recvlen
, replylen
);
7928 *statp
= EX_TEMPFAIL
;
7931 if (sm_io_getc(f
, SM_TIME_DEFAULT
) != ',')
7933 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7935 *statp
= EX_TEMPFAIL
;
7939 replybuf
[recvlen
] = '\0';
7940 value
= strchr(replybuf
, ' ');
7946 if (strcmp(status
, "OK") == 0)
7950 /* collect the return value */
7951 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
7952 rval
= map_rewrite(map
, key
, strlen(key
), NULL
);
7954 rval
= map_rewrite(map
, value
, strlen(value
), av
);
7956 else if (strcmp(status
, "NOTFOUND") == 0)
7958 *statp
= EX_NOTFOUND
;
7960 sm_dprintf("socket_map_lookup(%s): %s not found\n",
7961 map
->map_mname
, key
);
7966 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7967 map
->map_mname
, key
, status
,
7968 value
? value
: "");
7969 if ((strcmp(status
, "TEMP") == 0) ||
7970 (strcmp(status
, "TIMEOUT") == 0))
7971 *statp
= EX_TEMPFAIL
;
7972 else if(strcmp(status
, "PERM") == 0)
7973 *statp
= EX_UNAVAILABLE
;
7975 *statp
= EX_PROTOCOL
;
7978 if (replybuf
!= NULL
)
7983 socket_map_close(map
);
7985 if (replybuf
!= NULL
)
7989 #endif /* SOCKETMAP */