2 * Copyright (c) 1998-2003 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.645.2.10 2003/07/24 18:24:17 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 */
43 # if DB_VERSION_MAJOR < 2
44 static bool db_map_open
__P((MAP
*, int, char *, DBTYPE
, const void *));
45 # endif /* DB_VERSION_MAJOR < 2 */
46 # if DB_VERSION_MAJOR == 2
47 static bool db_map_open
__P((MAP
*, int, char *, DBTYPE
, DB_INFO
*));
48 # endif /* DB_VERSION_MAJOR == 2 */
49 # if DB_VERSION_MAJOR > 2
50 static bool db_map_open
__P((MAP
*, int, char *, DBTYPE
, void **));
51 # endif /* DB_VERSION_MAJOR > 2 */
53 static bool extract_canonname
__P((char *, char *, char *, char[], int));
54 static void map_close
__P((STAB
*, int));
55 static void map_init
__P((STAB
*, int));
57 static STAB
* ldapmap_findconn
__P((SM_LDAP_STRUCT
*));
60 static bool nisplus_getcanonname
__P((char *, int, int *));
63 static bool nis_getcanonname
__P((char *, int, int *));
66 static bool ni_getcanonname
__P((char *, int, int *));
68 static bool text_getcanonname
__P((char *, int, int *));
70 /* default error message for trying to open a map in write mode */
72 # define SM_EMAPCANTWRITE ENOSYS
75 # define SM_EMAPCANTWRITE EFTYPE
77 # define SM_EMAPCANTWRITE ENXIO
82 ** MAP.C -- implementations for various map classes.
84 ** Each map class implements a series of functions:
86 ** bool map_parse(MAP *map, char *args)
87 ** Parse the arguments from the config file. Return true
88 ** if they were ok, false otherwise. Fill in map with the
91 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
92 ** Look up the key in the given map. If found, do any
93 ** rewriting the map wants (including "args" if desired)
94 ** and return the value. Set *pstat to the appropriate status
95 ** on error and return NULL. Args will be NULL if called
96 ** from the alias routines, although this should probably
97 ** not be relied upon. It is suggested you call map_rewrite
98 ** to return the results -- it takes care of null termination
99 ** and uses a dynamically expanded buffer as needed.
101 ** void map_store(MAP *map, char *key, char *value)
102 ** Store the key:value pair in the map.
104 ** bool map_open(MAP *map, int mode)
105 ** Open the map for the indicated mode. Mode should
106 ** be either O_RDONLY or O_RDWR. Return true if it
107 ** was opened successfully, false otherwise. If the open
108 ** failed and the MF_OPTIONAL flag is not set, it should
109 ** also print an error. If the MF_ALIAS bit is set
110 ** and this map class understands the @:@ convention, it
111 ** should call aliaswait() before returning.
113 ** void map_close(MAP *map)
116 ** This file also includes the implementation for getcanonname.
117 ** It is currently implemented in a pretty ad-hoc manner; it ought
118 ** to be more properly integrated into the map structure.
121 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
122 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */
123 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
124 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
125 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
128 ** MAP_PARSEARGS -- parse config line arguments for database lookup
130 ** This is a generic version of the map_parse method.
133 ** map -- the map being initialized.
134 ** ap -- a pointer to the args on the config line.
137 ** true -- if everything parsed OK.
138 ** false -- otherwise.
141 ** null terminates the filename; stores it in map
145 map_parseargs(map
, ap
)
149 register char *p
= ap
;
152 ** There is no check whether there is really an argument,
153 ** but that's not important enough to warrant extra code.
156 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
157 map
->map_spacesub
= SpaceSub
; /* default value */
160 while (isascii(*p
) && isspace(*p
))
167 map
->map_mflags
|= MF_INCLNULL
;
168 map
->map_mflags
&= ~MF_TRY0NULL
;
172 map
->map_mflags
&= ~MF_TRY1NULL
;
176 map
->map_mflags
|= MF_OPTIONAL
;
180 map
->map_mflags
|= MF_NOFOLDCASE
;
184 map
->map_mflags
|= MF_MATCHONLY
;
188 map
->map_mflags
|= MF_APPEND
;
192 map
->map_mflags
|= MF_KEEPQUOTES
;
204 while (isascii(*++p
) && isspace(*p
))
206 map
->map_keycolnm
= p
;
210 while (isascii(*++p
) && isspace(*p
))
212 map
->map_valcolnm
= p
;
217 map
->map_coldelim
= *p
;
223 map
->map_coldelim
= '\n';
227 map
->map_coldelim
= '\t';
231 map
->map_coldelim
= '\\';
237 map
->map_mflags
|= MF_NODEFER
;
242 map
->map_spacesub
= *++p
;
246 map
->map_mflags
|= MF_DEFER
;
250 syserr("Illegal option %c map %s", *p
, map
->map_mname
);
253 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
258 if (map
->map_app
!= NULL
)
259 map
->map_app
= newstr(map
->map_app
);
260 if (map
->map_tapp
!= NULL
)
261 map
->map_tapp
= newstr(map
->map_tapp
);
262 if (map
->map_keycolnm
!= NULL
)
263 map
->map_keycolnm
= newstr(map
->map_keycolnm
);
264 if (map
->map_valcolnm
!= NULL
)
265 map
->map_valcolnm
= newstr(map
->map_valcolnm
);
270 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
274 map
->map_file
= newstr(map
->map_file
);
277 while (*p
!= '\0' && isascii(*p
) && isspace(*p
))
280 map
->map_rebuild
= newstr(p
);
282 if (map
->map_file
== NULL
&&
283 !bitset(MCF_OPTFILE
, map
->map_class
->map_cflags
))
285 syserr("No file name for %s map %s",
286 map
->map_class
->map_cname
, map
->map_mname
);
292 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
294 ** It also adds the map_app string. It can be used as a utility
295 ** in the map_lookup method.
298 ** map -- the map that causes this.
299 ** s -- the string to rewrite, NOT necessarily null terminated.
300 ** slen -- the length of s.
301 ** av -- arguments to interpolate into buf.
304 ** Pointer to rewritten result. This is static data that
305 ** should be copied if it is to be saved!
309 map_rewrite(map
, s
, slen
, av
)
311 register const char *s
;
321 static size_t buflen
= 0;
322 static char *buf
= NULL
;
326 sm_dprintf("map_rewrite(%.*s), av =", (int) slen
, s
);
328 sm_dprintf(" (nullv)");
331 for (avp
= av
; *avp
!= NULL
; avp
++)
332 sm_dprintf("\n\t%s", *avp
);
337 /* count expected size of output (can safely overestimate) */
343 while (l
-- > 0 && (c
= *sp
++) != '\0')
350 if (!(isascii(c
) && isdigit(c
)))
352 for (avp
= av
; --c
>= '0' && *avp
!= NULL
; avp
++)
359 if (map
->map_app
!= NULL
)
360 len
+= strlen(map
->map_app
);
363 /* need to malloc additional space */
367 buf
= sm_pmalloc_x(buflen
);
373 memmove(bp
, s
, slen
);
376 /* assert(len > slen); */
381 while (slen
-- > 0 && (c
= *s
++) != '\0')
391 if (slen
-- <= 0 || (c
= *s
++) == '\0')
395 if (!(isascii(c
) && isdigit(c
)))
402 for (avp
= av
; --c
>= '0' && *avp
!= NULL
; avp
++)
407 /* transliterate argument into output string */
408 for (ap
= *avp
; (c
= *ap
++) != '\0' && len
> 0; --len
)
412 if (map
->map_app
!= NULL
&& len
> 0)
413 (void) sm_strlcpy(bp
, map
->map_app
, len
);
417 sm_dprintf("map_rewrite => %s\n", buf
);
421 ** INITMAPS -- rebuild alias maps
434 checkfd012("entering initmaps");
436 stabapply(map_init
, 0);
438 checkfd012("exiting initmaps");
442 ** MAP_INIT -- rebuild a map
445 ** s -- STAB entry: if map: try to rebuild
446 ** unused -- unused variable
452 ** will close already open rebuildable map.
463 /* has to be a map */
464 if (s
->s_symtype
!= ST_MAP
)
468 if (!bitset(MF_VALID
, map
->map_mflags
))
472 sm_dprintf("map_init(%s:%s, %s)\n",
473 map
->map_class
->map_cname
== NULL
? "NULL" :
474 map
->map_class
->map_cname
,
475 map
->map_mname
== NULL
? "NULL" : map
->map_mname
,
476 map
->map_file
== NULL
? "NULL" : map
->map_file
);
478 if (!bitset(MF_ALIAS
, map
->map_mflags
) ||
479 !bitset(MCF_REBUILDABLE
, map
->map_class
->map_cflags
))
482 sm_dprintf("\tnot rebuildable\n");
486 /* if already open, close it (for nested open) */
487 if (bitset(MF_OPEN
, map
->map_mflags
))
489 map
->map_mflags
|= MF_CLOSING
;
490 map
->map_class
->map_close(map
);
491 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
494 (void) rebuildaliases(map
, false);
498 ** OPENMAP -- open a map
501 ** map -- map to open (it must not be open).
504 ** whether open succeeded.
511 bool restore
= false;
512 bool savehold
= HoldErrs
;
513 bool savequick
= QuickAbort
;
514 int saveerrors
= Errors
;
516 if (!bitset(MF_VALID
, map
->map_mflags
))
519 /* better safe than sorry... */
520 if (bitset(MF_OPEN
, map
->map_mflags
))
523 /* Don't send a map open error out via SMTP */
524 if ((OnlyOneError
|| QuickAbort
) &&
525 (OpMode
== MD_SMTP
|| OpMode
== MD_DAEMON
))
533 if (map
->map_class
->map_open(map
, O_RDONLY
))
536 sm_dprintf("openmap()\t%s:%s %s: valid\n",
537 map
->map_class
->map_cname
== NULL
? "NULL" :
538 map
->map_class
->map_cname
,
539 map
->map_mname
== NULL
? "NULL" :
541 map
->map_file
== NULL
? "NULL" :
543 map
->map_mflags
|= MF_OPEN
;
544 map
->map_pid
= CurrentPid
;
549 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
550 map
->map_class
->map_cname
== NULL
? "NULL" :
551 map
->map_class
->map_cname
,
552 map
->map_mname
== NULL
? "NULL" :
554 map
->map_file
== NULL
? "NULL" :
556 errno
== 0 ? "" : ": ",
557 errno
== 0 ? "" : sm_errstring(errno
));
558 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
560 extern MAPCLASS BogusMapClass
;
562 map
->map_orgclass
= map
->map_class
;
563 map
->map_class
= &BogusMapClass
;
564 map
->map_mflags
|= MF_OPEN
|MF_OPENBOGUS
;
565 map
->map_pid
= CurrentPid
;
569 /* don't try again */
570 map
->map_mflags
&= ~MF_VALID
;
578 QuickAbort
= savequick
;
581 return bitset(MF_OPEN
, map
->map_mflags
);
584 ** CLOSEMAPS -- close all open maps opened by the current pid.
587 ** bogus -- only close bogus maps.
597 stabapply(map_close
, bogus
);
600 ** MAP_CLOSE -- close a map opened by the current pid.
603 ** s -- STAB entry: if map: try to close
604 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
614 int bogus
; /* int because of stabapply(), used as bool */
617 extern MAPCLASS BogusMapClass
;
619 if (s
->s_symtype
!= ST_MAP
)
625 ** close the map iff:
626 ** it is valid and open and opened by this process
627 ** and (!bogus or it's a bogus map or it is not persistent)
628 ** negate this: return iff
629 ** it is not valid or it is not open or not opened by this process
630 ** or (bogus and it's not a bogus map and it's not not-persistent)
633 if (!bitset(MF_VALID
, map
->map_mflags
) ||
634 !bitset(MF_OPEN
, map
->map_mflags
) ||
635 bitset(MF_CLOSING
, map
->map_mflags
) ||
636 map
->map_pid
!= CurrentPid
||
637 (bogus
&& map
->map_class
!= &BogusMapClass
&&
638 !bitset(MCF_NOTPERSIST
, map
->map_class
->map_cflags
)))
641 if (map
->map_class
== &BogusMapClass
&& map
->map_orgclass
!= NULL
&&
642 map
->map_orgclass
!= &BogusMapClass
)
643 map
->map_class
= map
->map_orgclass
;
645 sm_dprintf("closemaps: closing %s (%s)\n",
646 map
->map_mname
== NULL
? "NULL" : map
->map_mname
,
647 map
->map_file
== NULL
? "NULL" : map
->map_file
);
649 if (!bitset(MF_OPENBOGUS
, map
->map_mflags
))
651 map
->map_mflags
|= MF_CLOSING
;
652 map
->map_class
->map_close(map
);
654 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_OPENBOGUS
|MF_CLOSING
);
657 ** GETCANONNAME -- look up name using service switch
660 ** host -- the host name to look up.
661 ** hbsize -- the size of the host buffer.
662 ** trymx -- if set, try MX records.
663 ** pttl -- pointer to return TTL (can be NULL).
666 ** true -- if the host was found.
667 ** false -- otherwise.
671 getcanonname(host
, hbsize
, trymx
, pttl
)
680 bool got_tempfail
= false;
682 char *maptype
[MAXMAPSTACK
];
683 short mapreturn
[MAXMAPACTIONS
];
685 nmaps
= switch_map_find("hosts", maptype
, mapreturn
);
687 *pttl
= SM_DEFAULT_TTL
;
688 for (mapno
= 0; mapno
< nmaps
; mapno
++)
693 sm_dprintf("getcanonname(%s), trying %s\n",
694 host
, maptype
[mapno
]);
695 if (strcmp("files", maptype
[mapno
]) == 0)
697 found
= text_getcanonname(host
, hbsize
, &status
);
700 else if (strcmp("nis", maptype
[mapno
]) == 0)
702 found
= nis_getcanonname(host
, hbsize
, &status
);
706 else if (strcmp("nisplus", maptype
[mapno
]) == 0)
708 found
= nisplus_getcanonname(host
, hbsize
, &status
);
712 else if (strcmp("dns", maptype
[mapno
]) == 0)
714 found
= dns_getcanonname(host
, hbsize
, trymx
, &status
, pttl
);
716 #endif /* NAMED_BIND */
718 else if (strcmp("netinfo", maptype
[mapno
]) == 0)
720 found
= ni_getcanonname(host
, hbsize
, &status
);
726 status
= EX_UNAVAILABLE
;
730 ** Heuristic: if $m is not set, we are running during system
731 ** startup. In this case, when a name is apparently found
732 ** but has no dot, treat is as not found. This avoids
733 ** problems if /etc/hosts has no FQDN but is listed first
734 ** in the service switch.
738 (macvalue('m', CurEnv
) != NULL
|| strchr(host
, '.') != NULL
))
741 /* see if we should continue */
742 if (status
== EX_TEMPFAIL
)
747 else if (status
== EX_NOTFOUND
)
751 if (bitset(1 << mapno
, mapreturn
[i
]))
760 sm_dprintf("getcanonname(%s), found\n", host
);
763 ** If returned name is still single token, compensate
764 ** by tagging on $m. This is because some sites set
765 ** up their DNS or NIS databases wrong.
768 if ((d
= strchr(host
, '.')) == NULL
|| d
[1] == '\0')
770 d
= macvalue('m', CurEnv
);
772 hbsize
> (int) (strlen(host
) + strlen(d
) + 1))
774 if (host
[strlen(host
) - 1] != '.')
775 (void) sm_strlcat2(host
, ".", d
,
778 (void) sm_strlcat(host
, d
, hbsize
);
787 sm_dprintf("getcanonname(%s), failed, status=%d\n", host
,
791 SM_SET_H_ERRNO(TRY_AGAIN
);
793 SM_SET_H_ERRNO(HOST_NOT_FOUND
);
798 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
801 ** name -- the name against which to match.
802 ** dot -- where to reinsert '.' to get FQDN
803 ** line -- the /etc/hosts line.
804 ** cbuf -- the location to store the result.
805 ** cbuflen -- the size of cbuf.
808 ** true -- if the line matched the desired name.
809 ** false -- otherwise.
813 extract_canonname(name
, dot
, line
, cbuf
, cbuflen
)
830 char nbuf
[MAXNAME
+ 1];
832 p
= get_column(line
, i
, '\0', nbuf
, sizeof nbuf
);
837 if (cbuf
[0] == '\0' ||
838 (strchr(cbuf
, '.') == NULL
&& strchr(p
, '.') != NULL
))
840 (void) sm_strlcpy(cbuf
, p
, cbuflen
);
842 if (sm_strcasecmp(name
, p
) == 0)
844 else if (dot
!= NULL
)
846 /* try looking for the FQDN as well */
848 if (sm_strcasecmp(name
, p
) == 0)
853 if (found
&& strchr(cbuf
, '.') == NULL
)
855 /* try to add a domain on the end of the name */
856 char *domain
= macvalue('m', CurEnv
);
858 if (domain
!= NULL
&&
859 strlen(domain
) + (i
= strlen(cbuf
)) + 1 < (size_t) cbuflen
)
863 (void) sm_strlcpy(p
, domain
, cbuflen
- i
- 1);
876 # include "sm_resolve.h"
877 # if NETINET || NETINET6
878 # include <arpa/inet.h>
879 # endif /* NETINET || NETINET6 */
882 ** DNS_MAP_OPEN -- stub to check proper value for dns map type
886 dns_map_open(map
, mode
)
891 sm_dprintf("dns_map_open(%s, %d)\n", map
->map_mname
, mode
);
894 if (mode
!= O_RDONLY
)
896 /* issue a pseudo-error message */
897 errno
= SM_EMAPCANTWRITE
;
904 ** DNS_MAP_PARSEARGS -- parse dns map definition args.
907 ** map -- pointer to MAP
908 ** args -- pointer to the args on the config line.
911 ** true -- if everything parsed OK.
912 ** false -- otherwise.
915 # if _FFR_DNSMAP_MULTILIMIT
916 # if !_FFR_DNSMAP_MULTI
917 ERROR README
: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
918 # endif /* ! _FFR_DNSMAP_MULTI */
919 # endif /* _FFR_DNSMAP_MULTILIMIT */
921 # if _FFR_DNSMAP_MULTI
922 # if _FFR_DNSMAP_MULTILIMIT
923 # define map_sizelimit map_lockfd /* overload field */
924 # endif /* _FFR_DNSMAP_MULTILIMIT */
925 # endif /* _FFR_DNSMAP_MULTI */
933 dns_map_parseargs(map
,args
)
937 register char *p
= args
;
938 struct dns_map
*map_p
;
940 map_p
= (struct dns_map
*) xalloc(sizeof *map_p
);
941 map_p
->dns_m_type
= -1;
942 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
946 while (isascii(*p
) && isspace(*p
))
953 map
->map_mflags
|= MF_INCLNULL
;
954 map
->map_mflags
&= ~MF_TRY0NULL
;
958 map
->map_mflags
&= ~MF_TRY1NULL
;
962 map
->map_mflags
|= MF_OPTIONAL
;
966 map
->map_mflags
|= MF_NOFOLDCASE
;
970 map
->map_mflags
|= MF_MATCHONLY
;
974 map
->map_mflags
|= MF_APPEND
;
978 map
->map_mflags
|= MF_KEEPQUOTES
;
982 map
->map_mflags
|= MF_NODEFER
;
1001 map
->map_timeout
= convtime(p
, 's');
1008 while (isascii(*++p
) && isspace(*p
))
1010 map
->map_retry
= atoi(p
);
1013 # if _FFR_DNSMAP_MULTI
1016 map
->map_coldelim
= *p
;
1022 map
->map_coldelim
= '\n';
1026 map
->map_coldelim
= '\t';
1030 map
->map_coldelim
= '\\';
1035 # if _FFR_DNSMAP_MULTILIMIT
1037 while (isascii(*++p
) && isspace(*p
))
1039 map
->map_sizelimit
= atoi(p
);
1041 # endif /* _FFR_DNSMAP_MULTILIMIT */
1042 # endif /* _FFR_DNSMAP_MULTI */
1044 /* Start of dns_map specific args */
1045 case 'R': /* search field */
1049 while (isascii(*++p
) && isspace(*p
))
1054 map_p
->dns_m_type
= dns_string_to_type(p
);
1057 if (map_p
->dns_m_type
< 0)
1058 syserr("dns map %s: wrong type %s",
1063 # if _FFR_DNSMAP_BASE
1064 case 'B': /* base domain */
1068 while (isascii(*++p
) && isspace(*p
))
1075 ** slight abuse of map->map_file; it isn't
1076 ** used otherwise in this map type.
1079 map
->map_file
= newstr(p
);
1084 # endif /* _FFR_DNSMAP_BASE */
1087 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
1092 if (map_p
->dns_m_type
< 0)
1093 syserr("dns map %s: missing -R type", map
->map_mname
);
1094 if (map
->map_app
!= NULL
)
1095 map
->map_app
= newstr(map
->map_app
);
1096 if (map
->map_tapp
!= NULL
)
1097 map
->map_tapp
= newstr(map
->map_tapp
);
1100 ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
1101 ** Even if this assumption is wrong, we use only one byte,
1102 ** so it doesn't really matter.
1105 map
->map_db1
= (ARBPTR_T
) map_p
;
1110 ** DNS_MAP_LOOKUP -- perform dns map lookup.
1113 ** map -- pointer to MAP
1114 ** name -- name to lookup
1115 ** av -- arguments to interpolate into buf.
1116 ** statp -- pointer to status (EX_)
1119 ** result of lookup if succeeded.
1120 ** NULL -- otherwise.
1124 dns_map_lookup(map
, name
, av
, statp
)
1130 # if _FFR_DNSMAP_MULTI
1131 # if _FFR_DNSMAP_MULTILIMIT
1133 # endif /* _FFR_DNSMAP_MULTILIMIT */
1134 # endif /* _FFR_DNSMAP_MULTI */
1135 char *vp
= NULL
, *result
= NULL
;
1137 struct dns_map
*map_p
;
1138 RESOURCE_RECORD_T
*rr
= NULL
;
1139 DNS_REPLY_T
*r
= NULL
;
1141 static char buf6
[INET6_ADDRSTRLEN
];
1142 # endif /* NETINET6 */
1145 sm_dprintf("dns_map_lookup(%s, %s)\n",
1146 map
->map_mname
, name
);
1148 map_p
= (struct dns_map
*)(map
->map_db1
);
1149 # if _FFR_DNSMAP_BASE
1150 if (map
->map_file
!= NULL
&& *map
->map_file
!= '\0')
1155 len
= strlen(map
->map_file
) + strlen(name
) + 2;
1156 appdomain
= (char *) sm_malloc(len
);
1157 if (appdomain
== NULL
)
1159 *statp
= EX_UNAVAILABLE
;
1162 (void) sm_strlcpyn(appdomain
, len
, 3, name
, ".", map
->map_file
);
1163 r
= dns_lookup_int(appdomain
, C_IN
, map_p
->dns_m_type
,
1164 map
->map_timeout
, map
->map_retry
);
1168 # endif /* _FFR_DNSMAP_BASE */
1170 r
= dns_lookup_int(name
, C_IN
, map_p
->dns_m_type
,
1171 map
->map_timeout
, map
->map_retry
);
1177 if (h_errno
== TRY_AGAIN
|| transienterror(errno
))
1178 *statp
= EX_TEMPFAIL
;
1180 *statp
= EX_NOTFOUND
;
1184 for (rr
= r
->dns_r_head
; rr
!= NULL
; rr
= rr
->rr_next
)
1189 switch (rr
->rr_type
)
1193 value
= rr
->rr_u
.rr_txt
;
1197 value
= rr
->rr_u
.rr_txt
;
1201 value
= rr
->rr_u
.rr_mx
->mx_r_domain
;
1205 value
= rr
->rr_u
.rr_srv
->srv_r_target
;
1209 value
= rr
->rr_u
.rr_txt
;
1213 value
= rr
->rr_u
.rr_txt
;
1217 value
= rr
->rr_u
.rr_mx
->mx_r_domain
;
1222 value
= inet_ntoa(*(rr
->rr_u
.rr_a
));
1224 # endif /* NETINET */
1228 value
= anynet_ntop(rr
->rr_u
.rr_aaaa
, buf6
,
1231 # endif /* NETINET6 */
1234 (void) strreplnonprt(value
, 'X');
1235 if (map_p
->dns_m_type
!= rr
->rr_type
)
1238 sm_dprintf("\tskipping type %s (%d) value %s\n",
1239 type
!= NULL
? type
: "<UNKNOWN>",
1241 value
!= NULL
? value
: "<NO VALUE>");
1246 if (rr
->rr_type
== T_AAAA
&& value
== NULL
)
1249 *statp
= EX_DATAERR
;
1251 sm_dprintf("\tbad T_AAAA conversion\n");
1254 # endif /* NETINET6 */
1256 sm_dprintf("\tfound type %s (%d) value %s\n",
1257 type
!= NULL
? type
: "<UNKNOWN>",
1259 value
!= NULL
? value
: "<NO VALUE>");
1260 # if _FFR_DNSMAP_MULTI
1261 if (value
!= NULL
&&
1262 (map
->map_coldelim
== '\0' ||
1263 # if _FFR_DNSMAP_MULTILIMIT
1264 map
->map_sizelimit
== 1 ||
1265 # endif /* _FFR_DNSMAP_MULTILIMIT */
1266 bitset(MF_MATCHONLY
, map
->map_mflags
)))
1268 /* Only care about the first match */
1272 else if (vp
== NULL
)
1279 /* concatenate the results */
1283 sz
= strlen(vp
) + strlen(value
) + 2;
1285 (void) sm_snprintf(new, sz
, "%s%c%s",
1286 vp
, map
->map_coldelim
, value
);
1289 # if _FFR_DNSMAP_MULTILIMIT
1290 if (map
->map_sizelimit
> 0 &&
1291 ++resnum
>= map
->map_sizelimit
)
1293 # endif /* _FFR_DNSMAP_MULTILIMIT */
1295 # else /* _FFR_DNSMAP_MULTI */
1298 # endif /* _FFR_DNSMAP_MULTI */
1303 *statp
= EX_NOTFOUND
;
1305 sm_dprintf("\tno match found\n");
1309 # if _FFR_DNSMAP_MULTI
1310 /* Cleanly truncate for rulesets */
1311 truncate_at_delim(vp
, PSBUFSIZE
/ 2, map
->map_coldelim
);
1312 # endif /* _FFR_DNSMAP_MULTI */
1317 sm_syslog(LOG_INFO
, CurEnv
->e_id
, "dns %.100s => %s",
1319 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
1320 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
1322 result
= map_rewrite(map
, vp
, vsize
, av
);
1325 # if _FFR_DNSMAP_MULTI
1328 # endif /* _FFR_DNSMAP_MULTI */
1333 # endif /* DNSMAP */
1334 #endif /* NAMED_BIND */
1343 ** NDBM_MAP_OPEN -- DBM-style map open
1347 ndbm_map_open(map
, mode
)
1357 int smode
= S_IREAD
;
1358 char dirfile
[MAXPATHLEN
];
1359 char pagfile
[MAXPATHLEN
];
1361 struct stat std
, stp
;
1364 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1365 map
->map_mname
, map
->map_file
, mode
);
1366 map
->map_lockfd
= -1;
1369 /* do initial file and directory checks */
1370 if (sm_strlcpyn(dirfile
, sizeof dirfile
, 2,
1371 map
->map_file
, ".dir") >= sizeof dirfile
||
1372 sm_strlcpyn(pagfile
, sizeof pagfile
, 2,
1373 map
->map_file
, ".pag") >= sizeof pagfile
)
1376 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1377 syserr("dbm map \"%s\": map file %s name too long",
1378 map
->map_mname
, map
->map_file
);
1381 sff
= SFF_ROOTOK
|SFF_REGONLY
;
1385 if (!bitnset(DBS_WRITEMAPTOSYMLINK
, DontBlameSendmail
))
1387 if (!bitnset(DBS_WRITEMAPTOHARDLINK
, DontBlameSendmail
))
1393 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
1396 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
1397 sff
|= SFF_SAFEDIRPATH
;
1398 ret
= safefile(dirfile
, RunAsUid
, RunAsGid
, RunAsUserName
,
1401 ret
= safefile(pagfile
, RunAsUid
, RunAsGid
, RunAsUserName
,
1406 char *prob
= "unsafe";
1408 /* cannot open this map */
1412 sm_dprintf("\t%s map file: %d\n", prob
, ret
);
1413 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1414 syserr("dbm map \"%s\": %s map file %s",
1415 map
->map_mname
, prob
, map
->map_file
);
1418 if (std
.st_mode
== ST_MODE_NOFILE
)
1419 mode
|= O_CREAT
|O_EXCL
;
1422 if (mode
== O_RDONLY
)
1425 mode
|= O_TRUNC
|O_EXLOCK
;
1426 # else /* LOCK_ON_OPEN */
1427 if ((mode
& O_ACCMODE
) == O_RDWR
)
1431 ** Warning: race condition. Try to lock the file as
1432 ** quickly as possible after opening it.
1433 ** This may also have security problems on some systems,
1434 ** but there isn't anything we can do about it.
1438 # else /* NOFTRUNCATE */
1440 ** This ugly code opens the map without truncating it,
1441 ** locks the file, then truncates it. Necessary to
1442 ** avoid race conditions.
1447 long sff
= SFF_CREAT
|SFF_OPENASROOT
;
1449 if (!bitnset(DBS_WRITEMAPTOSYMLINK
, DontBlameSendmail
))
1451 if (!bitnset(DBS_WRITEMAPTOHARDLINK
, DontBlameSendmail
))
1454 dirfd
= safeopen(dirfile
, mode
, DBMMODE
, sff
);
1455 pagfd
= safeopen(pagfile
, mode
, DBMMODE
, sff
);
1457 if (dirfd
< 0 || pagfd
< 0)
1461 (void) close(dirfd
);
1463 (void) close(pagfd
);
1465 syserr("ndbm_map_open: cannot create database %s",
1469 if (ftruncate(dirfd
, (off_t
) 0) < 0 ||
1470 ftruncate(pagfd
, (off_t
) 0) < 0)
1473 (void) close(dirfd
);
1474 (void) close(pagfd
);
1476 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1481 /* if new file, get "before" bits for later filechanged check */
1482 if (std
.st_mode
== ST_MODE_NOFILE
&&
1483 (fstat(dirfd
, &std
) < 0 || fstat(pagfd
, &stp
) < 0))
1486 (void) close(dirfd
);
1487 (void) close(pagfd
);
1489 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1494 /* have to save the lock for the duration (bletch) */
1495 map
->map_lockfd
= dirfd
;
1496 (void) close(pagfd
);
1498 /* twiddle bits for dbm_open */
1499 mode
&= ~(O_CREAT
|O_EXCL
);
1500 # endif /* NOFTRUNCATE */
1502 # endif /* LOCK_ON_OPEN */
1504 /* open the database */
1505 dbm
= dbm_open(map
->map_file
, mode
, DBMMODE
);
1509 if (bitset(MF_ALIAS
, map
->map_mflags
) &&
1510 aliaswait(map
, ".pag", false))
1512 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1513 if (map
->map_lockfd
>= 0)
1514 (void) close(map
->map_lockfd
);
1515 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1517 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1518 syserr("Cannot open DBM database %s", map
->map_file
);
1521 dfd
= dbm_dirfno(dbm
);
1522 pfd
= dbm_pagfno(dbm
);
1525 /* heuristic: if files are linked, this is actually gdbm */
1527 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1528 if (map
->map_lockfd
>= 0)
1529 (void) close(map
->map_lockfd
);
1530 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1532 syserr("dbm map \"%s\": cannot support GDBM",
1537 if (filechanged(dirfile
, dfd
, &std
) ||
1538 filechanged(pagfile
, pfd
, &stp
))
1542 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1543 if (map
->map_lockfd
>= 0)
1544 (void) close(map
->map_lockfd
);
1545 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1547 syserr("ndbm_map_open(%s): file changed after open",
1552 map
->map_db1
= (ARBPTR_T
) dbm
;
1555 ** Need to set map_mtime before the call to aliaswait()
1556 ** as aliaswait() will call map_lookup() which requires
1557 ** map_mtime to be set
1560 if (fstat(pfd
, &st
) >= 0)
1561 map
->map_mtime
= st
.st_mtime
;
1563 if (mode
== O_RDONLY
)
1567 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_UN
);
1569 (void) lockfile(pfd
, map
->map_file
, ".pag", LOCK_UN
);
1570 # endif /* LOCK_ON_OPEN */
1571 if (bitset(MF_ALIAS
, map
->map_mflags
) &&
1572 !aliaswait(map
, ".pag", true))
1577 map
->map_mflags
|= MF_LOCKED
;
1578 if (geteuid() == 0 && TrustedUid
!= 0)
1581 if (fchown(dfd
, TrustedUid
, -1) < 0 ||
1582 fchown(pfd
, TrustedUid
, -1) < 0)
1586 sm_syslog(LOG_ALERT
, NOQID
,
1587 "ownership change on %s failed: %s",
1588 map
->map_file
, sm_errstring(err
));
1589 message("050 ownership change on %s failed: %s",
1590 map
->map_file
, sm_errstring(err
));
1592 # else /* HASFCHOWN */
1593 sm_syslog(LOG_ALERT
, NOQID
,
1594 "no fchown(): cannot change ownership on %s",
1596 message("050 no fchown(): cannot change ownership on %s",
1598 # endif /* HASFCHOWN */
1606 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1610 ndbm_map_lookup(map
, name
, av
, statp
)
1618 char keybuf
[MAXNAME
+ 1];
1622 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1623 map
->map_mname
, name
);
1626 key
.dsize
= strlen(name
);
1627 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
1629 if (key
.dsize
> sizeof keybuf
- 1)
1630 key
.dsize
= sizeof keybuf
- 1;
1631 memmove(keybuf
, key
.dptr
, key
.dsize
);
1632 keybuf
[key
.dsize
] = '\0';
1637 dfd
= dbm_dirfno((DBM
*) map
->map_db1
);
1638 if (dfd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
1639 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_SH
);
1640 pfd
= dbm_pagfno((DBM
*) map
->map_db1
);
1641 if (pfd
< 0 || fstat(pfd
, &stbuf
) < 0 ||
1642 stbuf
.st_mtime
> map
->map_mtime
)
1644 /* Reopen the database to sync the cache */
1645 int omode
= bitset(map
->map_mflags
, MF_WRITABLE
) ? O_RDWR
1648 if (dfd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
1649 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_UN
);
1650 map
->map_mflags
|= MF_CLOSING
;
1651 map
->map_class
->map_close(map
);
1652 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
1653 if (map
->map_class
->map_open(map
, omode
))
1655 map
->map_mflags
|= MF_OPEN
;
1656 map
->map_pid
= CurrentPid
;
1657 if ((omode
&& O_ACCMODE
) == O_RDWR
)
1658 map
->map_mflags
|= MF_WRITABLE
;
1663 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1665 extern MAPCLASS BogusMapClass
;
1667 *statp
= EX_TEMPFAIL
;
1668 map
->map_orgclass
= map
->map_class
;
1669 map
->map_class
= &BogusMapClass
;
1670 map
->map_mflags
|= MF_OPEN
;
1671 map
->map_pid
= CurrentPid
;
1672 syserr("Cannot reopen NDBM database %s",
1679 if (bitset(MF_TRY0NULL
, map
->map_mflags
))
1681 val
= dbm_fetch((DBM
*) map
->map_db1
, key
);
1682 if (val
.dptr
!= NULL
)
1683 map
->map_mflags
&= ~MF_TRY1NULL
;
1685 if (val
.dptr
== NULL
&& bitset(MF_TRY1NULL
, map
->map_mflags
))
1688 val
= dbm_fetch((DBM
*) map
->map_db1
, key
);
1689 if (val
.dptr
!= NULL
)
1690 map
->map_mflags
&= ~MF_TRY0NULL
;
1692 if (dfd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
1693 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_UN
);
1694 if (val
.dptr
== NULL
)
1696 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
1697 return map_rewrite(map
, name
, strlen(name
), NULL
);
1699 return map_rewrite(map
, val
.dptr
, val
.dsize
, av
);
1704 ** NDBM_MAP_STORE -- store a datum in the database
1708 ndbm_map_store(map
, lhs
, rhs
)
1716 char keybuf
[MAXNAME
+ 1];
1719 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1720 map
->map_mname
, lhs
, rhs
);
1722 key
.dsize
= strlen(lhs
);
1724 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
1726 if (key
.dsize
> sizeof keybuf
- 1)
1727 key
.dsize
= sizeof keybuf
- 1;
1728 memmove(keybuf
, key
.dptr
, key
.dsize
);
1729 keybuf
[key
.dsize
] = '\0';
1734 data
.dsize
= strlen(rhs
);
1737 if (bitset(MF_INCLNULL
, map
->map_mflags
))
1743 status
= dbm_store((DBM
*) map
->map_db1
, key
, data
, DBM_INSERT
);
1746 if (!bitset(MF_APPEND
, map
->map_mflags
))
1747 message("050 Warning: duplicate alias name %s", lhs
);
1750 static char *buf
= NULL
;
1751 static int bufsiz
= 0;
1755 old
.dptr
= ndbm_map_lookup(map
, key
.dptr
,
1756 (char **) NULL
, &xstat
);
1757 if (old
.dptr
!= NULL
&& *(char *) old
.dptr
!= '\0')
1759 old
.dsize
= strlen(old
.dptr
);
1760 if (data
.dsize
+ old
.dsize
+ 2 > bufsiz
)
1763 (void) sm_free(buf
);
1764 bufsiz
= data
.dsize
+ old
.dsize
+ 2;
1765 buf
= sm_pmalloc_x(bufsiz
);
1767 (void) sm_strlcpyn(buf
, bufsiz
, 3,
1768 data
.dptr
, ",", old
.dptr
);
1769 data
.dsize
= data
.dsize
+ old
.dsize
+ 1;
1772 sm_dprintf("ndbm_map_store append=%s\n",
1776 status
= dbm_store((DBM
*) map
->map_db1
,
1777 key
, data
, DBM_REPLACE
);
1780 syserr("readaliases: dbm put (%s): %d", lhs
, status
);
1785 ** NDBM_MAP_CLOSE -- close the database
1793 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1794 map
->map_mname
, map
->map_file
, map
->map_mflags
);
1796 if (bitset(MF_WRITABLE
, map
->map_mflags
))
1798 # ifdef NDBM_YP_COMPAT
1800 char buf
[MAXHOSTNAMELEN
];
1802 inclnull
= bitset(MF_INCLNULL
, map
->map_mflags
);
1803 map
->map_mflags
&= ~MF_INCLNULL
;
1805 if (strstr(map
->map_file
, "/yp/") != NULL
)
1807 long save_mflags
= map
->map_mflags
;
1809 map
->map_mflags
|= MF_NOFOLDCASE
;
1811 (void) sm_snprintf(buf
, sizeof buf
, "%010ld", curtime());
1812 ndbm_map_store(map
, "YP_LAST_MODIFIED", buf
);
1814 (void) gethostname(buf
, sizeof buf
);
1815 ndbm_map_store(map
, "YP_MASTER_NAME", buf
);
1817 map
->map_mflags
= save_mflags
;
1821 map
->map_mflags
|= MF_INCLNULL
;
1822 # endif /* NDBM_YP_COMPAT */
1824 /* write out the distinguished alias */
1825 ndbm_map_store(map
, "@", "@");
1827 dbm_close((DBM
*) map
->map_db1
);
1829 /* release lock (if needed) */
1831 if (map
->map_lockfd
>= 0)
1832 (void) close(map
->map_lockfd
);
1833 # endif /* !LOCK_ON_OPEN */
1838 ** NEWDB (Hash and BTree) Modules
1844 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1846 ** These do rather bizarre locking. If you can lock on open,
1847 ** do that to avoid the condition of opening a database that
1848 ** is being rebuilt. If you don't, we'll try to fake it, but
1849 ** there will be a race condition. If opening for read-only,
1850 ** we immediately release the lock to avoid freezing things up.
1851 ** We really ought to hold the lock, but guarantee that we won't
1852 ** be pokey about it. That's hard to do.
1855 /* these should be K line arguments */
1856 # if DB_VERSION_MAJOR < 2
1857 # define db_cachesize cachesize
1858 # define h_nelem nelem
1859 # ifndef DB_CACHE_SIZE
1860 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1861 # endif /* ! DB_CACHE_SIZE */
1862 # ifndef DB_HASH_NELEM
1863 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1864 # endif /* ! DB_HASH_NELEM */
1865 # endif /* DB_VERSION_MAJOR < 2 */
1868 bt_map_open(map
, mode
)
1872 # if DB_VERSION_MAJOR < 2
1874 # endif /* DB_VERSION_MAJOR < 2 */
1875 # if DB_VERSION_MAJOR == 2
1877 # endif /* DB_VERSION_MAJOR == 2 */
1878 # if DB_VERSION_MAJOR > 2
1879 void *btinfo
= NULL
;
1880 # endif /* DB_VERSION_MAJOR > 2 */
1883 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1884 map
->map_mname
, map
->map_file
, mode
);
1886 # if DB_VERSION_MAJOR < 3
1887 memset(&btinfo
, '\0', sizeof btinfo
);
1888 # ifdef DB_CACHE_SIZE
1889 btinfo
.db_cachesize
= DB_CACHE_SIZE
;
1890 # endif /* DB_CACHE_SIZE */
1891 # endif /* DB_VERSION_MAJOR < 3 */
1893 return db_map_open(map
, mode
, "btree", DB_BTREE
, &btinfo
);
1897 hash_map_open(map
, mode
)
1901 # if DB_VERSION_MAJOR < 2
1903 # endif /* DB_VERSION_MAJOR < 2 */
1904 # if DB_VERSION_MAJOR == 2
1906 # endif /* DB_VERSION_MAJOR == 2 */
1907 # if DB_VERSION_MAJOR > 2
1909 # endif /* DB_VERSION_MAJOR > 2 */
1912 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1913 map
->map_mname
, map
->map_file
, mode
);
1915 # if DB_VERSION_MAJOR < 3
1916 memset(&hinfo
, '\0', sizeof hinfo
);
1917 # ifdef DB_HASH_NELEM
1918 hinfo
.h_nelem
= DB_HASH_NELEM
;
1919 # endif /* DB_HASH_NELEM */
1920 # ifdef DB_CACHE_SIZE
1921 hinfo
.db_cachesize
= DB_CACHE_SIZE
;
1922 # endif /* DB_CACHE_SIZE */
1923 # endif /* DB_VERSION_MAJOR < 3 */
1925 return db_map_open(map
, mode
, "hash", DB_HASH
, &hinfo
);
1929 db_map_open(map
, mode
, mapclassname
, dbtype
, openinfo
)
1934 # if DB_VERSION_MAJOR < 2
1935 const void *openinfo
;
1936 # endif /* DB_VERSION_MAJOR < 2 */
1937 # if DB_VERSION_MAJOR == 2
1939 # endif /* DB_VERSION_MAJOR == 2 */
1940 # if DB_VERSION_MAJOR > 2
1942 # endif /* DB_VERSION_MAJOR > 2 */
1947 int smode
= S_IREAD
;
1952 char buf
[MAXPATHLEN
];
1954 /* do initial file and directory checks */
1955 if (sm_strlcpy(buf
, map
->map_file
, sizeof buf
) >= sizeof buf
)
1958 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1959 syserr("map \"%s\": map file %s name too long",
1960 map
->map_mname
, map
->map_file
);
1964 if (i
< 3 || strcmp(&buf
[i
- 3], ".db") != 0)
1966 if (sm_strlcat(buf
, ".db", sizeof buf
) >= sizeof buf
)
1969 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1970 syserr("map \"%s\": map file %s name too long",
1971 map
->map_mname
, map
->map_file
);
1979 sff
= SFF_ROOTOK
|SFF_REGONLY
;
1983 if (!bitnset(DBS_WRITEMAPTOSYMLINK
, DontBlameSendmail
))
1985 if (!bitnset(DBS_WRITEMAPTOHARDLINK
, DontBlameSendmail
))
1991 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
1994 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
1995 sff
|= SFF_SAFEDIRPATH
;
1996 i
= safefile(buf
, RunAsUid
, RunAsGid
, RunAsUserName
, sff
, smode
, &st
);
2000 char *prob
= "unsafe";
2002 /* cannot open this map */
2006 sm_dprintf("\t%s map file: %s\n", prob
, sm_errstring(i
));
2008 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2009 syserr("%s map \"%s\": %s map file %s",
2010 mapclassname
, map
->map_mname
, prob
, buf
);
2013 if (st
.st_mode
== ST_MODE_NOFILE
)
2014 omode
|= O_CREAT
|O_EXCL
;
2016 map
->map_lockfd
= -1;
2020 omode
|= O_TRUNC
|O_EXLOCK
;
2023 # else /* LOCK_ON_OPEN */
2025 ** Pre-lock the file to avoid race conditions. In particular,
2026 ** since dbopen returns NULL if the file is zero length, we
2027 ** must have a locked instance around the dbopen.
2030 fd
= open(buf
, omode
, DBMMODE
);
2033 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2034 syserr("db_map_open: cannot pre-open database %s", buf
);
2038 /* make sure no baddies slipped in just before the open... */
2039 if (filechanged(buf
, fd
, &st
))
2044 syserr("db_map_open(%s): file changed after pre-open", buf
);
2048 /* if new file, get the "before" bits for later filechanged check */
2049 if (st
.st_mode
== ST_MODE_NOFILE
&& fstat(fd
, &st
) < 0)
2054 syserr("db_map_open(%s): cannot fstat pre-opened file",
2059 /* actually lock the pre-opened file */
2060 if (!lockfile(fd
, buf
, NULL
, mode
== O_RDONLY
? LOCK_SH
: LOCK_EX
))
2061 syserr("db_map_open: cannot lock %s", buf
);
2063 /* set up mode bits for dbopen */
2066 omode
&= ~(O_EXCL
|O_CREAT
);
2067 # endif /* LOCK_ON_OPEN */
2069 # if DB_VERSION_MAJOR < 2
2070 db
= dbopen(buf
, omode
, DBMMODE
, dbtype
, openinfo
);
2071 # else /* DB_VERSION_MAJOR < 2 */
2074 # if DB_VERSION_MAJOR > 2
2076 # endif /* DB_VERSION_MAJOR > 2 */
2078 if (mode
== O_RDONLY
)
2080 if (bitset(O_CREAT
, omode
))
2082 if (bitset(O_TRUNC
, omode
))
2083 flags
|= DB_TRUNCATE
;
2084 SM_DB_FLAG_ADD(flags
);
2086 # if DB_VERSION_MAJOR > 2
2087 ret
= db_create(&db
, NULL
, 0);
2088 # ifdef DB_CACHE_SIZE
2089 if (ret
== 0 && db
!= NULL
)
2091 ret
= db
->set_cachesize(db
, 0, DB_CACHE_SIZE
, 0);
2094 (void) db
->close(db
, 0);
2098 # endif /* DB_CACHE_SIZE */
2099 # ifdef DB_HASH_NELEM
2100 if (dbtype
== DB_HASH
&& ret
== 0 && db
!= NULL
)
2102 ret
= db
->set_h_nelem(db
, DB_HASH_NELEM
);
2105 (void) db
->close(db
, 0);
2109 # endif /* DB_HASH_NELEM */
2110 if (ret
== 0 && db
!= NULL
)
2113 DBTXN
/* transaction for DB 4.1 */
2114 buf
, NULL
, dbtype
, flags
, DBMMODE
);
2117 #ifdef DB_OLD_VERSION
2118 if (ret
== DB_OLD_VERSION
)
2120 #endif /* DB_OLD_VERSION */
2121 (void) db
->close(db
, 0);
2126 # else /* DB_VERSION_MAJOR > 2 */
2127 errno
= db_open(buf
, dbtype
, flags
, DBMMODE
,
2128 NULL
, openinfo
, &db
);
2129 # endif /* DB_VERSION_MAJOR > 2 */
2131 # endif /* DB_VERSION_MAJOR < 2 */
2136 map
->map_lockfd
= fd
;
2139 # endif /* !LOCK_ON_OPEN */
2143 if (mode
== O_RDONLY
&& bitset(MF_ALIAS
, map
->map_mflags
) &&
2144 aliaswait(map
, ".db", false))
2147 if (map
->map_lockfd
>= 0)
2148 (void) close(map
->map_lockfd
);
2149 # endif /* !LOCK_ON_OPEN */
2151 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2152 syserr("Cannot open %s database %s",
2157 # if DB_VERSION_MAJOR < 2
2159 # else /* DB_VERSION_MAJOR < 2 */
2161 errno
= db
->fd(db
, &fd
);
2162 # endif /* DB_VERSION_MAJOR < 2 */
2163 if (filechanged(buf
, fd
, &st
))
2166 # if DB_VERSION_MAJOR < 2
2167 (void) db
->close(db
);
2168 # else /* DB_VERSION_MAJOR < 2 */
2169 errno
= db
->close(db
, 0);
2170 # endif /* DB_VERSION_MAJOR < 2 */
2172 if (map
->map_lockfd
>= 0)
2173 (void) close(map
->map_lockfd
);
2174 # endif /* !LOCK_ON_OPEN */
2176 syserr("db_map_open(%s): file changed after open", buf
);
2181 map
->map_mflags
|= MF_LOCKED
;
2183 if (fd
>= 0 && mode
== O_RDONLY
)
2185 (void) lockfile(fd
, buf
, NULL
, LOCK_UN
);
2187 # endif /* LOCK_ON_OPEN */
2189 /* try to make sure that at least the database header is on disk */
2192 (void) db
->sync(db
, 0);
2193 if (geteuid() == 0 && TrustedUid
!= 0)
2196 if (fchown(fd
, TrustedUid
, -1) < 0)
2200 sm_syslog(LOG_ALERT
, NOQID
,
2201 "ownership change on %s failed: %s",
2202 buf
, sm_errstring(err
));
2203 message("050 ownership change on %s failed: %s",
2204 buf
, sm_errstring(err
));
2206 # else /* HASFCHOWN */
2207 sm_syslog(LOG_ALERT
, NOQID
,
2208 "no fchown(): cannot change ownership on %s",
2210 message("050 no fchown(): cannot change ownership on %s",
2212 # endif /* HASFCHOWN */
2216 map
->map_db2
= (ARBPTR_T
) db
;
2219 ** Need to set map_mtime before the call to aliaswait()
2220 ** as aliaswait() will call map_lookup() which requires
2221 ** map_mtime to be set
2224 if (fd
>= 0 && fstat(fd
, &st
) >= 0)
2225 map
->map_mtime
= st
.st_mtime
;
2227 if (mode
== O_RDONLY
&& bitset(MF_ALIAS
, map
->map_mflags
) &&
2228 !aliaswait(map
, ".db", true))
2235 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2239 db_map_lookup(map
, name
, av
, statp
)
2246 register DB
*db
= (DB
*) map
->map_db2
;
2252 char keybuf
[MAXNAME
+ 1];
2253 char buf
[MAXPATHLEN
];
2255 memset(&key
, '\0', sizeof key
);
2256 memset(&val
, '\0', sizeof val
);
2259 sm_dprintf("db_map_lookup(%s, %s)\n",
2260 map
->map_mname
, name
);
2262 if (sm_strlcpy(buf
, map
->map_file
, sizeof buf
) >= sizeof buf
)
2265 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2266 syserr("map \"%s\": map file %s name too long",
2267 map
->map_mname
, map
->map_file
);
2271 if (i
> 3 && strcmp(&buf
[i
- 3], ".db") == 0)
2274 key
.size
= strlen(name
);
2275 if (key
.size
> sizeof keybuf
- 1)
2276 key
.size
= sizeof keybuf
- 1;
2278 memmove(keybuf
, name
, key
.size
);
2279 keybuf
[key
.size
] = '\0';
2280 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
2283 # if DB_VERSION_MAJOR < 2
2285 # else /* DB_VERSION_MAJOR < 2 */
2287 errno
= db
->fd(db
, &fd
);
2288 # endif /* DB_VERSION_MAJOR < 2 */
2289 if (fd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
2290 (void) lockfile(fd
, buf
, ".db", LOCK_SH
);
2291 if (fd
< 0 || fstat(fd
, &stbuf
) < 0 || stbuf
.st_mtime
> map
->map_mtime
)
2293 /* Reopen the database to sync the cache */
2294 int omode
= bitset(map
->map_mflags
, MF_WRITABLE
) ? O_RDWR
2297 if (fd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
2298 (void) lockfile(fd
, buf
, ".db", LOCK_UN
);
2299 map
->map_mflags
|= MF_CLOSING
;
2300 map
->map_class
->map_close(map
);
2301 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
2302 if (map
->map_class
->map_open(map
, omode
))
2304 map
->map_mflags
|= MF_OPEN
;
2305 map
->map_pid
= CurrentPid
;
2306 if ((omode
&& O_ACCMODE
) == O_RDWR
)
2307 map
->map_mflags
|= MF_WRITABLE
;
2308 db
= (DB
*) map
->map_db2
;
2313 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2315 extern MAPCLASS BogusMapClass
;
2317 *statp
= EX_TEMPFAIL
;
2318 map
->map_orgclass
= map
->map_class
;
2319 map
->map_class
= &BogusMapClass
;
2320 map
->map_mflags
|= MF_OPEN
;
2321 map
->map_pid
= CurrentPid
;
2322 syserr("Cannot reopen DB database %s",
2330 if (bitset(MF_TRY0NULL
, map
->map_mflags
))
2332 # if DB_VERSION_MAJOR < 2
2333 st
= db
->get(db
, &key
, &val
, 0);
2334 # else /* DB_VERSION_MAJOR < 2 */
2335 errno
= db
->get(db
, NULL
, &key
, &val
, 0);
2351 # endif /* DB_VERSION_MAJOR < 2 */
2353 map
->map_mflags
&= ~MF_TRY1NULL
;
2355 if (st
!= 0 && bitset(MF_TRY1NULL
, map
->map_mflags
))
2358 # if DB_VERSION_MAJOR < 2
2359 st
= db
->get(db
, &key
, &val
, 0);
2360 # else /* DB_VERSION_MAJOR < 2 */
2361 errno
= db
->get(db
, NULL
, &key
, &val
, 0);
2377 # endif /* DB_VERSION_MAJOR < 2 */
2379 map
->map_mflags
&= ~MF_TRY0NULL
;
2382 if (fd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
2383 (void) lockfile(fd
, buf
, ".db", LOCK_UN
);
2388 syserr("db_map_lookup: get (%s)", name
);
2391 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
2392 return map_rewrite(map
, name
, strlen(name
), NULL
);
2394 return map_rewrite(map
, val
.data
, val
.size
, av
);
2399 ** DB_MAP_STORE -- store a datum in the NEWDB database
2403 db_map_store(map
, lhs
, rhs
)
2411 register DB
*db
= map
->map_db2
;
2412 char keybuf
[MAXNAME
+ 1];
2414 memset(&key
, '\0', sizeof key
);
2415 memset(&data
, '\0', sizeof data
);
2418 sm_dprintf("db_map_store(%s, %s, %s)\n",
2419 map
->map_mname
, lhs
, rhs
);
2421 key
.size
= strlen(lhs
);
2423 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
2425 if (key
.size
> sizeof keybuf
- 1)
2426 key
.size
= sizeof keybuf
- 1;
2427 memmove(keybuf
, key
.data
, key
.size
);
2428 keybuf
[key
.size
] = '\0';
2433 data
.size
= strlen(rhs
);
2436 if (bitset(MF_INCLNULL
, map
->map_mflags
))
2442 # if DB_VERSION_MAJOR < 2
2443 status
= db
->put(db
, &key
, &data
, R_NOOVERWRITE
);
2444 # else /* DB_VERSION_MAJOR < 2 */
2445 errno
= db
->put(db
, NULL
, &key
, &data
, DB_NOOVERWRITE
);
2460 # endif /* DB_VERSION_MAJOR < 2 */
2463 if (!bitset(MF_APPEND
, map
->map_mflags
))
2464 message("050 Warning: duplicate alias name %s", lhs
);
2467 static char *buf
= NULL
;
2468 static int bufsiz
= 0;
2471 memset(&old
, '\0', sizeof old
);
2473 old
.data
= db_map_lookup(map
, key
.data
,
2474 (char **) NULL
, &status
);
2475 if (old
.data
!= NULL
)
2477 old
.size
= strlen(old
.data
);
2478 if (data
.size
+ old
.size
+ 2 > (size_t) bufsiz
)
2482 bufsiz
= data
.size
+ old
.size
+ 2;
2483 buf
= sm_pmalloc_x(bufsiz
);
2485 (void) sm_strlcpyn(buf
, bufsiz
, 3,
2486 (char *) data
.data
, ",",
2488 data
.size
= data
.size
+ old
.size
+ 1;
2491 sm_dprintf("db_map_store append=%s\n",
2492 (char *) data
.data
);
2495 # if DB_VERSION_MAJOR < 2
2496 status
= db
->put(db
, &key
, &data
, 0);
2497 # else /* DB_VERSION_MAJOR < 2 */
2498 status
= errno
= db
->put(db
, NULL
, &key
, &data
, 0);
2499 # endif /* DB_VERSION_MAJOR < 2 */
2502 syserr("readaliases: db put (%s)", lhs
);
2507 ** DB_MAP_CLOSE -- add distinguished entries and close the database
2514 register DB
*db
= map
->map_db2
;
2517 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2518 map
->map_mname
, map
->map_file
, map
->map_mflags
);
2520 if (bitset(MF_WRITABLE
, map
->map_mflags
))
2522 /* write out the distinguished alias */
2523 db_map_store(map
, "@", "@");
2526 (void) db
->sync(db
, 0);
2529 if (map
->map_lockfd
>= 0)
2530 (void) close(map
->map_lockfd
);
2531 # endif /* !LOCK_ON_OPEN */
2533 # if DB_VERSION_MAJOR < 2
2534 if (db
->close(db
) != 0)
2535 # else /* DB_VERSION_MAJOR < 2 */
2537 ** Berkeley DB can use internal shared memory
2538 ** locking for its memory pool. Closing a map
2539 ** opened by another process will interfere
2540 ** with the shared memory and locks of the parent
2541 ** process leaving things in a bad state.
2545 ** If this map was not opened by the current
2546 ** process, do not close the map but recover
2547 ** the file descriptor.
2550 if (map
->map_pid
!= CurrentPid
)
2554 errno
= db
->fd(db
, &fd
);
2560 if ((errno
= db
->close(db
, 0)) != 0)
2561 # endif /* DB_VERSION_MAJOR < 2 */
2562 syserr("db_map_close(%s, %s, %lx): db close failure",
2563 map
->map_mname
, map
->map_file
, map
->map_mflags
);
2573 # define YPERR_BUSY 16
2574 # endif /* ! YPERR_BUSY */
2577 ** NIS_MAP_OPEN -- open DBM map
2581 nis_map_open(map
, mode
)
2591 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2592 map
->map_mname
, map
->map_file
, mode
);
2595 if (mode
!= O_RDONLY
)
2597 /* issue a pseudo-error message */
2598 errno
= SM_EMAPCANTWRITE
;
2602 p
= strchr(map
->map_file
, '@');
2607 map
->map_domain
= p
;
2610 if (*map
->map_file
== '\0')
2611 map
->map_file
= "mail.aliases";
2613 if (map
->map_domain
== NULL
)
2615 yperr
= yp_get_default_domain(&map
->map_domain
);
2618 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2619 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2625 /* check to see if this map actually exists */
2627 yperr
= yp_match(map
->map_domain
, map
->map_file
, "@", 1,
2630 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2631 map
->map_domain
, map
->map_file
, yperr_string(yperr
));
2635 if (yperr
== 0 || yperr
== YPERR_KEY
|| yperr
== YPERR_BUSY
)
2638 ** We ought to be calling aliaswait() here if this is an
2639 ** alias file, but powerful HP-UX NIS servers apparently
2640 ** don't insert the @:@ token into the alias map when it
2641 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2645 if (!bitset(MF_ALIAS
, map
->map_mflags
) ||
2646 aliaswait(map
, NULL
, true))
2651 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2653 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2654 map
->map_file
, map
->map_domain
, yperr_string(yperr
));
2662 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2667 nis_map_lookup(map
, name
, av
, statp
)
2677 char keybuf
[MAXNAME
+ 1];
2678 char *SM_NONVOLATILE result
= NULL
;
2681 sm_dprintf("nis_map_lookup(%s, %s)\n",
2682 map
->map_mname
, name
);
2684 buflen
= strlen(name
);
2685 if (buflen
> sizeof keybuf
- 1)
2686 buflen
= sizeof keybuf
- 1;
2687 memmove(keybuf
, name
, buflen
);
2688 keybuf
[buflen
] = '\0';
2689 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
2693 if (bitset(MF_TRY0NULL
, map
->map_mflags
))
2695 yperr
= yp_match(map
->map_domain
, map
->map_file
, keybuf
, buflen
,
2698 map
->map_mflags
&= ~MF_TRY1NULL
;
2700 if (yperr
== YPERR_KEY
&& bitset(MF_TRY1NULL
, map
->map_mflags
))
2704 yperr
= yp_match(map
->map_domain
, map
->map_file
, keybuf
, buflen
,
2707 map
->map_mflags
&= ~MF_TRY0NULL
;
2711 if (yperr
!= YPERR_KEY
&& yperr
!= YPERR_BUSY
)
2712 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
2718 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
2719 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
2721 result
= map_rewrite(map
, vp
, vsize
, av
);
2731 ** NIS_GETCANONNAME -- look up canonical name in NIS
2735 nis_getcanonname(name
, hbsize
, statp
)
2744 static bool try0null
= true;
2745 static bool try1null
= true;
2746 static char *yp_domain
= NULL
;
2747 char host_record
[MAXLINE
];
2749 char nbuf
[MAXNAME
+ 1];
2752 sm_dprintf("nis_getcanonname(%s)\n", name
);
2754 if (sm_strlcpy(nbuf
, name
, sizeof nbuf
) >= sizeof nbuf
)
2756 *statp
= EX_UNAVAILABLE
;
2759 (void) shorten_hostname(nbuf
);
2760 keylen
= strlen(nbuf
);
2762 if (yp_domain
== NULL
)
2763 (void) yp_get_default_domain(&yp_domain
);
2769 yperr
= yp_match(yp_domain
, "hosts.byname", nbuf
, keylen
,
2774 if (yperr
== YPERR_KEY
&& try1null
)
2778 yperr
= yp_match(yp_domain
, "hosts.byname", nbuf
, keylen
,
2785 if (yperr
== YPERR_KEY
)
2787 else if (yperr
== YPERR_BUSY
)
2788 *statp
= EX_TEMPFAIL
;
2790 *statp
= EX_UNAVAILABLE
;
2795 (void) sm_strlcpy(host_record
, vp
, sizeof host_record
);
2798 sm_dprintf("got record `%s'\n", host_record
);
2799 vp
= strpbrk(host_record
, "#\n");
2802 if (!extract_canonname(nbuf
, NULL
, host_record
, cbuf
, sizeof cbuf
))
2804 /* this should not happen, but.... */
2808 if (sm_strlcpy(name
, cbuf
, hbsize
) >= hbsize
)
2810 *statp
= EX_UNAVAILABLE
;
2821 ** This code donated by Sun Microsystems.
2826 # undef NIS /* symbol conflict in nis.h */
2827 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2828 # include <rpcsvc/nis.h>
2829 # include <rpcsvc/nislib.h>
2831 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2832 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2833 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2834 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2837 ** NISPLUS_MAP_OPEN -- open nisplus table
2841 nisplus_map_open(map
, mode
)
2845 nis_result
*res
= NULL
;
2846 int retry_cnt
, max_col
, i
;
2847 char qbuf
[MAXLINE
+ NIS_MAXNAMELEN
];
2850 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2851 map
->map_mname
, map
->map_file
, mode
);
2854 if (mode
!= O_RDONLY
)
2860 if (*map
->map_file
== '\0')
2861 map
->map_file
= "mail_aliases.org_dir";
2863 if (PARTIAL_NAME(map
->map_file
) && map
->map_domain
== NULL
)
2865 /* set default NISPLUS Domain to $m */
2866 map
->map_domain
= newstr(nisplus_default_domain());
2868 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2869 map
->map_file
, map
->map_domain
);
2871 if (!PARTIAL_NAME(map
->map_file
))
2873 map
->map_domain
= newstr("");
2874 (void) sm_strlcpy(qbuf
, map
->map_file
, sizeof qbuf
);
2878 /* check to see if this map actually exists */
2879 (void) sm_strlcpyn(qbuf
, sizeof qbuf
, 3,
2880 map
->map_file
, ".", map
->map_domain
);
2884 while (res
== NULL
|| res
->status
!= NIS_SUCCESS
)
2886 res
= nis_lookup(qbuf
, FOLLOW_LINKS
);
2887 switch (res
->status
)
2894 case NIS_NAMEUNREACHABLE
:
2895 if (retry_cnt
++ > 4)
2900 /* try not to overwhelm hosed server */
2904 default: /* all other nisplus errors */
2906 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2907 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2908 map
->map_file
, map
->map_domain
,
2909 nis_sperrno(res
->status
));
2916 if (NIS_RES_NUMOBJ(res
) != 1 ||
2917 (NIS_RES_OBJECT(res
)->zo_data
.zo_type
!= TABLE_OBJ
))
2920 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf
);
2922 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2923 syserr("451 4.3.5 %s.%s: %s is not a table",
2924 map
->map_file
, map
->map_domain
,
2925 nis_sperrno(res
->status
));
2930 /* default key column is column 0 */
2931 if (map
->map_keycolnm
== NULL
)
2932 map
->map_keycolnm
= newstr(COL_NAME(res
,0));
2934 max_col
= COL_MAX(res
);
2936 /* verify the key column exist */
2937 for (i
= 0; i
< max_col
; i
++)
2939 if (strcmp(map
->map_keycolnm
, COL_NAME(res
,i
)) == 0)
2945 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
2946 map
->map_file
, map
->map_keycolnm
);
2951 /* default value column is the last column */
2952 if (map
->map_valcolnm
== NULL
)
2954 map
->map_valcolno
= max_col
- 1;
2958 for (i
= 0; i
< max_col
; i
++)
2960 if (strcmp(map
->map_valcolnm
, COL_NAME(res
,i
)) == 0)
2962 map
->map_valcolno
= i
;
2968 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
2969 map
->map_file
, map
->map_keycolnm
);
2976 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2980 nisplus_map_lookup(map
, name
, av
, statp
)
2990 char search_key
[MAXNAME
+ 4];
2991 char qbuf
[MAXLINE
+ NIS_MAXNAMELEN
];
2995 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
2996 map
->map_mname
, name
);
2998 if (!bitset(MF_OPEN
, map
->map_mflags
))
3000 if (nisplus_map_open(map
, O_RDONLY
))
3002 map
->map_mflags
|= MF_OPEN
;
3003 map
->map_pid
= CurrentPid
;
3007 *statp
= EX_UNAVAILABLE
;
3013 ** Copy the name to the key buffer, escaping double quote characters
3014 ** by doubling them and quoting "]" and "," to avoid having the
3015 ** NIS+ parser choke on them.
3018 skleft
= sizeof search_key
- 4;
3020 for (p
= name
; *p
!= '\0' && skleft
> 0; p
++)
3026 /* quote the character */
3034 /* double the quote */
3046 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
3047 makelower(search_key
);
3049 /* construct the query */
3050 if (PARTIAL_NAME(map
->map_file
))
3051 (void) sm_snprintf(qbuf
, sizeof qbuf
, "[%s=%s],%s.%s",
3052 map
->map_keycolnm
, search_key
, map
->map_file
,
3055 (void) sm_snprintf(qbuf
, sizeof qbuf
, "[%s=%s],%s",
3056 map
->map_keycolnm
, search_key
, map
->map_file
);
3059 sm_dprintf("qbuf=%s\n", qbuf
);
3060 result
= nis_list(qbuf
, FOLLOW_LINKS
| FOLLOW_PATH
, NULL
, NULL
);
3061 if (result
->status
== NIS_SUCCESS
)
3066 if ((count
= NIS_RES_NUMOBJ(result
)) != 1)
3069 sm_syslog(LOG_WARNING
, CurEnv
->e_id
,
3070 "%s: lookup error, expected 1 entry, got %d",
3071 map
->map_file
, count
);
3073 /* ignore second entry */
3075 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3079 p
= ((NIS_RES_OBJECT(result
))->EN_col(map
->map_valcolno
));
3080 /* set the length of the result */
3085 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3087 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3088 str
= map_rewrite(map
, name
, strlen(name
), NULL
);
3090 str
= map_rewrite(map
, p
, vsize
, av
);
3091 nis_freeresult(result
);
3097 if (result
->status
== NIS_NOTFOUND
)
3098 *statp
= EX_NOTFOUND
;
3099 else if (result
->status
== NIS_TRYAGAIN
)
3100 *statp
= EX_TEMPFAIL
;
3103 *statp
= EX_UNAVAILABLE
;
3104 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
3108 sm_dprintf("nisplus_map_lookup(%s), failed\n", name
);
3109 nis_freeresult(result
);
3116 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3120 nisplus_getcanonname(name
, hbsize
, statp
)
3129 char nbuf
[MAXNAME
+ 1];
3130 char qbuf
[MAXLINE
+ NIS_MAXNAMELEN
];
3132 if (sm_strlcpy(nbuf
, name
, sizeof nbuf
) >= sizeof nbuf
)
3134 *statp
= EX_UNAVAILABLE
;
3137 (void) shorten_hostname(nbuf
);
3139 p
= strchr(nbuf
, '.');
3143 (void) sm_snprintf(qbuf
, sizeof qbuf
,
3144 "[name=%s],hosts.org_dir", nbuf
);
3146 else if (p
[1] != '\0')
3148 /* multi token -- take only first token in nbuf */
3150 (void) sm_snprintf(qbuf
, sizeof qbuf
,
3151 "[name=%s],hosts.org_dir.%s", nbuf
, &p
[1]);
3160 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3163 result
= nis_list(qbuf
, EXPAND_NAME
|FOLLOW_LINKS
|FOLLOW_PATH
,
3166 if (result
->status
== NIS_SUCCESS
)
3171 if ((count
= NIS_RES_NUMOBJ(result
)) != 1)
3174 sm_syslog(LOG_WARNING
, CurEnv
->e_id
,
3175 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3178 /* ignore second entry */
3180 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3185 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3186 name
, (NIS_RES_OBJECT(result
))->zo_domain
);
3189 vp
= ((NIS_RES_OBJECT(result
))->EN_col(0));
3192 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3194 if (strchr(vp
, '.') != NULL
)
3200 domain
= macvalue('m', CurEnv
);
3204 if (hbsize
> vsize
+ (int) strlen(domain
) + 1)
3206 if (domain
[0] == '\0')
3207 (void) sm_strlcpy(name
, vp
, hbsize
);
3209 (void) sm_snprintf(name
, hbsize
,
3210 "%s.%s", vp
, domain
);
3215 nis_freeresult(result
);
3220 if (result
->status
== NIS_NOTFOUND
)
3222 else if (result
->status
== NIS_TRYAGAIN
)
3223 *statp
= EX_TEMPFAIL
;
3225 *statp
= EX_UNAVAILABLE
;
3228 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3229 name
, result
->status
, *statp
);
3230 nis_freeresult(result
);
3235 nisplus_default_domain()
3237 static char default_domain
[MAXNAME
+ 1] = "";
3240 if (default_domain
[0] != '\0')
3241 return default_domain
;
3243 p
= nis_local_directory();
3244 (void) sm_strlcpy(default_domain
, p
, sizeof default_domain
);
3245 return default_domain
;
3248 #endif /* NISPLUS */
3254 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3257 #if defined(LDAPMAP) || defined(PH_MAP)
3260 # define ph_map_dequote ldapmap_dequote
3261 # endif /* PH_MAP */
3263 static char *ldapmap_dequote
__P((char *));
3266 ldapmap_dequote(str
)
3278 /* Should probably swallow initial whitespace here */
3283 while (*p
!= '"' && *p
!= '\0')
3289 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3293 static SM_LDAP_STRUCT
*LDAPDefaults
= NULL
;
3296 ** LDAPMAP_OPEN -- open LDAP map
3298 ** Connect to the LDAP server. Re-use existing connections since a
3299 ** single server connection to a host (with the same host, port,
3300 ** bind DN, and secret) can answer queries for multiple maps.
3304 ldapmap_open(map
, mode
)
3308 SM_LDAP_STRUCT
*lmap
;
3312 sm_dprintf("ldapmap_open(%s, %d): ", map
->map_mname
, mode
);
3316 /* sendmail doesn't have the ability to write to LDAP (yet) */
3317 if (mode
!= O_RDONLY
)
3319 /* issue a pseudo-error message */
3320 errno
= SM_EMAPCANTWRITE
;
3324 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3326 s
= ldapmap_findconn(lmap
);
3327 if (s
->s_lmap
!= NULL
)
3329 /* Already have a connection open to this LDAP server */
3330 lmap
->ldap_ld
= ((SM_LDAP_STRUCT
*)s
->s_lmap
->map_db1
)->ldap_ld
;
3331 lmap
->ldap_pid
= ((SM_LDAP_STRUCT
*)s
->s_lmap
->map_db1
)->ldap_pid
;
3333 /* Add this map as head of linked list */
3334 lmap
->ldap_next
= s
->s_lmap
;
3338 sm_dprintf("using cached connection\n");
3343 sm_dprintf("opening new connection\n");
3345 /* No connection yet, connect */
3346 if (!sm_ldap_start(map
->map_mname
, lmap
))
3348 if (errno
== ETIMEDOUT
)
3351 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
3352 "timeout conning to LDAP server %.100s",
3353 lmap
->ldap_target
== NULL
? "localhost" : lmap
->ldap_target
);
3356 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3358 if (bitset(MF_NODEFER
, map
->map_mflags
))
3359 syserr("%s failed to %s in map %s",
3361 "ldap_init/ldap_bind",
3362 # else /* USE_LDAP_INIT */
3364 # endif /* USE_LDAP_INIT */
3365 lmap
->ldap_target
== NULL
? "localhost"
3366 : lmap
->ldap_target
,
3369 syserr("451 4.3.5 %s failed to %s in map %s",
3371 "ldap_init/ldap_bind",
3372 # else /* USE_LDAP_INIT */
3374 # endif /* USE_LDAP_INIT */
3375 lmap
->ldap_target
== NULL
? "localhost"
3376 : lmap
->ldap_target
,
3382 /* Save connection for reuse */
3388 ** LDAPMAP_CLOSE -- close ldap map
3395 SM_LDAP_STRUCT
*lmap
;
3399 sm_dprintf("ldapmap_close(%s)\n", map
->map_mname
);
3401 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3403 /* Check if already closed */
3404 if (lmap
->ldap_ld
== NULL
)
3407 /* Close the LDAP connection */
3408 sm_ldap_close(lmap
);
3410 /* Mark all the maps that share the connection as closed */
3411 s
= ldapmap_findconn(lmap
);
3413 while (s
->s_lmap
!= NULL
)
3415 MAP
*smap
= s
->s_lmap
;
3417 if (tTd(38, 2) && smap
!= map
)
3418 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3419 map
->map_mname
, smap
->map_mname
);
3420 smap
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
);
3421 lmap
= (SM_LDAP_STRUCT
*) smap
->map_db1
;
3422 lmap
->ldap_ld
= NULL
;
3423 s
->s_lmap
= lmap
->ldap_next
;
3424 lmap
->ldap_next
= NULL
;
3430 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3431 ** This only makes sense at Stanford University.
3444 if (islower(*p
) || isdigit(*p
))
3449 else if (isupper(*p
))
3451 *p_last
= tolower(*p
);
3456 if (*p_last
!= '\0')
3460 # endif /* SUNET_ID */
3463 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3467 ldapmap_lookup(map
, name
, av
, statp
)
3473 # if _FFR_LDAP_RECURSION
3476 # else /* _FFR_LDAP_RECURSION */
3481 # endif /* _FFR_LDAP_RECURSION */
3485 char *result
= NULL
;
3486 SM_LDAP_STRUCT
*lmap
= NULL
;
3487 char keybuf
[MAXNAME
+ 1];
3490 sm_dprintf("ldapmap_lookup(%s, %s)\n", map
->map_mname
, name
);
3492 /* Get ldap struct pointer from map */
3493 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3494 sm_ldap_setopts(lmap
->ldap_ld
, lmap
);
3496 (void) sm_strlcpy(keybuf
, name
, sizeof keybuf
);
3498 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
3501 sunet_id_hash(keybuf
);
3502 # else /* SUNET_ID */
3504 # endif /* SUNET_ID */
3507 msgid
= sm_ldap_search(lmap
, keybuf
);
3510 errno
= sm_ldap_geterrno(lmap
->ldap_ld
) + E_LDAPBASE
;
3512 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3514 if (bitset(MF_NODEFER
, map
->map_mflags
))
3515 syserr("Error in ldap_search using %s in map %s",
3516 keybuf
, map
->map_mname
);
3518 syserr("451 4.3.5 Error in ldap_search using %s in map %s",
3519 keybuf
, map
->map_mname
);
3521 *statp
= EX_TEMPFAIL
;
3522 switch (save_errno
- E_LDAPBASE
)
3524 # ifdef LDAP_SERVER_DOWN
3525 case LDAP_SERVER_DOWN
:
3526 # endif /* LDAP_SERVER_DOWN */
3528 case LDAP_UNAVAILABLE
:
3529 /* server disappeared, try reopen on next search */
3537 *statp
= EX_NOTFOUND
;
3540 # if _FFR_LDAP_RECURSION
3546 if (bitset(MF_SINGLEMATCH
, map
->map_mflags
))
3547 flags
|= SM_LDAP_SINGLEMATCH
;
3548 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3549 flags
|= SM_LDAP_MATCHONLY
;
3551 /* Create an rpool for search related memory usage */
3552 rpool
= sm_rpool_new_x(NULL
);
3555 *statp
= sm_ldap_results(lmap
, msgid
, flags
, map
->map_coldelim
,
3556 rpool
, &p
, &plen
, &psize
, NULL
);
3559 /* Copy result so rpool can be freed */
3560 if (*statp
== EX_OK
&& p
!= NULL
)
3562 sm_rpool_free(rpool
);
3564 /* need to restart LDAP connection? */
3565 if (*statp
== EX_RESTART
)
3567 *statp
= EX_TEMPFAIL
;
3572 if (*statp
!= EX_OK
&& *statp
!= EX_NOTFOUND
)
3574 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3576 if (bitset(MF_NODEFER
, map
->map_mflags
))
3577 syserr("Error getting LDAP results in map %s",
3580 syserr("451 4.3.5 Error getting LDAP results in map %s",
3587 # else /* _FFR_LDAP_RECURSION */
3590 while ((ret
= ldap_result(lmap
->ldap_ld
, msgid
, 0,
3591 (lmap
->ldap_timeout
.tv_sec
== 0 ? NULL
:
3592 &(lmap
->ldap_timeout
)),
3593 &(lmap
->ldap_res
))) == LDAP_RES_SEARCH_ENTRY
)
3597 if (bitset(MF_SINGLEMATCH
, map
->map_mflags
))
3599 entries
+= ldap_count_entries(lmap
->ldap_ld
,
3603 *statp
= EX_NOTFOUND
;
3604 if (lmap
->ldap_res
!= NULL
)
3606 ldap_msgfree(lmap
->ldap_res
);
3607 lmap
->ldap_res
= NULL
;
3609 (void) ldap_abandon(lmap
->ldap_ld
, msgid
);
3611 sm_free(vp
); /* XXX */
3613 sm_dprintf("ldap search found multiple on a single match query\n");
3618 /* If we don't want multiple values and we have one, break */
3619 if (map
->map_coldelim
== '\0' && vp
!= NULL
)
3622 /* Cycle through all entries */
3623 for (entry
= ldap_first_entry(lmap
->ldap_ld
, lmap
->ldap_res
);
3625 entry
= ldap_next_entry(lmap
->ldap_ld
, lmap
->ldap_res
))
3632 ** If matching only and found an entry,
3633 ** no need to spin through attributes
3636 if (*statp
== EX_OK
&&
3637 bitset(MF_MATCHONLY
, map
->map_mflags
))
3640 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3642 ** Reset value to prevent lingering
3643 ** LDAP_DECODING_ERROR due to
3644 ** OpenLDAP 1.X's hack (see below)
3647 lmap
->ldap_ld
->ld_errno
= LDAP_SUCCESS
;
3648 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3650 for (attr
= ldap_first_attribute(lmap
->ldap_ld
, entry
,
3653 attr
= ldap_next_attribute(lmap
->ldap_ld
, entry
,
3658 if (lmap
->ldap_attrsonly
== LDAPMAP_FALSE
)
3660 vals
= ldap_get_values(lmap
->ldap_ld
,
3665 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
3666 if (save_errno
== LDAP_SUCCESS
)
3672 /* Must be an error */
3673 save_errno
+= E_LDAPBASE
;
3674 if (!bitset(MF_OPTIONAL
,
3678 if (bitset(MF_NODEFER
,
3680 syserr("Error getting LDAP values in map %s",
3683 syserr("451 4.3.5 Error getting LDAP values in map %s",
3686 *statp
= EX_TEMPFAIL
;
3688 if (lmap
->ldap_res
!= NULL
)
3690 ldap_msgfree(lmap
->ldap_res
);
3691 lmap
->ldap_res
= NULL
;
3693 (void) ldap_abandon(lmap
->ldap_ld
,
3696 sm_free(vp
); /* XXX */
3704 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT)
3706 ** Reset value to prevent lingering
3707 ** LDAP_DECODING_ERROR due to
3708 ** OpenLDAP 1.X's hack (see below)
3711 lmap
->ldap_ld
->ld_errno
= LDAP_SUCCESS
;
3712 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */
3715 ** If matching only,
3716 ** no need to spin through entries
3719 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3721 if (lmap
->ldap_attrsonly
== LDAPMAP_FALSE
)
3722 ldap_value_free(vals
);
3729 ** If we don't want multiple values,
3730 ** return first found.
3733 if (map
->map_coldelim
== '\0')
3735 if (lmap
->ldap_attrsonly
== LDAPMAP_TRUE
)
3742 if (vals
[0] == NULL
)
3744 ldap_value_free(vals
);
3749 vsize
= strlen(vals
[0]) + 1;
3750 if (lmap
->ldap_attrsep
!= '\0')
3751 vsize
+= strlen(attr
) + 1;
3753 if (lmap
->ldap_attrsep
!= '\0')
3754 sm_snprintf(vp
, vsize
,
3760 sm_strlcpy(vp
, vals
[0], vsize
);
3761 ldap_value_free(vals
);
3766 /* attributes only */
3767 if (lmap
->ldap_attrsonly
== LDAPMAP_TRUE
)
3773 vsize
= strlen(vp
) +
3775 tmp
= xalloc(vsize
);
3776 (void) sm_snprintf(tmp
,
3778 vp
, map
->map_coldelim
,
3780 sm_free(vp
); /* XXX */
3788 ** If there is more than one,
3789 ** munge then into a map_coldelim
3794 for (i
= 0; vals
[i
] != NULL
; i
++)
3796 vsize
+= strlen(vals
[i
]) + 1;
3797 if (lmap
->ldap_attrsep
!= '\0')
3798 vsize
+= strlen(attr
) + 1;
3800 vp_tmp
= xalloc(vsize
);
3804 for (i
= 0; vals
[i
] != NULL
; i
++)
3806 if (lmap
->ldap_attrsep
!= '\0')
3808 p
+= sm_strlcpy(p
, attr
,
3809 vsize
- (p
- vp_tmp
));
3810 if (p
>= vp_tmp
+ vsize
)
3811 syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values");
3812 *p
++ = lmap
->ldap_attrsep
;
3814 p
+= sm_strlcpy(p
, vals
[i
],
3815 vsize
- (p
- vp_tmp
));
3816 if (p
>= vp_tmp
+ vsize
)
3817 syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values");
3818 if (vals
[i
+ 1] != NULL
)
3819 *p
++ = map
->map_coldelim
;
3822 ldap_value_free(vals
);
3829 vsize
= strlen(vp
) + strlen(vp_tmp
) + 2;
3830 tmp
= xalloc(vsize
);
3831 (void) sm_snprintf(tmp
, vsize
, "%s%c%s",
3832 vp
, map
->map_coldelim
, vp_tmp
);
3834 sm_free(vp
); /* XXX */
3835 sm_free(vp_tmp
); /* XXX */
3838 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
3841 ** We check errno != LDAP_DECODING_ERROR since
3842 ** OpenLDAP 1.X has a very ugly *undocumented*
3843 ** hack of returning this error code from
3844 ** ldap_next_attribute() if the library freed the
3845 ** ber attribute. See:
3846 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html
3849 if (save_errno
!= LDAP_SUCCESS
&&
3850 save_errno
!= LDAP_DECODING_ERROR
)
3852 /* Must be an error */
3853 save_errno
+= E_LDAPBASE
;
3854 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3857 if (bitset(MF_NODEFER
, map
->map_mflags
))
3858 syserr("Error getting LDAP attributes in map %s",
3861 syserr("451 4.3.5 Error getting LDAP attributes in map %s",
3864 *statp
= EX_TEMPFAIL
;
3865 if (lmap
->ldap_res
!= NULL
)
3867 ldap_msgfree(lmap
->ldap_res
);
3868 lmap
->ldap_res
= NULL
;
3870 (void) ldap_abandon(lmap
->ldap_ld
, msgid
);
3872 sm_free(vp
); /* XXX */
3877 /* We don't want multiple values and we have one */
3878 if (map
->map_coldelim
== '\0' && vp
!= NULL
)
3881 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
3882 if (save_errno
!= LDAP_SUCCESS
&&
3883 save_errno
!= LDAP_DECODING_ERROR
)
3885 /* Must be an error */
3886 save_errno
+= E_LDAPBASE
;
3887 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3890 if (bitset(MF_NODEFER
, map
->map_mflags
))
3891 syserr("Error getting LDAP entries in map %s",
3894 syserr("451 4.3.5 Error getting LDAP entries in map %s",
3897 *statp
= EX_TEMPFAIL
;
3898 if (lmap
->ldap_res
!= NULL
)
3900 ldap_msgfree(lmap
->ldap_res
);
3901 lmap
->ldap_res
= NULL
;
3903 (void) ldap_abandon(lmap
->ldap_ld
, msgid
);
3905 sm_free(vp
); /* XXX */
3909 ldap_msgfree(lmap
->ldap_res
);
3910 lmap
->ldap_res
= NULL
;
3914 save_errno
= ETIMEDOUT
;
3916 save_errno
= sm_ldap_geterrno(lmap
->ldap_ld
);
3917 if (save_errno
!= LDAP_SUCCESS
)
3920 save_errno
+= E_LDAPBASE
;
3922 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3925 if (bitset(MF_NODEFER
, map
->map_mflags
))
3926 syserr("Error getting LDAP results in map %s",
3929 syserr("451 4.3.5 Error getting LDAP results in map %s",
3932 *statp
= EX_TEMPFAIL
;
3934 sm_free(vp
); /* XXX */
3936 switch (save_errno
- E_LDAPBASE
)
3938 # ifdef LDAP_SERVER_DOWN
3939 case LDAP_SERVER_DOWN
:
3940 # endif /* LDAP_SERVER_DOWN */
3942 case LDAP_UNAVAILABLE
:
3943 /* server disappeared, try reopen on next search */
3950 # endif /* _FFR_LDAP_RECURSION */
3952 /* Did we match anything? */
3953 if (vp
== NULL
&& !bitset(MF_MATCHONLY
, map
->map_mflags
))
3956 if (*statp
== EX_OK
)
3959 sm_syslog(LOG_INFO
, CurEnv
->e_id
,
3960 "ldap %.100s => %s", name
,
3961 vp
== NULL
? "<NULL>" : vp
);
3962 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3963 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
3966 /* vp != NULL according to test above */
3967 result
= map_rewrite(map
, vp
, strlen(vp
), av
);
3970 sm_free(vp
); /* XXX */
3976 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3978 ** Cache LDAP connections based on the host, port, bind DN,
3979 ** secret, and PID so we don't have multiple connections open to
3980 ** the same server for different maps. Need a separate connection
3981 ** per PID since a parent process may close the map before the
3982 ** child is done with it.
3985 ** lmap -- LDAP map information
3988 ** Symbol table entry for the LDAP connection.
3992 ldapmap_findconn(lmap
)
3993 SM_LDAP_STRUCT
*lmap
;
3997 STAB
*SM_NONVOLATILE s
= NULL
;
3999 # if _FFR_LDAP_SETVERSION
4000 format
= "%s%c%d%c%d%c%s%c%s%d";
4001 # else /* _FFR_LDAP_SETVERSION */
4002 format
= "%s%c%d%c%s%c%s%d";
4003 # endif /* _FFR_LDAP_SETVERSION */
4004 nbuf
= sm_stringf_x(format
,
4005 (lmap
->ldap_target
== NULL
? "localhost"
4006 : lmap
->ldap_target
),
4010 # if _FFR_LDAP_SETVERSION
4013 # endif /* _FFR_LDAP_SETVERSION */
4014 (lmap
->ldap_binddn
== NULL
? ""
4015 : lmap
->ldap_binddn
),
4017 (lmap
->ldap_secret
== NULL
? ""
4018 : lmap
->ldap_secret
),
4021 s
= stab(nbuf
, ST_LMAP
, ST_ENTER
);
4028 ** LDAPMAP_PARSEARGS -- parse ldap map definition args.
4031 static struct lamvalues LDAPAuthMethods
[] =
4033 { "none", LDAP_AUTH_NONE
},
4034 { "simple", LDAP_AUTH_SIMPLE
},
4035 # ifdef LDAP_AUTH_KRBV4
4036 { "krbv4", LDAP_AUTH_KRBV4
},
4037 # endif /* LDAP_AUTH_KRBV4 */
4041 static struct ladvalues LDAPAliasDereference
[] =
4043 { "never", LDAP_DEREF_NEVER
},
4044 { "always", LDAP_DEREF_ALWAYS
},
4045 { "search", LDAP_DEREF_SEARCHING
},
4046 { "find", LDAP_DEREF_FINDING
},
4050 static struct lssvalues LDAPSearchScope
[] =
4052 { "base", LDAP_SCOPE_BASE
},
4053 { "one", LDAP_SCOPE_ONELEVEL
},
4054 { "sub", LDAP_SCOPE_SUBTREE
},
4059 ldapmap_parseargs(map
, args
)
4063 bool secretread
= true;
4065 bool ldaphost
= false;
4066 # endif /* _FFR_LDAP_URI */
4068 register char *p
= args
;
4069 SM_LDAP_STRUCT
*lmap
;
4070 struct lamvalues
*lam
;
4071 struct ladvalues
*lad
;
4072 struct lssvalues
*lss
;
4073 char ldapfilt
[MAXLINE
];
4074 char m_tmp
[MAXPATHLEN
+ LDAPMAP_MAX_PASSWD
];
4076 /* Get ldap struct pointer from map */
4077 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
4079 /* Check if setting the initial LDAP defaults */
4080 if (lmap
== NULL
|| lmap
!= LDAPDefaults
)
4082 /* We need to alloc an SM_LDAP_STRUCT struct */
4083 lmap
= (SM_LDAP_STRUCT
*) xalloc(sizeof *lmap
);
4084 if (LDAPDefaults
== NULL
)
4085 sm_ldap_clear(lmap
);
4087 STRUCTCOPY(*LDAPDefaults
, *lmap
);
4090 /* there is no check whether there is really an argument */
4091 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
4092 map
->map_spacesub
= SpaceSub
; /* default value */
4094 /* Check if setting up an alias or file class LDAP map */
4095 if (bitset(MF_ALIAS
, map
->map_mflags
))
4097 /* Comma separate if used as an alias file */
4098 map
->map_coldelim
= ',';
4103 char jbuf
[MAXHOSTNAMELEN
];
4104 char lcbuf
[MAXLINE
];
4107 expand("\201j", jbuf
, sizeof jbuf
, &BlankEnvelope
);
4108 if (jbuf
[0] == '\0')
4110 (void) sm_strlcpy(jbuf
, "localhost",
4114 lc
= macvalue(macid("{sendmailMTACluster}"), CurEnv
);
4119 expand(lc
, lcbuf
, sizeof lcbuf
, CurEnv
);
4123 n
= sm_snprintf(ldapfilt
, sizeof ldapfilt
,
4124 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
4126 if (n
>= sizeof ldapfilt
)
4128 syserr("%s: Default LDAP string too long",
4133 /* default args for an alias LDAP entry */
4134 lmap
->ldap_filter
= ldapfilt
;
4135 lmap
->ldap_attr
[0] = "sendmailMTAAliasValue";
4136 lmap
->ldap_attr
[1] = NULL
;
4139 else if (bitset(MF_FILECLASS
, map
->map_mflags
))
4141 /* Space separate if used as a file class file */
4142 map
->map_coldelim
= ' ';
4147 while (isascii(*p
) && isspace(*p
))
4154 map
->map_mflags
|= MF_INCLNULL
;
4155 map
->map_mflags
&= ~MF_TRY0NULL
;
4159 map
->map_mflags
&= ~MF_TRY1NULL
;
4163 map
->map_mflags
|= MF_OPTIONAL
;
4167 map
->map_mflags
|= MF_NOFOLDCASE
;
4171 map
->map_mflags
|= MF_MATCHONLY
;
4175 map
->map_mflags
|= MF_APPEND
;
4179 map
->map_mflags
|= MF_KEEPQUOTES
;
4187 map
->map_tapp
= ++p
;
4191 map
->map_mflags
|= MF_NODEFER
;
4195 map
->map_spacesub
= *++p
;
4199 map
->map_mflags
|= MF_DEFER
;
4204 map
->map_coldelim
= *p
;
4210 map
->map_coldelim
= '\n';
4214 map
->map_coldelim
= '\t';
4218 map
->map_coldelim
= '\\';
4223 /* Start of ldapmap specific args */
4226 lmap
->ldap_attrsep
= *p
;
4232 lmap
->ldap_attrsep
= '\n';
4236 lmap
->ldap_attrsep
= '\t';
4240 lmap
->ldap_attrsep
= '\\';
4245 case 'k': /* search field */
4246 while (isascii(*++p
) && isspace(*p
))
4248 lmap
->ldap_filter
= p
;
4251 case 'v': /* attr to return */
4252 while (isascii(*++p
) && isspace(*p
))
4254 lmap
->ldap_attr
[0] = p
;
4255 lmap
->ldap_attr
[1] = NULL
;
4259 map
->map_mflags
|= MF_SINGLEMATCH
;
4262 /* args stolen from ldapsearch.c */
4263 case 'R': /* don't auto chase referrals */
4264 # ifdef LDAP_REFERRALS
4265 lmap
->ldap_options
&= ~LDAP_OPT_REFERRALS
;
4266 # else /* LDAP_REFERRALS */
4267 syserr("compile with -DLDAP_REFERRALS for referral support");
4268 # endif /* LDAP_REFERRALS */
4271 case 'n': /* retrieve attribute names only */
4272 lmap
->ldap_attrsonly
= LDAPMAP_TRUE
;
4275 case 'r': /* alias dereferencing */
4276 while (isascii(*++p
) && isspace(*p
))
4279 if (sm_strncasecmp(p
, "LDAP_DEREF_", 11) == 0)
4282 for (lad
= LDAPAliasDereference
;
4283 lad
!= NULL
&& lad
->lad_name
!= NULL
; lad
++)
4285 if (sm_strncasecmp(p
, lad
->lad_name
,
4286 strlen(lad
->lad_name
)) == 0)
4289 if (lad
->lad_name
!= NULL
)
4290 lmap
->ldap_deref
= lad
->lad_code
;
4293 /* bad config line */
4294 if (!bitset(MCF_OPTFILE
,
4295 map
->map_class
->map_cflags
))
4299 if ((ptr
= strchr(p
, ' ')) != NULL
)
4301 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
4310 case 's': /* search scope */
4311 while (isascii(*++p
) && isspace(*p
))
4314 if (sm_strncasecmp(p
, "LDAP_SCOPE_", 11) == 0)
4317 for (lss
= LDAPSearchScope
;
4318 lss
!= NULL
&& lss
->lss_name
!= NULL
; lss
++)
4320 if (sm_strncasecmp(p
, lss
->lss_name
,
4321 strlen(lss
->lss_name
)) == 0)
4324 if (lss
->lss_name
!= NULL
)
4325 lmap
->ldap_scope
= lss
->lss_code
;
4328 /* bad config line */
4329 if (!bitset(MCF_OPTFILE
,
4330 map
->map_class
->map_cflags
))
4334 if ((ptr
= strchr(p
, ' ')) != NULL
)
4336 syserr("Scope must be [base|one|sub] (not %s) in map %s",
4345 case 'h': /* ldap host */
4346 while (isascii(*++p
) && isspace(*p
))
4351 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4356 # endif /* _FFR_LDAP_URI */
4357 lmap
->ldap_target
= p
;
4360 case 'b': /* search base */
4361 while (isascii(*++p
) && isspace(*p
))
4363 lmap
->ldap_base
= p
;
4366 case 'p': /* ldap port */
4367 while (isascii(*++p
) && isspace(*p
))
4369 lmap
->ldap_port
= atoi(p
);
4372 case 'l': /* time limit */
4373 while (isascii(*++p
) && isspace(*p
))
4375 lmap
->ldap_timelimit
= atoi(p
);
4376 lmap
->ldap_timeout
.tv_sec
= lmap
->ldap_timelimit
;
4380 while (isascii(*++p
) && isspace(*p
))
4382 lmap
->ldap_sizelimit
= atoi(p
);
4385 case 'd': /* Dn to bind to server as */
4386 while (isascii(*++p
) && isspace(*p
))
4388 lmap
->ldap_binddn
= p
;
4391 case 'M': /* Method for binding */
4392 while (isascii(*++p
) && isspace(*p
))
4395 if (sm_strncasecmp(p
, "LDAP_AUTH_", 10) == 0)
4398 for (lam
= LDAPAuthMethods
;
4399 lam
!= NULL
&& lam
->lam_name
!= NULL
; lam
++)
4401 if (sm_strncasecmp(p
, lam
->lam_name
,
4402 strlen(lam
->lam_name
)) == 0)
4405 if (lam
->lam_name
!= NULL
)
4406 lmap
->ldap_method
= lam
->lam_code
;
4409 /* bad config line */
4410 if (!bitset(MCF_OPTFILE
,
4411 map
->map_class
->map_cflags
))
4415 if ((ptr
= strchr(p
, ' ')) != NULL
)
4417 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4428 ** This is a string that is dependent on the
4429 ** method used defined above.
4432 case 'P': /* Secret password for binding */
4433 while (isascii(*++p
) && isspace(*p
))
4435 lmap
->ldap_secret
= p
;
4440 case 'H': /* Use LDAP URI */
4442 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4445 # else /* !USE_LDAP_INIT */
4448 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4452 while (isascii(*++p
) && isspace(*p
))
4454 lmap
->ldap_target
= p
;
4455 lmap
->ldap_uri
= true;
4457 # endif /* !USE_LDAP_INIT */
4458 # endif /* _FFR_LDAP_URI */
4460 # if _FFR_LDAP_SETVERSION
4462 /* -w should be for passwd, -P should be for version */
4463 while (isascii(*++p
) && isspace(*p
))
4465 lmap
->ldap_version
= atoi(p
);
4466 # ifdef LDAP_VERSION_MAX
4467 if (lmap
->ldap_version
> LDAP_VERSION_MAX
)
4469 syserr("LDAP version %d exceeds max of %d in map %s",
4470 lmap
->ldap_version
, LDAP_VERSION_MAX
,
4474 # endif /* LDAP_VERSION_MAX */
4475 # ifdef LDAP_VERSION_MIN
4476 if (lmap
->ldap_version
< LDAP_VERSION_MIN
)
4478 syserr("LDAP version %d is lower than min of %d in map %s",
4479 lmap
->ldap_version
, LDAP_VERSION_MIN
,
4483 # endif /* LDAP_VERSION_MIN */
4485 # endif /* _FFR_LDAP_SETVERSION */
4488 syserr("Illegal option %c map %s", *p
, map
->map_mname
);
4492 /* need to account for quoted strings here */
4493 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
4497 while (*++p
!= '"' && *p
!= '\0')
4510 if (map
->map_app
!= NULL
)
4511 map
->map_app
= newstr(ldapmap_dequote(map
->map_app
));
4512 if (map
->map_tapp
!= NULL
)
4513 map
->map_tapp
= newstr(ldapmap_dequote(map
->map_tapp
));
4516 ** We need to swallow up all the stuff into a struct
4517 ** and dump it into map->map_dbptr1
4520 if (lmap
->ldap_target
!= NULL
&&
4521 (LDAPDefaults
== NULL
||
4522 LDAPDefaults
== lmap
||
4523 LDAPDefaults
->ldap_target
!= lmap
->ldap_target
))
4524 lmap
->ldap_target
= newstr(ldapmap_dequote(lmap
->ldap_target
));
4525 map
->map_domain
= lmap
->ldap_target
;
4527 if (lmap
->ldap_binddn
!= NULL
&&
4528 (LDAPDefaults
== NULL
||
4529 LDAPDefaults
== lmap
||
4530 LDAPDefaults
->ldap_binddn
!= lmap
->ldap_binddn
))
4531 lmap
->ldap_binddn
= newstr(ldapmap_dequote(lmap
->ldap_binddn
));
4533 if (lmap
->ldap_secret
!= NULL
&&
4534 (LDAPDefaults
== NULL
||
4535 LDAPDefaults
== lmap
||
4536 LDAPDefaults
->ldap_secret
!= lmap
->ldap_secret
))
4539 long sff
= SFF_OPENASROOT
|SFF_ROOTOK
|SFF_NOWLINK
|SFF_NOWWFILES
|SFF_NOGWFILES
;
4541 if (DontLockReadFiles
)
4544 /* need to use method to map secret to passwd string */
4545 switch (lmap
->ldap_method
)
4547 case LDAP_AUTH_NONE
:
4551 case LDAP_AUTH_SIMPLE
:
4554 ** Secret is the name of a file with
4555 ** the first line as the password.
4558 /* Already read in the secret? */
4562 sfd
= safefopen(ldapmap_dequote(lmap
->ldap_secret
),
4566 syserr("LDAP map: cannot open secret %s",
4567 ldapmap_dequote(lmap
->ldap_secret
));
4570 lmap
->ldap_secret
= sfgets(m_tmp
, sizeof m_tmp
,
4571 sfd
, TimeOuts
.to_fileopen
,
4572 "ldapmap_parseargs");
4573 (void) sm_io_close(sfd
, SM_TIME_DEFAULT
);
4574 if (strlen(m_tmp
) > LDAPMAP_MAX_PASSWD
)
4576 syserr("LDAP map: secret in %s too long",
4577 ldapmap_dequote(lmap
->ldap_secret
));
4580 if (lmap
->ldap_secret
!= NULL
&&
4584 if (m_tmp
[strlen(m_tmp
) - 1] == '\n')
4585 m_tmp
[strlen(m_tmp
) - 1] = '\0';
4587 lmap
->ldap_secret
= m_tmp
;
4591 # ifdef LDAP_AUTH_KRBV4
4592 case LDAP_AUTH_KRBV4
:
4595 ** Secret is where the ticket file is
4599 (void) sm_snprintf(m_tmp
, sizeof m_tmp
,
4601 ldapmap_dequote(lmap
->ldap_secret
));
4602 lmap
->ldap_secret
= m_tmp
;
4604 # endif /* LDAP_AUTH_KRBV4 */
4606 default: /* Should NEVER get here */
4607 syserr("LDAP map: Illegal value in lmap method");
4614 if (lmap
->ldap_secret
!= NULL
&&
4615 (LDAPDefaults
== NULL
||
4616 LDAPDefaults
== lmap
||
4617 LDAPDefaults
->ldap_secret
!= lmap
->ldap_secret
))
4618 lmap
->ldap_secret
= newstr(ldapmap_dequote(lmap
->ldap_secret
));
4620 if (lmap
->ldap_base
!= NULL
&&
4621 (LDAPDefaults
== NULL
||
4622 LDAPDefaults
== lmap
||
4623 LDAPDefaults
->ldap_base
!= lmap
->ldap_base
))
4624 lmap
->ldap_base
= newstr(ldapmap_dequote(lmap
->ldap_base
));
4627 ** Save the server from extra work. If request is for a single
4628 ** match, tell the server to only return enough records to
4629 ** determine if there is a single match or not. This can not
4630 ** be one since the server would only return one and we wouldn't
4631 ** know if there were others available.
4634 if (bitset(MF_SINGLEMATCH
, map
->map_mflags
))
4635 lmap
->ldap_sizelimit
= 2;
4637 /* If setting defaults, don't process ldap_filter and ldap_attr */
4638 if (lmap
== LDAPDefaults
)
4641 if (lmap
->ldap_filter
!= NULL
)
4642 lmap
->ldap_filter
= newstr(ldapmap_dequote(lmap
->ldap_filter
));
4645 if (!bitset(MCF_OPTFILE
, map
->map_class
->map_cflags
))
4647 syserr("No filter given in map %s", map
->map_mname
);
4652 if (lmap
->ldap_attr
[0] != NULL
)
4654 # if _FFR_LDAP_RECURSION
4655 bool recurse
= false;
4656 bool normalseen
= false;
4657 # endif /* _FFR_LDAP_RECURSION */
4660 p
= ldapmap_dequote(lmap
->ldap_attr
[0]);
4661 lmap
->ldap_attr
[0] = NULL
;
4663 # if _FFR_LDAP_RECURSION
4664 /* Prime the attr list with the objectClass attribute */
4665 lmap
->ldap_attr
[i
] = "objectClass";
4666 lmap
->ldap_attr_type
[i
] = SM_LDAP_ATTR_OBJCLASS
;
4667 lmap
->ldap_attr_needobjclass
[i
] = NULL
;
4669 # endif /* _FFR_LDAP_RECURSION */
4675 while (isascii(*p
) && isspace(*p
))
4684 if (i
>= LDAPMAP_MAX_ATTR
)
4686 syserr("Too many return attributes in %s (max %d)",
4687 map
->map_mname
, LDAPMAP_MAX_ATTR
);
4692 # if _FFR_LDAP_RECURSION
4698 type
= strchr(v
, ':');
4702 needobjclass
= strchr(type
, ':');
4703 if (needobjclass
!= NULL
)
4704 *needobjclass
++ = '\0';
4708 needobjclass
= NULL
;
4713 /* allow override on "objectClass" type */
4714 if (sm_strcasecmp(v
, "objectClass") == 0 &&
4715 lmap
->ldap_attr_type
[0] == SM_LDAP_ATTR_OBJCLASS
)
4722 ** Don't add something to attribute
4726 for (j
= 1; j
< i
; j
++)
4728 if (sm_strcasecmp(v
, lmap
->ldap_attr
[j
]) == 0)
4730 syserr("Duplicate attribute (%s) in %s",
4736 lmap
->ldap_attr
[use
] = newstr(v
);
4737 if (needobjclass
!= NULL
&&
4738 *needobjclass
!= '\0' &&
4739 *needobjclass
!= '*')
4741 lmap
->ldap_attr_needobjclass
[use
] = newstr(needobjclass
);
4745 lmap
->ldap_attr_needobjclass
[use
] = NULL
;
4750 if (type
!= NULL
&& *type
!= '\0')
4752 if (sm_strcasecmp(type
, "dn") == 0)
4755 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_DN
;
4757 else if (sm_strcasecmp(type
, "filter") == 0)
4760 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_FILTER
;
4762 else if (sm_strcasecmp(type
, "url") == 0)
4765 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_URL
;
4767 else if (sm_strcasecmp(type
, "normal") == 0)
4769 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_NORMAL
;
4774 syserr("Unknown attribute type (%s) in %s",
4775 type
, map
->map_mname
);
4781 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_NORMAL
;
4784 # else /* _FFR_LDAP_RECURSION */
4785 lmap
->ldap_attr
[i
] = newstr(v
);
4786 # endif /* _FFR_LDAP_RECURSION */
4790 lmap
->ldap_attr
[i
] = NULL
;
4791 # if _FFR_LDAP_RECURSION
4792 if (recurse
&& !normalseen
)
4794 syserr("LDAP recursion requested in %s but no returnable attribute given",
4798 if (recurse
&& lmap
->ldap_attrsonly
== LDAPMAP_TRUE
)
4800 syserr("LDAP recursion requested in %s can not be used with -n",
4804 # endif /* _FFR_LDAP_RECURSION */
4806 map
->map_db1
= (ARBPTR_T
) lmap
;
4811 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4814 ** spec -- map argument string from LDAPDefaults option
4821 ldapmap_set_defaults(spec
)
4827 /* Allocate and set the default values */
4828 if (LDAPDefaults
== NULL
)
4829 LDAPDefaults
= (SM_LDAP_STRUCT
*) xalloc(sizeof *LDAPDefaults
);
4830 sm_ldap_clear(LDAPDefaults
);
4832 memset(&map
, '\0', sizeof map
);
4834 /* look up the class */
4835 class = stab("ldap", ST_MAPCLASS
, ST_FIND
);
4838 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4841 map
.map_class
= &class->s_mapclass
;
4842 map
.map_db1
= (ARBPTR_T
) LDAPDefaults
;
4843 map
.map_mname
= "O LDAPDefaultSpec";
4845 (void) ldapmap_parseargs(&map
, spec
);
4847 /* These should never be set in LDAPDefaults */
4848 if (map
.map_mflags
!= (MF_TRY0NULL
|MF_TRY1NULL
) ||
4849 map
.map_spacesub
!= SpaceSub
||
4850 map
.map_app
!= NULL
||
4851 map
.map_tapp
!= NULL
)
4853 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4854 SM_FREE_CLR(map
.map_app
);
4855 SM_FREE_CLR(map
.map_tapp
);
4858 if (LDAPDefaults
->ldap_filter
!= NULL
)
4860 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4862 /* don't free, it isn't malloc'ed in parseargs */
4863 LDAPDefaults
->ldap_filter
= NULL
;
4866 if (LDAPDefaults
->ldap_attr
[0] != NULL
)
4868 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4869 /* don't free, they aren't malloc'ed in parseargs */
4870 LDAPDefaults
->ldap_attr
[0] = NULL
;
4873 #endif /* LDAPMAP */
4881 ** Support for the CCSO Nameserver (ph/qi).
4882 ** This code is intended to replace the so-called "ph mailer".
4883 ** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support.
4886 /* what version of the ph map code we're running */
4887 static char phmap_id
[128];
4889 /* sendmail version for phmap id string */
4890 extern const char Version
[];
4892 /* assume we're using nph-1.1.x if not specified */
4893 # ifndef NPH_VERSION
4894 # define NPH_VERSION 10100
4897 /* compatibility for versions older than nph-1.2.0 */
4898 # if NPH_VERSION < 10200
4899 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4900 # define PH_OPEN_DONTID PH_DONTID
4901 # define PH_CLOSE_FAST PH_FASTCLOSE
4902 # define PH_ERR_DATAERR PH_DATAERR
4903 # define PH_ERR_NOMATCH PH_NOMATCH
4904 # endif /* NPH_VERSION < 10200 */
4907 ** PH_MAP_PARSEARGS -- parse ph map definition args.
4911 ph_map_parseargs(map
, args
)
4916 register char *p
= args
;
4917 PH_MAP_STRUCT
*pmap
= NULL
;
4919 /* initialize version string */
4920 (void) sm_snprintf(phmap_id
, sizeof phmap_id
,
4921 "sendmail-%s phmap-20010529 libphclient-%s",
4922 Version
, libphclient_version
);
4924 pmap
= (PH_MAP_STRUCT
*) xalloc(sizeof *pmap
);
4927 pmap
->ph_servers
= NULL
;
4928 pmap
->ph_field_list
= NULL
;
4930 pmap
->ph_timeout
= 0;
4931 pmap
->ph_fastclose
= 0;
4933 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
4936 while (isascii(*p
) && isspace(*p
))
4943 map
->map_mflags
|= MF_INCLNULL
;
4944 map
->map_mflags
&= ~MF_TRY0NULL
;
4948 map
->map_mflags
&= ~MF_TRY1NULL
;
4952 map
->map_mflags
|= MF_OPTIONAL
;
4956 map
->map_mflags
|= MF_NOFOLDCASE
;
4960 map
->map_mflags
|= MF_MATCHONLY
;
4964 map
->map_mflags
|= MF_APPEND
;
4968 map
->map_mflags
|= MF_KEEPQUOTES
;
4972 map
->map_mflags
|= MF_NODEFER
;
4980 map
->map_tapp
= ++p
;
4984 while (isascii(*++p
) && isspace(*p
))
4986 pmap
->ph_timeout
= atoi(p
);
4990 map
->map_spacesub
= *++p
;
4994 map
->map_mflags
|= MF_DEFER
;
4997 case 'h': /* PH server list */
4998 while (isascii(*++p
) && isspace(*p
))
5000 pmap
->ph_servers
= p
;
5004 sm_syslog(LOG_WARNING
, NULL
,
5005 "ph_map_parseargs: WARNING: -v option will be removed in a future release - please use -k instead");
5006 /* intentional fallthrough for backward compatibility */
5009 case 'k': /* fields to search for */
5010 while (isascii(*++p
) && isspace(*p
))
5012 pmap
->ph_field_list
= p
;
5016 syserr("ph_map_parseargs: unknown option -%c", *p
);
5019 /* try to account for quoted strings */
5020 done
= isascii(*p
) && isspace(*p
);
5021 while (*p
!= '\0' && !done
)
5025 while (*++p
!= '"' && *p
!= '\0')
5032 done
= isascii(*p
) && isspace(*p
);
5039 if (map
->map_app
!= NULL
)
5040 map
->map_app
= newstr(ph_map_dequote(map
->map_app
));
5041 if (map
->map_tapp
!= NULL
)
5042 map
->map_tapp
= newstr(ph_map_dequote(map
->map_tapp
));
5044 if (pmap
->ph_field_list
!= NULL
)
5045 pmap
->ph_field_list
= newstr(ph_map_dequote(pmap
->ph_field_list
));
5047 if (pmap
->ph_servers
!= NULL
)
5048 pmap
->ph_servers
= newstr(ph_map_dequote(pmap
->ph_servers
));
5051 syserr("ph_map_parseargs: -h flag is required");
5055 map
->map_db1
= (ARBPTR_T
) pmap
;
5060 ** PH_MAP_CLOSE -- close the connection to the ph server
5067 PH_MAP_STRUCT
*pmap
;
5069 pmap
= (PH_MAP_STRUCT
*)map
->map_db1
;
5071 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
5072 map
->map_mname
, pmap
->ph_fastclose
);
5075 if (pmap
->ph
!= NULL
)
5077 ph_set_sendhook(pmap
->ph
, NULL
);
5078 ph_set_recvhook(pmap
->ph
, NULL
);
5079 ph_close(pmap
->ph
, pmap
->ph_fastclose
);
5082 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
);
5085 static jmp_buf PHTimeout
;
5093 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
5094 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
5099 longjmp(PHTimeout
, 1);
5103 #if NPH_VERSION >= 10200
5104 ph_map_send_debug(appdata
, text
)
5107 ph_map_send_debug(text
)
5112 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
5113 "ph_map_send_debug: ==> %s", text
);
5115 sm_dprintf("ph_map_send_debug: ==> %s\n", text
);
5119 #if NPH_VERSION >= 10200
5120 ph_map_recv_debug(appdata
, text
)
5123 ph_map_recv_debug(text
)
5128 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
5129 "ph_map_recv_debug: <== %s", text
);
5131 sm_dprintf("ph_map_recv_debug: <== %s\n", text
);
5135 ** PH_MAP_OPEN -- sub for opening PH map
5138 ph_map_open(map
, mode
)
5142 PH_MAP_STRUCT
*pmap
;
5143 register SM_EVENT
*ev
= NULL
;
5145 char *hostlist
, *host
;
5148 sm_dprintf("ph_map_open(%s)\n", map
->map_mname
);
5151 if (mode
!= O_RDONLY
)
5153 /* issue a pseudo-error message */
5154 errno
= SM_EMAPCANTWRITE
;
5158 if (CurEnv
!= NULL
&& CurEnv
->e_sendmode
== SM_DEFER
&&
5159 bitset(MF_DEFER
, map
->map_mflags
))
5162 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
5166 ** Unset MF_DEFER here so that map_lookup() returns
5167 ** a temporary failure using the bogus map and
5168 ** map->map_tapp instead of the default permanent error.
5171 map
->map_mflags
&= ~MF_DEFER
;
5175 pmap
= (PH_MAP_STRUCT
*)map
->map_db1
;
5176 pmap
->ph_fastclose
= 0; /* refresh field for reopen */
5178 /* try each host in the list */
5179 hostlist
= newstr(pmap
->ph_servers
);
5180 for (host
= strtok(hostlist
, " ");
5182 host
= strtok(NULL
, " "))
5185 if (pmap
->ph_timeout
!= 0)
5187 if (setjmp(PHTimeout
) != 0)
5191 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
5192 "timeout connecting to PH server %.100s",
5195 goto ph_map_open_abort
;
5197 ev
= sm_setevent(pmap
->ph_timeout
, ph_timeout
, 0);
5200 /* open connection to server */
5201 if (ph_open(&(pmap
->ph
), host
,
5202 PH_OPEN_ROUNDROBIN
|PH_OPEN_DONTID
,
5203 ph_map_send_debug
, ph_map_recv_debug
5204 #if NPH_VERSION >= 10200
5208 && ph_id(pmap
->ph
, phmap_id
) == 0)
5212 sm_free(hostlist
); /* XXX */
5220 pmap
->ph_fastclose
= PH_CLOSE_FAST
;
5225 if (bitset(MF_NODEFER
, map
->map_mflags
))
5229 syserr("ph_map_open: %s: cannot connect to PH server",
5232 else if (!bitset(MF_OPTIONAL
, map
->map_mflags
) && LogLevel
> 1)
5233 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
5234 "ph_map_open: %s: cannot connect to PH server",
5236 sm_free(hostlist
); /* XXX */
5241 ** PH_MAP_LOOKUP -- look up key from ph server
5245 ph_map_lookup(map
, key
, args
, pstat
)
5251 int i
, save_errno
= 0;
5252 register SM_EVENT
*ev
= NULL
;
5253 PH_MAP_STRUCT
*pmap
;
5256 pmap
= (PH_MAP_STRUCT
*)map
->map_db1
;
5261 if (pmap
->ph_timeout
!= 0)
5263 if (setjmp(PHTimeout
) != 0)
5267 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
5268 "timeout during PH lookup of %.100s",
5271 *pstat
= EX_TEMPFAIL
;
5272 goto ph_map_lookup_abort
;
5274 ev
= sm_setevent(pmap
->ph_timeout
, ph_timeout
, 0);
5277 /* perform lookup */
5278 i
= ph_email_resolve(pmap
->ph
, key
, pmap
->ph_field_list
, &value
);
5280 *pstat
= EX_TEMPFAIL
;
5281 else if (i
== PH_ERR_NOMATCH
|| i
== PH_ERR_DATAERR
)
5282 *pstat
= EX_UNAVAILABLE
;
5284 ph_map_lookup_abort
:
5289 ** Close the connection if the timer popped
5290 ** or we got a temporary PH error
5293 if (*pstat
== EX_TEMPFAIL
)
5296 pmap
->ph_fastclose
= PH_CLOSE_FAST
;
5301 if (*pstat
== EX_OK
)
5304 sm_dprintf("ph_map_lookup: %s => %s\n", key
, value
);
5306 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5307 return map_rewrite(map
, key
, strlen(key
), NULL
);
5309 return map_rewrite(map
, value
, strlen(value
), args
);
5319 #define map_prio map_lockfd /* overload field */
5322 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
5326 syslog_map_parseargs(map
, args
)
5331 char *priority
= NULL
;
5333 /* there is no check whether there is really an argument */
5336 while (isascii(*p
) && isspace(*p
))
5343 map
->map_mflags
|= MF_DEFER
;
5348 map
->map_spacesub
= *++p
;
5354 while (*++p
!= '\0' && isascii(*p
) && isspace(*p
))
5359 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
5366 syserr("Illegal option %c map syslog", *p
);
5371 if (priority
== NULL
)
5372 map
->map_prio
= LOG_INFO
;
5375 if (sm_strncasecmp("LOG_", priority
, 4) == 0)
5379 if (sm_strcasecmp("EMERG", priority
) == 0)
5380 map
->map_prio
= LOG_EMERG
;
5382 #endif /* LOG_EMERG */
5384 if (sm_strcasecmp("ALERT", priority
) == 0)
5385 map
->map_prio
= LOG_ALERT
;
5387 #endif /* LOG_ALERT */
5389 if (sm_strcasecmp("CRIT", priority
) == 0)
5390 map
->map_prio
= LOG_CRIT
;
5392 #endif /* LOG_CRIT */
5394 if (sm_strcasecmp("ERR", priority
) == 0)
5395 map
->map_prio
= LOG_ERR
;
5397 #endif /* LOG_ERR */
5399 if (sm_strcasecmp("WARNING", priority
) == 0)
5400 map
->map_prio
= LOG_WARNING
;
5402 #endif /* LOG_WARNING */
5404 if (sm_strcasecmp("NOTICE", priority
) == 0)
5405 map
->map_prio
= LOG_NOTICE
;
5407 #endif /* LOG_NOTICE */
5409 if (sm_strcasecmp("INFO", priority
) == 0)
5410 map
->map_prio
= LOG_INFO
;
5412 #endif /* LOG_INFO */
5414 if (sm_strcasecmp("DEBUG", priority
) == 0)
5415 map
->map_prio
= LOG_DEBUG
;
5417 #endif /* LOG_DEBUG */
5419 syserr("syslog_map_parseargs: Unknown priority %s",
5428 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5432 syslog_map_lookup(map
, string
, args
, statp
)
5438 char *ptr
= map_rewrite(map
, string
, strlen(string
), args
);
5443 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5444 map
->map_mname
, map
->map_prio
, ptr
);
5446 sm_syslog(map
->map_prio
, CurEnv
->e_id
, "%s", ptr
);
5460 hes_map_open(map
, mode
)
5465 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5466 map
->map_mname
, map
->map_file
, mode
);
5468 if (mode
!= O_RDONLY
)
5470 /* issue a pseudo-error message */
5471 errno
= SM_EMAPCANTWRITE
;
5476 if (HesiodContext
!= NULL
|| hesiod_init(&HesiodContext
) == 0)
5479 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
5480 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5481 sm_errstring(errno
));
5483 # else /* HESIOD_INIT */
5484 if (hes_error() == HES_ER_UNINIT
)
5486 switch (hes_error())
5489 case HES_ER_NOTFOUND
:
5493 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
5494 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5497 # endif /* HESIOD_INIT */
5501 hes_map_lookup(map
, name
, av
, statp
)
5510 sm_dprintf("hes_map_lookup(%s, %s)\n", map
->map_file
, name
);
5512 if (name
[0] == '\\')
5520 if (nl
< sizeof nbuf
- 1)
5523 np
= xalloc(strlen(name
) + 2);
5525 (void) sm_strlcpy(&np
[1], name
, (sizeof nbuf
) - 1);
5527 hp
= hesiod_resolve(HesiodContext
, np
, map
->map_file
);
5528 # else /* HESIOD_INIT */
5529 hp
= hes_resolve(np
, map
->map_file
);
5530 # endif /* HESIOD_INIT */
5533 sm_free(np
); /* XXX */
5539 hp
= hesiod_resolve(HesiodContext
, name
, map
->map_file
);
5540 # else /* HESIOD_INIT */
5541 hp
= hes_resolve(name
, map
->map_file
);
5542 # endif /* HESIOD_INIT */
5545 if (hp
== NULL
|| *hp
== NULL
)
5550 *statp
= EX_NOTFOUND
;
5553 *statp
= EX_TEMPFAIL
;
5558 *statp
= EX_UNAVAILABLE
;
5562 hesiod_free_list(HesiodContext
, hp
);
5565 # else /* HESIOD_INIT */
5566 if (hp
== NULL
|| hp
[0] == NULL
)
5568 switch (hes_error())
5574 case HES_ER_NOTFOUND
:
5575 *statp
= EX_NOTFOUND
;
5579 *statp
= EX_UNAVAILABLE
;
5583 *statp
= EX_TEMPFAIL
;
5588 # endif /* HESIOD_INIT */
5590 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5591 return map_rewrite(map
, name
, strlen(name
), NULL
);
5593 return map_rewrite(map
, hp
[0], strlen(hp
[0]), av
);
5597 ** HES_MAP_CLOSE -- free the Hesiod context
5605 sm_dprintf("hes_map_close(%s)\n", map
->map_file
);
5608 /* Free the hesiod context */
5609 if (HesiodContext
!= NULL
)
5611 hesiod_end(HesiodContext
);
5612 HesiodContext
= NULL
;
5614 # endif /* HESIOD_INIT */
5619 ** NeXT NETINFO Modules
5624 # define NETINFO_DEFAULT_DIR "/aliases"
5625 # define NETINFO_DEFAULT_PROPERTY "members"
5628 ** NI_MAP_OPEN -- open NetInfo Aliases
5632 ni_map_open(map
, mode
)
5637 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5638 map
->map_mname
, map
->map_file
, mode
);
5641 if (*map
->map_file
== '\0')
5642 map
->map_file
= NETINFO_DEFAULT_DIR
;
5644 if (map
->map_valcolnm
== NULL
)
5645 map
->map_valcolnm
= NETINFO_DEFAULT_PROPERTY
;
5647 if (map
->map_coldelim
== '\0')
5649 if (bitset(MF_ALIAS
, map
->map_mflags
))
5650 map
->map_coldelim
= ',';
5651 else if (bitset(MF_FILECLASS
, map
->map_mflags
))
5652 map
->map_coldelim
= ' ';
5659 ** NI_MAP_LOOKUP -- look up a datum in NetInfo
5663 ni_map_lookup(map
, name
, av
, statp
)
5673 sm_dprintf("ni_map_lookup(%s, %s)\n", map
->map_mname
, name
);
5675 propval
= ni_propval(map
->map_file
, map
->map_keycolnm
, name
,
5676 map
->map_valcolnm
, map
->map_coldelim
);
5678 if (propval
== NULL
)
5682 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5683 res
= map_rewrite(map
, name
, strlen(name
), NULL
);
5685 res
= map_rewrite(map
, propval
, strlen(propval
), av
);
5694 ni_getcanonname(name
, hbsize
, statp
)
5701 char nbuf
[MAXNAME
+ 1];
5704 sm_dprintf("ni_getcanonname(%s)\n", name
);
5706 if (sm_strlcpy(nbuf
, name
, sizeof nbuf
) >= sizeof nbuf
)
5708 *statp
= EX_UNAVAILABLE
;
5711 (void) shorten_hostname(nbuf
);
5713 /* we only accept single token search key */
5714 if (strchr(nbuf
, '.'))
5721 vptr
= ni_propval("/machines", NULL
, nbuf
, "name", '\n');
5729 /* Only want the first machine name */
5730 if ((ptr
= strchr(vptr
, '\n')) != NULL
)
5733 if (sm_strlcpy(name
, vptr
, hbsize
) >= hbsize
)
5736 *statp
= EX_UNAVAILABLE
;
5743 #endif /* NETINFO */
5745 ** TEXT (unindexed text file) Modules
5747 ** This code donated by Sun Microsystems.
5750 #define map_sff map_lockfd /* overload field */
5754 ** TEXT_MAP_OPEN -- open text table
5758 text_map_open(map
, mode
)
5766 sm_dprintf("text_map_open(%s, %s, %d)\n",
5767 map
->map_mname
, map
->map_file
, mode
);
5770 if (mode
!= O_RDONLY
)
5776 if (*map
->map_file
== '\0')
5778 syserr("text map \"%s\": file name required",
5783 if (map
->map_file
[0] != '/')
5785 syserr("text map \"%s\": file name must be fully qualified",
5790 sff
= SFF_ROOTOK
|SFF_REGONLY
;
5791 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
5793 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
5794 sff
|= SFF_SAFEDIRPATH
;
5795 if ((i
= safefile(map
->map_file
, RunAsUid
, RunAsGid
, RunAsUserName
,
5796 sff
, S_IRUSR
, NULL
)) != 0)
5798 int save_errno
= errno
;
5800 /* cannot open this map */
5802 sm_dprintf("\tunsafe map file: %d\n", i
);
5804 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
5805 syserr("text map \"%s\": unsafe map file %s",
5806 map
->map_mname
, map
->map_file
);
5810 if (map
->map_keycolnm
== NULL
)
5811 map
->map_keycolno
= 0;
5814 if (!(isascii(*map
->map_keycolnm
) && isdigit(*map
->map_keycolnm
)))
5816 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5817 map
->map_mname
, map
->map_file
,
5821 map
->map_keycolno
= atoi(map
->map_keycolnm
);
5824 if (map
->map_valcolnm
== NULL
)
5825 map
->map_valcolno
= 0;
5828 if (!(isascii(*map
->map_valcolnm
) && isdigit(*map
->map_valcolnm
)))
5830 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5831 map
->map_mname
, map
->map_file
,
5835 map
->map_valcolno
= atoi(map
->map_valcolnm
);
5840 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5841 map
->map_mname
, map
->map_file
);
5842 if (map
->map_coldelim
== '\0')
5843 sm_dprintf("(white space)\n");
5845 sm_dprintf("%c\n", map
->map_coldelim
);
5854 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5858 text_map_lookup(map
, name
, av
, statp
)
5871 long sff
= map
->map_sff
;
5872 char search_key
[MAXNAME
+ 1];
5873 char linebuf
[MAXLINE
];
5874 char buf
[MAXNAME
+ 1];
5878 sm_dprintf("text_map_lookup(%s, %s)\n", map
->map_mname
, name
);
5880 buflen
= strlen(name
);
5881 if (buflen
> sizeof search_key
- 1)
5882 buflen
= sizeof search_key
- 1; /* XXX just cut if off? */
5883 memmove(search_key
, name
, buflen
);
5884 search_key
[buflen
] = '\0';
5885 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
5886 makelower(search_key
);
5888 f
= safefopen(map
->map_file
, O_RDONLY
, FileMode
, sff
);
5891 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
5892 *statp
= EX_UNAVAILABLE
;
5895 key_idx
= map
->map_keycolno
;
5896 delim
= map
->map_coldelim
;
5897 while (sm_io_fgets(f
, SM_TIME_DEFAULT
,
5898 linebuf
, sizeof linebuf
) != NULL
)
5902 /* skip comment line */
5903 if (linebuf
[0] == '#')
5905 p
= strchr(linebuf
, '\n');
5908 p
= get_column(linebuf
, key_idx
, delim
, buf
, sizeof buf
);
5909 if (p
!= NULL
&& sm_strcasecmp(search_key
, p
) == 0)
5915 (void) sm_io_close(f
, SM_TIME_DEFAULT
);
5918 *statp
= EX_NOTFOUND
;
5921 vp
= get_column(linebuf
, map
->map_valcolno
, delim
, buf
, sizeof buf
);
5924 *statp
= EX_NOTFOUND
;
5929 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5930 return map_rewrite(map
, name
, strlen(name
), NULL
);
5932 return map_rewrite(map
, vp
, vsize
, av
);
5936 ** TEXT_GETCANONNAME -- look up canonical name in hosts file
5940 text_getcanonname(name
, hbsize
, statp
)
5948 char linebuf
[MAXLINE
];
5949 char cbuf
[MAXNAME
+ 1];
5950 char nbuf
[MAXNAME
+ 1];
5953 sm_dprintf("text_getcanonname(%s)\n", name
);
5955 if (sm_strlcpy(nbuf
, name
, sizeof nbuf
) >= sizeof nbuf
)
5957 *statp
= EX_UNAVAILABLE
;
5960 dot
= shorten_hostname(nbuf
);
5962 f
= sm_io_open(SmFtStdio
, SM_TIME_DEFAULT
, HostsFile
, SM_IO_RDONLY
,
5966 *statp
= EX_UNAVAILABLE
;
5971 sm_io_fgets(f
, SM_TIME_DEFAULT
,
5972 linebuf
, sizeof linebuf
) != NULL
)
5974 char *p
= strpbrk(linebuf
, "#\n");
5978 if (linebuf
[0] != '\0')
5979 found
= extract_canonname(nbuf
, dot
, linebuf
,
5982 (void) sm_io_close(f
, SM_TIME_DEFAULT
);
5989 if (sm_strlcpy(name
, cbuf
, hbsize
) >= hbsize
)
5991 *statp
= EX_UNAVAILABLE
;
5998 ** STAB (Symbol Table) Modules
6003 ** STAB_MAP_LOOKUP -- look up alias in symbol table
6008 stab_map_lookup(map
, name
, av
, pstat
)
6017 sm_dprintf("stab_lookup(%s, %s)\n",
6018 map
->map_mname
, name
);
6020 s
= stab(name
, ST_ALIAS
, ST_FIND
);
6028 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
6032 stab_map_store(map
, lhs
, rhs
)
6039 s
= stab(lhs
, ST_ALIAS
, ST_ENTER
);
6040 s
->s_alias
= newstr(rhs
);
6045 ** STAB_MAP_OPEN -- initialize (reads data file)
6047 ** This is a wierd case -- it is only intended as a fallback for
6048 ** aliases. For this reason, opens for write (only during a
6049 ** "newaliases") always fails, and opens for read open the
6050 ** actual underlying text file instead of the database.
6054 stab_map_open(map
, mode
)
6063 sm_dprintf("stab_map_open(%s, %s, %d)\n",
6064 map
->map_mname
, map
->map_file
, mode
);
6067 if (mode
!= O_RDONLY
)
6073 sff
= SFF_ROOTOK
|SFF_REGONLY
;
6074 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
6076 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
6077 sff
|= SFF_SAFEDIRPATH
;
6078 af
= safefopen(map
->map_file
, O_RDONLY
, 0444, sff
);
6081 readaliases(map
, af
, false, false);
6083 if (fstat(sm_io_getinfo(af
, SM_IO_WHAT_FD
, NULL
), &st
) >= 0)
6084 map
->map_mtime
= st
.st_mtime
;
6085 (void) sm_io_close(af
, SM_TIME_DEFAULT
);
6092 ** Tries several types. For back compatibility of aliases.
6097 ** IMPL_MAP_LOOKUP -- lookup in best open database
6101 impl_map_lookup(map
, name
, av
, pstat
)
6108 sm_dprintf("impl_map_lookup(%s, %s)\n",
6109 map
->map_mname
, name
);
6112 if (bitset(MF_IMPL_HASH
, map
->map_mflags
))
6113 return db_map_lookup(map
, name
, av
, pstat
);
6116 if (bitset(MF_IMPL_NDBM
, map
->map_mflags
))
6117 return ndbm_map_lookup(map
, name
, av
, pstat
);
6119 return stab_map_lookup(map
, name
, av
, pstat
);
6123 ** IMPL_MAP_STORE -- store in open databases
6127 impl_map_store(map
, lhs
, rhs
)
6133 sm_dprintf("impl_map_store(%s, %s, %s)\n",
6134 map
->map_mname
, lhs
, rhs
);
6136 if (bitset(MF_IMPL_HASH
, map
->map_mflags
))
6137 db_map_store(map
, lhs
, rhs
);
6140 if (bitset(MF_IMPL_NDBM
, map
->map_mflags
))
6141 ndbm_map_store(map
, lhs
, rhs
);
6143 stab_map_store(map
, lhs
, rhs
);
6147 ** IMPL_MAP_OPEN -- implicit database open
6151 impl_map_open(map
, mode
)
6156 sm_dprintf("impl_map_open(%s, %s, %d)\n",
6157 map
->map_mname
, map
->map_file
, mode
);
6161 map
->map_mflags
|= MF_IMPL_HASH
;
6162 if (hash_map_open(map
, mode
))
6164 # ifdef NDBM_YP_COMPAT
6165 if (mode
== O_RDONLY
|| strstr(map
->map_file
, "/yp/") == NULL
)
6166 # endif /* NDBM_YP_COMPAT */
6170 map
->map_mflags
&= ~MF_IMPL_HASH
;
6173 map
->map_mflags
|= MF_IMPL_NDBM
;
6174 if (ndbm_map_open(map
, mode
))
6179 map
->map_mflags
&= ~MF_IMPL_NDBM
;
6182 #if defined(NEWDB) || defined(NDBM)
6184 message("WARNING: cannot open alias database %s%s",
6186 mode
== O_RDONLY
? "; reading text version" : "");
6187 #else /* defined(NEWDB) || defined(NDBM) */
6188 if (mode
!= O_RDONLY
)
6189 usrerr("Cannot rebuild aliases: no database format defined");
6190 #endif /* defined(NEWDB) || defined(NDBM) */
6192 if (mode
== O_RDONLY
)
6193 return stab_map_open(map
, mode
);
6200 ** IMPL_MAP_CLOSE -- close any open database(s)
6208 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
6209 map
->map_mname
, map
->map_file
, map
->map_mflags
);
6211 if (bitset(MF_IMPL_HASH
, map
->map_mflags
))
6214 map
->map_mflags
&= ~MF_IMPL_HASH
;
6219 if (bitset(MF_IMPL_NDBM
, map
->map_mflags
))
6221 ndbm_map_close(map
);
6222 map
->map_mflags
&= ~MF_IMPL_NDBM
;
6229 ** Provides access to the system password file.
6233 ** USER_MAP_OPEN -- open user map
6235 ** Really just binds field names to field numbers.
6239 user_map_open(map
, mode
)
6244 sm_dprintf("user_map_open(%s, %d)\n",
6245 map
->map_mname
, mode
);
6248 if (mode
!= O_RDONLY
)
6250 /* issue a pseudo-error message */
6251 errno
= SM_EMAPCANTWRITE
;
6254 if (map
->map_valcolnm
== NULL
)
6257 else if (sm_strcasecmp(map
->map_valcolnm
, "name") == 0)
6258 map
->map_valcolno
= 1;
6259 else if (sm_strcasecmp(map
->map_valcolnm
, "passwd") == 0)
6260 map
->map_valcolno
= 2;
6261 else if (sm_strcasecmp(map
->map_valcolnm
, "uid") == 0)
6262 map
->map_valcolno
= 3;
6263 else if (sm_strcasecmp(map
->map_valcolnm
, "gid") == 0)
6264 map
->map_valcolno
= 4;
6265 else if (sm_strcasecmp(map
->map_valcolnm
, "gecos") == 0)
6266 map
->map_valcolno
= 5;
6267 else if (sm_strcasecmp(map
->map_valcolnm
, "dir") == 0)
6268 map
->map_valcolno
= 6;
6269 else if (sm_strcasecmp(map
->map_valcolnm
, "shell") == 0)
6270 map
->map_valcolno
= 7;
6273 syserr("User map %s: unknown column name %s",
6274 map
->map_mname
, map
->map_valcolnm
);
6282 ** USER_MAP_LOOKUP -- look up a user in the passwd file.
6287 user_map_lookup(map
, key
, av
, statp
)
6297 sm_dprintf("user_map_lookup(%s, %s)\n",
6298 map
->map_mname
, key
);
6300 *statp
= finduser(key
, &fuzzy
, &user
);
6301 if (*statp
!= EX_OK
)
6303 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
6304 return map_rewrite(map
, key
, strlen(key
), NULL
);
6310 switch (map
->map_valcolno
)
6314 rwval
= user
.mbdb_name
;
6318 rwval
= "x"; /* passwd no longer supported */
6322 (void) sm_snprintf(buf
, sizeof buf
, "%d",
6323 (int) user
.mbdb_uid
);
6328 (void) sm_snprintf(buf
, sizeof buf
, "%d",
6329 (int) user
.mbdb_gid
);
6334 rwval
= user
.mbdb_fullname
;
6338 rwval
= user
.mbdb_homedir
;
6342 rwval
= user
.mbdb_shell
;
6345 return map_rewrite(map
, rwval
, strlen(rwval
), av
);
6349 ** Program map type.
6351 ** This provides access to arbitrary programs. It should be used
6352 ** only very sparingly, since there is no way to bound the cost
6353 ** of invoking an arbitrary program.
6357 prog_map_lookup(map
, name
, av
, statp
)
6370 char *argv
[MAXPV
+ 1];
6374 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6375 map
->map_mname
, name
, map
->map_file
);
6378 argv
[i
++] = map
->map_file
;
6379 if (map
->map_rebuild
!= NULL
)
6381 (void) sm_strlcpy(buf
, map
->map_rebuild
, sizeof buf
);
6382 for (p
= strtok(buf
, " \t"); p
!= NULL
; p
= strtok(NULL
, " \t"))
6393 sm_dprintf("prog_open:");
6394 for (i
= 0; argv
[i
] != NULL
; i
++)
6395 sm_dprintf(" %s", argv
[i
]);
6398 (void) sm_blocksignal(SIGCHLD
);
6399 pid
= prog_open(argv
, &fd
, CurEnv
);
6402 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
6403 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6404 map
->map_mname
, sm_errstring(errno
));
6405 else if (tTd(38, 9))
6406 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6407 map
->map_mname
, sm_errstring(errno
));
6408 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
6412 i
= read(fd
, buf
, sizeof buf
- 1);
6415 syserr("prog_map_lookup(%s): read error %s",
6416 map
->map_mname
, sm_errstring(errno
));
6422 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6429 p
= strchr(buf
, '\n');
6433 /* collect the return value */
6434 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
6435 rval
= map_rewrite(map
, name
, strlen(name
), NULL
);
6437 rval
= map_rewrite(map
, buf
, strlen(buf
), av
);
6439 /* now flush any additional output */
6440 while ((i
= read(fd
, buf
, sizeof buf
)) > 0)
6444 /* wait for the process to terminate */
6446 status
= waitfor(pid
);
6448 (void) sm_releasesignal(SIGCHLD
);
6453 syserr("prog_map_lookup(%s): wait error %s",
6454 map
->map_mname
, sm_errstring(errno
));
6455 *statp
= EX_SOFTWARE
;
6458 else if (WIFEXITED(status
))
6460 if ((*statp
= WEXITSTATUS(status
)) != EX_OK
)
6465 syserr("prog_map_lookup(%s): child died on signal %d",
6466 map
->map_mname
, status
);
6467 *statp
= EX_UNAVAILABLE
;
6473 ** Sequenced map type.
6475 ** Tries each map in order until something matches, much like
6476 ** implicit. Stores go to the first map in the list that can
6479 ** This is slightly unusual in that there are two interfaces.
6480 ** The "sequence" interface lets you stack maps arbitrarily.
6481 ** The "switch" interface builds a sequence map by looking
6482 ** at a system-dependent configuration file such as
6483 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6485 ** We don't need an explicit open, since all maps are
6486 ** opened on demand.
6490 ** SEQ_MAP_PARSE -- Sequenced map parsing
6494 seq_map_parse(map
, ap
)
6501 sm_dprintf("seq_map_parse(%s, %s)\n", map
->map_mname
, ap
);
6508 /* find beginning of map name */
6509 while (isascii(*ap
) && isspace(*ap
))
6512 (isascii(*p
) && isalnum(*p
)) || *p
== '_' || *p
== '.';
6517 while (*p
!= '\0' && (!isascii(*p
) || !isalnum(*p
)))
6524 s
= stab(ap
, ST_MAP
, ST_FIND
);
6527 syserr("Sequence map %s: unknown member map %s",
6528 map
->map_mname
, ap
);
6530 else if (maxmap
>= MAXMAPSTACK
)
6532 syserr("Sequence map %s: too many member maps (%d max)",
6533 map
->map_mname
, MAXMAPSTACK
);
6536 else if (maxmap
< MAXMAPSTACK
)
6538 map
->map_stack
[maxmap
++] = &s
->s_map
;
6546 ** SWITCH_MAP_OPEN -- open a switched map
6548 ** This looks at the system-dependent configuration and builds
6549 ** a sequence map that does the same thing.
6551 ** Every system must define a switch_map_find routine in conf.c
6552 ** that will return the list of service types associated with a
6553 ** given service class.
6557 switch_map_open(map
, mode
)
6563 char *maptype
[MAXMAPSTACK
];
6566 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6567 map
->map_mname
, map
->map_file
, mode
);
6570 nmaps
= switch_map_find(map
->map_file
, maptype
, map
->map_return
);
6573 sm_dprintf("\tswitch_map_find => %d\n", nmaps
);
6574 for (mapno
= 0; mapno
< nmaps
; mapno
++)
6575 sm_dprintf("\t\t%s\n", maptype
[mapno
]);
6577 if (nmaps
<= 0 || nmaps
> MAXMAPSTACK
)
6580 for (mapno
= 0; mapno
< nmaps
; mapno
++)
6583 char nbuf
[MAXNAME
+ 1];
6585 if (maptype
[mapno
] == NULL
)
6587 (void) sm_strlcpyn(nbuf
, sizeof nbuf
, 3,
6588 map
->map_mname
, ".", maptype
[mapno
]);
6589 s
= stab(nbuf
, ST_MAP
, ST_FIND
);
6592 syserr("Switch map %s: unknown member map %s",
6593 map
->map_mname
, nbuf
);
6597 map
->map_stack
[mapno
] = &s
->s_map
;
6599 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6601 s
->s_map
.map_class
->map_cname
,
6610 ** SEQ_MAP_CLOSE -- close all underlying maps
6620 sm_dprintf("seq_map_close(%s)\n", map
->map_mname
);
6622 for (mapno
= 0; mapno
< MAXMAPSTACK
; mapno
++)
6624 MAP
*mm
= map
->map_stack
[mapno
];
6626 if (mm
== NULL
|| !bitset(MF_OPEN
, mm
->map_mflags
))
6628 mm
->map_mflags
|= MF_CLOSING
;
6629 mm
->map_class
->map_close(mm
);
6630 mm
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
6636 ** SEQ_MAP_LOOKUP -- sequenced map lookup
6640 seq_map_lookup(map
, key
, args
, pstat
)
6648 bool tempfail
= false;
6651 sm_dprintf("seq_map_lookup(%s, %s)\n", map
->map_mname
, key
);
6653 for (mapno
= 0; mapno
< MAXMAPSTACK
; mapbit
<<= 1, mapno
++)
6655 MAP
*mm
= map
->map_stack
[mapno
];
6660 if (!bitset(MF_OPEN
, mm
->map_mflags
) &&
6663 if (bitset(mapbit
, map
->map_return
[MA_UNAVAIL
]))
6665 *pstat
= EX_UNAVAILABLE
;
6671 rv
= mm
->map_class
->map_lookup(mm
, key
, args
, pstat
);
6674 if (*pstat
== EX_TEMPFAIL
)
6676 if (bitset(mapbit
, map
->map_return
[MA_TRYAGAIN
]))
6680 else if (bitset(mapbit
, map
->map_return
[MA_NOTFOUND
]))
6684 *pstat
= EX_TEMPFAIL
;
6685 else if (*pstat
== EX_OK
)
6686 *pstat
= EX_NOTFOUND
;
6691 ** SEQ_MAP_STORE -- sequenced map store
6695 seq_map_store(map
, key
, val
)
6703 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6704 map
->map_mname
, key
, val
);
6706 for (mapno
= 0; mapno
< MAXMAPSTACK
; mapno
++)
6708 MAP
*mm
= map
->map_stack
[mapno
];
6710 if (mm
== NULL
|| !bitset(MF_WRITABLE
, mm
->map_mflags
))
6713 mm
->map_class
->map_store(mm
, key
, val
);
6716 syserr("seq_map_store(%s, %s, %s): no writable map",
6717 map
->map_mname
, key
, val
);
6725 null_map_open(map
, mode
)
6741 null_map_lookup(map
, key
, args
, pstat
)
6747 *pstat
= EX_NOTFOUND
;
6753 null_map_store(map
, key
, val
)
6766 bogus_map_lookup(map
, key
, args
, pstat
)
6772 *pstat
= EX_TEMPFAIL
;
6776 MAPCLASS BogusMapClass
=
6778 "bogus-map", NULL
, 0,
6779 NULL
, bogus_map_lookup
, null_map_store
,
6780 null_map_open
, null_map_close
,
6787 macro_map_lookup(map
, name
, av
, statp
)
6796 sm_dprintf("macro_map_lookup(%s, %s)\n", map
->map_mname
,
6797 name
== NULL
? "NULL" : name
);
6801 (mid
= macid(name
)) == 0)
6808 macdefine(&CurEnv
->e_macro
, A_PERM
, mid
, NULL
);
6810 macdefine(&CurEnv
->e_macro
, A_TEMP
, mid
, av
[1]);
6823 # define DEFAULT_DELIM CONDELSE
6824 # define END_OF_FIELDS -1
6825 # define ERRBUF_SIZE 80
6826 # define MAX_MATCH 32
6828 # define xnalloc(s) memset(xalloc(s), '\0', s);
6832 regex_t
*regex_pattern_buf
; /* xalloc it */
6833 int *regex_subfields
; /* move to type MAP */
6834 char *regex_delim
; /* move to type MAP */
6838 parse_fields(s
, ibuf
, blen
, nr_substrings
)
6840 int *ibuf
; /* array */
6841 int blen
; /* number of elements in ibuf */
6842 int nr_substrings
; /* number of substrings in the pattern */
6846 bool lastone
= false;
6848 blen
--; /* for terminating END_OF_FIELDS */
6869 if (val
< 0 || val
>= nr_substrings
)
6871 syserr("field (%d) out of range, only %d substrings in pattern",
6872 val
, nr_substrings
);
6879 syserr("too many fields, %d max", blen
);
6884 ibuf
[i
] = END_OF_FIELDS
;
6889 regex_map_init(map
, ap
)
6894 struct regex_map
*map_p
;
6896 char *sub_param
= NULL
;
6898 static char defdstr
[] = { (char) DEFAULT_DELIM
, '\0' };
6901 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6902 map
->map_mname
, ap
);
6904 pflags
= REG_ICASE
| REG_EXTENDED
| REG_NOSUB
;
6906 map_p
= (struct regex_map
*) xnalloc(sizeof *map_p
);
6907 map_p
->regex_pattern_buf
= (regex_t
*)xnalloc(sizeof(regex_t
));
6911 while (isascii(*p
) && isspace(*p
))
6918 map
->map_mflags
|= MF_REGEX_NOT
;
6921 case 'f': /* case sensitive */
6922 map
->map_mflags
|= MF_NOFOLDCASE
;
6923 pflags
&= ~REG_ICASE
;
6926 case 'b': /* basic regular expressions */
6927 pflags
&= ~REG_EXTENDED
;
6930 case 's': /* substring match () syntax */
6932 pflags
&= ~REG_NOSUB
;
6935 case 'd': /* delimiter */
6936 map_p
->regex_delim
= ++p
;
6939 case 'a': /* map append */
6943 case 'm': /* matchonly */
6944 map
->map_mflags
|= MF_MATCHONLY
;
6948 map
->map_mflags
|= MF_KEEPQUOTES
;
6952 map
->map_spacesub
= *++p
;
6956 map
->map_mflags
|= MF_DEFER
;
6960 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
6966 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p
, pflags
);
6968 if ((regerr
= regcomp(map_p
->regex_pattern_buf
, p
, pflags
)) != 0)
6971 char errbuf
[ERRBUF_SIZE
];
6973 (void) regerror(regerr
, map_p
->regex_pattern_buf
,
6974 errbuf
, sizeof errbuf
);
6975 syserr("pattern-compile-error: %s", errbuf
);
6976 sm_free(map_p
->regex_pattern_buf
); /* XXX */
6977 sm_free(map_p
); /* XXX */
6981 if (map
->map_app
!= NULL
)
6982 map
->map_app
= newstr(map
->map_app
);
6983 if (map_p
->regex_delim
!= NULL
)
6984 map_p
->regex_delim
= newstr(map_p
->regex_delim
);
6986 map_p
->regex_delim
= defdstr
;
6988 if (!bitset(REG_NOSUB
, pflags
))
6990 /* substring matching */
6992 int *fields
= (int *) xalloc(sizeof(int) * (MAX_MATCH
+ 1));
6994 substrings
= map_p
->regex_pattern_buf
->re_nsub
+ 1;
6997 sm_dprintf("regex_map_init: nr of substrings %d\n",
7000 if (substrings
>= MAX_MATCH
)
7002 syserr("too many substrings, %d max", MAX_MATCH
);
7003 sm_free(map_p
->regex_pattern_buf
); /* XXX */
7004 sm_free(map_p
); /* XXX */
7007 if (sub_param
!= NULL
&& sub_param
[0] != '\0')
7009 /* optional parameter -sfields */
7010 if (parse_fields(sub_param
, fields
,
7011 MAX_MATCH
+ 1, substrings
) == -1)
7018 /* set default fields */
7019 for (i
= 0; i
< substrings
; i
++)
7021 fields
[i
] = END_OF_FIELDS
;
7023 map_p
->regex_subfields
= fields
;
7028 sm_dprintf("regex_map_init: subfields");
7029 for (ip
= fields
; *ip
!= END_OF_FIELDS
; ip
++)
7030 sm_dprintf(" %d", *ip
);
7034 map
->map_db1
= (ARBPTR_T
) map_p
; /* dirty hack */
7039 regex_map_rewrite(map
, s
, slen
, av
)
7045 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
7046 return map_rewrite(map
, av
[0], strlen(av
[0]), NULL
);
7048 return map_rewrite(map
, s
, slen
, av
);
7052 regex_map_lookup(map
, name
, av
, statp
)
7059 struct regex_map
*map_p
;
7060 regmatch_t pmatch
[MAX_MATCH
];
7066 sm_dprintf("regex_map_lookup: key '%s'\n", name
);
7067 for (cpp
= av
; cpp
!= NULL
&& *cpp
!= NULL
; cpp
++)
7068 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp
);
7071 map_p
= (struct regex_map
*)(map
->map_db1
);
7072 reg_res
= regexec(map_p
->regex_pattern_buf
,
7073 name
, MAX_MATCH
, pmatch
, 0);
7075 if (bitset(MF_REGEX_NOT
, map
->map_mflags
))
7078 if (reg_res
== REG_NOMATCH
)
7079 return regex_map_rewrite(map
, "", (size_t) 0, av
);
7083 if (reg_res
== REG_NOMATCH
)
7086 if (map_p
->regex_subfields
!= NULL
)
7089 static char retbuf
[MAXNAME
];
7090 int fields
[MAX_MATCH
+ 1];
7092 int anglecnt
= 0, cmntcnt
= 0, spacecnt
= 0;
7093 bool quotemode
= false, bslashmode
= false;
7094 register char *dp
, *sp
;
7099 ldp
= retbuf
+ sizeof(retbuf
) - 1;
7103 if (parse_fields(av
[1], fields
, MAX_MATCH
+ 1,
7104 (int) map_p
->regex_pattern_buf
->re_nsub
+ 1) == -1)
7112 ip
= map_p
->regex_subfields
;
7114 for ( ; *ip
!= END_OF_FIELDS
; ip
++)
7118 for (sp
= map_p
->regex_delim
; *sp
; sp
++)
7127 if (*ip
>= MAX_MATCH
||
7128 pmatch
[*ip
].rm_so
< 0 || pmatch
[*ip
].rm_eo
< 0)
7131 sp
= name
+ pmatch
[*ip
].rm_so
;
7132 endp
= name
+ pmatch
[*ip
].rm_eo
;
7133 for (; endp
> sp
; sp
++)
7142 else if (quotemode
&& *sp
!= '"' &&
7147 else switch (*dp
++ = *sp
)
7174 quotemode
= !quotemode
;
7180 if (anglecnt
!= 0 || cmntcnt
!= 0 || quotemode
||
7181 bslashmode
|| spacecnt
!= 0)
7183 sm_syslog(LOG_WARNING
, NOQID
,
7184 "Warning: regex may cause prescan() failure map=%s lookup=%s",
7185 map
->map_mname
, name
);
7191 return regex_map_rewrite(map
, retbuf
, strlen(retbuf
), av
);
7193 return regex_map_rewrite(map
, "", (size_t)0, av
);
7195 #endif /* MAP_REGEX */
7202 # define _DATUM_DEFINED
7203 # include <ns_api.h>
7205 typedef struct ns_map_list
7207 ns_map_t
*map
; /* XXX ns_ ? */
7209 struct ns_map_list
*next
;
7213 ns_map_t_find(mapname
)
7216 static ns_map_list_t
*ns_maps
= NULL
;
7217 ns_map_list_t
*ns_map
;
7219 /* walk the list of maps looking for the correctly named map */
7220 for (ns_map
= ns_maps
; ns_map
!= NULL
; ns_map
= ns_map
->next
)
7222 if (strcmp(ns_map
->mapname
, mapname
) == 0)
7226 /* if we are looking at a NULL ns_map_list_t, then create a new one */
7229 ns_map
= (ns_map_list_t
*) xalloc(sizeof *ns_map
);
7230 ns_map
->mapname
= newstr(mapname
);
7231 ns_map
->map
= (ns_map_t
*) xalloc(sizeof *ns_map
->map
);
7232 memset(ns_map
->map
, '\0', sizeof *ns_map
->map
);
7233 ns_map
->next
= ns_maps
;
7240 nsd_map_lookup(map
, name
, av
, statp
)
7249 char keybuf
[MAXNAME
+ 1];
7253 sm_dprintf("nsd_map_lookup(%s, %s)\n", map
->map_mname
, name
);
7255 buflen
= strlen(name
);
7256 if (buflen
> sizeof keybuf
- 1)
7257 buflen
= sizeof keybuf
- 1; /* XXX simply cut off? */
7258 memmove(keybuf
, name
, buflen
);
7259 keybuf
[buflen
] = '\0';
7260 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
7263 ns_map
= ns_map_t_find(map
->map_file
);
7267 sm_dprintf("nsd_map_t_find failed\n");
7268 *statp
= EX_UNAVAILABLE
;
7271 r
= ns_lookup(ns_map
, NULL
, map
->map_file
, keybuf
, NULL
,
7273 if (r
== NS_UNAVAIL
|| r
== NS_TRYAGAIN
)
7275 *statp
= EX_TEMPFAIL
;
7281 # endif /* NS_NOPERM */
7287 if (r
!= NS_SUCCESS
)
7289 *statp
= EX_NOTFOUND
;
7295 /* Null out trailing \n */
7296 if ((p
= strchr(buf
, '\n')) != NULL
)
7299 return map_rewrite(map
, buf
, strlen(buf
), av
);
7301 #endif /* MAP_NSD */
7304 arith_map_lookup(map
, name
, av
, statp
)
7314 static char result
[16];
7319 sm_dprintf("arith_map_lookup: key '%s'\n", name
);
7320 for (cpp
= av
; cpp
!= NULL
&& *cpp
!= NULL
; cpp
++)
7321 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp
);
7329 ** read arguments for arith map
7330 ** - no check is made whether they are really numbers
7331 ** - just ignores args after the second
7334 for (++cpp
; cpp
!= NULL
&& *cpp
!= NULL
&& r
< 2; cpp
++)
7335 v
[r
++] = strtol(*cpp
, NULL
, 0);
7337 /* operator and (at least) two operands given? */
7338 if (name
!= NULL
&& r
== 2)
7387 sm_syslog(LOG_WARNING
, NOQID
,
7388 "arith_map: unknown operator %c",
7389 isprint(*name
) ? *name
: '?');
7393 (void) sm_snprintf(result
, sizeof result
,
7394 res
? "TRUE" : "FALSE");
7396 (void) sm_snprintf(result
, sizeof result
, "%ld", r
);