Commit | Line | Data |
---|---|---|
814c4102 EA |
1 | /* |
2 | * Copyright (c) 1983 Eric P. Allman | |
3 | * Copyright (c) 1988 Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
6 | * %sccs.include.redist.c% | |
7 | */ | |
8 | ||
9 | #ifndef lint | |
83b7c7b1 | 10 | #ifdef USERDB |
256fe397 | 11 | static char sccsid [] = "@(#)udb.c 5.10 (Berkeley) %G% (with USERDB)"; |
83b7c7b1 | 12 | #else |
256fe397 | 13 | static char sccsid [] = "@(#)udb.c 5.10 (Berkeley) %G% (without USERDB)"; |
83b7c7b1 | 14 | #endif |
814c4102 EA |
15 | #endif |
16 | ||
17 | #include "sendmail.h" | |
18 | ||
19 | #ifdef USERDB | |
20 | ||
21 | #include <sys/file.h> | |
83b7c7b1 EA |
22 | #include <sys/time.h> |
23 | #include <fcntl.h> | |
24 | #include <netdb.h> | |
814c4102 EA |
25 | #include <db.h> |
26 | ||
27 | /* | |
7a059839 | 28 | ** UDBEXPAND.C -- interface between sendmail and Berkeley User Data Base. |
814c4102 | 29 | ** |
7a059839 | 30 | ** This depends on the 4.4BSD db package. |
814c4102 EA |
31 | */ |
32 | ||
dac6c90e | 33 | |
83b7c7b1 EA |
34 | struct udbent |
35 | { | |
36 | char *udb_spec; /* string version of spec */ | |
37 | int udb_type; /* type of entry */ | |
38 | union | |
39 | { | |
40 | /* type UE_REMOTE -- do remote call for lookup */ | |
41 | struct | |
42 | { | |
83b7c7b1 EA |
43 | struct sockaddr_in _udb_addr; /* address */ |
44 | int _udb_timeout; /* timeout */ | |
45 | } udb_remote; | |
83b7c7b1 EA |
46 | #define udb_addr udb_u.udb_remote._udb_addr |
47 | #define udb_timeout udb_u.udb_remote._udb_timeout | |
48 | ||
49 | /* type UE_FORWARD -- forward message to remote */ | |
50 | struct | |
51 | { | |
52 | char *_udb_fwdhost; /* name of forward host */ | |
53 | } udb_forward; | |
54 | #define udb_fwdhost udb_u.udb_forward._udb_fwdhost | |
55 | ||
56 | /* type UE_LOOKUP -- lookup in local database */ | |
57 | struct | |
58 | { | |
59 | char *_udb_dbname; /* pathname of database */ | |
60 | DB *_udb_dbp; /* open database ptr */ | |
61 | } udb_lookup; | |
62 | #define udb_dbname udb_u.udb_lookup._udb_dbname | |
63 | #define udb_dbp udb_u.udb_lookup._udb_dbp | |
64 | } udb_u; | |
65 | }; | |
66 | ||
67 | #define UDB_EOLIST 0 /* end of list */ | |
68 | #define UDB_SKIP 1 /* skip this entry */ | |
69 | #define UDB_REMOTE 2 /* look up in remote database */ | |
70 | #define UDB_LOOKUP 3 /* look up in local database */ | |
71 | #define UDB_FORWARD 4 /* forward to remote host */ | |
72 | ||
73 | #define MAXUDBENT 10 /* maximum number of UDB entries */ | |
74 | ||
7a059839 EA |
75 | |
76 | struct option | |
77 | { | |
78 | char *name; | |
79 | char *val; | |
80 | }; | |
81 | \f/* | |
82 | ** UDBEXPAND -- look up user in database and expand | |
83 | ** | |
84 | ** Parameters: | |
85 | ** a -- address to expand. | |
86 | ** sendq -- pointer to head of sendq to put the expansions in. | |
87 | ** | |
88 | ** Returns: | |
89 | ** none. | |
90 | ** | |
91 | ** Side Effects: | |
92 | ** Modifies sendq. | |
93 | */ | |
94 | ||
95 | int UdbPort = 1616; | |
96 | int UdbTimeout = 10; | |
97 | ||
dac6c90e EA |
98 | struct udbent UdbEnts[MAXUDBENT + 1]; |
99 | int UdbSock = -1; | |
83b7c7b1 | 100 | |
814c4102 EA |
101 | void |
102 | udbexpand(a, sendq) | |
103 | register ADDRESS *a; | |
104 | ADDRESS **sendq; | |
105 | { | |
106 | int i; | |
107 | register char *p; | |
814c4102 EA |
108 | DBT key; |
109 | DBT info; | |
83b7c7b1 | 110 | static bool firstcall = TRUE; |
83b7c7b1 EA |
111 | bool breakout; |
112 | register struct udbent *up; | |
dac6c90e EA |
113 | int keylen; |
114 | char keybuf[128]; | |
814c4102 EA |
115 | char buf[8192]; |
116 | ||
117 | if (tTd(28, 1)) | |
118 | printf("expand(%s)\n", a->q_paddr); | |
119 | ||
120 | /* make certain we are supposed to send to this address */ | |
83b7c7b1 EA |
121 | if (bitset(QDONTSEND, a->q_flags) || |
122 | UdbSpec == NULL || UdbSpec[0] == '\0') | |
814c4102 EA |
123 | return; |
124 | CurEnv->e_to = a->q_paddr; | |
125 | ||
83b7c7b1 EA |
126 | /* on first call, locate the database */ |
127 | if (firstcall) | |
814c4102 | 128 | { |
dac6c90e EA |
129 | extern void _udbx_init(); |
130 | ||
131 | _udbx_init(); | |
83b7c7b1 | 132 | firstcall = FALSE; |
dac6c90e | 133 | } |
83b7c7b1 | 134 | |
dac6c90e EA |
135 | /* if name is too long, assume it won't match */ |
136 | if (strlen(a->q_user) > sizeof keybuf - 12) | |
137 | return; | |
83b7c7b1 | 138 | |
dac6c90e EA |
139 | /* if name begins with a colon, it indicates our metadata */ |
140 | if (a->q_user[0] == ':') | |
141 | return; | |
142 | ||
143 | /* build actual database key */ | |
144 | (void) strcpy(keybuf, a->q_user); | |
145 | (void) strcat(keybuf, ":maildrop"); | |
146 | keylen = strlen(keybuf); | |
814c4102 | 147 | |
83b7c7b1 | 148 | breakout = FALSE; |
dac6c90e | 149 | for (up = UdbEnts; !breakout; up++) |
814c4102 | 150 | { |
83b7c7b1 EA |
151 | char *user; |
152 | struct timeval timeout; | |
153 | fd_set fdset; | |
814c4102 | 154 | |
83b7c7b1 EA |
155 | /* |
156 | ** Select action based on entry type. | |
157 | ** | |
158 | ** On dropping out of this switch, "class" should | |
159 | ** explain the type of the data, and "user" should | |
160 | ** contain the user information. | |
161 | */ | |
814c4102 | 162 | |
83b7c7b1 EA |
163 | switch (up->udb_type) |
164 | { | |
165 | case UDB_LOOKUP: | |
dac6c90e EA |
166 | key.data = keybuf; |
167 | key.size = keylen; | |
168 | i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); | |
83b7c7b1 EA |
169 | if (i != 0 || info.size <= 0) |
170 | { | |
171 | if (i < 0) | |
172 | syserr("udbexpand: db-get stat %s"); | |
173 | if (tTd(28, 2)) | |
dac6c90e | 174 | printf("expand: no match on %s\n", keybuf); |
83b7c7b1 EA |
175 | continue; |
176 | } | |
814c4102 | 177 | |
256fe397 EA |
178 | while (i == 0 && key.size == keylen && |
179 | bcmp(key.data, keybuf, keylen) == 0) | |
dac6c90e | 180 | { |
256fe397 | 181 | breakout = TRUE; |
dac6c90e EA |
182 | if (info.size < sizeof buf) |
183 | user = buf; | |
184 | else | |
185 | user = xalloc(info.size + 1); | |
186 | bcopy(info.data, user, info.size); | |
187 | user[info.size] = '\0'; | |
188 | ||
189 | message(Arpa_Info, "expanded to %s", user); | |
190 | AliasLevel++; | |
191 | sendtolist(user, a, sendq); | |
192 | AliasLevel--; | |
193 | ||
194 | if (user != buf) | |
195 | free(user); | |
196 | ||
197 | /* get the next record */ | |
198 | i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); | |
256fe397 | 199 | } |
83b7c7b1 EA |
200 | break; |
201 | ||
202 | case UDB_REMOTE: | |
84a5cc29 EA |
203 | /* not yet implemented */ |
204 | continue; | |
83b7c7b1 EA |
205 | |
206 | case UDB_FORWARD: | |
83b7c7b1 EA |
207 | i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; |
208 | if (i < sizeof buf) | |
209 | user = buf; | |
210 | else | |
211 | user = xalloc(i + 1); | |
212 | (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); | |
dac6c90e EA |
213 | message(Arpa_Info, "expanded to %s", user); |
214 | AliasLevel++; | |
215 | sendtolist(user, a, sendq); | |
216 | AliasLevel--; | |
217 | if (user != buf) | |
218 | free(user); | |
219 | breakout = TRUE; | |
83b7c7b1 EA |
220 | break; |
221 | ||
222 | case UDB_EOLIST: | |
223 | breakout = TRUE; | |
224 | continue; | |
225 | ||
226 | default: | |
227 | /* unknown entry type */ | |
228 | continue; | |
229 | } | |
dac6c90e EA |
230 | } |
231 | } | |
83b7c7b1 | 232 | |
7a059839 EA |
233 | #define MAXUDBOPTS 27 |
234 | ||
dac6c90e EA |
235 | void |
236 | _udbx_init() | |
237 | { | |
238 | register char *p; | |
239 | int i; | |
240 | register struct udbent *up; | |
241 | char buf[8192]; | |
83b7c7b1 | 242 | |
dac6c90e EA |
243 | p = UdbSpec; |
244 | up = UdbEnts; | |
b375cb3b | 245 | while (p != NULL) |
dac6c90e EA |
246 | { |
247 | char *spec; | |
248 | auto int rcode; | |
7a059839 | 249 | int nopts; |
dac6c90e EA |
250 | int nmx; |
251 | register struct hostent *h; | |
252 | char *mxhosts[MAXMXHOSTS + 1]; | |
7a059839 | 253 | struct option opts[MAXUDBOPTS + 1]; |
dac6c90e EA |
254 | |
255 | while (*p == ' ' || *p == '\t' || *p == ',') | |
256 | p++; | |
257 | if (*p == '\0') | |
258 | break; | |
259 | spec = p; | |
260 | p = index(p, ','); | |
93f1f3e4 | 261 | if (p != NULL) |
dac6c90e | 262 | *p++ = '\0'; |
7a059839 EA |
263 | |
264 | /* extract options */ | |
265 | nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); | |
266 | ||
267 | /* | |
268 | ** Decode database specification. | |
269 | ** | |
270 | ** In the sendmail tradition, the leading character | |
271 | ** defines the semantics of the rest of the entry. | |
272 | ** | |
273 | ** +hostname -- send a datagram to the udb server | |
274 | ** on host "hostname" asking for the | |
275 | ** home mail server for this user. | |
276 | ** *hostname -- similar to +hostname, except that the | |
277 | ** hostname is searched as an MX record; | |
278 | ** resulting hosts are searched as for | |
279 | ** +mxhostname. If no MX host is found, | |
280 | ** this is the same as +hostname. | |
281 | ** @hostname -- forward email to the indicated host. | |
282 | ** This should be the last in the list, | |
283 | ** since it always matches the input. | |
284 | ** /dbname -- search the named database on the local | |
285 | ** host using the Berkeley db package. | |
286 | */ | |
287 | ||
dac6c90e | 288 | switch (*spec) |
83b7c7b1 | 289 | { |
dac6c90e | 290 | case '+': /* search remote database */ |
7a059839 EA |
291 | case '*': /* search remote database (expand MX) */ |
292 | if (*spec == '*') | |
dac6c90e | 293 | { |
7a059839 EA |
294 | nmx = getmxrr(spec + 1, mxhosts, "", &rcode); |
295 | if (tTd(28, 16)) | |
296 | { | |
297 | int i; | |
298 | ||
299 | printf("getmxrr(%s): %d", spec + 1, nmx); | |
300 | for (i = 0; i <= nmx; i++) | |
301 | printf(" %s", mxhosts[i]); | |
302 | printf("\n"); | |
303 | } | |
dac6c90e | 304 | } |
7a059839 | 305 | else |
dac6c90e | 306 | { |
7a059839 EA |
307 | nmx = 1; |
308 | mxhosts[0] = spec + 1; | |
dac6c90e | 309 | } |
7a059839 | 310 | |
dac6c90e EA |
311 | for (i = 0; i < nmx; i++) |
312 | { | |
313 | h = gethostbyname(mxhosts[i]); | |
314 | if (h == NULL) | |
315 | continue; | |
316 | up->udb_type = UDB_REMOTE; | |
317 | up->udb_addr.sin_family = h->h_addrtype; | |
dac6c90e EA |
318 | bcopy(h->h_addr_list[0], |
319 | (char *) &up->udb_addr.sin_addr, | |
320 | h->h_length); | |
321 | up->udb_addr.sin_port = UdbPort; | |
322 | up->udb_timeout = UdbTimeout; | |
323 | up++; | |
324 | } | |
325 | ||
326 | /* set up a datagram socket */ | |
327 | if (UdbSock < 0) | |
328 | { | |
329 | UdbSock = socket(AF_INET, SOCK_DGRAM, 0); | |
330 | (void) fcntl(UdbSock, F_SETFD, 1); | |
331 | } | |
332 | break; | |
333 | ||
334 | case '@': /* forward to remote host */ | |
335 | up->udb_type = UDB_FORWARD; | |
336 | up->udb_fwdhost = spec + 1; | |
337 | up++; | |
338 | break; | |
339 | ||
340 | case '/': /* look up remote name */ | |
341 | up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); | |
342 | if (up->udb_dbp == NULL) | |
343 | break; | |
344 | up->udb_type = UDB_LOOKUP; | |
345 | up++; | |
346 | break; | |
83b7c7b1 | 347 | } |
dac6c90e EA |
348 | } |
349 | up->udb_type = UDB_EOLIST; | |
83b7c7b1 | 350 | |
dac6c90e EA |
351 | if (tTd(28, 4)) |
352 | { | |
353 | for (up = UdbEnts; ; up++) | |
83b7c7b1 | 354 | { |
dac6c90e EA |
355 | switch (up->udb_type) |
356 | { | |
357 | case UDB_EOLIST: | |
358 | return; | |
359 | ||
360 | case UDB_REMOTE: | |
361 | printf("REMOTE: addr %s, timeo %d\n", | |
362 | inet_ntoa(up->udb_addr.sin_addr), | |
363 | up->udb_timeout); | |
364 | break; | |
365 | ||
366 | case UDB_LOOKUP: | |
256fe397 EA |
367 | printf("LOOKUP: file %s\n", |
368 | up->udb_dbname); | |
dac6c90e EA |
369 | break; |
370 | ||
371 | case UDB_FORWARD: | |
372 | printf("FORWARD: host %s\n", | |
373 | up->udb_fwdhost); | |
374 | break; | |
375 | ||
376 | default: | |
377 | printf("UNKNOWN\n"); | |
378 | break; | |
379 | } | |
83b7c7b1 | 380 | } |
814c4102 | 381 | } |
83b7c7b1 | 382 | } |
814c4102 | 383 | |
7a059839 EA |
384 | int |
385 | _udb_parsespec(udbspec, opt, maxopts) | |
386 | char *udbspec; | |
387 | struct option opt[]; | |
388 | int maxopts; | |
389 | { | |
390 | register char *spec; | |
391 | register char *spec_end; | |
392 | register int optnum; | |
393 | ||
394 | spec_end = index(udbspec, ':'); | |
395 | for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) | |
396 | { | |
397 | register char *p; | |
398 | ||
399 | while (isspace(*spec)) | |
400 | spec++; | |
401 | spec_end = index(spec, ':'); | |
402 | if (spec_end != NULL) | |
403 | *spec_end++ = '\0'; | |
404 | ||
405 | opt[optnum].name = spec; | |
406 | opt[optnum].val = NULL; | |
407 | p = index(spec, '='); | |
408 | if (p != NULL) | |
409 | opt[optnum].val = ++p; | |
410 | } | |
411 | return optnum; | |
412 | } | |
413 | ||
83b7c7b1 EA |
414 | #else /* not USERDB */ |
415 | ||
416 | void | |
417 | udbexpand(a, sendq) | |
418 | ADDRESS *a; | |
419 | ADDRESS **sendq; | |
420 | { | |
421 | return; | |
814c4102 EA |
422 | } |
423 | ||
424 | #endif /* USERDB */ |