make years in $t be four digits; do case folding for DBM databases
[unix-history] / usr / src / usr.sbin / sendmail / src / udb.c
CommitLineData
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
f3d8f6d6 11static char sccsid [] = "@(#)udb.c 5.20 (Berkeley) %G% (with USERDB)";
83b7c7b1 12#else
f3d8f6d6 13static char sccsid [] = "@(#)udb.c 5.20 (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 22#include <sys/time.h>
6ab0fb92 23#include <errno.h>
83b7c7b1
EA
24#include <fcntl.h>
25#include <netdb.h>
814c4102
EA
26#include <db.h>
27
28/*
42fa5d67 29** UDB.C -- interface between sendmail and Berkeley User Data Base.
814c4102 30**
7a059839 31** This depends on the 4.4BSD db package.
814c4102
EA
32*/
33
dac6c90e 34
83b7c7b1
EA
35struct udbent
36{
37 char *udb_spec; /* string version of spec */
38 int udb_type; /* type of entry */
f61c3c40 39 char *udb_default; /* default host for outgoing mail */
83b7c7b1
EA
40 union
41 {
42 /* type UE_REMOTE -- do remote call for lookup */
43 struct
44 {
83b7c7b1
EA
45 struct sockaddr_in _udb_addr; /* address */
46 int _udb_timeout; /* timeout */
47 } udb_remote;
83b7c7b1
EA
48#define udb_addr udb_u.udb_remote._udb_addr
49#define udb_timeout udb_u.udb_remote._udb_timeout
50
51 /* type UE_FORWARD -- forward message to remote */
52 struct
53 {
54 char *_udb_fwdhost; /* name of forward host */
55 } udb_forward;
56#define udb_fwdhost udb_u.udb_forward._udb_fwdhost
57
f61c3c40 58 /* type UE_FETCH -- lookup in local database */
83b7c7b1
EA
59 struct
60 {
61 char *_udb_dbname; /* pathname of database */
62 DB *_udb_dbp; /* open database ptr */
63 } udb_lookup;
64#define udb_dbname udb_u.udb_lookup._udb_dbname
65#define udb_dbp udb_u.udb_lookup._udb_dbp
66 } udb_u;
67};
68
69#define UDB_EOLIST 0 /* end of list */
70#define UDB_SKIP 1 /* skip this entry */
71#define UDB_REMOTE 2 /* look up in remote database */
f61c3c40 72#define UDB_DBFETCH 3 /* look up in local database */
83b7c7b1
EA
73#define UDB_FORWARD 4 /* forward to remote host */
74
75#define MAXUDBENT 10 /* maximum number of UDB entries */
76
7a059839
EA
77
78struct option
79{
80 char *name;
81 char *val;
82};
83\f/*
84** UDBEXPAND -- look up user in database and expand
85**
86** Parameters:
87** a -- address to expand.
88** sendq -- pointer to head of sendq to put the expansions in.
89**
90** Returns:
6ab0fb92
EA
91** EX_TEMPFAIL -- if something "odd" happened -- probably due
92** to accessing a file on an NFS server that is down.
93** EX_OK -- otherwise.
7a059839
EA
94**
95** Side Effects:
96** Modifies sendq.
97*/
98
99int UdbPort = 1616;
100int UdbTimeout = 10;
101
d2836270
EA
102struct udbent UdbEnts[MAXUDBENT + 1];
103int UdbSock = -1;
104bool UdbInitialized = FALSE;
83b7c7b1 105
6ab0fb92 106int
a4076aed 107udbexpand(a, sendq, e)
814c4102
EA
108 register ADDRESS *a;
109 ADDRESS **sendq;
a4076aed 110 register ENVELOPE *e;
814c4102
EA
111{
112 int i;
113 register char *p;
814c4102
EA
114 DBT key;
115 DBT info;
83b7c7b1
EA
116 bool breakout;
117 register struct udbent *up;
dac6c90e
EA
118 int keylen;
119 char keybuf[128];
814c4102
EA
120 char buf[8192];
121
122 if (tTd(28, 1))
123 printf("expand(%s)\n", a->q_paddr);
124
125 /* make certain we are supposed to send to this address */
d27b0e11 126 if (bitset(QDONTSEND, a->q_flags))
6ab0fb92 127 return EX_OK;
a4076aed 128 e->e_to = a->q_paddr;
814c4102 129
83b7c7b1 130 /* on first call, locate the database */
f61c3c40 131 if (!UdbInitialized)
814c4102 132 {
6ab0fb92 133 extern int _udbx_init();
dac6c90e 134
6ab0fb92
EA
135 if (_udbx_init() == EX_TEMPFAIL)
136 return EX_TEMPFAIL;
dac6c90e 137 }
83b7c7b1 138
d27b0e11
EA
139 /* short circuit the process if no chance of a match */
140 if (UdbSpec == NULL || UdbSpec[0] == '\0')
6ab0fb92 141 return EX_OK;
d27b0e11 142
dac6c90e
EA
143 /* if name is too long, assume it won't match */
144 if (strlen(a->q_user) > sizeof keybuf - 12)
6ab0fb92 145 return EX_OK;
83b7c7b1 146
dac6c90e
EA
147 /* if name begins with a colon, it indicates our metadata */
148 if (a->q_user[0] == ':')
6ab0fb92 149 return EX_OK;
dac6c90e
EA
150
151 /* build actual database key */
152 (void) strcpy(keybuf, a->q_user);
153 (void) strcat(keybuf, ":maildrop");
154 keylen = strlen(keybuf);
814c4102 155
83b7c7b1 156 breakout = FALSE;
dac6c90e 157 for (up = UdbEnts; !breakout; up++)
814c4102 158 {
83b7c7b1
EA
159 char *user;
160 struct timeval timeout;
161 fd_set fdset;
814c4102 162
83b7c7b1
EA
163 /*
164 ** Select action based on entry type.
165 **
166 ** On dropping out of this switch, "class" should
167 ** explain the type of the data, and "user" should
168 ** contain the user information.
169 */
814c4102 170
83b7c7b1
EA
171 switch (up->udb_type)
172 {
f61c3c40 173 case UDB_DBFETCH:
dac6c90e
EA
174 key.data = keybuf;
175 key.size = keylen;
176 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
6ab0fb92 177 if (i > 0 || info.size <= 0)
83b7c7b1 178 {
83b7c7b1 179 if (tTd(28, 2))
dac6c90e 180 printf("expand: no match on %s\n", keybuf);
83b7c7b1
EA
181 continue;
182 }
814c4102 183
256fe397
EA
184 while (i == 0 && key.size == keylen &&
185 bcmp(key.data, keybuf, keylen) == 0)
dac6c90e 186 {
256fe397 187 breakout = TRUE;
dac6c90e
EA
188 if (info.size < sizeof buf)
189 user = buf;
190 else
191 user = xalloc(info.size + 1);
192 bcopy(info.data, user, info.size);
193 user[info.size] = '\0';
194
195 message(Arpa_Info, "expanded to %s", user);
196 AliasLevel++;
a4076aed 197 sendtolist(user, a, sendq, e);
dac6c90e
EA
198 AliasLevel--;
199
200 if (user != buf)
201 free(user);
202
203 /* get the next record */
204 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
256fe397 205 }
6ab0fb92
EA
206 if (i < 0)
207 {
208 syserr("udbexpand: db-get stat %s");
209 return EX_TEMPFAIL;
210 }
83b7c7b1
EA
211 break;
212
213 case UDB_REMOTE:
84a5cc29
EA
214 /* not yet implemented */
215 continue;
83b7c7b1
EA
216
217 case UDB_FORWARD:
83b7c7b1
EA
218 i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
219 if (i < sizeof buf)
220 user = buf;
221 else
222 user = xalloc(i + 1);
223 (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost);
dac6c90e
EA
224 message(Arpa_Info, "expanded to %s", user);
225 AliasLevel++;
a4076aed 226 sendtolist(user, a, sendq, e);
dac6c90e
EA
227 AliasLevel--;
228 if (user != buf)
229 free(user);
230 breakout = TRUE;
83b7c7b1
EA
231 break;
232
233 case UDB_EOLIST:
234 breakout = TRUE;
235 continue;
236
237 default:
238 /* unknown entry type */
239 continue;
240 }
dac6c90e 241 }
6ab0fb92 242 return EX_OK;
dac6c90e 243}
f61c3c40
EA
244\f/*
245** UDBSENDER -- return canonical external name of sender, given local name
246**
247** Parameters:
248** sender -- the name of the sender on the local machine.
249**
250** Returns:
251** The external name for this sender, if derivable from the
252** database.
253** NULL -- if nothing is changed from the database.
254**
255** Side Effects:
256** none.
257*/
258
259char *
260udbsender(sender)
261 char *sender;
262{
263 register char *p;
264 register struct udbent *up;
265 int i;
266 int keylen;
267 DBT key, info;
268 char keybuf[128];
269
270 if (tTd(28, 1))
271 printf("udbsender(%s)\n", sender);
272
273 if (!UdbInitialized)
274 {
275 if (_udbx_init() == EX_TEMPFAIL)
276 return NULL;
277 }
278
279 /* short circuit if no spec */
280 if (UdbSpec == NULL || UdbSpec[0] == '\0')
281 return NULL;
282
283 /* long names can never match and are a pain to deal with */
284 if (strlen(sender) > sizeof keybuf - 12)
285 return NULL;
286
287 /* names beginning with colons indicate metadata */
288 if (sender[0] == ':')
289 return NULL;
290
291 /* build database key */
292 (void) strcpy(keybuf, sender);
293 (void) strcat(keybuf, ":mailname");
294 keylen = strlen(keybuf);
295
296 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
297 {
298 /*
299 ** Select action based on entry type.
300 */
301
302 switch (up->udb_type)
303 {
304 case UDB_DBFETCH:
305 key.data = keybuf;
306 key.size = keylen;
307 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
308 if (i != 0 || info.size <= 0)
309 {
310 if (tTd(28, 2))
311 printf("udbsender: no match on %s\n",
312 keybuf);
313 continue;
314 }
315
316 p = xalloc(info.size + 1);
317 bcopy(info.data, p, info.size);
318 p[info.size] = '\0';
319 if (tTd(28, 1))
320 printf("udbsender ==> %s\n", p);
321 return p;
322 }
323 }
324
325 /*
326 ** Nothing yet. Search again for a default case. But only
327 ** use it if we also have a forward (:maildrop) pointer already
328 ** in the database.
329 */
330
331 /* build database key */
332 (void) strcpy(keybuf, sender);
333 (void) strcat(keybuf, ":maildrop");
334 keylen = strlen(keybuf);
335
336 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
337 {
338 switch (up->udb_type)
339 {
340 case UDB_DBFETCH:
341 /* get the default case for this database */
342 if (up->udb_default == NULL)
343 {
344 key.data = ":default:mailname";
345 key.size = strlen(key.data);
346 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
347 if (i != 0 || info.size <= 0)
348 {
349 /* no default case */
350 up->udb_default = "";
351 continue;
352 }
353
354 /* save the default case */
355 up->udb_default = xalloc(info.size + 1);
356 bcopy(info.data, up->udb_default, info.size);
357 up->udb_default[info.size] = '\0';
358 }
359 else if (up->udb_default[0] == '\0')
360 continue;
361
362 /* we have a default case -- verify user:maildrop */
363 key.data = keybuf;
364 key.size = keylen;
365 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
366 if (i != 0 || info.size <= 0)
367 {
368 /* nope -- no aliasing for this user */
369 continue;
370 }
371
372 /* they exist -- build the actual address */
373 p = xalloc(strlen(sender) + strlen(up->udb_default) + 2);
374 (void) strcpy(p, sender);
375 (void) strcat(p, "@");
376 (void) strcat(p, up->udb_default);
377 if (tTd(28, 1))
378 printf("udbsender ==> %s\n", p);
379 return p;
380 }
381 }
382
383 /* still nothing.... too bad */
384 return NULL;
385}
386\f/*
387** _UDBX_INIT -- parse the UDB specification, opening any valid entries.
388**
389** Parameters:
390** none.
391**
392** Returns:
393** EX_TEMPFAIL -- if it appeared it couldn't get hold of a
394** database due to a host being down or some similar
395** (recoverable) situation.
396** EX_OK -- otherwise.
397**
398** Side Effects:
399** Fills in the UdbEnts structure from UdbSpec.
400*/
83b7c7b1 401
7a059839
EA
402#define MAXUDBOPTS 27
403
d2836270 404int
dac6c90e
EA
405_udbx_init()
406{
407 register char *p;
408 int i;
409 register struct udbent *up;
410 char buf[8192];
83b7c7b1 411
f61c3c40
EA
412 if (UdbInitialized)
413 return EX_OK;
414
fe993526
EA
415# ifdef UDB_DEFAULT_SPEC
416 if (UdbSpec == NULL)
417 UdbSpec = UDB_DEFAULT_SPEC;
418# endif
419
dac6c90e
EA
420 p = UdbSpec;
421 up = UdbEnts;
b375cb3b 422 while (p != NULL)
dac6c90e
EA
423 {
424 char *spec;
425 auto int rcode;
7a059839 426 int nopts;
dac6c90e
EA
427 int nmx;
428 register struct hostent *h;
429 char *mxhosts[MAXMXHOSTS + 1];
7a059839 430 struct option opts[MAXUDBOPTS + 1];
dac6c90e
EA
431
432 while (*p == ' ' || *p == '\t' || *p == ',')
433 p++;
434 if (*p == '\0')
435 break;
436 spec = p;
f3d8f6d6 437 p = strchr(p, ',');
93f1f3e4 438 if (p != NULL)
dac6c90e 439 *p++ = '\0';
7a059839
EA
440
441 /* extract options */
442 nopts = _udb_parsespec(spec, opts, MAXUDBOPTS);
443
444 /*
445 ** Decode database specification.
446 **
447 ** In the sendmail tradition, the leading character
448 ** defines the semantics of the rest of the entry.
449 **
450 ** +hostname -- send a datagram to the udb server
451 ** on host "hostname" asking for the
452 ** home mail server for this user.
453 ** *hostname -- similar to +hostname, except that the
454 ** hostname is searched as an MX record;
455 ** resulting hosts are searched as for
456 ** +mxhostname. If no MX host is found,
457 ** this is the same as +hostname.
458 ** @hostname -- forward email to the indicated host.
459 ** This should be the last in the list,
460 ** since it always matches the input.
461 ** /dbname -- search the named database on the local
462 ** host using the Berkeley db package.
463 */
464
dac6c90e 465 switch (*spec)
83b7c7b1 466 {
dac6c90e 467 case '+': /* search remote database */
7a059839
EA
468 case '*': /* search remote database (expand MX) */
469 if (*spec == '*')
dac6c90e 470 {
7a059839
EA
471 nmx = getmxrr(spec + 1, mxhosts, "", &rcode);
472 if (tTd(28, 16))
473 {
474 int i;
475
476 printf("getmxrr(%s): %d", spec + 1, nmx);
477 for (i = 0; i <= nmx; i++)
478 printf(" %s", mxhosts[i]);
479 printf("\n");
480 }
dac6c90e 481 }
7a059839 482 else
dac6c90e 483 {
7a059839
EA
484 nmx = 1;
485 mxhosts[0] = spec + 1;
dac6c90e 486 }
7a059839 487
dac6c90e
EA
488 for (i = 0; i < nmx; i++)
489 {
490 h = gethostbyname(mxhosts[i]);
491 if (h == NULL)
492 continue;
493 up->udb_type = UDB_REMOTE;
494 up->udb_addr.sin_family = h->h_addrtype;
dac6c90e
EA
495 bcopy(h->h_addr_list[0],
496 (char *) &up->udb_addr.sin_addr,
497 h->h_length);
498 up->udb_addr.sin_port = UdbPort;
499 up->udb_timeout = UdbTimeout;
500 up++;
501 }
502
503 /* set up a datagram socket */
504 if (UdbSock < 0)
505 {
506 UdbSock = socket(AF_INET, SOCK_DGRAM, 0);
507 (void) fcntl(UdbSock, F_SETFD, 1);
508 }
509 break;
510
511 case '@': /* forward to remote host */
512 up->udb_type = UDB_FORWARD;
513 up->udb_fwdhost = spec + 1;
514 up++;
515 break;
516
517 case '/': /* look up remote name */
834f8378 518 up->udb_dbname = spec;
6ab0fb92 519 errno = 0;
dac6c90e
EA
520 up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL);
521 if (up->udb_dbp == NULL)
6ab0fb92
EA
522 {
523 if (errno != ENOENT && errno != EACCES)
f61c3c40
EA
524 {
525 up->udb_type = UDB_EOLIST;
526 goto tempfail;
527 }
dac6c90e 528 break;
6ab0fb92 529 }
f61c3c40 530 up->udb_type = UDB_DBFETCH;
dac6c90e
EA
531 up++;
532 break;
83b7c7b1 533 }
dac6c90e
EA
534 }
535 up->udb_type = UDB_EOLIST;
83b7c7b1 536
dac6c90e
EA
537 if (tTd(28, 4))
538 {
f61c3c40 539 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
83b7c7b1 540 {
dac6c90e
EA
541 switch (up->udb_type)
542 {
dac6c90e
EA
543 case UDB_REMOTE:
544 printf("REMOTE: addr %s, timeo %d\n",
545 inet_ntoa(up->udb_addr.sin_addr),
546 up->udb_timeout);
547 break;
548
f61c3c40
EA
549 case UDB_DBFETCH:
550 printf("FETCH: file %s\n",
256fe397 551 up->udb_dbname);
dac6c90e
EA
552 break;
553
554 case UDB_FORWARD:
555 printf("FORWARD: host %s\n",
556 up->udb_fwdhost);
557 break;
558
559 default:
560 printf("UNKNOWN\n");
561 break;
562 }
83b7c7b1 563 }
814c4102 564 }
f61c3c40
EA
565
566 UdbInitialized = TRUE;
847ac56a 567 errno = 0;
f61c3c40
EA
568 return EX_OK;
569
570 /*
571 ** On temporary failure, back out anything we've already done
572 */
573
574 tempfail:
575 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
576 {
577 if (up->udb_type == UDB_DBFETCH)
578 {
579 (*up->udb_dbp->close)(up->udb_dbp);
580 }
581 }
582 return EX_TEMPFAIL;
83b7c7b1 583}
814c4102 584
7a059839
EA
585int
586_udb_parsespec(udbspec, opt, maxopts)
587 char *udbspec;
588 struct option opt[];
589 int maxopts;
590{
591 register char *spec;
592 register char *spec_end;
593 register int optnum;
594
f3d8f6d6 595 spec_end = strchr(udbspec, ':');
7a059839
EA
596 for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
597 {
598 register char *p;
599
600 while (isspace(*spec))
601 spec++;
f3d8f6d6 602 spec_end = strchr(spec, ':');
7a059839
EA
603 if (spec_end != NULL)
604 *spec_end++ = '\0';
605
606 opt[optnum].name = spec;
607 opt[optnum].val = NULL;
f3d8f6d6 608 p = strchr(spec, '=');
7a059839
EA
609 if (p != NULL)
610 opt[optnum].val = ++p;
611 }
612 return optnum;
613}
614
83b7c7b1
EA
615#else /* not USERDB */
616
6ab0fb92 617int
a4076aed 618udbexpand(a, sendq, e)
83b7c7b1
EA
619 ADDRESS *a;
620 ADDRESS **sendq;
a4076aed 621 ENVELOPE *e;
83b7c7b1 622{
6ab0fb92 623 return EX_OK;
814c4102
EA
624}
625
626#endif /* USERDB */