2 * Copyright (c) 1998-2005 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.672 2006/04/18 01:26:41 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 static STAB
*socket_map_findconn
__P((const char*));
72 /* XXX arbitrary limit for sanity */
73 # define SOCKETMAP_MAXL 1000000
74 #endif /* SOCKETMAP */
76 /* default error message for trying to open a map in write mode */
78 # define SM_EMAPCANTWRITE ENOSYS
81 # define SM_EMAPCANTWRITE EFTYPE
83 # define SM_EMAPCANTWRITE ENXIO
88 ** MAP.C -- implementations for various map classes.
90 ** Each map class implements a series of functions:
92 ** bool map_parse(MAP *map, char *args)
93 ** Parse the arguments from the config file. Return true
94 ** if they were ok, false otherwise. Fill in map with the
97 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat)
98 ** Look up the key in the given map. If found, do any
99 ** rewriting the map wants (including "args" if desired)
100 ** and return the value. Set *pstat to the appropriate status
101 ** on error and return NULL. Args will be NULL if called
102 ** from the alias routines, although this should probably
103 ** not be relied upon. It is suggested you call map_rewrite
104 ** to return the results -- it takes care of null termination
105 ** and uses a dynamically expanded buffer as needed.
107 ** void map_store(MAP *map, char *key, char *value)
108 ** Store the key:value pair in the map.
110 ** bool map_open(MAP *map, int mode)
111 ** Open the map for the indicated mode. Mode should
112 ** be either O_RDONLY or O_RDWR. Return true if it
113 ** was opened successfully, false otherwise. If the open
114 ** failed and the MF_OPTIONAL flag is not set, it should
115 ** also print an error. If the MF_ALIAS bit is set
116 ** and this map class understands the @:@ convention, it
117 ** should call aliaswait() before returning.
119 ** void map_close(MAP *map)
122 ** This file also includes the implementation for getcanonname.
123 ** It is currently implemented in a pretty ad-hoc manner; it ought
124 ** to be more properly integrated into the map structure.
127 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL
128 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */
129 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
130 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */
131 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */
134 ** MAP_PARSEARGS -- parse config line arguments for database lookup
136 ** This is a generic version of the map_parse method.
139 ** map -- the map being initialized.
140 ** ap -- a pointer to the args on the config line.
143 ** true -- if everything parsed OK.
144 ** false -- otherwise.
147 ** null terminates the filename; stores it in map
151 map_parseargs(map
, ap
)
155 register char *p
= ap
;
158 ** There is no check whether there is really an argument,
159 ** but that's not important enough to warrant extra code.
162 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
163 map
->map_spacesub
= SpaceSub
; /* default value */
166 while (isascii(*p
) && isspace(*p
))
173 map
->map_mflags
|= MF_INCLNULL
;
174 map
->map_mflags
&= ~MF_TRY0NULL
;
178 map
->map_mflags
&= ~MF_TRY1NULL
;
182 map
->map_mflags
|= MF_OPTIONAL
;
186 map
->map_mflags
|= MF_NOFOLDCASE
;
190 map
->map_mflags
|= MF_MATCHONLY
;
194 map
->map_mflags
|= MF_APPEND
;
198 map
->map_mflags
|= MF_KEEPQUOTES
;
210 while (isascii(*++p
) && isspace(*p
))
212 map
->map_keycolnm
= p
;
216 while (isascii(*++p
) && isspace(*p
))
218 map
->map_valcolnm
= p
;
223 map
->map_coldelim
= *p
;
229 map
->map_coldelim
= '\n';
233 map
->map_coldelim
= '\t';
237 map
->map_coldelim
= '\\';
243 map
->map_mflags
|= MF_NODEFER
;
248 map
->map_spacesub
= *++p
;
252 map
->map_mflags
|= MF_DEFER
;
256 syserr("Illegal option %c map %s", *p
, map
->map_mname
);
259 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
264 if (map
->map_app
!= NULL
)
265 map
->map_app
= newstr(map
->map_app
);
266 if (map
->map_tapp
!= NULL
)
267 map
->map_tapp
= newstr(map
->map_tapp
);
268 if (map
->map_keycolnm
!= NULL
)
269 map
->map_keycolnm
= newstr(map
->map_keycolnm
);
270 if (map
->map_valcolnm
!= NULL
)
271 map
->map_valcolnm
= newstr(map
->map_valcolnm
);
276 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
280 map
->map_file
= newstr(map
->map_file
);
283 while (*p
!= '\0' && isascii(*p
) && isspace(*p
))
286 map
->map_rebuild
= newstr(p
);
288 if (map
->map_file
== NULL
&&
289 !bitset(MCF_OPTFILE
, map
->map_class
->map_cflags
))
291 syserr("No file name for %s map %s",
292 map
->map_class
->map_cname
, map
->map_mname
);
298 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications.
300 ** It also adds the map_app string. It can be used as a utility
301 ** in the map_lookup method.
304 ** map -- the map that causes this.
305 ** s -- the string to rewrite, NOT necessarily null terminated.
306 ** slen -- the length of s.
307 ** av -- arguments to interpolate into buf.
310 ** Pointer to rewritten result. This is static data that
311 ** should be copied if it is to be saved!
315 map_rewrite(map
, s
, slen
, av
)
317 register const char *s
;
327 static size_t buflen
= 0;
328 static char *buf
= NULL
;
332 sm_dprintf("map_rewrite(%.*s), av =", (int) slen
, s
);
334 sm_dprintf(" (nullv)");
337 for (avp
= av
; *avp
!= NULL
; avp
++)
338 sm_dprintf("\n\t%s", *avp
);
343 /* count expected size of output (can safely overestimate) */
349 while (l
-- > 0 && (c
= *sp
++) != '\0')
356 if (!(isascii(c
) && isdigit(c
)))
358 for (avp
= av
; --c
>= '0' && *avp
!= NULL
; avp
++)
365 if (map
->map_app
!= NULL
)
366 len
+= strlen(map
->map_app
);
369 /* need to malloc additional space */
373 buf
= sm_pmalloc_x(buflen
);
379 memmove(bp
, s
, slen
);
382 /* assert(len > slen); */
387 while (slen
-- > 0 && (c
= *s
++) != '\0')
397 if (slen
-- <= 0 || (c
= *s
++) == '\0')
401 if (!(isascii(c
) && isdigit(c
)))
408 for (avp
= av
; --c
>= '0' && *avp
!= NULL
; avp
++)
413 /* transliterate argument into output string */
414 for (ap
= *avp
; (c
= *ap
++) != '\0' && len
> 0; --len
)
418 if (map
->map_app
!= NULL
&& len
> 0)
419 (void) sm_strlcpy(bp
, map
->map_app
, len
);
423 sm_dprintf("map_rewrite => %s\n", buf
);
427 ** INITMAPS -- rebuild alias maps
440 checkfd012("entering initmaps");
442 stabapply(map_init
, 0);
444 checkfd012("exiting initmaps");
448 ** MAP_INIT -- rebuild a map
451 ** s -- STAB entry: if map: try to rebuild
452 ** unused -- unused variable
458 ** will close already open rebuildable map.
469 /* has to be a map */
470 if (s
->s_symtype
!= ST_MAP
)
474 if (!bitset(MF_VALID
, map
->map_mflags
))
478 sm_dprintf("map_init(%s:%s, %s)\n",
479 map
->map_class
->map_cname
== NULL
? "NULL" :
480 map
->map_class
->map_cname
,
481 map
->map_mname
== NULL
? "NULL" : map
->map_mname
,
482 map
->map_file
== NULL
? "NULL" : map
->map_file
);
484 if (!bitset(MF_ALIAS
, map
->map_mflags
) ||
485 !bitset(MCF_REBUILDABLE
, map
->map_class
->map_cflags
))
488 sm_dprintf("\tnot rebuildable\n");
492 /* if already open, close it (for nested open) */
493 if (bitset(MF_OPEN
, map
->map_mflags
))
495 map
->map_mflags
|= MF_CLOSING
;
496 map
->map_class
->map_close(map
);
497 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
500 (void) rebuildaliases(map
, false);
504 ** OPENMAP -- open a map
507 ** map -- map to open (it must not be open).
510 ** whether open succeeded.
517 bool restore
= false;
518 bool savehold
= HoldErrs
;
519 bool savequick
= QuickAbort
;
520 int saveerrors
= Errors
;
522 if (!bitset(MF_VALID
, map
->map_mflags
))
525 /* better safe than sorry... */
526 if (bitset(MF_OPEN
, map
->map_mflags
))
529 /* Don't send a map open error out via SMTP */
530 if ((OnlyOneError
|| QuickAbort
) &&
531 (OpMode
== MD_SMTP
|| OpMode
== MD_DAEMON
))
539 if (map
->map_class
->map_open(map
, O_RDONLY
))
542 sm_dprintf("openmap()\t%s:%s %s: valid\n",
543 map
->map_class
->map_cname
== NULL
? "NULL" :
544 map
->map_class
->map_cname
,
545 map
->map_mname
== NULL
? "NULL" :
547 map
->map_file
== NULL
? "NULL" :
549 map
->map_mflags
|= MF_OPEN
;
550 map
->map_pid
= CurrentPid
;
555 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n",
556 map
->map_class
->map_cname
== NULL
? "NULL" :
557 map
->map_class
->map_cname
,
558 map
->map_mname
== NULL
? "NULL" :
560 map
->map_file
== NULL
? "NULL" :
562 errno
== 0 ? "" : ": ",
563 errno
== 0 ? "" : sm_errstring(errno
));
564 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
566 extern MAPCLASS BogusMapClass
;
568 map
->map_orgclass
= map
->map_class
;
569 map
->map_class
= &BogusMapClass
;
570 map
->map_mflags
|= MF_OPEN
|MF_OPENBOGUS
;
571 map
->map_pid
= CurrentPid
;
575 /* don't try again */
576 map
->map_mflags
&= ~MF_VALID
;
584 QuickAbort
= savequick
;
587 return bitset(MF_OPEN
, map
->map_mflags
);
590 ** CLOSEMAPS -- close all open maps opened by the current pid.
593 ** bogus -- only close bogus maps.
603 stabapply(map_close
, bogus
);
606 ** MAP_CLOSE -- close a map opened by the current pid.
609 ** s -- STAB entry: if map: try to close
610 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps.
620 int bogus
; /* int because of stabapply(), used as bool */
623 extern MAPCLASS BogusMapClass
;
625 if (s
->s_symtype
!= ST_MAP
)
631 ** close the map iff:
632 ** it is valid and open and opened by this process
633 ** and (!bogus or it's a bogus map or it is not persistent)
634 ** negate this: return iff
635 ** it is not valid or it is not open or not opened by this process
636 ** or (bogus and it's not a bogus map and it's not not-persistent)
639 if (!bitset(MF_VALID
, map
->map_mflags
) ||
640 !bitset(MF_OPEN
, map
->map_mflags
) ||
641 bitset(MF_CLOSING
, map
->map_mflags
) ||
642 map
->map_pid
!= CurrentPid
||
643 (bogus
&& map
->map_class
!= &BogusMapClass
&&
644 !bitset(MCF_NOTPERSIST
, map
->map_class
->map_cflags
)))
647 if (map
->map_class
== &BogusMapClass
&& map
->map_orgclass
!= NULL
&&
648 map
->map_orgclass
!= &BogusMapClass
)
649 map
->map_class
= map
->map_orgclass
;
651 sm_dprintf("closemaps: closing %s (%s)\n",
652 map
->map_mname
== NULL
? "NULL" : map
->map_mname
,
653 map
->map_file
== NULL
? "NULL" : map
->map_file
);
655 if (!bitset(MF_OPENBOGUS
, map
->map_mflags
))
657 map
->map_mflags
|= MF_CLOSING
;
658 map
->map_class
->map_close(map
);
660 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_OPENBOGUS
|MF_CLOSING
);
663 ** GETCANONNAME -- look up name using service switch
666 ** host -- the host name to look up.
667 ** hbsize -- the size of the host buffer.
668 ** trymx -- if set, try MX records.
669 ** pttl -- pointer to return TTL (can be NULL).
672 ** true -- if the host was found.
673 ** false -- otherwise.
677 getcanonname(host
, hbsize
, trymx
, pttl
)
686 bool got_tempfail
= false;
688 char *maptype
[MAXMAPSTACK
];
689 short mapreturn
[MAXMAPACTIONS
];
691 nmaps
= switch_map_find("hosts", maptype
, mapreturn
);
693 *pttl
= SM_DEFAULT_TTL
;
694 for (mapno
= 0; mapno
< nmaps
; mapno
++)
699 sm_dprintf("getcanonname(%s), trying %s\n",
700 host
, maptype
[mapno
]);
701 if (strcmp("files", maptype
[mapno
]) == 0)
703 found
= text_getcanonname(host
, hbsize
, &status
);
706 else if (strcmp("nis", maptype
[mapno
]) == 0)
708 found
= nis_getcanonname(host
, hbsize
, &status
);
712 else if (strcmp("nisplus", maptype
[mapno
]) == 0)
714 found
= nisplus_getcanonname(host
, hbsize
, &status
);
718 else if (strcmp("dns", maptype
[mapno
]) == 0)
720 found
= dns_getcanonname(host
, hbsize
, trymx
, &status
, pttl
);
722 #endif /* NAMED_BIND */
724 else if (strcmp("netinfo", maptype
[mapno
]) == 0)
726 found
= ni_getcanonname(host
, hbsize
, &status
);
732 status
= EX_UNAVAILABLE
;
736 ** Heuristic: if $m is not set, we are running during system
737 ** startup. In this case, when a name is apparently found
738 ** but has no dot, treat is as not found. This avoids
739 ** problems if /etc/hosts has no FQDN but is listed first
740 ** in the service switch.
744 (macvalue('m', CurEnv
) != NULL
|| strchr(host
, '.') != NULL
))
747 /* see if we should continue */
748 if (status
== EX_TEMPFAIL
)
753 else if (status
== EX_NOTFOUND
)
757 if (bitset(1 << mapno
, mapreturn
[i
]))
766 sm_dprintf("getcanonname(%s), found\n", host
);
769 ** If returned name is still single token, compensate
770 ** by tagging on $m. This is because some sites set
771 ** up their DNS or NIS databases wrong.
774 if ((d
= strchr(host
, '.')) == NULL
|| d
[1] == '\0')
776 d
= macvalue('m', CurEnv
);
778 hbsize
> (int) (strlen(host
) + strlen(d
) + 1))
780 if (host
[strlen(host
) - 1] != '.')
781 (void) sm_strlcat2(host
, ".", d
,
784 (void) sm_strlcat(host
, d
, hbsize
);
793 sm_dprintf("getcanonname(%s), failed, status=%d\n", host
,
797 SM_SET_H_ERRNO(TRY_AGAIN
);
799 SM_SET_H_ERRNO(HOST_NOT_FOUND
);
804 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry
807 ** name -- the name against which to match.
808 ** dot -- where to reinsert '.' to get FQDN
809 ** line -- the /etc/hosts line.
810 ** cbuf -- the location to store the result.
811 ** cbuflen -- the size of cbuf.
814 ** true -- if the line matched the desired name.
815 ** false -- otherwise.
819 extract_canonname(name
, dot
, line
, cbuf
, cbuflen
)
836 char nbuf
[MAXNAME
+ 1];
838 p
= get_column(line
, i
, '\0', nbuf
, sizeof nbuf
);
843 if (cbuf
[0] == '\0' ||
844 (strchr(cbuf
, '.') == NULL
&& strchr(p
, '.') != NULL
))
846 (void) sm_strlcpy(cbuf
, p
, cbuflen
);
848 if (sm_strcasecmp(name
, p
) == 0)
850 else if (dot
!= NULL
)
852 /* try looking for the FQDN as well */
854 if (sm_strcasecmp(name
, p
) == 0)
859 if (found
&& strchr(cbuf
, '.') == NULL
)
861 /* try to add a domain on the end of the name */
862 char *domain
= macvalue('m', CurEnv
);
864 if (domain
!= NULL
&&
865 strlen(domain
) + (i
= strlen(cbuf
)) + 1 < (size_t) cbuflen
)
869 (void) sm_strlcpy(p
, domain
, cbuflen
- i
- 1);
882 # include "sm_resolve.h"
883 # if NETINET || NETINET6
884 # include <arpa/inet.h>
885 # endif /* NETINET || NETINET6 */
888 ** DNS_MAP_OPEN -- stub to check proper value for dns map type
892 dns_map_open(map
, mode
)
897 sm_dprintf("dns_map_open(%s, %d)\n", map
->map_mname
, mode
);
900 if (mode
!= O_RDONLY
)
902 /* issue a pseudo-error message */
903 errno
= SM_EMAPCANTWRITE
;
910 ** DNS_MAP_PARSEARGS -- parse dns map definition args.
913 ** map -- pointer to MAP
914 ** args -- pointer to the args on the config line.
917 ** true -- if everything parsed OK.
918 ** false -- otherwise.
921 # if _FFR_DNSMAP_MULTILIMIT
922 # if !_FFR_DNSMAP_MULTI
923 ERROR README
: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT
924 # endif /* ! _FFR_DNSMAP_MULTI */
925 # endif /* _FFR_DNSMAP_MULTILIMIT */
927 # if _FFR_DNSMAP_MULTI
928 # if _FFR_DNSMAP_MULTILIMIT
929 # define map_sizelimit map_lockfd /* overload field */
930 # endif /* _FFR_DNSMAP_MULTILIMIT */
931 # endif /* _FFR_DNSMAP_MULTI */
939 dns_map_parseargs(map
,args
)
943 register char *p
= args
;
944 struct dns_map
*map_p
;
946 map_p
= (struct dns_map
*) xalloc(sizeof *map_p
);
947 map_p
->dns_m_type
= -1;
948 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
952 while (isascii(*p
) && isspace(*p
))
959 map
->map_mflags
|= MF_INCLNULL
;
960 map
->map_mflags
&= ~MF_TRY0NULL
;
964 map
->map_mflags
&= ~MF_TRY1NULL
;
968 map
->map_mflags
|= MF_OPTIONAL
;
972 map
->map_mflags
|= MF_NOFOLDCASE
;
976 map
->map_mflags
|= MF_MATCHONLY
;
980 map
->map_mflags
|= MF_APPEND
;
984 map
->map_mflags
|= MF_KEEPQUOTES
;
988 map
->map_mflags
|= MF_NODEFER
;
1007 map
->map_timeout
= convtime(p
, 's');
1014 while (isascii(*++p
) && isspace(*p
))
1016 map
->map_retry
= atoi(p
);
1019 # if _FFR_DNSMAP_MULTI
1022 map
->map_coldelim
= *p
;
1028 map
->map_coldelim
= '\n';
1032 map
->map_coldelim
= '\t';
1036 map
->map_coldelim
= '\\';
1041 # if _FFR_DNSMAP_MULTILIMIT
1043 while (isascii(*++p
) && isspace(*p
))
1045 map
->map_sizelimit
= atoi(p
);
1047 # endif /* _FFR_DNSMAP_MULTILIMIT */
1048 # endif /* _FFR_DNSMAP_MULTI */
1050 /* Start of dns_map specific args */
1051 case 'R': /* search field */
1055 while (isascii(*++p
) && isspace(*p
))
1060 map_p
->dns_m_type
= dns_string_to_type(p
);
1063 if (map_p
->dns_m_type
< 0)
1064 syserr("dns map %s: wrong type %s",
1069 # if _FFR_DNSMAP_BASE
1070 case 'B': /* base domain */
1074 while (isascii(*++p
) && isspace(*p
))
1081 ** slight abuse of map->map_file; it isn't
1082 ** used otherwise in this map type.
1085 map
->map_file
= newstr(p
);
1090 # endif /* _FFR_DNSMAP_BASE */
1093 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
1098 if (map_p
->dns_m_type
< 0)
1099 syserr("dns map %s: missing -R type", map
->map_mname
);
1100 if (map
->map_app
!= NULL
)
1101 map
->map_app
= newstr(map
->map_app
);
1102 if (map
->map_tapp
!= NULL
)
1103 map
->map_tapp
= newstr(map
->map_tapp
);
1106 ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T));
1107 ** Even if this assumption is wrong, we use only one byte,
1108 ** so it doesn't really matter.
1111 map
->map_db1
= (ARBPTR_T
) map_p
;
1116 ** DNS_MAP_LOOKUP -- perform dns map lookup.
1119 ** map -- pointer to MAP
1120 ** name -- name to lookup
1121 ** av -- arguments to interpolate into buf.
1122 ** statp -- pointer to status (EX_)
1125 ** result of lookup if succeeded.
1126 ** NULL -- otherwise.
1130 dns_map_lookup(map
, name
, av
, statp
)
1136 # if _FFR_DNSMAP_MULTI
1137 # if _FFR_DNSMAP_MULTILIMIT
1139 # endif /* _FFR_DNSMAP_MULTILIMIT */
1140 # endif /* _FFR_DNSMAP_MULTI */
1141 char *vp
= NULL
, *result
= NULL
;
1143 struct dns_map
*map_p
;
1144 RESOURCE_RECORD_T
*rr
= NULL
;
1145 DNS_REPLY_T
*r
= NULL
;
1147 static char buf6
[INET6_ADDRSTRLEN
];
1148 # endif /* NETINET6 */
1151 sm_dprintf("dns_map_lookup(%s, %s)\n",
1152 map
->map_mname
, name
);
1154 map_p
= (struct dns_map
*)(map
->map_db1
);
1155 # if _FFR_DNSMAP_BASE
1156 if (map
->map_file
!= NULL
&& *map
->map_file
!= '\0')
1161 len
= strlen(map
->map_file
) + strlen(name
) + 2;
1162 appdomain
= (char *) sm_malloc(len
);
1163 if (appdomain
== NULL
)
1165 *statp
= EX_UNAVAILABLE
;
1168 (void) sm_strlcpyn(appdomain
, len
, 3, name
, ".", map
->map_file
);
1169 r
= dns_lookup_int(appdomain
, C_IN
, map_p
->dns_m_type
,
1170 map
->map_timeout
, map
->map_retry
);
1174 # endif /* _FFR_DNSMAP_BASE */
1176 r
= dns_lookup_int(name
, C_IN
, map_p
->dns_m_type
,
1177 map
->map_timeout
, map
->map_retry
);
1183 if (h_errno
== TRY_AGAIN
|| transienterror(errno
))
1184 *statp
= EX_TEMPFAIL
;
1186 *statp
= EX_NOTFOUND
;
1190 for (rr
= r
->dns_r_head
; rr
!= NULL
; rr
= rr
->rr_next
)
1195 switch (rr
->rr_type
)
1199 value
= rr
->rr_u
.rr_txt
;
1203 value
= rr
->rr_u
.rr_txt
;
1207 value
= rr
->rr_u
.rr_mx
->mx_r_domain
;
1211 value
= rr
->rr_u
.rr_srv
->srv_r_target
;
1215 value
= rr
->rr_u
.rr_txt
;
1219 value
= rr
->rr_u
.rr_txt
;
1223 value
= rr
->rr_u
.rr_mx
->mx_r_domain
;
1228 value
= inet_ntoa(*(rr
->rr_u
.rr_a
));
1230 # endif /* NETINET */
1234 value
= anynet_ntop(rr
->rr_u
.rr_aaaa
, buf6
,
1237 # endif /* NETINET6 */
1240 (void) strreplnonprt(value
, 'X');
1241 if (map_p
->dns_m_type
!= rr
->rr_type
)
1244 sm_dprintf("\tskipping type %s (%d) value %s\n",
1245 type
!= NULL
? type
: "<UNKNOWN>",
1247 value
!= NULL
? value
: "<NO VALUE>");
1252 if (rr
->rr_type
== T_AAAA
&& value
== NULL
)
1255 *statp
= EX_DATAERR
;
1257 sm_dprintf("\tbad T_AAAA conversion\n");
1260 # endif /* NETINET6 */
1262 sm_dprintf("\tfound type %s (%d) value %s\n",
1263 type
!= NULL
? type
: "<UNKNOWN>",
1265 value
!= NULL
? value
: "<NO VALUE>");
1266 # if _FFR_DNSMAP_MULTI
1267 if (value
!= NULL
&&
1268 (map
->map_coldelim
== '\0' ||
1269 # if _FFR_DNSMAP_MULTILIMIT
1270 map
->map_sizelimit
== 1 ||
1271 # endif /* _FFR_DNSMAP_MULTILIMIT */
1272 bitset(MF_MATCHONLY
, map
->map_mflags
)))
1274 /* Only care about the first match */
1278 else if (vp
== NULL
)
1285 /* concatenate the results */
1289 sz
= strlen(vp
) + strlen(value
) + 2;
1291 (void) sm_snprintf(new, sz
, "%s%c%s",
1292 vp
, map
->map_coldelim
, value
);
1295 # if _FFR_DNSMAP_MULTILIMIT
1296 if (map
->map_sizelimit
> 0 &&
1297 ++resnum
>= map
->map_sizelimit
)
1299 # endif /* _FFR_DNSMAP_MULTILIMIT */
1301 # else /* _FFR_DNSMAP_MULTI */
1304 # endif /* _FFR_DNSMAP_MULTI */
1309 *statp
= EX_NOTFOUND
;
1311 sm_dprintf("\tno match found\n");
1315 # if _FFR_DNSMAP_MULTI
1316 /* Cleanly truncate for rulesets */
1317 truncate_at_delim(vp
, PSBUFSIZE
/ 2, map
->map_coldelim
);
1318 # endif /* _FFR_DNSMAP_MULTI */
1323 sm_syslog(LOG_INFO
, CurEnv
->e_id
, "dns %.100s => %s",
1325 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
1326 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
1328 result
= map_rewrite(map
, vp
, vsize
, av
);
1331 # if _FFR_DNSMAP_MULTI
1334 # endif /* _FFR_DNSMAP_MULTI */
1339 # endif /* DNSMAP */
1340 #endif /* NAMED_BIND */
1349 ** NDBM_MAP_OPEN -- DBM-style map open
1353 ndbm_map_open(map
, mode
)
1363 int smode
= S_IREAD
;
1364 char dirfile
[MAXPATHLEN
];
1365 char pagfile
[MAXPATHLEN
];
1367 struct stat std
, stp
;
1370 sm_dprintf("ndbm_map_open(%s, %s, %d)\n",
1371 map
->map_mname
, map
->map_file
, mode
);
1372 map
->map_lockfd
= -1;
1375 /* do initial file and directory checks */
1376 if (sm_strlcpyn(dirfile
, sizeof dirfile
, 2,
1377 map
->map_file
, ".dir") >= sizeof dirfile
||
1378 sm_strlcpyn(pagfile
, sizeof pagfile
, 2,
1379 map
->map_file
, ".pag") >= sizeof pagfile
)
1382 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1383 syserr("dbm map \"%s\": map file %s name too long",
1384 map
->map_mname
, map
->map_file
);
1387 sff
= SFF_ROOTOK
|SFF_REGONLY
;
1391 if (!bitnset(DBS_WRITEMAPTOSYMLINK
, DontBlameSendmail
))
1393 if (!bitnset(DBS_WRITEMAPTOHARDLINK
, DontBlameSendmail
))
1399 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
1402 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
1403 sff
|= SFF_SAFEDIRPATH
;
1404 ret
= safefile(dirfile
, RunAsUid
, RunAsGid
, RunAsUserName
,
1407 ret
= safefile(pagfile
, RunAsUid
, RunAsGid
, RunAsUserName
,
1412 char *prob
= "unsafe";
1414 /* cannot open this map */
1418 sm_dprintf("\t%s map file: %d\n", prob
, ret
);
1419 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1420 syserr("dbm map \"%s\": %s map file %s",
1421 map
->map_mname
, prob
, map
->map_file
);
1424 if (std
.st_mode
== ST_MODE_NOFILE
)
1425 mode
|= O_CREAT
|O_EXCL
;
1428 if (mode
== O_RDONLY
)
1431 mode
|= O_TRUNC
|O_EXLOCK
;
1432 # else /* LOCK_ON_OPEN */
1433 if ((mode
& O_ACCMODE
) == O_RDWR
)
1437 ** Warning: race condition. Try to lock the file as
1438 ** quickly as possible after opening it.
1439 ** This may also have security problems on some systems,
1440 ** but there isn't anything we can do about it.
1444 # else /* NOFTRUNCATE */
1446 ** This ugly code opens the map without truncating it,
1447 ** locks the file, then truncates it. Necessary to
1448 ** avoid race conditions.
1453 long sff
= SFF_CREAT
|SFF_OPENASROOT
;
1455 if (!bitnset(DBS_WRITEMAPTOSYMLINK
, DontBlameSendmail
))
1457 if (!bitnset(DBS_WRITEMAPTOHARDLINK
, DontBlameSendmail
))
1460 dirfd
= safeopen(dirfile
, mode
, DBMMODE
, sff
);
1461 pagfd
= safeopen(pagfile
, mode
, DBMMODE
, sff
);
1463 if (dirfd
< 0 || pagfd
< 0)
1467 (void) close(dirfd
);
1469 (void) close(pagfd
);
1471 syserr("ndbm_map_open: cannot create database %s",
1475 if (ftruncate(dirfd
, (off_t
) 0) < 0 ||
1476 ftruncate(pagfd
, (off_t
) 0) < 0)
1479 (void) close(dirfd
);
1480 (void) close(pagfd
);
1482 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}",
1487 /* if new file, get "before" bits for later filechanged check */
1488 if (std
.st_mode
== ST_MODE_NOFILE
&&
1489 (fstat(dirfd
, &std
) < 0 || fstat(pagfd
, &stp
) < 0))
1492 (void) close(dirfd
);
1493 (void) close(pagfd
);
1495 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file",
1500 /* have to save the lock for the duration (bletch) */
1501 map
->map_lockfd
= dirfd
;
1502 (void) close(pagfd
);
1504 /* twiddle bits for dbm_open */
1505 mode
&= ~(O_CREAT
|O_EXCL
);
1506 # endif /* NOFTRUNCATE */
1508 # endif /* LOCK_ON_OPEN */
1510 /* open the database */
1511 dbm
= dbm_open(map
->map_file
, mode
, DBMMODE
);
1515 if (bitset(MF_ALIAS
, map
->map_mflags
) &&
1516 aliaswait(map
, ".pag", false))
1518 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1519 if (map
->map_lockfd
>= 0)
1520 (void) close(map
->map_lockfd
);
1521 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1523 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1524 syserr("Cannot open DBM database %s", map
->map_file
);
1527 dfd
= dbm_dirfno(dbm
);
1528 pfd
= dbm_pagfno(dbm
);
1531 /* heuristic: if files are linked, this is actually gdbm */
1533 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1534 if (map
->map_lockfd
>= 0)
1535 (void) close(map
->map_lockfd
);
1536 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1538 syserr("dbm map \"%s\": cannot support GDBM",
1543 if (filechanged(dirfile
, dfd
, &std
) ||
1544 filechanged(pagfile
, pfd
, &stp
))
1548 # if !LOCK_ON_OPEN && !NOFTRUNCATE
1549 if (map
->map_lockfd
>= 0)
1550 (void) close(map
->map_lockfd
);
1551 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */
1553 syserr("ndbm_map_open(%s): file changed after open",
1558 map
->map_db1
= (ARBPTR_T
) dbm
;
1561 ** Need to set map_mtime before the call to aliaswait()
1562 ** as aliaswait() will call map_lookup() which requires
1563 ** map_mtime to be set
1566 if (fstat(pfd
, &st
) >= 0)
1567 map
->map_mtime
= st
.st_mtime
;
1569 if (mode
== O_RDONLY
)
1573 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_UN
);
1575 (void) lockfile(pfd
, map
->map_file
, ".pag", LOCK_UN
);
1576 # endif /* LOCK_ON_OPEN */
1577 if (bitset(MF_ALIAS
, map
->map_mflags
) &&
1578 !aliaswait(map
, ".pag", true))
1583 map
->map_mflags
|= MF_LOCKED
;
1584 if (geteuid() == 0 && TrustedUid
!= 0)
1587 if (fchown(dfd
, TrustedUid
, -1) < 0 ||
1588 fchown(pfd
, TrustedUid
, -1) < 0)
1592 sm_syslog(LOG_ALERT
, NOQID
,
1593 "ownership change on %s failed: %s",
1594 map
->map_file
, sm_errstring(err
));
1595 message("050 ownership change on %s failed: %s",
1596 map
->map_file
, sm_errstring(err
));
1598 # else /* HASFCHOWN */
1599 sm_syslog(LOG_ALERT
, NOQID
,
1600 "no fchown(): cannot change ownership on %s",
1602 message("050 no fchown(): cannot change ownership on %s",
1604 # endif /* HASFCHOWN */
1612 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map
1616 ndbm_map_lookup(map
, name
, av
, statp
)
1624 char keybuf
[MAXNAME
+ 1];
1628 sm_dprintf("ndbm_map_lookup(%s, %s)\n",
1629 map
->map_mname
, name
);
1632 key
.dsize
= strlen(name
);
1633 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
1635 if (key
.dsize
> sizeof keybuf
- 1)
1636 key
.dsize
= sizeof keybuf
- 1;
1637 memmove(keybuf
, key
.dptr
, key
.dsize
);
1638 keybuf
[key
.dsize
] = '\0';
1643 dfd
= dbm_dirfno((DBM
*) map
->map_db1
);
1644 if (dfd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
1645 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_SH
);
1646 pfd
= dbm_pagfno((DBM
*) map
->map_db1
);
1647 if (pfd
< 0 || fstat(pfd
, &stbuf
) < 0 ||
1648 stbuf
.st_mtime
> map
->map_mtime
)
1650 /* Reopen the database to sync the cache */
1651 int omode
= bitset(map
->map_mflags
, MF_WRITABLE
) ? O_RDWR
1654 if (dfd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
1655 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_UN
);
1656 map
->map_mflags
|= MF_CLOSING
;
1657 map
->map_class
->map_close(map
);
1658 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
1659 if (map
->map_class
->map_open(map
, omode
))
1661 map
->map_mflags
|= MF_OPEN
;
1662 map
->map_pid
= CurrentPid
;
1663 if ((omode
&& O_ACCMODE
) == O_RDWR
)
1664 map
->map_mflags
|= MF_WRITABLE
;
1669 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1671 extern MAPCLASS BogusMapClass
;
1673 *statp
= EX_TEMPFAIL
;
1674 map
->map_orgclass
= map
->map_class
;
1675 map
->map_class
= &BogusMapClass
;
1676 map
->map_mflags
|= MF_OPEN
;
1677 map
->map_pid
= CurrentPid
;
1678 syserr("Cannot reopen NDBM database %s",
1685 if (bitset(MF_TRY0NULL
, map
->map_mflags
))
1687 val
= dbm_fetch((DBM
*) map
->map_db1
, key
);
1688 if (val
.dptr
!= NULL
)
1689 map
->map_mflags
&= ~MF_TRY1NULL
;
1691 if (val
.dptr
== NULL
&& bitset(MF_TRY1NULL
, map
->map_mflags
))
1694 val
= dbm_fetch((DBM
*) map
->map_db1
, key
);
1695 if (val
.dptr
!= NULL
)
1696 map
->map_mflags
&= ~MF_TRY0NULL
;
1698 if (dfd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
1699 (void) lockfile(dfd
, map
->map_file
, ".dir", LOCK_UN
);
1700 if (val
.dptr
== NULL
)
1702 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
1703 return map_rewrite(map
, name
, strlen(name
), NULL
);
1705 return map_rewrite(map
, val
.dptr
, val
.dsize
, av
);
1710 ** NDBM_MAP_STORE -- store a datum in the database
1714 ndbm_map_store(map
, lhs
, rhs
)
1722 char keybuf
[MAXNAME
+ 1];
1725 sm_dprintf("ndbm_map_store(%s, %s, %s)\n",
1726 map
->map_mname
, lhs
, rhs
);
1728 key
.dsize
= strlen(lhs
);
1730 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
1732 if (key
.dsize
> sizeof keybuf
- 1)
1733 key
.dsize
= sizeof keybuf
- 1;
1734 memmove(keybuf
, key
.dptr
, key
.dsize
);
1735 keybuf
[key
.dsize
] = '\0';
1740 data
.dsize
= strlen(rhs
);
1743 if (bitset(MF_INCLNULL
, map
->map_mflags
))
1749 status
= dbm_store((DBM
*) map
->map_db1
, key
, data
, DBM_INSERT
);
1752 if (!bitset(MF_APPEND
, map
->map_mflags
))
1753 message("050 Warning: duplicate alias name %s", lhs
);
1756 static char *buf
= NULL
;
1757 static int bufsiz
= 0;
1761 old
.dptr
= ndbm_map_lookup(map
, key
.dptr
,
1762 (char **) NULL
, &xstat
);
1763 if (old
.dptr
!= NULL
&& *(char *) old
.dptr
!= '\0')
1765 old
.dsize
= strlen(old
.dptr
);
1766 if (data
.dsize
+ old
.dsize
+ 2 > bufsiz
)
1769 (void) sm_free(buf
);
1770 bufsiz
= data
.dsize
+ old
.dsize
+ 2;
1771 buf
= sm_pmalloc_x(bufsiz
);
1773 (void) sm_strlcpyn(buf
, bufsiz
, 3,
1774 data
.dptr
, ",", old
.dptr
);
1775 data
.dsize
= data
.dsize
+ old
.dsize
+ 1;
1778 sm_dprintf("ndbm_map_store append=%s\n",
1782 status
= dbm_store((DBM
*) map
->map_db1
,
1783 key
, data
, DBM_REPLACE
);
1786 syserr("readaliases: dbm put (%s): %d", lhs
, status
);
1791 ** NDBM_MAP_CLOSE -- close the database
1799 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n",
1800 map
->map_mname
, map
->map_file
, map
->map_mflags
);
1802 if (bitset(MF_WRITABLE
, map
->map_mflags
))
1804 # ifdef NDBM_YP_COMPAT
1806 char buf
[MAXHOSTNAMELEN
];
1808 inclnull
= bitset(MF_INCLNULL
, map
->map_mflags
);
1809 map
->map_mflags
&= ~MF_INCLNULL
;
1811 if (strstr(map
->map_file
, "/yp/") != NULL
)
1813 long save_mflags
= map
->map_mflags
;
1815 map
->map_mflags
|= MF_NOFOLDCASE
;
1817 (void) sm_snprintf(buf
, sizeof buf
, "%010ld", curtime());
1818 ndbm_map_store(map
, "YP_LAST_MODIFIED", buf
);
1820 (void) gethostname(buf
, sizeof buf
);
1821 ndbm_map_store(map
, "YP_MASTER_NAME", buf
);
1823 map
->map_mflags
= save_mflags
;
1827 map
->map_mflags
|= MF_INCLNULL
;
1828 # endif /* NDBM_YP_COMPAT */
1830 /* write out the distinguished alias */
1831 ndbm_map_store(map
, "@", "@");
1833 dbm_close((DBM
*) map
->map_db1
);
1835 /* release lock (if needed) */
1837 if (map
->map_lockfd
>= 0)
1838 (void) close(map
->map_lockfd
);
1839 # endif /* !LOCK_ON_OPEN */
1844 ** NEWDB (Hash and BTree) Modules
1850 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives.
1852 ** These do rather bizarre locking. If you can lock on open,
1853 ** do that to avoid the condition of opening a database that
1854 ** is being rebuilt. If you don't, we'll try to fake it, but
1855 ** there will be a race condition. If opening for read-only,
1856 ** we immediately release the lock to avoid freezing things up.
1857 ** We really ought to hold the lock, but guarantee that we won't
1858 ** be pokey about it. That's hard to do.
1861 /* these should be K line arguments */
1862 # if DB_VERSION_MAJOR < 2
1863 # define db_cachesize cachesize
1864 # define h_nelem nelem
1865 # ifndef DB_CACHE_SIZE
1866 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */
1867 # endif /* ! DB_CACHE_SIZE */
1868 # ifndef DB_HASH_NELEM
1869 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */
1870 # endif /* ! DB_HASH_NELEM */
1871 # endif /* DB_VERSION_MAJOR < 2 */
1874 bt_map_open(map
, mode
)
1878 # if DB_VERSION_MAJOR < 2
1880 # endif /* DB_VERSION_MAJOR < 2 */
1881 # if DB_VERSION_MAJOR == 2
1883 # endif /* DB_VERSION_MAJOR == 2 */
1884 # if DB_VERSION_MAJOR > 2
1885 void *btinfo
= NULL
;
1886 # endif /* DB_VERSION_MAJOR > 2 */
1889 sm_dprintf("bt_map_open(%s, %s, %d)\n",
1890 map
->map_mname
, map
->map_file
, mode
);
1892 # if DB_VERSION_MAJOR < 3
1893 memset(&btinfo
, '\0', sizeof btinfo
);
1894 # ifdef DB_CACHE_SIZE
1895 btinfo
.db_cachesize
= DB_CACHE_SIZE
;
1896 # endif /* DB_CACHE_SIZE */
1897 # endif /* DB_VERSION_MAJOR < 3 */
1899 return db_map_open(map
, mode
, "btree", DB_BTREE
, &btinfo
);
1903 hash_map_open(map
, mode
)
1907 # if DB_VERSION_MAJOR < 2
1909 # endif /* DB_VERSION_MAJOR < 2 */
1910 # if DB_VERSION_MAJOR == 2
1912 # endif /* DB_VERSION_MAJOR == 2 */
1913 # if DB_VERSION_MAJOR > 2
1915 # endif /* DB_VERSION_MAJOR > 2 */
1918 sm_dprintf("hash_map_open(%s, %s, %d)\n",
1919 map
->map_mname
, map
->map_file
, mode
);
1921 # if DB_VERSION_MAJOR < 3
1922 memset(&hinfo
, '\0', sizeof hinfo
);
1923 # ifdef DB_HASH_NELEM
1924 hinfo
.h_nelem
= DB_HASH_NELEM
;
1925 # endif /* DB_HASH_NELEM */
1926 # ifdef DB_CACHE_SIZE
1927 hinfo
.db_cachesize
= DB_CACHE_SIZE
;
1928 # endif /* DB_CACHE_SIZE */
1929 # endif /* DB_VERSION_MAJOR < 3 */
1931 return db_map_open(map
, mode
, "hash", DB_HASH
, &hinfo
);
1935 db_map_open(map
, mode
, mapclassname
, dbtype
, openinfo
)
1940 # if DB_VERSION_MAJOR < 2
1941 const void *openinfo
;
1942 # endif /* DB_VERSION_MAJOR < 2 */
1943 # if DB_VERSION_MAJOR == 2
1945 # endif /* DB_VERSION_MAJOR == 2 */
1946 # if DB_VERSION_MAJOR > 2
1948 # endif /* DB_VERSION_MAJOR > 2 */
1953 int smode
= S_IREAD
;
1958 char buf
[MAXPATHLEN
];
1960 /* do initial file and directory checks */
1961 if (sm_strlcpy(buf
, map
->map_file
, sizeof buf
) >= sizeof buf
)
1964 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1965 syserr("map \"%s\": map file %s name too long",
1966 map
->map_mname
, map
->map_file
);
1970 if (i
< 3 || strcmp(&buf
[i
- 3], ".db") != 0)
1972 if (sm_strlcat(buf
, ".db", sizeof buf
) >= sizeof buf
)
1975 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
1976 syserr("map \"%s\": map file %s name too long",
1977 map
->map_mname
, map
->map_file
);
1985 sff
= SFF_ROOTOK
|SFF_REGONLY
;
1989 if (!bitnset(DBS_WRITEMAPTOSYMLINK
, DontBlameSendmail
))
1991 if (!bitnset(DBS_WRITEMAPTOHARDLINK
, DontBlameSendmail
))
1997 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
2000 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
2001 sff
|= SFF_SAFEDIRPATH
;
2002 i
= safefile(buf
, RunAsUid
, RunAsGid
, RunAsUserName
, sff
, smode
, &st
);
2006 char *prob
= "unsafe";
2008 /* cannot open this map */
2012 sm_dprintf("\t%s map file: %s\n", prob
, sm_errstring(i
));
2014 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2015 syserr("%s map \"%s\": %s map file %s",
2016 mapclassname
, map
->map_mname
, prob
, buf
);
2019 if (st
.st_mode
== ST_MODE_NOFILE
)
2020 omode
|= O_CREAT
|O_EXCL
;
2022 map
->map_lockfd
= -1;
2026 omode
|= O_TRUNC
|O_EXLOCK
;
2029 # else /* LOCK_ON_OPEN */
2031 ** Pre-lock the file to avoid race conditions. In particular,
2032 ** since dbopen returns NULL if the file is zero length, we
2033 ** must have a locked instance around the dbopen.
2036 fd
= open(buf
, omode
, DBMMODE
);
2039 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2040 syserr("db_map_open: cannot pre-open database %s", buf
);
2044 /* make sure no baddies slipped in just before the open... */
2045 if (filechanged(buf
, fd
, &st
))
2050 syserr("db_map_open(%s): file changed after pre-open", buf
);
2054 /* if new file, get the "before" bits for later filechanged check */
2055 if (st
.st_mode
== ST_MODE_NOFILE
&& fstat(fd
, &st
) < 0)
2060 syserr("db_map_open(%s): cannot fstat pre-opened file",
2065 /* actually lock the pre-opened file */
2066 if (!lockfile(fd
, buf
, NULL
, mode
== O_RDONLY
? LOCK_SH
: LOCK_EX
))
2067 syserr("db_map_open: cannot lock %s", buf
);
2069 /* set up mode bits for dbopen */
2072 omode
&= ~(O_EXCL
|O_CREAT
);
2073 # endif /* LOCK_ON_OPEN */
2075 # if DB_VERSION_MAJOR < 2
2076 db
= dbopen(buf
, omode
, DBMMODE
, dbtype
, openinfo
);
2077 # else /* DB_VERSION_MAJOR < 2 */
2080 # if DB_VERSION_MAJOR > 2
2082 # endif /* DB_VERSION_MAJOR > 2 */
2084 if (mode
== O_RDONLY
)
2086 if (bitset(O_CREAT
, omode
))
2088 if (bitset(O_TRUNC
, omode
))
2089 flags
|= DB_TRUNCATE
;
2090 SM_DB_FLAG_ADD(flags
);
2092 # if DB_VERSION_MAJOR > 2
2093 ret
= db_create(&db
, NULL
, 0);
2094 # ifdef DB_CACHE_SIZE
2095 if (ret
== 0 && db
!= NULL
)
2097 ret
= db
->set_cachesize(db
, 0, DB_CACHE_SIZE
, 0);
2100 (void) db
->close(db
, 0);
2104 # endif /* DB_CACHE_SIZE */
2105 # ifdef DB_HASH_NELEM
2106 if (dbtype
== DB_HASH
&& ret
== 0 && db
!= NULL
)
2108 ret
= db
->set_h_nelem(db
, DB_HASH_NELEM
);
2111 (void) db
->close(db
, 0);
2115 # endif /* DB_HASH_NELEM */
2116 if (ret
== 0 && db
!= NULL
)
2119 DBTXN
/* transaction for DB 4.1 */
2120 buf
, NULL
, dbtype
, flags
, DBMMODE
);
2123 #ifdef DB_OLD_VERSION
2124 if (ret
== DB_OLD_VERSION
)
2126 #endif /* DB_OLD_VERSION */
2127 (void) db
->close(db
, 0);
2132 # else /* DB_VERSION_MAJOR > 2 */
2133 errno
= db_open(buf
, dbtype
, flags
, DBMMODE
,
2134 NULL
, openinfo
, &db
);
2135 # endif /* DB_VERSION_MAJOR > 2 */
2137 # endif /* DB_VERSION_MAJOR < 2 */
2142 map
->map_lockfd
= fd
;
2145 # endif /* !LOCK_ON_OPEN */
2149 if (mode
== O_RDONLY
&& bitset(MF_ALIAS
, map
->map_mflags
) &&
2150 aliaswait(map
, ".db", false))
2153 if (map
->map_lockfd
>= 0)
2154 (void) close(map
->map_lockfd
);
2155 # endif /* !LOCK_ON_OPEN */
2157 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2158 syserr("Cannot open %s database %s",
2163 # if DB_VERSION_MAJOR < 2
2165 # else /* DB_VERSION_MAJOR < 2 */
2167 errno
= db
->fd(db
, &fd
);
2168 # endif /* DB_VERSION_MAJOR < 2 */
2169 if (filechanged(buf
, fd
, &st
))
2172 # if DB_VERSION_MAJOR < 2
2173 (void) db
->close(db
);
2174 # else /* DB_VERSION_MAJOR < 2 */
2175 errno
= db
->close(db
, 0);
2176 # endif /* DB_VERSION_MAJOR < 2 */
2178 if (map
->map_lockfd
>= 0)
2179 (void) close(map
->map_lockfd
);
2180 # endif /* !LOCK_ON_OPEN */
2182 syserr("db_map_open(%s): file changed after open", buf
);
2187 map
->map_mflags
|= MF_LOCKED
;
2189 if (fd
>= 0 && mode
== O_RDONLY
)
2191 (void) lockfile(fd
, buf
, NULL
, LOCK_UN
);
2193 # endif /* LOCK_ON_OPEN */
2195 /* try to make sure that at least the database header is on disk */
2198 (void) db
->sync(db
, 0);
2199 if (geteuid() == 0 && TrustedUid
!= 0)
2202 if (fchown(fd
, TrustedUid
, -1) < 0)
2206 sm_syslog(LOG_ALERT
, NOQID
,
2207 "ownership change on %s failed: %s",
2208 buf
, sm_errstring(err
));
2209 message("050 ownership change on %s failed: %s",
2210 buf
, sm_errstring(err
));
2212 # else /* HASFCHOWN */
2213 sm_syslog(LOG_ALERT
, NOQID
,
2214 "no fchown(): cannot change ownership on %s",
2216 message("050 no fchown(): cannot change ownership on %s",
2218 # endif /* HASFCHOWN */
2222 map
->map_db2
= (ARBPTR_T
) db
;
2225 ** Need to set map_mtime before the call to aliaswait()
2226 ** as aliaswait() will call map_lookup() which requires
2227 ** map_mtime to be set
2230 if (fd
>= 0 && fstat(fd
, &st
) >= 0)
2231 map
->map_mtime
= st
.st_mtime
;
2233 if (mode
== O_RDONLY
&& bitset(MF_ALIAS
, map
->map_mflags
) &&
2234 !aliaswait(map
, ".db", true))
2241 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map
2245 db_map_lookup(map
, name
, av
, statp
)
2252 register DB
*db
= (DB
*) map
->map_db2
;
2258 char keybuf
[MAXNAME
+ 1];
2259 char buf
[MAXPATHLEN
];
2261 memset(&key
, '\0', sizeof key
);
2262 memset(&val
, '\0', sizeof val
);
2265 sm_dprintf("db_map_lookup(%s, %s)\n",
2266 map
->map_mname
, name
);
2268 if (sm_strlcpy(buf
, map
->map_file
, sizeof buf
) >= sizeof buf
)
2271 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2272 syserr("map \"%s\": map file %s name too long",
2273 map
->map_mname
, map
->map_file
);
2277 if (i
> 3 && strcmp(&buf
[i
- 3], ".db") == 0)
2280 key
.size
= strlen(name
);
2281 if (key
.size
> sizeof keybuf
- 1)
2282 key
.size
= sizeof keybuf
- 1;
2284 memmove(keybuf
, name
, key
.size
);
2285 keybuf
[key
.size
] = '\0';
2286 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
2289 # if DB_VERSION_MAJOR < 2
2291 # else /* DB_VERSION_MAJOR < 2 */
2293 errno
= db
->fd(db
, &fd
);
2294 # endif /* DB_VERSION_MAJOR < 2 */
2295 if (fd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
2296 (void) lockfile(fd
, buf
, ".db", LOCK_SH
);
2297 if (fd
< 0 || fstat(fd
, &stbuf
) < 0 || stbuf
.st_mtime
> map
->map_mtime
)
2299 /* Reopen the database to sync the cache */
2300 int omode
= bitset(map
->map_mflags
, MF_WRITABLE
) ? O_RDWR
2303 if (fd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
2304 (void) lockfile(fd
, buf
, ".db", LOCK_UN
);
2305 map
->map_mflags
|= MF_CLOSING
;
2306 map
->map_class
->map_close(map
);
2307 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
2308 if (map
->map_class
->map_open(map
, omode
))
2310 map
->map_mflags
|= MF_OPEN
;
2311 map
->map_pid
= CurrentPid
;
2312 if ((omode
&& O_ACCMODE
) == O_RDWR
)
2313 map
->map_mflags
|= MF_WRITABLE
;
2314 db
= (DB
*) map
->map_db2
;
2319 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2321 extern MAPCLASS BogusMapClass
;
2323 *statp
= EX_TEMPFAIL
;
2324 map
->map_orgclass
= map
->map_class
;
2325 map
->map_class
= &BogusMapClass
;
2326 map
->map_mflags
|= MF_OPEN
;
2327 map
->map_pid
= CurrentPid
;
2328 syserr("Cannot reopen DB database %s",
2336 if (bitset(MF_TRY0NULL
, map
->map_mflags
))
2338 # if DB_VERSION_MAJOR < 2
2339 st
= db
->get(db
, &key
, &val
, 0);
2340 # else /* DB_VERSION_MAJOR < 2 */
2341 errno
= db
->get(db
, NULL
, &key
, &val
, 0);
2357 # endif /* DB_VERSION_MAJOR < 2 */
2359 map
->map_mflags
&= ~MF_TRY1NULL
;
2361 if (st
!= 0 && bitset(MF_TRY1NULL
, map
->map_mflags
))
2364 # if DB_VERSION_MAJOR < 2
2365 st
= db
->get(db
, &key
, &val
, 0);
2366 # else /* DB_VERSION_MAJOR < 2 */
2367 errno
= db
->get(db
, NULL
, &key
, &val
, 0);
2383 # endif /* DB_VERSION_MAJOR < 2 */
2385 map
->map_mflags
&= ~MF_TRY0NULL
;
2388 if (fd
>= 0 && !bitset(MF_LOCKED
, map
->map_mflags
))
2389 (void) lockfile(fd
, buf
, ".db", LOCK_UN
);
2394 syserr("db_map_lookup: get (%s)", name
);
2397 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
2398 return map_rewrite(map
, name
, strlen(name
), NULL
);
2400 return map_rewrite(map
, val
.data
, val
.size
, av
);
2405 ** DB_MAP_STORE -- store a datum in the NEWDB database
2409 db_map_store(map
, lhs
, rhs
)
2417 register DB
*db
= map
->map_db2
;
2418 char keybuf
[MAXNAME
+ 1];
2420 memset(&key
, '\0', sizeof key
);
2421 memset(&data
, '\0', sizeof data
);
2424 sm_dprintf("db_map_store(%s, %s, %s)\n",
2425 map
->map_mname
, lhs
, rhs
);
2427 key
.size
= strlen(lhs
);
2429 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
2431 if (key
.size
> sizeof keybuf
- 1)
2432 key
.size
= sizeof keybuf
- 1;
2433 memmove(keybuf
, key
.data
, key
.size
);
2434 keybuf
[key
.size
] = '\0';
2439 data
.size
= strlen(rhs
);
2442 if (bitset(MF_INCLNULL
, map
->map_mflags
))
2448 # if DB_VERSION_MAJOR < 2
2449 status
= db
->put(db
, &key
, &data
, R_NOOVERWRITE
);
2450 # else /* DB_VERSION_MAJOR < 2 */
2451 errno
= db
->put(db
, NULL
, &key
, &data
, DB_NOOVERWRITE
);
2466 # endif /* DB_VERSION_MAJOR < 2 */
2469 if (!bitset(MF_APPEND
, map
->map_mflags
))
2470 message("050 Warning: duplicate alias name %s", lhs
);
2473 static char *buf
= NULL
;
2474 static int bufsiz
= 0;
2477 memset(&old
, '\0', sizeof old
);
2479 old
.data
= db_map_lookup(map
, key
.data
,
2480 (char **) NULL
, &status
);
2481 if (old
.data
!= NULL
)
2483 old
.size
= strlen(old
.data
);
2484 if (data
.size
+ old
.size
+ 2 > (size_t) bufsiz
)
2488 bufsiz
= data
.size
+ old
.size
+ 2;
2489 buf
= sm_pmalloc_x(bufsiz
);
2491 (void) sm_strlcpyn(buf
, bufsiz
, 3,
2492 (char *) data
.data
, ",",
2494 data
.size
= data
.size
+ old
.size
+ 1;
2497 sm_dprintf("db_map_store append=%s\n",
2498 (char *) data
.data
);
2501 # if DB_VERSION_MAJOR < 2
2502 status
= db
->put(db
, &key
, &data
, 0);
2503 # else /* DB_VERSION_MAJOR < 2 */
2504 status
= errno
= db
->put(db
, NULL
, &key
, &data
, 0);
2505 # endif /* DB_VERSION_MAJOR < 2 */
2508 syserr("readaliases: db put (%s)", lhs
);
2513 ** DB_MAP_CLOSE -- add distinguished entries and close the database
2520 register DB
*db
= map
->map_db2
;
2523 sm_dprintf("db_map_close(%s, %s, %lx)\n",
2524 map
->map_mname
, map
->map_file
, map
->map_mflags
);
2526 if (bitset(MF_WRITABLE
, map
->map_mflags
))
2528 /* write out the distinguished alias */
2529 db_map_store(map
, "@", "@");
2532 (void) db
->sync(db
, 0);
2535 if (map
->map_lockfd
>= 0)
2536 (void) close(map
->map_lockfd
);
2537 # endif /* !LOCK_ON_OPEN */
2539 # if DB_VERSION_MAJOR < 2
2540 if (db
->close(db
) != 0)
2541 # else /* DB_VERSION_MAJOR < 2 */
2543 ** Berkeley DB can use internal shared memory
2544 ** locking for its memory pool. Closing a map
2545 ** opened by another process will interfere
2546 ** with the shared memory and locks of the parent
2547 ** process leaving things in a bad state.
2551 ** If this map was not opened by the current
2552 ** process, do not close the map but recover
2553 ** the file descriptor.
2556 if (map
->map_pid
!= CurrentPid
)
2560 errno
= db
->fd(db
, &fd
);
2566 if ((errno
= db
->close(db
, 0)) != 0)
2567 # endif /* DB_VERSION_MAJOR < 2 */
2568 syserr("db_map_close(%s, %s, %lx): db close failure",
2569 map
->map_mname
, map
->map_file
, map
->map_mflags
);
2579 # define YPERR_BUSY 16
2580 # endif /* ! YPERR_BUSY */
2583 ** NIS_MAP_OPEN -- open DBM map
2587 nis_map_open(map
, mode
)
2597 sm_dprintf("nis_map_open(%s, %s, %d)\n",
2598 map
->map_mname
, map
->map_file
, mode
);
2601 if (mode
!= O_RDONLY
)
2603 /* issue a pseudo-error message */
2604 errno
= SM_EMAPCANTWRITE
;
2608 p
= strchr(map
->map_file
, '@');
2613 map
->map_domain
= p
;
2616 if (*map
->map_file
== '\0')
2617 map
->map_file
= "mail.aliases";
2619 if (map
->map_domain
== NULL
)
2621 yperr
= yp_get_default_domain(&map
->map_domain
);
2624 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2625 syserr("451 4.3.5 NIS map %s specified, but NIS not running",
2631 /* check to see if this map actually exists */
2633 yperr
= yp_match(map
->map_domain
, map
->map_file
, "@", 1,
2636 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n",
2637 map
->map_domain
, map
->map_file
, yperr_string(yperr
));
2641 if (yperr
== 0 || yperr
== YPERR_KEY
|| yperr
== YPERR_BUSY
)
2644 ** We ought to be calling aliaswait() here if this is an
2645 ** alias file, but powerful HP-UX NIS servers apparently
2646 ** don't insert the @:@ token into the alias map when it
2647 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX.
2651 if (!bitset(MF_ALIAS
, map
->map_mflags
) ||
2652 aliaswait(map
, NULL
, true))
2657 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2659 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s",
2660 map
->map_file
, map
->map_domain
, yperr_string(yperr
));
2668 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map
2673 nis_map_lookup(map
, name
, av
, statp
)
2683 char keybuf
[MAXNAME
+ 1];
2684 char *SM_NONVOLATILE result
= NULL
;
2687 sm_dprintf("nis_map_lookup(%s, %s)\n",
2688 map
->map_mname
, name
);
2690 buflen
= strlen(name
);
2691 if (buflen
> sizeof keybuf
- 1)
2692 buflen
= sizeof keybuf
- 1;
2693 memmove(keybuf
, name
, buflen
);
2694 keybuf
[buflen
] = '\0';
2695 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
2699 if (bitset(MF_TRY0NULL
, map
->map_mflags
))
2701 yperr
= yp_match(map
->map_domain
, map
->map_file
, keybuf
, buflen
,
2704 map
->map_mflags
&= ~MF_TRY1NULL
;
2706 if (yperr
== YPERR_KEY
&& bitset(MF_TRY1NULL
, map
->map_mflags
))
2710 yperr
= yp_match(map
->map_domain
, map
->map_file
, keybuf
, buflen
,
2713 map
->map_mflags
&= ~MF_TRY0NULL
;
2717 if (yperr
!= YPERR_KEY
&& yperr
!= YPERR_BUSY
)
2718 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
2724 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
2725 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
2727 result
= map_rewrite(map
, vp
, vsize
, av
);
2737 ** NIS_GETCANONNAME -- look up canonical name in NIS
2741 nis_getcanonname(name
, hbsize
, statp
)
2750 static bool try0null
= true;
2751 static bool try1null
= true;
2752 static char *yp_domain
= NULL
;
2753 char host_record
[MAXLINE
];
2755 char nbuf
[MAXNAME
+ 1];
2758 sm_dprintf("nis_getcanonname(%s)\n", name
);
2760 if (sm_strlcpy(nbuf
, name
, sizeof nbuf
) >= sizeof nbuf
)
2762 *statp
= EX_UNAVAILABLE
;
2765 (void) shorten_hostname(nbuf
);
2766 keylen
= strlen(nbuf
);
2768 if (yp_domain
== NULL
)
2769 (void) yp_get_default_domain(&yp_domain
);
2775 yperr
= yp_match(yp_domain
, "hosts.byname", nbuf
, keylen
,
2780 if (yperr
== YPERR_KEY
&& try1null
)
2784 yperr
= yp_match(yp_domain
, "hosts.byname", nbuf
, keylen
,
2791 if (yperr
== YPERR_KEY
)
2793 else if (yperr
== YPERR_BUSY
)
2794 *statp
= EX_TEMPFAIL
;
2796 *statp
= EX_UNAVAILABLE
;
2801 (void) sm_strlcpy(host_record
, vp
, sizeof host_record
);
2804 sm_dprintf("got record `%s'\n", host_record
);
2805 vp
= strpbrk(host_record
, "#\n");
2808 if (!extract_canonname(nbuf
, NULL
, host_record
, cbuf
, sizeof cbuf
))
2810 /* this should not happen, but.... */
2814 if (sm_strlcpy(name
, cbuf
, hbsize
) >= hbsize
)
2816 *statp
= EX_UNAVAILABLE
;
2827 ** This code donated by Sun Microsystems.
2832 # undef NIS /* symbol conflict in nis.h */
2833 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */
2834 # include <rpcsvc/nis.h>
2835 # include <rpcsvc/nislib.h>
2837 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val
2838 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name
2839 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len)
2840 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.')
2843 ** NISPLUS_MAP_OPEN -- open nisplus table
2847 nisplus_map_open(map
, mode
)
2851 nis_result
*res
= NULL
;
2852 int retry_cnt
, max_col
, i
;
2853 char qbuf
[MAXLINE
+ NIS_MAXNAMELEN
];
2856 sm_dprintf("nisplus_map_open(%s, %s, %d)\n",
2857 map
->map_mname
, map
->map_file
, mode
);
2860 if (mode
!= O_RDONLY
)
2866 if (*map
->map_file
== '\0')
2867 map
->map_file
= "mail_aliases.org_dir";
2869 if (PARTIAL_NAME(map
->map_file
) && map
->map_domain
== NULL
)
2871 /* set default NISPLUS Domain to $m */
2872 map
->map_domain
= newstr(nisplus_default_domain());
2874 sm_dprintf("nisplus_map_open(%s): using domain %s\n",
2875 map
->map_file
, map
->map_domain
);
2877 if (!PARTIAL_NAME(map
->map_file
))
2879 map
->map_domain
= newstr("");
2880 (void) sm_strlcpy(qbuf
, map
->map_file
, sizeof qbuf
);
2884 /* check to see if this map actually exists */
2885 (void) sm_strlcpyn(qbuf
, sizeof qbuf
, 3,
2886 map
->map_file
, ".", map
->map_domain
);
2890 while (res
== NULL
|| res
->status
!= NIS_SUCCESS
)
2892 res
= nis_lookup(qbuf
, FOLLOW_LINKS
);
2893 switch (res
->status
)
2900 case NIS_NAMEUNREACHABLE
:
2901 if (retry_cnt
++ > 4)
2906 /* try not to overwhelm hosed server */
2910 default: /* all other nisplus errors */
2912 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2913 syserr("451 4.3.5 Cannot find table %s.%s: %s",
2914 map
->map_file
, map
->map_domain
,
2915 nis_sperrno(res
->status
));
2922 if (NIS_RES_NUMOBJ(res
) != 1 ||
2923 (NIS_RES_OBJECT(res
)->zo_data
.zo_type
!= TABLE_OBJ
))
2926 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf
);
2928 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
2929 syserr("451 4.3.5 %s.%s: %s is not a table",
2930 map
->map_file
, map
->map_domain
,
2931 nis_sperrno(res
->status
));
2936 /* default key column is column 0 */
2937 if (map
->map_keycolnm
== NULL
)
2938 map
->map_keycolnm
= newstr(COL_NAME(res
,0));
2940 max_col
= COL_MAX(res
);
2942 /* verify the key column exist */
2943 for (i
= 0; i
< max_col
; i
++)
2945 if (strcmp(map
->map_keycolnm
, COL_NAME(res
,i
)) == 0)
2951 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n",
2952 map
->map_file
, map
->map_keycolnm
);
2957 /* default value column is the last column */
2958 if (map
->map_valcolnm
== NULL
)
2960 map
->map_valcolno
= max_col
- 1;
2964 for (i
= 0; i
< max_col
; i
++)
2966 if (strcmp(map
->map_valcolnm
, COL_NAME(res
,i
)) == 0)
2968 map
->map_valcolno
= i
;
2974 sm_dprintf("nisplus_map_open(%s): can not find column %s\n",
2975 map
->map_file
, map
->map_keycolnm
);
2982 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table
2986 nisplus_map_lookup(map
, name
, av
, statp
)
2996 char search_key
[MAXNAME
+ 4];
2997 char qbuf
[MAXLINE
+ NIS_MAXNAMELEN
];
3001 sm_dprintf("nisplus_map_lookup(%s, %s)\n",
3002 map
->map_mname
, name
);
3004 if (!bitset(MF_OPEN
, map
->map_mflags
))
3006 if (nisplus_map_open(map
, O_RDONLY
))
3008 map
->map_mflags
|= MF_OPEN
;
3009 map
->map_pid
= CurrentPid
;
3013 *statp
= EX_UNAVAILABLE
;
3019 ** Copy the name to the key buffer, escaping double quote characters
3020 ** by doubling them and quoting "]" and "," to avoid having the
3021 ** NIS+ parser choke on them.
3024 skleft
= sizeof search_key
- 4;
3026 for (p
= name
; *p
!= '\0' && skleft
> 0; p
++)
3032 /* quote the character */
3040 /* double the quote */
3052 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
3053 makelower(search_key
);
3055 /* construct the query */
3056 if (PARTIAL_NAME(map
->map_file
))
3057 (void) sm_snprintf(qbuf
, sizeof qbuf
, "[%s=%s],%s.%s",
3058 map
->map_keycolnm
, search_key
, map
->map_file
,
3061 (void) sm_snprintf(qbuf
, sizeof qbuf
, "[%s=%s],%s",
3062 map
->map_keycolnm
, search_key
, map
->map_file
);
3065 sm_dprintf("qbuf=%s\n", qbuf
);
3066 result
= nis_list(qbuf
, FOLLOW_LINKS
| FOLLOW_PATH
, NULL
, NULL
);
3067 if (result
->status
== NIS_SUCCESS
)
3072 if ((count
= NIS_RES_NUMOBJ(result
)) != 1)
3075 sm_syslog(LOG_WARNING
, CurEnv
->e_id
,
3076 "%s: lookup error, expected 1 entry, got %d",
3077 map
->map_file
, count
);
3079 /* ignore second entry */
3081 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n",
3085 p
= ((NIS_RES_OBJECT(result
))->EN_col(map
->map_valcolno
));
3086 /* set the length of the result */
3091 sm_dprintf("nisplus_map_lookup(%s), found %s\n",
3093 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3094 str
= map_rewrite(map
, name
, strlen(name
), NULL
);
3096 str
= map_rewrite(map
, p
, vsize
, av
);
3097 nis_freeresult(result
);
3103 if (result
->status
== NIS_NOTFOUND
)
3104 *statp
= EX_NOTFOUND
;
3105 else if (result
->status
== NIS_TRYAGAIN
)
3106 *statp
= EX_TEMPFAIL
;
3109 *statp
= EX_UNAVAILABLE
;
3110 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
3114 sm_dprintf("nisplus_map_lookup(%s), failed\n", name
);
3115 nis_freeresult(result
);
3122 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+
3126 nisplus_getcanonname(name
, hbsize
, statp
)
3135 char nbuf
[MAXNAME
+ 1];
3136 char qbuf
[MAXLINE
+ NIS_MAXNAMELEN
];
3138 if (sm_strlcpy(nbuf
, name
, sizeof nbuf
) >= sizeof nbuf
)
3140 *statp
= EX_UNAVAILABLE
;
3143 (void) shorten_hostname(nbuf
);
3145 p
= strchr(nbuf
, '.');
3149 (void) sm_snprintf(qbuf
, sizeof qbuf
,
3150 "[name=%s],hosts.org_dir", nbuf
);
3152 else if (p
[1] != '\0')
3154 /* multi token -- take only first token in nbuf */
3156 (void) sm_snprintf(qbuf
, sizeof qbuf
,
3157 "[name=%s],hosts.org_dir.%s", nbuf
, &p
[1]);
3166 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n",
3169 result
= nis_list(qbuf
, EXPAND_NAME
|FOLLOW_LINKS
|FOLLOW_PATH
,
3172 if (result
->status
== NIS_SUCCESS
)
3177 if ((count
= NIS_RES_NUMOBJ(result
)) != 1)
3180 sm_syslog(LOG_WARNING
, CurEnv
->e_id
,
3181 "nisplus_getcanonname: lookup error, expected 1 entry, got %d",
3184 /* ignore second entry */
3186 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n",
3191 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n",
3192 name
, (NIS_RES_OBJECT(result
))->zo_domain
);
3195 vp
= ((NIS_RES_OBJECT(result
))->EN_col(0));
3198 sm_dprintf("nisplus_getcanonname(%s), found %s\n",
3200 if (strchr(vp
, '.') != NULL
)
3206 domain
= macvalue('m', CurEnv
);
3210 if (hbsize
> vsize
+ (int) strlen(domain
) + 1)
3212 if (domain
[0] == '\0')
3213 (void) sm_strlcpy(name
, vp
, hbsize
);
3215 (void) sm_snprintf(name
, hbsize
,
3216 "%s.%s", vp
, domain
);
3221 nis_freeresult(result
);
3226 if (result
->status
== NIS_NOTFOUND
)
3228 else if (result
->status
== NIS_TRYAGAIN
)
3229 *statp
= EX_TEMPFAIL
;
3231 *statp
= EX_UNAVAILABLE
;
3234 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n",
3235 name
, result
->status
, *statp
);
3236 nis_freeresult(result
);
3241 nisplus_default_domain()
3243 static char default_domain
[MAXNAME
+ 1] = "";
3246 if (default_domain
[0] != '\0')
3247 return default_domain
;
3249 p
= nis_local_directory();
3250 (void) sm_strlcpy(default_domain
, p
, sizeof default_domain
);
3251 return default_domain
;
3254 #endif /* NISPLUS */
3260 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs
3263 #if defined(LDAPMAP) || defined(PH_MAP)
3266 # define ph_map_dequote ldapmap_dequote
3267 # endif /* PH_MAP */
3269 static char *ldapmap_dequote
__P((char *));
3272 ldapmap_dequote(str
)
3284 /* Should probably swallow initial whitespace here */
3289 while (*p
!= '"' && *p
!= '\0')
3295 #endif /* defined(LDAPMAP) || defined(PH_MAP) */
3299 static SM_LDAP_STRUCT
*LDAPDefaults
= NULL
;
3302 ** LDAPMAP_OPEN -- open LDAP map
3304 ** Connect to the LDAP server. Re-use existing connections since a
3305 ** single server connection to a host (with the same host, port,
3306 ** bind DN, and secret) can answer queries for multiple maps.
3310 ldapmap_open(map
, mode
)
3314 SM_LDAP_STRUCT
*lmap
;
3319 sm_dprintf("ldapmap_open(%s, %d): ", map
->map_mname
, mode
);
3323 /* sendmail doesn't have the ability to write to LDAP (yet) */
3324 if (mode
!= O_RDONLY
)
3326 /* issue a pseudo-error message */
3327 errno
= SM_EMAPCANTWRITE
;
3331 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3333 s
= ldapmap_findconn(lmap
);
3334 if (s
->s_lmap
!= NULL
)
3336 /* Already have a connection open to this LDAP server */
3337 lmap
->ldap_ld
= ((SM_LDAP_STRUCT
*)s
->s_lmap
->map_db1
)->ldap_ld
;
3338 lmap
->ldap_pid
= ((SM_LDAP_STRUCT
*)s
->s_lmap
->map_db1
)->ldap_pid
;
3340 /* Add this map as head of linked list */
3341 lmap
->ldap_next
= s
->s_lmap
;
3345 sm_dprintf("using cached connection\n");
3350 sm_dprintf("opening new connection\n");
3352 if (lmap
->ldap_host
!= NULL
)
3353 id
= lmap
->ldap_host
;
3354 else if (lmap
->ldap_uri
!= NULL
)
3355 id
= lmap
->ldap_uri
;
3359 /* No connection yet, connect */
3360 if (!sm_ldap_start(map
->map_mname
, lmap
))
3362 if (errno
== ETIMEDOUT
)
3365 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
3366 "timeout conning to LDAP server %.100s",
3370 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3372 if (bitset(MF_NODEFER
, map
->map_mflags
))
3374 syserr("%s failed to %s in map %s",
3376 "ldap_init/ldap_bind",
3377 # else /* USE_LDAP_INIT */
3379 # endif /* USE_LDAP_INIT */
3380 id
, map
->map_mname
);
3384 syserr("451 4.3.5 %s failed to %s in map %s",
3386 "ldap_init/ldap_bind",
3387 # else /* USE_LDAP_INIT */
3389 # endif /* USE_LDAP_INIT */
3390 id
, map
->map_mname
);
3396 /* Save connection for reuse */
3402 ** LDAPMAP_CLOSE -- close ldap map
3409 SM_LDAP_STRUCT
*lmap
;
3413 sm_dprintf("ldapmap_close(%s)\n", map
->map_mname
);
3415 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3417 /* Check if already closed */
3418 if (lmap
->ldap_ld
== NULL
)
3421 /* Close the LDAP connection */
3422 sm_ldap_close(lmap
);
3424 /* Mark all the maps that share the connection as closed */
3425 s
= ldapmap_findconn(lmap
);
3427 while (s
->s_lmap
!= NULL
)
3429 MAP
*smap
= s
->s_lmap
;
3431 if (tTd(38, 2) && smap
!= map
)
3432 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n",
3433 map
->map_mname
, smap
->map_mname
);
3434 smap
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
);
3435 lmap
= (SM_LDAP_STRUCT
*) smap
->map_db1
;
3436 lmap
->ldap_ld
= NULL
;
3437 s
->s_lmap
= lmap
->ldap_next
;
3438 lmap
->ldap_next
= NULL
;
3444 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form
3445 ** This only makes sense at Stanford University.
3458 if (islower(*p
) || isdigit(*p
))
3463 else if (isupper(*p
))
3465 *p_last
= tolower(*p
);
3470 if (*p_last
!= '\0')
3474 # endif /* SUNET_ID */
3477 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map
3481 ldapmap_lookup(map
, name
, av
, statp
)
3493 char *result
= NULL
;
3495 SM_LDAP_STRUCT
*lmap
= NULL
;
3496 char keybuf
[MAXKEY
];
3499 sm_dprintf("ldapmap_lookup(%s, %s)\n", map
->map_mname
, name
);
3501 /* Get ldap struct pointer from map */
3502 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3503 sm_ldap_setopts(lmap
->ldap_ld
, lmap
);
3505 (void) sm_strlcpy(keybuf
, name
, sizeof keybuf
);
3507 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
3510 sunet_id_hash(keybuf
);
3511 # else /* SUNET_ID */
3513 # endif /* SUNET_ID */
3516 msgid
= sm_ldap_search(lmap
, keybuf
);
3519 errno
= sm_ldap_geterrno(lmap
->ldap_ld
) + E_LDAPBASE
;
3521 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3523 if (bitset(MF_NODEFER
, map
->map_mflags
))
3524 syserr("Error in ldap_search using %s in map %s",
3525 keybuf
, map
->map_mname
);
3527 syserr("451 4.3.5 Error in ldap_search using %s in map %s",
3528 keybuf
, map
->map_mname
);
3530 *statp
= EX_TEMPFAIL
;
3531 switch (save_errno
- E_LDAPBASE
)
3533 # ifdef LDAP_SERVER_DOWN
3534 case LDAP_SERVER_DOWN
:
3535 # endif /* LDAP_SERVER_DOWN */
3537 case LDAP_UNAVAILABLE
:
3538 /* server disappeared, try reopen on next search */
3546 *statp
= EX_NOTFOUND
;
3550 if (bitset(MF_SINGLEMATCH
, map
->map_mflags
))
3551 flags
|= SM_LDAP_SINGLEMATCH
;
3552 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3553 flags
|= SM_LDAP_MATCHONLY
;
3554 # if _FFR_LDAP_SINGLEDN
3555 if (bitset(MF_SINGLEDN
, map
->map_mflags
))
3556 flags
|= SM_LDAP_SINGLEDN
;
3557 # endif /* _FFR_LDAP_SINGLEDN */
3559 /* Create an rpool for search related memory usage */
3560 rpool
= sm_rpool_new_x(NULL
);
3563 *statp
= sm_ldap_results(lmap
, msgid
, flags
, map
->map_coldelim
,
3564 rpool
, &p
, &plen
, &psize
, NULL
);
3567 /* Copy result so rpool can be freed */
3568 if (*statp
== EX_OK
&& p
!= NULL
)
3570 sm_rpool_free(rpool
);
3572 /* need to restart LDAP connection? */
3573 if (*statp
== EX_RESTART
)
3575 *statp
= EX_TEMPFAIL
;
3580 if (*statp
!= EX_OK
&& *statp
!= EX_NOTFOUND
)
3582 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
3584 if (bitset(MF_NODEFER
, map
->map_mflags
))
3585 syserr("Error getting LDAP results in map %s",
3588 syserr("451 4.3.5 Error getting LDAP results in map %s",
3595 /* Did we match anything? */
3596 if (vp
== NULL
&& !bitset(MF_MATCHONLY
, map
->map_mflags
))
3599 if (*statp
== EX_OK
)
3602 sm_syslog(LOG_INFO
, CurEnv
->e_id
,
3603 "ldap %.100s => %s", name
,
3604 vp
== NULL
? "<NULL>" : vp
);
3605 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
3606 result
= map_rewrite(map
, name
, strlen(name
), NULL
);
3609 /* vp != NULL according to test above */
3610 result
= map_rewrite(map
, vp
, strlen(vp
), av
);
3613 sm_free(vp
); /* XXX */
3619 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server
3621 ** Cache LDAP connections based on the host, port, bind DN,
3622 ** secret, and PID so we don't have multiple connections open to
3623 ** the same server for different maps. Need a separate connection
3624 ** per PID since a parent process may close the map before the
3625 ** child is done with it.
3628 ** lmap -- LDAP map information
3631 ** Symbol table entry for the LDAP connection.
3635 ldapmap_findconn(lmap
)
3636 SM_LDAP_STRUCT
*lmap
;
3641 STAB
*SM_NONVOLATILE s
= NULL
;
3643 if (lmap
->ldap_host
!= NULL
)
3644 id
= lmap
->ldap_host
;
3645 else if (lmap
->ldap_uri
!= NULL
)
3646 id
= lmap
->ldap_uri
;
3650 format
= "%s%c%d%c%d%c%s%c%s%d";
3651 nbuf
= sm_stringf_x(format
,
3658 (lmap
->ldap_binddn
== NULL
? ""
3659 : lmap
->ldap_binddn
),
3661 (lmap
->ldap_secret
== NULL
? ""
3662 : lmap
->ldap_secret
),
3665 s
= stab(nbuf
, ST_LMAP
, ST_ENTER
);
3672 ** LDAPMAP_PARSEARGS -- parse ldap map definition args.
3675 static struct lamvalues LDAPAuthMethods
[] =
3677 { "none", LDAP_AUTH_NONE
},
3678 { "simple", LDAP_AUTH_SIMPLE
},
3679 # ifdef LDAP_AUTH_KRBV4
3680 { "krbv4", LDAP_AUTH_KRBV4
},
3681 # endif /* LDAP_AUTH_KRBV4 */
3685 static struct ladvalues LDAPAliasDereference
[] =
3687 { "never", LDAP_DEREF_NEVER
},
3688 { "always", LDAP_DEREF_ALWAYS
},
3689 { "search", LDAP_DEREF_SEARCHING
},
3690 { "find", LDAP_DEREF_FINDING
},
3694 static struct lssvalues LDAPSearchScope
[] =
3696 { "base", LDAP_SCOPE_BASE
},
3697 { "one", LDAP_SCOPE_ONELEVEL
},
3698 { "sub", LDAP_SCOPE_SUBTREE
},
3703 ldapmap_parseargs(map
, args
)
3707 bool secretread
= true;
3708 bool attrssetup
= false;
3710 register char *p
= args
;
3711 SM_LDAP_STRUCT
*lmap
;
3712 struct lamvalues
*lam
;
3713 struct ladvalues
*lad
;
3714 struct lssvalues
*lss
;
3715 char ldapfilt
[MAXLINE
];
3716 char m_tmp
[MAXPATHLEN
+ LDAPMAP_MAX_PASSWD
];
3718 /* Get ldap struct pointer from map */
3719 lmap
= (SM_LDAP_STRUCT
*) map
->map_db1
;
3721 /* Check if setting the initial LDAP defaults */
3722 if (lmap
== NULL
|| lmap
!= LDAPDefaults
)
3724 /* We need to alloc an SM_LDAP_STRUCT struct */
3725 lmap
= (SM_LDAP_STRUCT
*) xalloc(sizeof *lmap
);
3726 if (LDAPDefaults
== NULL
)
3727 sm_ldap_clear(lmap
);
3729 STRUCTCOPY(*LDAPDefaults
, *lmap
);
3732 /* there is no check whether there is really an argument */
3733 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
3734 map
->map_spacesub
= SpaceSub
; /* default value */
3736 /* Check if setting up an alias or file class LDAP map */
3737 if (bitset(MF_ALIAS
, map
->map_mflags
))
3739 /* Comma separate if used as an alias file */
3740 map
->map_coldelim
= ',';
3745 char jbuf
[MAXHOSTNAMELEN
];
3746 char lcbuf
[MAXLINE
];
3749 expand("\201j", jbuf
, sizeof jbuf
, &BlankEnvelope
);
3750 if (jbuf
[0] == '\0')
3752 (void) sm_strlcpy(jbuf
, "localhost",
3756 lc
= macvalue(macid("{sendmailMTACluster}"), CurEnv
);
3761 expand(lc
, lcbuf
, sizeof lcbuf
, CurEnv
);
3765 n
= sm_snprintf(ldapfilt
, sizeof ldapfilt
,
3766 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))",
3768 if (n
>= sizeof ldapfilt
)
3770 syserr("%s: Default LDAP string too long",
3775 /* default args for an alias LDAP entry */
3776 lmap
->ldap_filter
= ldapfilt
;
3777 lmap
->ldap_attr
[0] = "objectClass";
3778 lmap
->ldap_attr_type
[0] = SM_LDAP_ATTR_OBJCLASS
;
3779 lmap
->ldap_attr_needobjclass
[0] = NULL
;
3780 lmap
->ldap_attr
[1] = "sendmailMTAAliasValue";
3781 lmap
->ldap_attr_type
[1] = SM_LDAP_ATTR_NORMAL
;
3782 lmap
->ldap_attr_needobjclass
[1] = NULL
;
3783 lmap
->ldap_attr
[2] = "sendmailMTAAliasSearch";
3784 lmap
->ldap_attr_type
[2] = SM_LDAP_ATTR_FILTER
;
3785 lmap
->ldap_attr_needobjclass
[2] = "sendmailMTAMapObject";
3786 lmap
->ldap_attr
[3] = "sendmailMTAAliasURL";
3787 lmap
->ldap_attr_type
[3] = SM_LDAP_ATTR_URL
;
3788 lmap
->ldap_attr_needobjclass
[3] = "sendmailMTAMapObject";
3789 lmap
->ldap_attr
[4] = NULL
;
3790 lmap
->ldap_attr_type
[4] = SM_LDAP_ATTR_NONE
;
3791 lmap
->ldap_attr_needobjclass
[4] = NULL
;
3795 else if (bitset(MF_FILECLASS
, map
->map_mflags
))
3797 /* Space separate if used as a file class file */
3798 map
->map_coldelim
= ' ';
3803 while (isascii(*p
) && isspace(*p
))
3810 map
->map_mflags
|= MF_INCLNULL
;
3811 map
->map_mflags
&= ~MF_TRY0NULL
;
3815 map
->map_mflags
&= ~MF_TRY1NULL
;
3819 map
->map_mflags
|= MF_OPTIONAL
;
3823 map
->map_mflags
|= MF_NOFOLDCASE
;
3827 map
->map_mflags
|= MF_MATCHONLY
;
3831 map
->map_mflags
|= MF_APPEND
;
3835 map
->map_mflags
|= MF_KEEPQUOTES
;
3843 map
->map_tapp
= ++p
;
3847 map
->map_mflags
|= MF_NODEFER
;
3851 map
->map_spacesub
= *++p
;
3855 map
->map_mflags
|= MF_DEFER
;
3860 map
->map_coldelim
= *p
;
3866 map
->map_coldelim
= '\n';
3870 map
->map_coldelim
= '\t';
3874 map
->map_coldelim
= '\\';
3879 /* Start of ldapmap specific args */
3882 lmap
->ldap_attrsep
= *p
;
3888 lmap
->ldap_attrsep
= '\n';
3892 lmap
->ldap_attrsep
= '\t';
3896 lmap
->ldap_attrsep
= '\\';
3901 case 'k': /* search field */
3902 while (isascii(*++p
) && isspace(*p
))
3904 lmap
->ldap_filter
= p
;
3907 case 'v': /* attr to return */
3908 while (isascii(*++p
) && isspace(*p
))
3910 lmap
->ldap_attr
[0] = p
;
3911 lmap
->ldap_attr
[1] = NULL
;
3915 map
->map_mflags
|= MF_SINGLEMATCH
;
3918 # if _FFR_LDAP_SINGLEDN
3920 map
->map_mflags
|= MF_SINGLEDN
;
3922 # endif /* _FFR_LDAP_SINGLEDN */
3924 /* args stolen from ldapsearch.c */
3925 case 'R': /* don't auto chase referrals */
3926 # ifdef LDAP_REFERRALS
3927 lmap
->ldap_options
&= ~LDAP_OPT_REFERRALS
;
3928 # else /* LDAP_REFERRALS */
3929 syserr("compile with -DLDAP_REFERRALS for referral support");
3930 # endif /* LDAP_REFERRALS */
3933 case 'n': /* retrieve attribute names only */
3934 lmap
->ldap_attrsonly
= LDAPMAP_TRUE
;
3937 case 'r': /* alias dereferencing */
3938 while (isascii(*++p
) && isspace(*p
))
3941 if (sm_strncasecmp(p
, "LDAP_DEREF_", 11) == 0)
3944 for (lad
= LDAPAliasDereference
;
3945 lad
!= NULL
&& lad
->lad_name
!= NULL
; lad
++)
3947 if (sm_strncasecmp(p
, lad
->lad_name
,
3948 strlen(lad
->lad_name
)) == 0)
3951 if (lad
->lad_name
!= NULL
)
3952 lmap
->ldap_deref
= lad
->lad_code
;
3955 /* bad config line */
3956 if (!bitset(MCF_OPTFILE
,
3957 map
->map_class
->map_cflags
))
3961 if ((ptr
= strchr(p
, ' ')) != NULL
)
3963 syserr("Deref must be [never|always|search|find] (not %s) in map %s",
3972 case 's': /* search scope */
3973 while (isascii(*++p
) && isspace(*p
))
3976 if (sm_strncasecmp(p
, "LDAP_SCOPE_", 11) == 0)
3979 for (lss
= LDAPSearchScope
;
3980 lss
!= NULL
&& lss
->lss_name
!= NULL
; lss
++)
3982 if (sm_strncasecmp(p
, lss
->lss_name
,
3983 strlen(lss
->lss_name
)) == 0)
3986 if (lss
->lss_name
!= NULL
)
3987 lmap
->ldap_scope
= lss
->lss_code
;
3990 /* bad config line */
3991 if (!bitset(MCF_OPTFILE
,
3992 map
->map_class
->map_cflags
))
3996 if ((ptr
= strchr(p
, ' ')) != NULL
)
3998 syserr("Scope must be [base|one|sub] (not %s) in map %s",
4007 case 'h': /* ldap host */
4008 while (isascii(*++p
) && isspace(*p
))
4010 if (lmap
->ldap_uri
!= NULL
)
4012 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4016 lmap
->ldap_host
= p
;
4019 case 'b': /* search base */
4020 while (isascii(*++p
) && isspace(*p
))
4022 lmap
->ldap_base
= p
;
4025 case 'p': /* ldap port */
4026 while (isascii(*++p
) && isspace(*p
))
4028 lmap
->ldap_port
= atoi(p
);
4031 case 'l': /* time limit */
4032 while (isascii(*++p
) && isspace(*p
))
4034 lmap
->ldap_timelimit
= atoi(p
);
4035 lmap
->ldap_timeout
.tv_sec
= lmap
->ldap_timelimit
;
4039 while (isascii(*++p
) && isspace(*p
))
4041 lmap
->ldap_sizelimit
= atoi(p
);
4044 case 'd': /* Dn to bind to server as */
4045 while (isascii(*++p
) && isspace(*p
))
4047 lmap
->ldap_binddn
= p
;
4050 case 'M': /* Method for binding */
4051 while (isascii(*++p
) && isspace(*p
))
4054 if (sm_strncasecmp(p
, "LDAP_AUTH_", 10) == 0)
4057 for (lam
= LDAPAuthMethods
;
4058 lam
!= NULL
&& lam
->lam_name
!= NULL
; lam
++)
4060 if (sm_strncasecmp(p
, lam
->lam_name
,
4061 strlen(lam
->lam_name
)) == 0)
4064 if (lam
->lam_name
!= NULL
)
4065 lmap
->ldap_method
= lam
->lam_code
;
4068 /* bad config line */
4069 if (!bitset(MCF_OPTFILE
,
4070 map
->map_class
->map_cflags
))
4074 if ((ptr
= strchr(p
, ' ')) != NULL
)
4076 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s",
4087 ** This is a string that is dependent on the
4088 ** method used defined above.
4091 case 'P': /* Secret password for binding */
4092 while (isascii(*++p
) && isspace(*p
))
4094 lmap
->ldap_secret
= p
;
4098 case 'H': /* Use LDAP URI */
4100 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s",
4103 # else /* !USE_LDAP_INIT */
4104 if (lmap
->ldap_host
!= NULL
)
4106 syserr("Can not specify both an LDAP host and an LDAP URI in map %s",
4110 while (isascii(*++p
) && isspace(*p
))
4114 # endif /* !USE_LDAP_INIT */
4117 /* -w should be for passwd, -P should be for version */
4118 while (isascii(*++p
) && isspace(*p
))
4120 lmap
->ldap_version
= atoi(p
);
4121 # ifdef LDAP_VERSION_MAX
4122 if (lmap
->ldap_version
> LDAP_VERSION_MAX
)
4124 syserr("LDAP version %d exceeds max of %d in map %s",
4125 lmap
->ldap_version
, LDAP_VERSION_MAX
,
4129 # endif /* LDAP_VERSION_MAX */
4130 # ifdef LDAP_VERSION_MIN
4131 if (lmap
->ldap_version
< LDAP_VERSION_MIN
)
4133 syserr("LDAP version %d is lower than min of %d in map %s",
4134 lmap
->ldap_version
, LDAP_VERSION_MIN
,
4138 # endif /* LDAP_VERSION_MIN */
4142 syserr("Illegal option %c map %s", *p
, map
->map_mname
);
4146 /* need to account for quoted strings here */
4147 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
4151 while (*++p
!= '"' && *p
!= '\0')
4164 if (map
->map_app
!= NULL
)
4165 map
->map_app
= newstr(ldapmap_dequote(map
->map_app
));
4166 if (map
->map_tapp
!= NULL
)
4167 map
->map_tapp
= newstr(ldapmap_dequote(map
->map_tapp
));
4170 ** We need to swallow up all the stuff into a struct
4171 ** and dump it into map->map_dbptr1
4174 if (lmap
->ldap_host
!= NULL
&&
4175 (LDAPDefaults
== NULL
||
4176 LDAPDefaults
== lmap
||
4177 LDAPDefaults
->ldap_host
!= lmap
->ldap_host
))
4178 lmap
->ldap_host
= newstr(ldapmap_dequote(lmap
->ldap_host
));
4179 map
->map_domain
= lmap
->ldap_host
;
4181 if (lmap
->ldap_uri
!= NULL
&&
4182 (LDAPDefaults
== NULL
||
4183 LDAPDefaults
== lmap
||
4184 LDAPDefaults
->ldap_uri
!= lmap
->ldap_uri
))
4185 lmap
->ldap_uri
= newstr(ldapmap_dequote(lmap
->ldap_uri
));
4186 map
->map_domain
= lmap
->ldap_uri
;
4188 if (lmap
->ldap_binddn
!= NULL
&&
4189 (LDAPDefaults
== NULL
||
4190 LDAPDefaults
== lmap
||
4191 LDAPDefaults
->ldap_binddn
!= lmap
->ldap_binddn
))
4192 lmap
->ldap_binddn
= newstr(ldapmap_dequote(lmap
->ldap_binddn
));
4194 if (lmap
->ldap_secret
!= NULL
&&
4195 (LDAPDefaults
== NULL
||
4196 LDAPDefaults
== lmap
||
4197 LDAPDefaults
->ldap_secret
!= lmap
->ldap_secret
))
4200 long sff
= SFF_OPENASROOT
|SFF_ROOTOK
|SFF_NOWLINK
|SFF_NOWWFILES
|SFF_NOGWFILES
;
4202 if (DontLockReadFiles
)
4205 /* need to use method to map secret to passwd string */
4206 switch (lmap
->ldap_method
)
4208 case LDAP_AUTH_NONE
:
4212 case LDAP_AUTH_SIMPLE
:
4215 ** Secret is the name of a file with
4216 ** the first line as the password.
4219 /* Already read in the secret? */
4223 sfd
= safefopen(ldapmap_dequote(lmap
->ldap_secret
),
4227 syserr("LDAP map: cannot open secret %s",
4228 ldapmap_dequote(lmap
->ldap_secret
));
4231 lmap
->ldap_secret
= sfgets(m_tmp
, sizeof m_tmp
,
4232 sfd
, TimeOuts
.to_fileopen
,
4233 "ldapmap_parseargs");
4234 (void) sm_io_close(sfd
, SM_TIME_DEFAULT
);
4235 if (strlen(m_tmp
) > LDAPMAP_MAX_PASSWD
)
4237 syserr("LDAP map: secret in %s too long",
4238 ldapmap_dequote(lmap
->ldap_secret
));
4241 if (lmap
->ldap_secret
!= NULL
&&
4245 if (m_tmp
[strlen(m_tmp
) - 1] == '\n')
4246 m_tmp
[strlen(m_tmp
) - 1] = '\0';
4248 lmap
->ldap_secret
= m_tmp
;
4252 # ifdef LDAP_AUTH_KRBV4
4253 case LDAP_AUTH_KRBV4
:
4256 ** Secret is where the ticket file is
4260 (void) sm_snprintf(m_tmp
, sizeof m_tmp
,
4262 ldapmap_dequote(lmap
->ldap_secret
));
4263 lmap
->ldap_secret
= m_tmp
;
4265 # endif /* LDAP_AUTH_KRBV4 */
4267 default: /* Should NEVER get here */
4268 syserr("LDAP map: Illegal value in lmap method");
4275 if (lmap
->ldap_secret
!= NULL
&&
4276 (LDAPDefaults
== NULL
||
4277 LDAPDefaults
== lmap
||
4278 LDAPDefaults
->ldap_secret
!= lmap
->ldap_secret
))
4279 lmap
->ldap_secret
= newstr(ldapmap_dequote(lmap
->ldap_secret
));
4281 if (lmap
->ldap_base
!= NULL
&&
4282 (LDAPDefaults
== NULL
||
4283 LDAPDefaults
== lmap
||
4284 LDAPDefaults
->ldap_base
!= lmap
->ldap_base
))
4285 lmap
->ldap_base
= newstr(ldapmap_dequote(lmap
->ldap_base
));
4288 ** Save the server from extra work. If request is for a single
4289 ** match, tell the server to only return enough records to
4290 ** determine if there is a single match or not. This can not
4291 ** be one since the server would only return one and we wouldn't
4292 ** know if there were others available.
4295 if (bitset(MF_SINGLEMATCH
, map
->map_mflags
))
4296 lmap
->ldap_sizelimit
= 2;
4298 /* If setting defaults, don't process ldap_filter and ldap_attr */
4299 if (lmap
== LDAPDefaults
)
4302 if (lmap
->ldap_filter
!= NULL
)
4303 lmap
->ldap_filter
= newstr(ldapmap_dequote(lmap
->ldap_filter
));
4306 if (!bitset(MCF_OPTFILE
, map
->map_class
->map_cflags
))
4308 syserr("No filter given in map %s", map
->map_mname
);
4313 if (!attrssetup
&& lmap
->ldap_attr
[0] != NULL
)
4315 bool recurse
= false;
4316 bool normalseen
= false;
4319 p
= ldapmap_dequote(lmap
->ldap_attr
[0]);
4320 lmap
->ldap_attr
[0] = NULL
;
4322 /* Prime the attr list with the objectClass attribute */
4323 lmap
->ldap_attr
[i
] = "objectClass";
4324 lmap
->ldap_attr_type
[i
] = SM_LDAP_ATTR_OBJCLASS
;
4325 lmap
->ldap_attr_needobjclass
[i
] = NULL
;
4332 while (isascii(*p
) && isspace(*p
))
4341 if (i
>= LDAPMAP_MAX_ATTR
)
4343 syserr("Too many return attributes in %s (max %d)",
4344 map
->map_mname
, LDAPMAP_MAX_ATTR
);
4354 type
= strchr(v
, ':');
4358 needobjclass
= strchr(type
, ':');
4359 if (needobjclass
!= NULL
)
4360 *needobjclass
++ = '\0';
4364 needobjclass
= NULL
;
4369 /* allow override on "objectClass" type */
4370 if (sm_strcasecmp(v
, "objectClass") == 0 &&
4371 lmap
->ldap_attr_type
[0] == SM_LDAP_ATTR_OBJCLASS
)
4378 ** Don't add something to attribute
4382 for (j
= 1; j
< i
; j
++)
4384 if (sm_strcasecmp(v
, lmap
->ldap_attr
[j
]) == 0)
4386 syserr("Duplicate attribute (%s) in %s",
4392 lmap
->ldap_attr
[use
] = newstr(v
);
4393 if (needobjclass
!= NULL
&&
4394 *needobjclass
!= '\0' &&
4395 *needobjclass
!= '*')
4397 lmap
->ldap_attr_needobjclass
[use
] = newstr(needobjclass
);
4401 lmap
->ldap_attr_needobjclass
[use
] = NULL
;
4406 if (type
!= NULL
&& *type
!= '\0')
4408 if (sm_strcasecmp(type
, "dn") == 0)
4411 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_DN
;
4413 else if (sm_strcasecmp(type
, "filter") == 0)
4416 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_FILTER
;
4418 else if (sm_strcasecmp(type
, "url") == 0)
4421 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_URL
;
4423 else if (sm_strcasecmp(type
, "normal") == 0)
4425 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_NORMAL
;
4430 syserr("Unknown attribute type (%s) in %s",
4431 type
, map
->map_mname
);
4437 lmap
->ldap_attr_type
[use
] = SM_LDAP_ATTR_NORMAL
;
4443 lmap
->ldap_attr
[i
] = NULL
;
4445 /* Set in case needed in future code */
4448 if (recurse
&& !normalseen
)
4450 syserr("LDAP recursion requested in %s but no returnable attribute given",
4454 if (recurse
&& lmap
->ldap_attrsonly
== LDAPMAP_TRUE
)
4456 syserr("LDAP recursion requested in %s can not be used with -n",
4461 map
->map_db1
= (ARBPTR_T
) lmap
;
4466 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf
4469 ** spec -- map argument string from LDAPDefaults option
4476 ldapmap_set_defaults(spec
)
4482 /* Allocate and set the default values */
4483 if (LDAPDefaults
== NULL
)
4484 LDAPDefaults
= (SM_LDAP_STRUCT
*) xalloc(sizeof *LDAPDefaults
);
4485 sm_ldap_clear(LDAPDefaults
);
4487 memset(&map
, '\0', sizeof map
);
4489 /* look up the class */
4490 class = stab("ldap", ST_MAPCLASS
, ST_FIND
);
4493 syserr("readcf: LDAPDefaultSpec: class ldap not available");
4496 map
.map_class
= &class->s_mapclass
;
4497 map
.map_db1
= (ARBPTR_T
) LDAPDefaults
;
4498 map
.map_mname
= "O LDAPDefaultSpec";
4500 (void) ldapmap_parseargs(&map
, spec
);
4502 /* These should never be set in LDAPDefaults */
4503 if (map
.map_mflags
!= (MF_TRY0NULL
|MF_TRY1NULL
) ||
4504 map
.map_spacesub
!= SpaceSub
||
4505 map
.map_app
!= NULL
||
4506 map
.map_tapp
!= NULL
)
4508 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags");
4509 SM_FREE_CLR(map
.map_app
);
4510 SM_FREE_CLR(map
.map_tapp
);
4513 if (LDAPDefaults
->ldap_filter
!= NULL
)
4515 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter");
4517 /* don't free, it isn't malloc'ed in parseargs */
4518 LDAPDefaults
->ldap_filter
= NULL
;
4521 if (LDAPDefaults
->ldap_attr
[0] != NULL
)
4523 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes");
4524 /* don't free, they aren't malloc'ed in parseargs */
4525 LDAPDefaults
->ldap_attr
[0] = NULL
;
4528 #endif /* LDAPMAP */
4536 ** Support for the CCSO Nameserver (ph/qi).
4537 ** This code is intended to replace the so-called "ph mailer".
4538 ** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support.
4541 /* what version of the ph map code we're running */
4542 static char phmap_id
[128];
4544 /* sendmail version for phmap id string */
4545 extern const char Version
[];
4547 /* assume we're using nph-1.2.x if not specified */
4548 # ifndef NPH_VERSION
4549 # define NPH_VERSION 10200
4552 /* compatibility for versions older than nph-1.2.0 */
4553 # if NPH_VERSION < 10200
4554 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN
4555 # define PH_OPEN_DONTID PH_DONTID
4556 # define PH_CLOSE_FAST PH_FASTCLOSE
4557 # define PH_ERR_DATAERR PH_DATAERR
4558 # define PH_ERR_NOMATCH PH_NOMATCH
4559 # endif /* NPH_VERSION < 10200 */
4562 ** PH_MAP_PARSEARGS -- parse ph map definition args.
4566 ph_map_parseargs(map
, args
)
4571 register char *p
= args
;
4572 PH_MAP_STRUCT
*pmap
= NULL
;
4574 /* initialize version string */
4575 (void) sm_snprintf(phmap_id
, sizeof phmap_id
,
4576 "sendmail-%s phmap-20010529 libphclient-%s",
4577 Version
, libphclient_version
);
4579 pmap
= (PH_MAP_STRUCT
*) xalloc(sizeof *pmap
);
4582 pmap
->ph_servers
= NULL
;
4583 pmap
->ph_field_list
= NULL
;
4585 pmap
->ph_timeout
= 0;
4586 pmap
->ph_fastclose
= 0;
4588 map
->map_mflags
|= MF_TRY0NULL
|MF_TRY1NULL
;
4591 while (isascii(*p
) && isspace(*p
))
4598 map
->map_mflags
|= MF_INCLNULL
;
4599 map
->map_mflags
&= ~MF_TRY0NULL
;
4603 map
->map_mflags
&= ~MF_TRY1NULL
;
4607 map
->map_mflags
|= MF_OPTIONAL
;
4611 map
->map_mflags
|= MF_NOFOLDCASE
;
4615 map
->map_mflags
|= MF_MATCHONLY
;
4619 map
->map_mflags
|= MF_APPEND
;
4623 map
->map_mflags
|= MF_KEEPQUOTES
;
4627 map
->map_mflags
|= MF_NODEFER
;
4635 map
->map_tapp
= ++p
;
4639 while (isascii(*++p
) && isspace(*p
))
4641 pmap
->ph_timeout
= atoi(p
);
4645 map
->map_spacesub
= *++p
;
4649 map
->map_mflags
|= MF_DEFER
;
4652 case 'h': /* PH server list */
4653 while (isascii(*++p
) && isspace(*p
))
4655 pmap
->ph_servers
= p
;
4658 case 'k': /* fields to search for */
4659 while (isascii(*++p
) && isspace(*p
))
4661 pmap
->ph_field_list
= p
;
4665 syserr("ph_map_parseargs: unknown option -%c", *p
);
4668 /* try to account for quoted strings */
4669 done
= isascii(*p
) && isspace(*p
);
4670 while (*p
!= '\0' && !done
)
4674 while (*++p
!= '"' && *p
!= '\0')
4681 done
= isascii(*p
) && isspace(*p
);
4688 if (map
->map_app
!= NULL
)
4689 map
->map_app
= newstr(ph_map_dequote(map
->map_app
));
4690 if (map
->map_tapp
!= NULL
)
4691 map
->map_tapp
= newstr(ph_map_dequote(map
->map_tapp
));
4693 if (pmap
->ph_field_list
!= NULL
)
4694 pmap
->ph_field_list
= newstr(ph_map_dequote(pmap
->ph_field_list
));
4696 if (pmap
->ph_servers
!= NULL
)
4697 pmap
->ph_servers
= newstr(ph_map_dequote(pmap
->ph_servers
));
4700 syserr("ph_map_parseargs: -h flag is required");
4704 map
->map_db1
= (ARBPTR_T
) pmap
;
4709 ** PH_MAP_CLOSE -- close the connection to the ph server
4716 PH_MAP_STRUCT
*pmap
;
4718 pmap
= (PH_MAP_STRUCT
*)map
->map_db1
;
4720 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n",
4721 map
->map_mname
, pmap
->ph_fastclose
);
4724 if (pmap
->ph
!= NULL
)
4726 ph_set_sendhook(pmap
->ph
, NULL
);
4727 ph_set_recvhook(pmap
->ph
, NULL
);
4728 ph_close(pmap
->ph
, pmap
->ph_fastclose
);
4731 map
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
);
4734 static jmp_buf PHTimeout
;
4742 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
4743 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
4748 longjmp(PHTimeout
, 1);
4752 #if NPH_VERSION >= 10200
4753 ph_map_send_debug(appdata
, text
)
4756 ph_map_send_debug(text
)
4761 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
4762 "ph_map_send_debug: ==> %s", text
);
4764 sm_dprintf("ph_map_send_debug: ==> %s\n", text
);
4768 #if NPH_VERSION >= 10200
4769 ph_map_recv_debug(appdata
, text
)
4772 ph_map_recv_debug(text
)
4777 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
4778 "ph_map_recv_debug: <== %s", text
);
4780 sm_dprintf("ph_map_recv_debug: <== %s\n", text
);
4784 ** PH_MAP_OPEN -- sub for opening PH map
4787 ph_map_open(map
, mode
)
4791 PH_MAP_STRUCT
*pmap
;
4792 register SM_EVENT
*ev
= NULL
;
4794 char *hostlist
, *host
;
4797 sm_dprintf("ph_map_open(%s)\n", map
->map_mname
);
4800 if (mode
!= O_RDONLY
)
4802 /* issue a pseudo-error message */
4803 errno
= SM_EMAPCANTWRITE
;
4807 if (CurEnv
!= NULL
&& CurEnv
->e_sendmode
== SM_DEFER
&&
4808 bitset(MF_DEFER
, map
->map_mflags
))
4811 sm_dprintf("ph_map_open(%s) => DEFERRED\n",
4815 ** Unset MF_DEFER here so that map_lookup() returns
4816 ** a temporary failure using the bogus map and
4817 ** map->map_tapp instead of the default permanent error.
4820 map
->map_mflags
&= ~MF_DEFER
;
4824 pmap
= (PH_MAP_STRUCT
*)map
->map_db1
;
4825 pmap
->ph_fastclose
= 0; /* refresh field for reopen */
4827 /* try each host in the list */
4828 hostlist
= newstr(pmap
->ph_servers
);
4829 for (host
= strtok(hostlist
, " ");
4831 host
= strtok(NULL
, " "))
4834 if (pmap
->ph_timeout
!= 0)
4836 if (setjmp(PHTimeout
) != 0)
4840 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
4841 "timeout connecting to PH server %.100s",
4844 goto ph_map_open_abort
;
4846 ev
= sm_setevent(pmap
->ph_timeout
, ph_timeout
, 0);
4849 /* open connection to server */
4850 if (ph_open(&(pmap
->ph
), host
,
4851 PH_OPEN_ROUNDROBIN
|PH_OPEN_DONTID
,
4852 ph_map_send_debug
, ph_map_recv_debug
4853 #if NPH_VERSION >= 10200
4857 && ph_id(pmap
->ph
, phmap_id
) == 0)
4861 sm_free(hostlist
); /* XXX */
4869 pmap
->ph_fastclose
= PH_CLOSE_FAST
;
4874 if (bitset(MF_NODEFER
, map
->map_mflags
))
4878 syserr("ph_map_open: %s: cannot connect to PH server",
4881 else if (!bitset(MF_OPTIONAL
, map
->map_mflags
) && LogLevel
> 1)
4882 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
4883 "ph_map_open: %s: cannot connect to PH server",
4885 sm_free(hostlist
); /* XXX */
4890 ** PH_MAP_LOOKUP -- look up key from ph server
4894 ph_map_lookup(map
, key
, args
, pstat
)
4900 int i
, save_errno
= 0;
4901 register SM_EVENT
*ev
= NULL
;
4902 PH_MAP_STRUCT
*pmap
;
4905 pmap
= (PH_MAP_STRUCT
*)map
->map_db1
;
4910 if (pmap
->ph_timeout
!= 0)
4912 if (setjmp(PHTimeout
) != 0)
4916 sm_syslog(LOG_NOTICE
, CurEnv
->e_id
,
4917 "timeout during PH lookup of %.100s",
4920 *pstat
= EX_TEMPFAIL
;
4921 goto ph_map_lookup_abort
;
4923 ev
= sm_setevent(pmap
->ph_timeout
, ph_timeout
, 0);
4926 /* perform lookup */
4927 i
= ph_email_resolve(pmap
->ph
, key
, pmap
->ph_field_list
, &value
);
4929 *pstat
= EX_TEMPFAIL
;
4930 else if (i
== PH_ERR_NOMATCH
|| i
== PH_ERR_DATAERR
)
4931 *pstat
= EX_UNAVAILABLE
;
4933 ph_map_lookup_abort
:
4938 ** Close the connection if the timer popped
4939 ** or we got a temporary PH error
4942 if (*pstat
== EX_TEMPFAIL
)
4945 pmap
->ph_fastclose
= PH_CLOSE_FAST
;
4950 if (*pstat
== EX_OK
)
4953 sm_dprintf("ph_map_lookup: %s => %s\n", key
, value
);
4955 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
4956 return map_rewrite(map
, key
, strlen(key
), NULL
);
4958 return map_rewrite(map
, value
, strlen(value
), args
);
4968 #define map_prio map_lockfd /* overload field */
4971 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages.
4975 syslog_map_parseargs(map
, args
)
4980 char *priority
= NULL
;
4982 /* there is no check whether there is really an argument */
4985 while (isascii(*p
) && isspace(*p
))
4992 map
->map_mflags
|= MF_DEFER
;
4997 map
->map_spacesub
= *++p
;
5003 while (*++p
!= '\0' && isascii(*p
) && isspace(*p
))
5008 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
5015 syserr("Illegal option %c map syslog", *p
);
5020 if (priority
== NULL
)
5021 map
->map_prio
= LOG_INFO
;
5024 if (sm_strncasecmp("LOG_", priority
, 4) == 0)
5028 if (sm_strcasecmp("EMERG", priority
) == 0)
5029 map
->map_prio
= LOG_EMERG
;
5031 #endif /* LOG_EMERG */
5033 if (sm_strcasecmp("ALERT", priority
) == 0)
5034 map
->map_prio
= LOG_ALERT
;
5036 #endif /* LOG_ALERT */
5038 if (sm_strcasecmp("CRIT", priority
) == 0)
5039 map
->map_prio
= LOG_CRIT
;
5041 #endif /* LOG_CRIT */
5043 if (sm_strcasecmp("ERR", priority
) == 0)
5044 map
->map_prio
= LOG_ERR
;
5046 #endif /* LOG_ERR */
5048 if (sm_strcasecmp("WARNING", priority
) == 0)
5049 map
->map_prio
= LOG_WARNING
;
5051 #endif /* LOG_WARNING */
5053 if (sm_strcasecmp("NOTICE", priority
) == 0)
5054 map
->map_prio
= LOG_NOTICE
;
5056 #endif /* LOG_NOTICE */
5058 if (sm_strcasecmp("INFO", priority
) == 0)
5059 map
->map_prio
= LOG_INFO
;
5061 #endif /* LOG_INFO */
5063 if (sm_strcasecmp("DEBUG", priority
) == 0)
5064 map
->map_prio
= LOG_DEBUG
;
5066 #endif /* LOG_DEBUG */
5068 syserr("syslog_map_parseargs: Unknown priority %s",
5077 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string
5081 syslog_map_lookup(map
, string
, args
, statp
)
5087 char *ptr
= map_rewrite(map
, string
, strlen(string
), args
);
5092 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n",
5093 map
->map_mname
, map
->map_prio
, ptr
);
5095 sm_syslog(map
->map_prio
, CurEnv
->e_id
, "%s", ptr
);
5109 hes_map_open(map
, mode
)
5114 sm_dprintf("hes_map_open(%s, %s, %d)\n",
5115 map
->map_mname
, map
->map_file
, mode
);
5117 if (mode
!= O_RDONLY
)
5119 /* issue a pseudo-error message */
5120 errno
= SM_EMAPCANTWRITE
;
5125 if (HesiodContext
!= NULL
|| hesiod_init(&HesiodContext
) == 0)
5128 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
5129 syserr("451 4.3.5 cannot initialize Hesiod map (%s)",
5130 sm_errstring(errno
));
5132 # else /* HESIOD_INIT */
5133 if (hes_error() == HES_ER_UNINIT
)
5135 switch (hes_error())
5138 case HES_ER_NOTFOUND
:
5142 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
5143 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error());
5146 # endif /* HESIOD_INIT */
5150 hes_map_lookup(map
, name
, av
, statp
)
5159 sm_dprintf("hes_map_lookup(%s, %s)\n", map
->map_file
, name
);
5161 if (name
[0] == '\\')
5169 if (nl
< sizeof nbuf
- 1)
5172 np
= xalloc(strlen(name
) + 2);
5174 (void) sm_strlcpy(&np
[1], name
, (sizeof nbuf
) - 1);
5176 hp
= hesiod_resolve(HesiodContext
, np
, map
->map_file
);
5177 # else /* HESIOD_INIT */
5178 hp
= hes_resolve(np
, map
->map_file
);
5179 # endif /* HESIOD_INIT */
5182 sm_free(np
); /* XXX */
5188 hp
= hesiod_resolve(HesiodContext
, name
, map
->map_file
);
5189 # else /* HESIOD_INIT */
5190 hp
= hes_resolve(name
, map
->map_file
);
5191 # endif /* HESIOD_INIT */
5194 if (hp
== NULL
|| *hp
== NULL
)
5199 *statp
= EX_NOTFOUND
;
5202 *statp
= EX_TEMPFAIL
;
5207 *statp
= EX_UNAVAILABLE
;
5211 hesiod_free_list(HesiodContext
, hp
);
5214 # else /* HESIOD_INIT */
5215 if (hp
== NULL
|| hp
[0] == NULL
)
5217 switch (hes_error())
5223 case HES_ER_NOTFOUND
:
5224 *statp
= EX_NOTFOUND
;
5228 *statp
= EX_UNAVAILABLE
;
5232 *statp
= EX_TEMPFAIL
;
5237 # endif /* HESIOD_INIT */
5239 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5240 return map_rewrite(map
, name
, strlen(name
), NULL
);
5242 return map_rewrite(map
, hp
[0], strlen(hp
[0]), av
);
5246 ** HES_MAP_CLOSE -- free the Hesiod context
5254 sm_dprintf("hes_map_close(%s)\n", map
->map_file
);
5257 /* Free the hesiod context */
5258 if (HesiodContext
!= NULL
)
5260 hesiod_end(HesiodContext
);
5261 HesiodContext
= NULL
;
5263 # endif /* HESIOD_INIT */
5268 ** NeXT NETINFO Modules
5273 # define NETINFO_DEFAULT_DIR "/aliases"
5274 # define NETINFO_DEFAULT_PROPERTY "members"
5277 ** NI_MAP_OPEN -- open NetInfo Aliases
5281 ni_map_open(map
, mode
)
5286 sm_dprintf("ni_map_open(%s, %s, %d)\n",
5287 map
->map_mname
, map
->map_file
, mode
);
5290 if (*map
->map_file
== '\0')
5291 map
->map_file
= NETINFO_DEFAULT_DIR
;
5293 if (map
->map_valcolnm
== NULL
)
5294 map
->map_valcolnm
= NETINFO_DEFAULT_PROPERTY
;
5296 if (map
->map_coldelim
== '\0')
5298 if (bitset(MF_ALIAS
, map
->map_mflags
))
5299 map
->map_coldelim
= ',';
5300 else if (bitset(MF_FILECLASS
, map
->map_mflags
))
5301 map
->map_coldelim
= ' ';
5308 ** NI_MAP_LOOKUP -- look up a datum in NetInfo
5312 ni_map_lookup(map
, name
, av
, statp
)
5322 sm_dprintf("ni_map_lookup(%s, %s)\n", map
->map_mname
, name
);
5324 propval
= ni_propval(map
->map_file
, map
->map_keycolnm
, name
,
5325 map
->map_valcolnm
, map
->map_coldelim
);
5327 if (propval
== NULL
)
5331 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5332 res
= map_rewrite(map
, name
, strlen(name
), NULL
);
5334 res
= map_rewrite(map
, propval
, strlen(propval
), av
);
5343 ni_getcanonname(name
, hbsize
, statp
)
5350 char nbuf
[MAXNAME
+ 1];
5353 sm_dprintf("ni_getcanonname(%s)\n", name
);
5355 if (sm_strlcpy(nbuf
, name
, sizeof nbuf
) >= sizeof nbuf
)
5357 *statp
= EX_UNAVAILABLE
;
5360 (void) shorten_hostname(nbuf
);
5362 /* we only accept single token search key */
5363 if (strchr(nbuf
, '.'))
5370 vptr
= ni_propval("/machines", NULL
, nbuf
, "name", '\n');
5378 /* Only want the first machine name */
5379 if ((ptr
= strchr(vptr
, '\n')) != NULL
)
5382 if (sm_strlcpy(name
, vptr
, hbsize
) >= hbsize
)
5385 *statp
= EX_UNAVAILABLE
;
5392 #endif /* NETINFO */
5394 ** TEXT (unindexed text file) Modules
5396 ** This code donated by Sun Microsystems.
5399 #define map_sff map_lockfd /* overload field */
5403 ** TEXT_MAP_OPEN -- open text table
5407 text_map_open(map
, mode
)
5415 sm_dprintf("text_map_open(%s, %s, %d)\n",
5416 map
->map_mname
, map
->map_file
, mode
);
5419 if (mode
!= O_RDONLY
)
5425 if (*map
->map_file
== '\0')
5427 syserr("text map \"%s\": file name required",
5432 if (map
->map_file
[0] != '/')
5434 syserr("text map \"%s\": file name must be fully qualified",
5439 sff
= SFF_ROOTOK
|SFF_REGONLY
;
5440 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
5442 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
5443 sff
|= SFF_SAFEDIRPATH
;
5444 if ((i
= safefile(map
->map_file
, RunAsUid
, RunAsGid
, RunAsUserName
,
5445 sff
, S_IRUSR
, NULL
)) != 0)
5447 int save_errno
= errno
;
5449 /* cannot open this map */
5451 sm_dprintf("\tunsafe map file: %d\n", i
);
5453 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
5454 syserr("text map \"%s\": unsafe map file %s",
5455 map
->map_mname
, map
->map_file
);
5459 if (map
->map_keycolnm
== NULL
)
5460 map
->map_keycolno
= 0;
5463 if (!(isascii(*map
->map_keycolnm
) && isdigit(*map
->map_keycolnm
)))
5465 syserr("text map \"%s\", file %s: -k should specify a number, not %s",
5466 map
->map_mname
, map
->map_file
,
5470 map
->map_keycolno
= atoi(map
->map_keycolnm
);
5473 if (map
->map_valcolnm
== NULL
)
5474 map
->map_valcolno
= 0;
5477 if (!(isascii(*map
->map_valcolnm
) && isdigit(*map
->map_valcolnm
)))
5479 syserr("text map \"%s\", file %s: -v should specify a number, not %s",
5480 map
->map_mname
, map
->map_file
,
5484 map
->map_valcolno
= atoi(map
->map_valcolnm
);
5489 sm_dprintf("text_map_open(%s, %s): delimiter = ",
5490 map
->map_mname
, map
->map_file
);
5491 if (map
->map_coldelim
== '\0')
5492 sm_dprintf("(white space)\n");
5494 sm_dprintf("%c\n", map
->map_coldelim
);
5503 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table
5507 text_map_lookup(map
, name
, av
, statp
)
5520 long sff
= map
->map_sff
;
5521 char search_key
[MAXNAME
+ 1];
5522 char linebuf
[MAXLINE
];
5523 char buf
[MAXNAME
+ 1];
5527 sm_dprintf("text_map_lookup(%s, %s)\n", map
->map_mname
, name
);
5529 buflen
= strlen(name
);
5530 if (buflen
> sizeof search_key
- 1)
5531 buflen
= sizeof search_key
- 1; /* XXX just cut if off? */
5532 memmove(search_key
, name
, buflen
);
5533 search_key
[buflen
] = '\0';
5534 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
5535 makelower(search_key
);
5537 f
= safefopen(map
->map_file
, O_RDONLY
, FileMode
, sff
);
5540 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
5541 *statp
= EX_UNAVAILABLE
;
5544 key_idx
= map
->map_keycolno
;
5545 delim
= map
->map_coldelim
;
5546 while (sm_io_fgets(f
, SM_TIME_DEFAULT
,
5547 linebuf
, sizeof linebuf
) != NULL
)
5551 /* skip comment line */
5552 if (linebuf
[0] == '#')
5554 p
= strchr(linebuf
, '\n');
5557 p
= get_column(linebuf
, key_idx
, delim
, buf
, sizeof buf
);
5558 if (p
!= NULL
&& sm_strcasecmp(search_key
, p
) == 0)
5564 (void) sm_io_close(f
, SM_TIME_DEFAULT
);
5567 *statp
= EX_NOTFOUND
;
5570 vp
= get_column(linebuf
, map
->map_valcolno
, delim
, buf
, sizeof buf
);
5573 *statp
= EX_NOTFOUND
;
5578 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5579 return map_rewrite(map
, name
, strlen(name
), NULL
);
5581 return map_rewrite(map
, vp
, vsize
, av
);
5585 ** TEXT_GETCANONNAME -- look up canonical name in hosts file
5589 text_getcanonname(name
, hbsize
, statp
)
5597 char linebuf
[MAXLINE
];
5598 char cbuf
[MAXNAME
+ 1];
5599 char nbuf
[MAXNAME
+ 1];
5602 sm_dprintf("text_getcanonname(%s)\n", name
);
5604 if (sm_strlcpy(nbuf
, name
, sizeof nbuf
) >= sizeof nbuf
)
5606 *statp
= EX_UNAVAILABLE
;
5609 dot
= shorten_hostname(nbuf
);
5611 f
= sm_io_open(SmFtStdio
, SM_TIME_DEFAULT
, HostsFile
, SM_IO_RDONLY
,
5615 *statp
= EX_UNAVAILABLE
;
5620 sm_io_fgets(f
, SM_TIME_DEFAULT
,
5621 linebuf
, sizeof linebuf
) != NULL
)
5623 char *p
= strpbrk(linebuf
, "#\n");
5627 if (linebuf
[0] != '\0')
5628 found
= extract_canonname(nbuf
, dot
, linebuf
,
5631 (void) sm_io_close(f
, SM_TIME_DEFAULT
);
5638 if (sm_strlcpy(name
, cbuf
, hbsize
) >= hbsize
)
5640 *statp
= EX_UNAVAILABLE
;
5647 ** STAB (Symbol Table) Modules
5652 ** STAB_MAP_LOOKUP -- look up alias in symbol table
5657 stab_map_lookup(map
, name
, av
, pstat
)
5666 sm_dprintf("stab_lookup(%s, %s)\n",
5667 map
->map_mname
, name
);
5669 s
= stab(name
, ST_ALIAS
, ST_FIND
);
5672 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5673 return map_rewrite(map
, name
, strlen(name
), NULL
);
5675 return map_rewrite(map
, s
->s_alias
, strlen(s
->s_alias
), av
);
5679 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild)
5683 stab_map_store(map
, lhs
, rhs
)
5690 s
= stab(lhs
, ST_ALIAS
, ST_ENTER
);
5691 s
->s_alias
= newstr(rhs
);
5696 ** STAB_MAP_OPEN -- initialize (reads data file)
5698 ** This is a wierd case -- it is only intended as a fallback for
5699 ** aliases. For this reason, opens for write (only during a
5700 ** "newaliases") always fails, and opens for read open the
5701 ** actual underlying text file instead of the database.
5705 stab_map_open(map
, mode
)
5714 sm_dprintf("stab_map_open(%s, %s, %d)\n",
5715 map
->map_mname
, map
->map_file
, mode
);
5718 if (mode
!= O_RDONLY
)
5724 sff
= SFF_ROOTOK
|SFF_REGONLY
;
5725 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR
, DontBlameSendmail
))
5727 if (!bitnset(DBS_MAPINUNSAFEDIRPATH
, DontBlameSendmail
))
5728 sff
|= SFF_SAFEDIRPATH
;
5729 af
= safefopen(map
->map_file
, O_RDONLY
, 0444, sff
);
5732 readaliases(map
, af
, false, false);
5734 if (fstat(sm_io_getinfo(af
, SM_IO_WHAT_FD
, NULL
), &st
) >= 0)
5735 map
->map_mtime
= st
.st_mtime
;
5736 (void) sm_io_close(af
, SM_TIME_DEFAULT
);
5743 ** Tries several types. For back compatibility of aliases.
5748 ** IMPL_MAP_LOOKUP -- lookup in best open database
5752 impl_map_lookup(map
, name
, av
, pstat
)
5759 sm_dprintf("impl_map_lookup(%s, %s)\n",
5760 map
->map_mname
, name
);
5763 if (bitset(MF_IMPL_HASH
, map
->map_mflags
))
5764 return db_map_lookup(map
, name
, av
, pstat
);
5767 if (bitset(MF_IMPL_NDBM
, map
->map_mflags
))
5768 return ndbm_map_lookup(map
, name
, av
, pstat
);
5770 return stab_map_lookup(map
, name
, av
, pstat
);
5774 ** IMPL_MAP_STORE -- store in open databases
5778 impl_map_store(map
, lhs
, rhs
)
5784 sm_dprintf("impl_map_store(%s, %s, %s)\n",
5785 map
->map_mname
, lhs
, rhs
);
5787 if (bitset(MF_IMPL_HASH
, map
->map_mflags
))
5788 db_map_store(map
, lhs
, rhs
);
5791 if (bitset(MF_IMPL_NDBM
, map
->map_mflags
))
5792 ndbm_map_store(map
, lhs
, rhs
);
5794 stab_map_store(map
, lhs
, rhs
);
5798 ** IMPL_MAP_OPEN -- implicit database open
5802 impl_map_open(map
, mode
)
5807 sm_dprintf("impl_map_open(%s, %s, %d)\n",
5808 map
->map_mname
, map
->map_file
, mode
);
5812 map
->map_mflags
|= MF_IMPL_HASH
;
5813 if (hash_map_open(map
, mode
))
5815 # ifdef NDBM_YP_COMPAT
5816 if (mode
== O_RDONLY
|| strstr(map
->map_file
, "/yp/") == NULL
)
5817 # endif /* NDBM_YP_COMPAT */
5821 map
->map_mflags
&= ~MF_IMPL_HASH
;
5824 map
->map_mflags
|= MF_IMPL_NDBM
;
5825 if (ndbm_map_open(map
, mode
))
5830 map
->map_mflags
&= ~MF_IMPL_NDBM
;
5833 #if defined(NEWDB) || defined(NDBM)
5835 message("WARNING: cannot open alias database %s%s",
5837 mode
== O_RDONLY
? "; reading text version" : "");
5838 #else /* defined(NEWDB) || defined(NDBM) */
5839 if (mode
!= O_RDONLY
)
5840 usrerr("Cannot rebuild aliases: no database format defined");
5841 #endif /* defined(NEWDB) || defined(NDBM) */
5843 if (mode
== O_RDONLY
)
5844 return stab_map_open(map
, mode
);
5851 ** IMPL_MAP_CLOSE -- close any open database(s)
5859 sm_dprintf("impl_map_close(%s, %s, %lx)\n",
5860 map
->map_mname
, map
->map_file
, map
->map_mflags
);
5862 if (bitset(MF_IMPL_HASH
, map
->map_mflags
))
5865 map
->map_mflags
&= ~MF_IMPL_HASH
;
5870 if (bitset(MF_IMPL_NDBM
, map
->map_mflags
))
5872 ndbm_map_close(map
);
5873 map
->map_mflags
&= ~MF_IMPL_NDBM
;
5880 ** Provides access to the system password file.
5884 ** USER_MAP_OPEN -- open user map
5886 ** Really just binds field names to field numbers.
5890 user_map_open(map
, mode
)
5895 sm_dprintf("user_map_open(%s, %d)\n",
5896 map
->map_mname
, mode
);
5899 if (mode
!= O_RDONLY
)
5901 /* issue a pseudo-error message */
5902 errno
= SM_EMAPCANTWRITE
;
5905 if (map
->map_valcolnm
== NULL
)
5908 else if (sm_strcasecmp(map
->map_valcolnm
, "name") == 0)
5909 map
->map_valcolno
= 1;
5910 else if (sm_strcasecmp(map
->map_valcolnm
, "passwd") == 0)
5911 map
->map_valcolno
= 2;
5912 else if (sm_strcasecmp(map
->map_valcolnm
, "uid") == 0)
5913 map
->map_valcolno
= 3;
5914 else if (sm_strcasecmp(map
->map_valcolnm
, "gid") == 0)
5915 map
->map_valcolno
= 4;
5916 else if (sm_strcasecmp(map
->map_valcolnm
, "gecos") == 0)
5917 map
->map_valcolno
= 5;
5918 else if (sm_strcasecmp(map
->map_valcolnm
, "dir") == 0)
5919 map
->map_valcolno
= 6;
5920 else if (sm_strcasecmp(map
->map_valcolnm
, "shell") == 0)
5921 map
->map_valcolno
= 7;
5924 syserr("User map %s: unknown column name %s",
5925 map
->map_mname
, map
->map_valcolnm
);
5933 ** USER_MAP_LOOKUP -- look up a user in the passwd file.
5938 user_map_lookup(map
, key
, av
, statp
)
5948 sm_dprintf("user_map_lookup(%s, %s)\n",
5949 map
->map_mname
, key
);
5951 *statp
= finduser(key
, &fuzzy
, &user
);
5952 if (*statp
!= EX_OK
)
5954 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
5955 return map_rewrite(map
, key
, strlen(key
), NULL
);
5961 switch (map
->map_valcolno
)
5965 rwval
= user
.mbdb_name
;
5969 rwval
= "x"; /* passwd no longer supported */
5973 (void) sm_snprintf(buf
, sizeof buf
, "%d",
5974 (int) user
.mbdb_uid
);
5979 (void) sm_snprintf(buf
, sizeof buf
, "%d",
5980 (int) user
.mbdb_gid
);
5985 rwval
= user
.mbdb_fullname
;
5989 rwval
= user
.mbdb_homedir
;
5993 rwval
= user
.mbdb_shell
;
5996 syserr("user_map %s: bogus field %d",
5997 map
->map_mname
, map
->map_valcolno
);
6000 return map_rewrite(map
, rwval
, strlen(rwval
), av
);
6004 ** Program map type.
6006 ** This provides access to arbitrary programs. It should be used
6007 ** only very sparingly, since there is no way to bound the cost
6008 ** of invoking an arbitrary program.
6012 prog_map_lookup(map
, name
, av
, statp
)
6025 char *argv
[MAXPV
+ 1];
6029 sm_dprintf("prog_map_lookup(%s, %s) %s\n",
6030 map
->map_mname
, name
, map
->map_file
);
6033 argv
[i
++] = map
->map_file
;
6034 if (map
->map_rebuild
!= NULL
)
6036 (void) sm_strlcpy(buf
, map
->map_rebuild
, sizeof buf
);
6037 for (p
= strtok(buf
, " \t"); p
!= NULL
; p
= strtok(NULL
, " \t"))
6048 sm_dprintf("prog_open:");
6049 for (i
= 0; argv
[i
] != NULL
; i
++)
6050 sm_dprintf(" %s", argv
[i
]);
6053 (void) sm_blocksignal(SIGCHLD
);
6054 pid
= prog_open(argv
, &fd
, CurEnv
);
6057 if (!bitset(MF_OPTIONAL
, map
->map_mflags
))
6058 syserr("prog_map_lookup(%s) failed (%s) -- closing",
6059 map
->map_mname
, sm_errstring(errno
));
6060 else if (tTd(38, 9))
6061 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing",
6062 map
->map_mname
, sm_errstring(errno
));
6063 map
->map_mflags
&= ~(MF_VALID
|MF_OPEN
);
6067 i
= read(fd
, buf
, sizeof buf
- 1);
6070 syserr("prog_map_lookup(%s): read error %s",
6071 map
->map_mname
, sm_errstring(errno
));
6077 sm_dprintf("prog_map_lookup(%s): empty answer\n",
6084 p
= strchr(buf
, '\n');
6088 /* collect the return value */
6089 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
6090 rval
= map_rewrite(map
, name
, strlen(name
), NULL
);
6092 rval
= map_rewrite(map
, buf
, strlen(buf
), av
);
6094 /* now flush any additional output */
6095 while ((i
= read(fd
, buf
, sizeof buf
)) > 0)
6099 /* wait for the process to terminate */
6101 status
= waitfor(pid
);
6103 (void) sm_releasesignal(SIGCHLD
);
6108 syserr("prog_map_lookup(%s): wait error %s",
6109 map
->map_mname
, sm_errstring(errno
));
6110 *statp
= EX_SOFTWARE
;
6113 else if (WIFEXITED(status
))
6115 if ((*statp
= WEXITSTATUS(status
)) != EX_OK
)
6120 syserr("prog_map_lookup(%s): child died on signal %d",
6121 map
->map_mname
, status
);
6122 *statp
= EX_UNAVAILABLE
;
6128 ** Sequenced map type.
6130 ** Tries each map in order until something matches, much like
6131 ** implicit. Stores go to the first map in the list that can
6134 ** This is slightly unusual in that there are two interfaces.
6135 ** The "sequence" interface lets you stack maps arbitrarily.
6136 ** The "switch" interface builds a sequence map by looking
6137 ** at a system-dependent configuration file such as
6138 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix.
6140 ** We don't need an explicit open, since all maps are
6141 ** opened on demand.
6145 ** SEQ_MAP_PARSE -- Sequenced map parsing
6149 seq_map_parse(map
, ap
)
6156 sm_dprintf("seq_map_parse(%s, %s)\n", map
->map_mname
, ap
);
6163 /* find beginning of map name */
6164 while (isascii(*ap
) && isspace(*ap
))
6167 (isascii(*p
) && isalnum(*p
)) || *p
== '_' || *p
== '.';
6172 while (*p
!= '\0' && (!isascii(*p
) || !isalnum(*p
)))
6179 s
= stab(ap
, ST_MAP
, ST_FIND
);
6182 syserr("Sequence map %s: unknown member map %s",
6183 map
->map_mname
, ap
);
6185 else if (maxmap
>= MAXMAPSTACK
)
6187 syserr("Sequence map %s: too many member maps (%d max)",
6188 map
->map_mname
, MAXMAPSTACK
);
6191 else if (maxmap
< MAXMAPSTACK
)
6193 map
->map_stack
[maxmap
++] = &s
->s_map
;
6201 ** SWITCH_MAP_OPEN -- open a switched map
6203 ** This looks at the system-dependent configuration and builds
6204 ** a sequence map that does the same thing.
6206 ** Every system must define a switch_map_find routine in conf.c
6207 ** that will return the list of service types associated with a
6208 ** given service class.
6212 switch_map_open(map
, mode
)
6218 char *maptype
[MAXMAPSTACK
];
6221 sm_dprintf("switch_map_open(%s, %s, %d)\n",
6222 map
->map_mname
, map
->map_file
, mode
);
6225 nmaps
= switch_map_find(map
->map_file
, maptype
, map
->map_return
);
6228 sm_dprintf("\tswitch_map_find => %d\n", nmaps
);
6229 for (mapno
= 0; mapno
< nmaps
; mapno
++)
6230 sm_dprintf("\t\t%s\n", maptype
[mapno
]);
6232 if (nmaps
<= 0 || nmaps
> MAXMAPSTACK
)
6235 for (mapno
= 0; mapno
< nmaps
; mapno
++)
6238 char nbuf
[MAXNAME
+ 1];
6240 if (maptype
[mapno
] == NULL
)
6242 (void) sm_strlcpyn(nbuf
, sizeof nbuf
, 3,
6243 map
->map_mname
, ".", maptype
[mapno
]);
6244 s
= stab(nbuf
, ST_MAP
, ST_FIND
);
6247 syserr("Switch map %s: unknown member map %s",
6248 map
->map_mname
, nbuf
);
6252 map
->map_stack
[mapno
] = &s
->s_map
;
6254 sm_dprintf("\tmap_stack[%d] = %s:%s\n",
6256 s
->s_map
.map_class
->map_cname
,
6265 ** SEQ_MAP_CLOSE -- close all underlying maps
6275 sm_dprintf("seq_map_close(%s)\n", map
->map_mname
);
6277 for (mapno
= 0; mapno
< MAXMAPSTACK
; mapno
++)
6279 MAP
*mm
= map
->map_stack
[mapno
];
6281 if (mm
== NULL
|| !bitset(MF_OPEN
, mm
->map_mflags
))
6283 mm
->map_mflags
|= MF_CLOSING
;
6284 mm
->map_class
->map_close(mm
);
6285 mm
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
|MF_CLOSING
);
6291 ** SEQ_MAP_LOOKUP -- sequenced map lookup
6295 seq_map_lookup(map
, key
, args
, pstat
)
6303 bool tempfail
= false;
6306 sm_dprintf("seq_map_lookup(%s, %s)\n", map
->map_mname
, key
);
6308 for (mapno
= 0; mapno
< MAXMAPSTACK
; mapbit
<<= 1, mapno
++)
6310 MAP
*mm
= map
->map_stack
[mapno
];
6315 if (!bitset(MF_OPEN
, mm
->map_mflags
) &&
6318 if (bitset(mapbit
, map
->map_return
[MA_UNAVAIL
]))
6320 *pstat
= EX_UNAVAILABLE
;
6326 rv
= mm
->map_class
->map_lookup(mm
, key
, args
, pstat
);
6329 if (*pstat
== EX_TEMPFAIL
)
6331 if (bitset(mapbit
, map
->map_return
[MA_TRYAGAIN
]))
6335 else if (bitset(mapbit
, map
->map_return
[MA_NOTFOUND
]))
6339 *pstat
= EX_TEMPFAIL
;
6340 else if (*pstat
== EX_OK
)
6341 *pstat
= EX_NOTFOUND
;
6346 ** SEQ_MAP_STORE -- sequenced map store
6350 seq_map_store(map
, key
, val
)
6358 sm_dprintf("seq_map_store(%s, %s, %s)\n",
6359 map
->map_mname
, key
, val
);
6361 for (mapno
= 0; mapno
< MAXMAPSTACK
; mapno
++)
6363 MAP
*mm
= map
->map_stack
[mapno
];
6365 if (mm
== NULL
|| !bitset(MF_WRITABLE
, mm
->map_mflags
))
6368 mm
->map_class
->map_store(mm
, key
, val
);
6371 syserr("seq_map_store(%s, %s, %s): no writable map",
6372 map
->map_mname
, key
, val
);
6380 null_map_open(map
, mode
)
6396 null_map_lookup(map
, key
, args
, pstat
)
6402 *pstat
= EX_NOTFOUND
;
6408 null_map_store(map
, key
, val
)
6421 bogus_map_lookup(map
, key
, args
, pstat
)
6427 *pstat
= EX_TEMPFAIL
;
6431 MAPCLASS BogusMapClass
=
6433 "bogus-map", NULL
, 0,
6434 NULL
, bogus_map_lookup
, null_map_store
,
6435 null_map_open
, null_map_close
,
6442 macro_map_lookup(map
, name
, av
, statp
)
6451 sm_dprintf("macro_map_lookup(%s, %s)\n", map
->map_mname
,
6452 name
== NULL
? "NULL" : name
);
6456 (mid
= macid(name
)) == 0)
6463 macdefine(&CurEnv
->e_macro
, A_PERM
, mid
, NULL
);
6465 macdefine(&CurEnv
->e_macro
, A_TEMP
, mid
, av
[1]);
6478 # define DEFAULT_DELIM CONDELSE
6479 # define END_OF_FIELDS -1
6480 # define ERRBUF_SIZE 80
6481 # define MAX_MATCH 32
6483 # define xnalloc(s) memset(xalloc(s), '\0', s);
6487 regex_t
*regex_pattern_buf
; /* xalloc it */
6488 int *regex_subfields
; /* move to type MAP */
6489 char *regex_delim
; /* move to type MAP */
6492 static int parse_fields
__P((char *, int *, int, int));
6493 static char *regex_map_rewrite
__P((MAP
*, const char*, size_t, char **));
6496 parse_fields(s
, ibuf
, blen
, nr_substrings
)
6498 int *ibuf
; /* array */
6499 int blen
; /* number of elements in ibuf */
6500 int nr_substrings
; /* number of substrings in the pattern */
6504 bool lastone
= false;
6506 blen
--; /* for terminating END_OF_FIELDS */
6527 if (val
< 0 || val
>= nr_substrings
)
6529 syserr("field (%d) out of range, only %d substrings in pattern",
6530 val
, nr_substrings
);
6537 syserr("too many fields, %d max", blen
);
6542 ibuf
[i
] = END_OF_FIELDS
;
6547 regex_map_init(map
, ap
)
6552 struct regex_map
*map_p
;
6554 char *sub_param
= NULL
;
6556 static char defdstr
[] = { (char) DEFAULT_DELIM
, '\0' };
6559 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n",
6560 map
->map_mname
, ap
);
6562 pflags
= REG_ICASE
| REG_EXTENDED
| REG_NOSUB
;
6564 map_p
= (struct regex_map
*) xnalloc(sizeof *map_p
);
6565 map_p
->regex_pattern_buf
= (regex_t
*)xnalloc(sizeof(regex_t
));
6569 while (isascii(*p
) && isspace(*p
))
6576 map
->map_mflags
|= MF_REGEX_NOT
;
6579 case 'f': /* case sensitive */
6580 map
->map_mflags
|= MF_NOFOLDCASE
;
6581 pflags
&= ~REG_ICASE
;
6584 case 'b': /* basic regular expressions */
6585 pflags
&= ~REG_EXTENDED
;
6588 case 's': /* substring match () syntax */
6590 pflags
&= ~REG_NOSUB
;
6593 case 'd': /* delimiter */
6594 map_p
->regex_delim
= ++p
;
6597 case 'a': /* map append */
6601 case 'm': /* matchonly */
6602 map
->map_mflags
|= MF_MATCHONLY
;
6606 map
->map_mflags
|= MF_KEEPQUOTES
;
6610 map
->map_spacesub
= *++p
;
6614 map
->map_mflags
|= MF_DEFER
;
6618 while (*p
!= '\0' && !(isascii(*p
) && isspace(*p
)))
6624 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p
, pflags
);
6626 if ((regerr
= regcomp(map_p
->regex_pattern_buf
, p
, pflags
)) != 0)
6629 char errbuf
[ERRBUF_SIZE
];
6631 (void) regerror(regerr
, map_p
->regex_pattern_buf
,
6632 errbuf
, sizeof errbuf
);
6633 syserr("pattern-compile-error: %s", errbuf
);
6634 sm_free(map_p
->regex_pattern_buf
); /* XXX */
6635 sm_free(map_p
); /* XXX */
6639 if (map
->map_app
!= NULL
)
6640 map
->map_app
= newstr(map
->map_app
);
6641 if (map_p
->regex_delim
!= NULL
)
6642 map_p
->regex_delim
= newstr(map_p
->regex_delim
);
6644 map_p
->regex_delim
= defdstr
;
6646 if (!bitset(REG_NOSUB
, pflags
))
6648 /* substring matching */
6650 int *fields
= (int *) xalloc(sizeof(int) * (MAX_MATCH
+ 1));
6652 substrings
= map_p
->regex_pattern_buf
->re_nsub
+ 1;
6655 sm_dprintf("regex_map_init: nr of substrings %d\n",
6658 if (substrings
>= MAX_MATCH
)
6660 syserr("too many substrings, %d max", MAX_MATCH
);
6661 sm_free(map_p
->regex_pattern_buf
); /* XXX */
6662 sm_free(map_p
); /* XXX */
6665 if (sub_param
!= NULL
&& sub_param
[0] != '\0')
6667 /* optional parameter -sfields */
6668 if (parse_fields(sub_param
, fields
,
6669 MAX_MATCH
+ 1, substrings
) == -1)
6676 /* set default fields */
6677 for (i
= 0; i
< substrings
; i
++)
6679 fields
[i
] = END_OF_FIELDS
;
6681 map_p
->regex_subfields
= fields
;
6686 sm_dprintf("regex_map_init: subfields");
6687 for (ip
= fields
; *ip
!= END_OF_FIELDS
; ip
++)
6688 sm_dprintf(" %d", *ip
);
6692 map
->map_db1
= (ARBPTR_T
) map_p
; /* dirty hack */
6697 regex_map_rewrite(map
, s
, slen
, av
)
6703 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
6704 return map_rewrite(map
, av
[0], strlen(av
[0]), NULL
);
6706 return map_rewrite(map
, s
, slen
, av
);
6710 regex_map_lookup(map
, name
, av
, statp
)
6717 struct regex_map
*map_p
;
6718 regmatch_t pmatch
[MAX_MATCH
];
6724 sm_dprintf("regex_map_lookup: key '%s'\n", name
);
6725 for (cpp
= av
; cpp
!= NULL
&& *cpp
!= NULL
; cpp
++)
6726 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp
);
6729 map_p
= (struct regex_map
*)(map
->map_db1
);
6730 reg_res
= regexec(map_p
->regex_pattern_buf
,
6731 name
, MAX_MATCH
, pmatch
, 0);
6733 if (bitset(MF_REGEX_NOT
, map
->map_mflags
))
6736 if (reg_res
== REG_NOMATCH
)
6737 return regex_map_rewrite(map
, "", (size_t) 0, av
);
6741 if (reg_res
== REG_NOMATCH
)
6744 if (map_p
->regex_subfields
!= NULL
)
6747 static char retbuf
[MAXNAME
];
6748 int fields
[MAX_MATCH
+ 1];
6750 int anglecnt
= 0, cmntcnt
= 0, spacecnt
= 0;
6751 bool quotemode
= false, bslashmode
= false;
6752 register char *dp
, *sp
;
6757 ldp
= retbuf
+ sizeof(retbuf
) - 1;
6761 if (parse_fields(av
[1], fields
, MAX_MATCH
+ 1,
6762 (int) map_p
->regex_pattern_buf
->re_nsub
+ 1) == -1)
6770 ip
= map_p
->regex_subfields
;
6772 for ( ; *ip
!= END_OF_FIELDS
; ip
++)
6776 for (sp
= map_p
->regex_delim
; *sp
; sp
++)
6785 if (*ip
>= MAX_MATCH
||
6786 pmatch
[*ip
].rm_so
< 0 || pmatch
[*ip
].rm_eo
< 0)
6789 sp
= name
+ pmatch
[*ip
].rm_so
;
6790 endp
= name
+ pmatch
[*ip
].rm_eo
;
6791 for (; endp
> sp
; sp
++)
6800 else if (quotemode
&& *sp
!= '"' &&
6805 else switch (*dp
++ = *sp
)
6832 quotemode
= !quotemode
;
6838 if (anglecnt
!= 0 || cmntcnt
!= 0 || quotemode
||
6839 bslashmode
|| spacecnt
!= 0)
6841 sm_syslog(LOG_WARNING
, NOQID
,
6842 "Warning: regex may cause prescan() failure map=%s lookup=%s",
6843 map
->map_mname
, name
);
6849 return regex_map_rewrite(map
, retbuf
, strlen(retbuf
), av
);
6851 return regex_map_rewrite(map
, "", (size_t)0, av
);
6853 #endif /* MAP_REGEX */
6860 # define _DATUM_DEFINED
6861 # include <ns_api.h>
6863 typedef struct ns_map_list
6865 ns_map_t
*map
; /* XXX ns_ ? */
6867 struct ns_map_list
*next
;
6871 ns_map_t_find(mapname
)
6874 static ns_map_list_t
*ns_maps
= NULL
;
6875 ns_map_list_t
*ns_map
;
6877 /* walk the list of maps looking for the correctly named map */
6878 for (ns_map
= ns_maps
; ns_map
!= NULL
; ns_map
= ns_map
->next
)
6880 if (strcmp(ns_map
->mapname
, mapname
) == 0)
6884 /* if we are looking at a NULL ns_map_list_t, then create a new one */
6887 ns_map
= (ns_map_list_t
*) xalloc(sizeof *ns_map
);
6888 ns_map
->mapname
= newstr(mapname
);
6889 ns_map
->map
= (ns_map_t
*) xalloc(sizeof *ns_map
->map
);
6890 memset(ns_map
->map
, '\0', sizeof *ns_map
->map
);
6891 ns_map
->next
= ns_maps
;
6898 nsd_map_lookup(map
, name
, av
, statp
)
6907 char keybuf
[MAXNAME
+ 1];
6911 sm_dprintf("nsd_map_lookup(%s, %s)\n", map
->map_mname
, name
);
6913 buflen
= strlen(name
);
6914 if (buflen
> sizeof keybuf
- 1)
6915 buflen
= sizeof keybuf
- 1; /* XXX simply cut off? */
6916 memmove(keybuf
, name
, buflen
);
6917 keybuf
[buflen
] = '\0';
6918 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
6921 ns_map
= ns_map_t_find(map
->map_file
);
6925 sm_dprintf("nsd_map_t_find failed\n");
6926 *statp
= EX_UNAVAILABLE
;
6929 r
= ns_lookup(ns_map
, NULL
, map
->map_file
, keybuf
, NULL
,
6931 if (r
== NS_UNAVAIL
|| r
== NS_TRYAGAIN
)
6933 *statp
= EX_TEMPFAIL
;
6939 # endif /* NS_NOPERM */
6945 if (r
!= NS_SUCCESS
)
6947 *statp
= EX_NOTFOUND
;
6953 /* Null out trailing \n */
6954 if ((p
= strchr(buf
, '\n')) != NULL
)
6957 return map_rewrite(map
, buf
, strlen(buf
), av
);
6959 #endif /* MAP_NSD */
6962 arith_map_lookup(map
, name
, av
, statp
)
6972 static char result
[16];
6977 sm_dprintf("arith_map_lookup: key '%s'\n", name
);
6978 for (cpp
= av
; cpp
!= NULL
&& *cpp
!= NULL
; cpp
++)
6979 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp
);
6987 ** read arguments for arith map
6988 ** - no check is made whether they are really numbers
6989 ** - just ignores args after the second
6992 for (++cpp
; cpp
!= NULL
&& *cpp
!= NULL
&& r
< 2; cpp
++)
6993 v
[r
++] = strtol(*cpp
, NULL
, 0);
6995 /* operator and (at least) two operands given? */
6996 if (name
!= NULL
&& r
== 2)
7045 sm_syslog(LOG_WARNING
, NOQID
,
7046 "arith_map: unknown operator %c",
7047 isprint(*name
) ? *name
: '?');
7051 (void) sm_snprintf(result
, sizeof result
,
7052 res
? "TRUE" : "FALSE");
7054 (void) sm_snprintf(result
, sizeof result
, "%ld", r
);
7063 # if NETINET || NETINET6
7064 # include <arpa/inet.h>
7065 # endif /* NETINET || NETINET6 */
7067 # define socket_map_next map_stack[0]
7070 ** SOCKET_MAP_OPEN -- open socket table
7074 socket_map_open(map
, mode
)
7080 SOCKADDR_LEN_T addrlen
= 0;
7086 struct hostent
*hp
= NULL
;
7090 sm_dprintf("socket_map_open(%s, %s, %d)\n",
7091 map
->map_mname
, map
->map_file
, mode
);
7095 /* sendmail doesn't have the ability to write to SOCKET (yet) */
7096 if (mode
!= O_RDONLY
)
7098 /* issue a pseudo-error message */
7099 errno
= SM_EMAPCANTWRITE
;
7103 if (*map
->map_file
== '\0')
7105 syserr("socket map \"%s\": empty or missing socket information",
7110 s
= socket_map_findconn(map
->map_file
);
7111 if (s
->s_socketmap
!= NULL
)
7113 /* Copy open connection */
7114 map
->map_db1
= s
->s_socketmap
->map_db1
;
7116 /* Add this map as head of linked list */
7117 map
->socket_map_next
= s
->s_socketmap
;
7118 s
->s_socketmap
= map
;
7121 sm_dprintf("using cached connection\n");
7126 sm_dprintf("opening new connection\n");
7128 /* following code is ripped from milter.c */
7129 /* XXX It should be put in a library... */
7131 /* protocol:filename or protocol:port@host */
7132 memset(&addr
, '\0', sizeof addr
);
7134 colon
= strchr(p
, ':');
7142 /* default to AF_UNIX */
7143 addr
.sa
.sa_family
= AF_UNIX
;
7144 # else /* NETUNIX */
7146 /* default to AF_INET */
7147 addr
.sa
.sa_family
= AF_INET
;
7148 # else /* NETINET */
7150 /* default to AF_INET6 */
7151 addr
.sa
.sa_family
= AF_INET6
;
7152 # else /* NETINET6 */
7153 /* no protocols available */
7154 syserr("socket map \"%s\": no valid socket protocols available",
7157 # endif /* NETINET6 */
7158 # endif /* NETINET */
7159 # endif /* NETUNIX */
7162 else if (sm_strcasecmp(p
, "unix") == 0 ||
7163 sm_strcasecmp(p
, "local") == 0)
7164 addr
.sa
.sa_family
= AF_UNIX
;
7165 # endif /* NETUNIX */
7167 else if (sm_strcasecmp(p
, "inet") == 0)
7168 addr
.sa
.sa_family
= AF_INET
;
7169 # endif /* NETINET */
7171 else if (sm_strcasecmp(p
, "inet6") == 0)
7172 addr
.sa
.sa_family
= AF_INET6
;
7173 # endif /* NETINET6 */
7176 # ifdef EPROTONOSUPPORT
7177 errno
= EPROTONOSUPPORT
;
7178 # else /* EPROTONOSUPPORT */
7180 # endif /* EPROTONOSUPPORT */
7181 syserr("socket map \"%s\": unknown socket type %s",
7191 /* default to AF_UNIX */
7192 addr
.sa
.sa_family
= AF_UNIX
;
7195 /* default to AF_INET */
7196 addr
.sa
.sa_family
= AF_INET
;
7197 # else /* NETINET */
7199 /* default to AF_INET6 */
7200 addr
.sa
.sa_family
= AF_INET6
;
7201 # else /* NETINET6 */
7202 syserr("socket map \"%s\": unknown socket type %s",
7205 # endif /* NETINET6 */
7206 # endif /* NETINET */
7207 #endif /* NETUNIX */
7211 if (addr
.sa
.sa_family
== AF_UNIX
)
7213 long sff
= SFF_SAFEDIRPATH
|SFF_OPENASROOT
|SFF_NOLINK
|SFF_EXECOK
;
7216 if (strlen(colon
) >= sizeof addr
.sunix
.sun_path
)
7218 syserr("socket map \"%s\": local socket name %s too long",
7219 map
->map_mname
, colon
);
7222 errno
= safefile(colon
, RunAsUid
, RunAsGid
, RunAsUserName
, sff
,
7223 S_IRUSR
|S_IWUSR
, NULL
);
7227 /* if not safe, don't create */
7228 syserr("socket map \"%s\": local socket name %s unsafe",
7229 map
->map_mname
, colon
);
7233 (void) sm_strlcpy(addr
.sunix
.sun_path
, colon
,
7234 sizeof addr
.sunix
.sun_path
);
7235 addrlen
= sizeof (struct sockaddr_un
);
7238 # endif /* NETUNIX */
7239 # if NETINET || NETINET6
7242 || addr
.sa
.sa_family
== AF_INET
7243 # endif /* NETINET */
7245 || addr
.sa
.sa_family
== AF_INET6
7246 # endif /* NETINET6 */
7249 unsigned short port
;
7251 /* Parse port@host */
7252 at
= strchr(colon
, '@');
7255 syserr("socket map \"%s\": bad address %s (expected port@host)",
7256 map
->map_mname
, colon
);
7260 if (isascii(*colon
) && isdigit(*colon
))
7261 port
= htons((unsigned short) atoi(colon
));
7264 # ifdef NO_GETSERVBYNAME
7265 syserr("socket map \"%s\": invalid port number %s",
7266 map
->map_mname
, colon
);
7268 # else /* NO_GETSERVBYNAME */
7269 register struct servent
*sp
;
7271 sp
= getservbyname(colon
, "tcp");
7274 syserr("socket map \"%s\": unknown port name %s",
7275 map
->map_mname
, colon
);
7279 # endif /* NO_GETSERVBYNAME */
7286 end
= strchr(at
, ']');
7291 unsigned long hid
= INADDR_NONE
;
7292 # endif /* NETINET */
7294 struct sockaddr_in6 hid6
;
7295 # endif /* NETINET6 */
7299 if (addr
.sa
.sa_family
== AF_INET
&&
7300 (hid
= inet_addr(&at
[1])) != INADDR_NONE
)
7302 addr
.sin
.sin_addr
.s_addr
= hid
;
7303 addr
.sin
.sin_port
= port
;
7306 # endif /* NETINET */
7308 (void) memset(&hid6
, '\0', sizeof hid6
);
7309 if (addr
.sa
.sa_family
== AF_INET6
&&
7310 anynet_pton(AF_INET6
, &at
[1],
7311 &hid6
.sin6_addr
) == 1)
7313 addr
.sin6
.sin6_addr
= hid6
.sin6_addr
;
7314 addr
.sin6
.sin6_port
= port
;
7317 # endif /* NETINET6 */
7321 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7322 map
->map_mname
, at
);
7328 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"",
7329 map
->map_mname
, at
);
7335 hp
= sm_gethostbyname(at
, addr
.sa
.sa_family
);
7338 syserr("socket map \"%s\": Unknown host name %s",
7339 map
->map_mname
, at
);
7342 addr
.sa
.sa_family
= hp
->h_addrtype
;
7343 switch (hp
->h_addrtype
)
7347 memmove(&addr
.sin
.sin_addr
,
7348 hp
->h_addr
, INADDRSZ
);
7349 addr
.sin
.sin_port
= port
;
7350 addrlen
= sizeof (struct sockaddr_in
);
7353 # endif /* NETINET */
7357 memmove(&addr
.sin6
.sin6_addr
,
7358 hp
->h_addr
, IN6ADDRSZ
);
7359 addr
.sin6
.sin6_port
= port
;
7360 addrlen
= sizeof (struct sockaddr_in6
);
7363 # endif /* NETINET6 */
7366 syserr("socket map \"%s\": Unknown protocol for %s (%d)",
7367 map
->map_mname
, at
, hp
->h_addrtype
);
7370 # endif /* NETINET6 */
7376 # endif /* NETINET || NETINET6 */
7378 syserr("socket map \"%s\": unknown socket protocol",
7383 /* nope, actually connecting */
7386 sock
= socket(addr
.sa
.sa_family
, SOCK_STREAM
, 0);
7391 sm_dprintf("socket map \"%s\": error creating socket: %s\n",
7393 sm_errstring(save_errno
));
7397 # endif /* NETINET6 */
7401 if (connect(sock
, (struct sockaddr
*) &addr
, addrlen
) >= 0)
7404 /* couldn't connect.... try next address */
7409 sm_dprintf("socket_open (%s): open %s failed: %s\n",
7410 map
->map_mname
, at
, sm_errstring(save_errno
));
7414 /* try next address */
7415 if (hp
!= NULL
&& hp
->h_addr_list
[addrno
] != NULL
)
7417 switch (addr
.sa
.sa_family
)
7421 memmove(&addr
.sin
.sin_addr
,
7422 hp
->h_addr_list
[addrno
++],
7425 # endif /* NETINET */
7429 memmove(&addr
.sin6
.sin6_addr
,
7430 hp
->h_addr_list
[addrno
++],
7433 # endif /* NETINET6 */
7437 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n",
7442 # endif /* NETINET6 */
7450 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n",
7451 map
->map_mname
, sm_errstring(save_errno
));
7456 # endif /* NETINET6 */
7465 # endif /* NETINET6 */
7466 if ((map
->map_db1
= (ARBPTR_T
) sm_io_open(SmFtStdiofd
,
7474 sm_dprintf("socket_open (%s): failed to create stream: %s\n",
7475 map
->map_mname
, sm_errstring(errno
));
7479 /* Save connection for reuse */
7480 s
->s_socketmap
= map
;
7485 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server
7487 ** Cache SOCKET connections based on the connection specifier
7488 ** and PID so we don't have multiple connections open to
7489 ** the same server for different maps. Need a separate connection
7490 ** per PID since a parent process may close the map before the
7491 ** child is done with it.
7494 ** conn -- SOCKET map connection specifier
7497 ** Symbol table entry for the SOCKET connection.
7501 socket_map_findconn(conn
)
7505 STAB
*SM_NONVOLATILE s
= NULL
;
7507 nbuf
= sm_stringf_x("%s%c%d", conn
, CONDELSE
, (int) CurrentPid
);
7509 s
= stab(nbuf
, ST_SOCKETMAP
, ST_ENTER
);
7517 ** SOCKET_MAP_CLOSE -- close the socket
7521 socket_map_close(map
)
7528 sm_dprintf("socket_map_close(%s), pid=%ld\n", map
->map_file
,
7531 /* Check if already closed */
7532 if (map
->map_db1
== NULL
)
7535 sm_dprintf("socket_map_close(%s) already closed\n",
7539 sm_io_close((SM_FILE_T
*)map
->map_db1
, SM_TIME_DEFAULT
);
7541 /* Mark all the maps that share the connection as closed */
7542 s
= socket_map_findconn(map
->map_file
);
7543 smap
= s
->s_socketmap
;
7544 while (smap
!= NULL
)
7548 if (tTd(38, 2) && smap
!= map
)
7549 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n",
7550 map
->map_mname
, smap
->map_mname
);
7552 smap
->map_mflags
&= ~(MF_OPEN
|MF_WRITABLE
);
7553 smap
->map_db1
= NULL
;
7554 next
= smap
->socket_map_next
;
7555 smap
->socket_map_next
= NULL
;
7558 s
->s_socketmap
= NULL
;
7562 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table
7566 socket_map_lookup(map
, name
, av
, statp
)
7572 unsigned int nettolen
, replylen
, recvlen
;
7573 char *replybuf
, *rval
, *value
, *status
, *key
;
7575 char keybuf
[MAXNAME
+ 1];
7579 f
= (SM_FILE_T
*)map
->map_db1
;
7581 sm_dprintf("socket_map_lookup(%s, %s) %s\n",
7582 map
->map_mname
, name
, map
->map_file
);
7584 if (!bitset(MF_NOFOLDCASE
, map
->map_mflags
))
7586 nettolen
= strlen(name
);
7587 if (nettolen
> sizeof keybuf
- 1)
7588 nettolen
= sizeof keybuf
- 1;
7589 memmove(keybuf
, name
, nettolen
);
7590 keybuf
[nettolen
] = '\0';
7597 nettolen
= strlen(map
->map_mname
) + 1 + strlen(key
);
7598 SM_ASSERT(nettolen
> strlen(map
->map_mname
));
7599 SM_ASSERT(nettolen
> strlen(key
));
7600 if ((sm_io_fprintf(f
, SM_TIME_DEFAULT
, "%u:%s %s,",
7601 nettolen
, map
->map_mname
, key
) == SM_IO_EOF
) ||
7602 (sm_io_flush(f
, SM_TIME_DEFAULT
) != 0) ||
7605 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request",
7607 *statp
= EX_TEMPFAIL
;
7611 if (sm_io_fscanf(f
, SM_TIME_DEFAULT
, "%9u", &replylen
) != 1)
7613 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply",
7615 *statp
= EX_TEMPFAIL
;
7618 if (replylen
> SOCKETMAP_MAXL
)
7620 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u",
7621 map
->map_mname
, replylen
);
7622 *statp
= EX_TEMPFAIL
;
7625 if (sm_io_getc(f
, SM_TIME_DEFAULT
) != ':')
7627 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply",
7629 *statp
= EX_TEMPFAIL
;
7633 replybuf
= (char *) sm_malloc(replylen
+ 1);
7634 if (replybuf
== NULL
)
7636 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes",
7637 map
->map_mname
, replylen
+ 1);
7642 recvlen
= sm_io_read(f
, SM_TIME_DEFAULT
, replybuf
, replylen
);
7643 if (recvlen
< replylen
)
7645 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters",
7646 map
->map_mname
, recvlen
, replylen
);
7647 *statp
= EX_TEMPFAIL
;
7650 if (sm_io_getc(f
, SM_TIME_DEFAULT
) != ',')
7652 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply",
7654 *statp
= EX_TEMPFAIL
;
7658 replybuf
[recvlen
] = '\0';
7659 value
= strchr(replybuf
, ' ');
7665 if (strcmp(status
, "OK") == 0)
7669 /* collect the return value */
7670 if (bitset(MF_MATCHONLY
, map
->map_mflags
))
7671 rval
= map_rewrite(map
, key
, strlen(key
), NULL
);
7673 rval
= map_rewrite(map
, value
, strlen(value
), av
);
7675 else if (strcmp(status
, "NOTFOUND") == 0)
7677 *statp
= EX_NOTFOUND
;
7679 sm_dprintf("socket_map_lookup(%s): %s not found\n",
7680 map
->map_mname
, key
);
7685 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n",
7686 map
->map_mname
, key
, status
,
7687 value
? value
: "");
7688 if ((strcmp(status
, "TEMP") == 0) ||
7689 (strcmp(status
, "TIMEOUT") == 0))
7690 *statp
= EX_TEMPFAIL
;
7691 else if(strcmp(status
, "PERM") == 0)
7692 *statp
= EX_UNAVAILABLE
;
7694 *statp
= EX_PROTOCOL
;
7697 if (replybuf
!= NULL
)
7702 socket_map_close(map
);
7704 if (replybuf
!= NULL
)
7708 #endif /* SOCKETMAP */