This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.1'.
[unix-history] / usr.sbin / sendmail / src / udb.c
CommitLineData
6f14531a
RG
1/*
2 * Copyright (c) 1983 Eric P. Allman
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "sendmail.h"
36
37#ifndef lint
38#ifdef USERDB
c2714ef5 39static char sccsid [] = "@(#)udb.c 8.6 (Berkeley) 3/11/94 (with USERDB)";
6f14531a 40#else
c2714ef5 41static char sccsid [] = "@(#)udb.c 8.6 (Berkeley) 3/11/94 (without USERDB)";
6f14531a
RG
42#endif
43#endif
44
45#ifdef USERDB
46
6f14531a
RG
47#include <errno.h>
48#include <netdb.h>
49#include <db.h>
50
51/*
52** UDB.C -- interface between sendmail and Berkeley User Data Base.
53**
54** This depends on the 4.4BSD db package.
55*/
56
57
58struct udbent
59{
60 char *udb_spec; /* string version of spec */
61 int udb_type; /* type of entry */
62 char *udb_default; /* default host for outgoing mail */
63 union
64 {
65 /* type UE_REMOTE -- do remote call for lookup */
66 struct
67 {
68 struct sockaddr_in _udb_addr; /* address */
69 int _udb_timeout; /* timeout */
70 } udb_remote;
71#define udb_addr udb_u.udb_remote._udb_addr
72#define udb_timeout udb_u.udb_remote._udb_timeout
73
74 /* type UE_FORWARD -- forward message to remote */
75 struct
76 {
77 char *_udb_fwdhost; /* name of forward host */
78 } udb_forward;
79#define udb_fwdhost udb_u.udb_forward._udb_fwdhost
80
81 /* type UE_FETCH -- lookup in local database */
82 struct
83 {
84 char *_udb_dbname; /* pathname of database */
85 DB *_udb_dbp; /* open database ptr */
86 } udb_lookup;
87#define udb_dbname udb_u.udb_lookup._udb_dbname
88#define udb_dbp udb_u.udb_lookup._udb_dbp
89 } udb_u;
90};
91
92#define UDB_EOLIST 0 /* end of list */
93#define UDB_SKIP 1 /* skip this entry */
94#define UDB_REMOTE 2 /* look up in remote database */
95#define UDB_DBFETCH 3 /* look up in local database */
96#define UDB_FORWARD 4 /* forward to remote host */
97
98#define MAXUDBENT 10 /* maximum number of UDB entries */
99
100
101struct option
102{
103 char *name;
104 char *val;
105};
106\f/*
107** UDBEXPAND -- look up user in database and expand
108**
109** Parameters:
110** a -- address to expand.
111** sendq -- pointer to head of sendq to put the expansions in.
112**
113** Returns:
114** EX_TEMPFAIL -- if something "odd" happened -- probably due
115** to accessing a file on an NFS server that is down.
116** EX_OK -- otherwise.
117**
118** Side Effects:
119** Modifies sendq.
120*/
121
122int UdbPort = 1616;
123int UdbTimeout = 10;
124
125struct udbent UdbEnts[MAXUDBENT + 1];
126int UdbSock = -1;
127bool UdbInitialized = FALSE;
128
129int
130udbexpand(a, sendq, e)
131 register ADDRESS *a;
132 ADDRESS **sendq;
133 register ENVELOPE *e;
134{
135 int i;
136 register char *p;
137 DBT key;
138 DBT info;
139 bool breakout;
140 register struct udbent *up;
141 int keylen;
142 int naddrs;
143 char keybuf[MAXKEY];
144 char buf[BUFSIZ];
145
146 if (tTd(28, 1))
147 printf("udbexpand(%s)\n", a->q_paddr);
148
149 /* make certain we are supposed to send to this address */
150 if (bitset(QDONTSEND|QVERIFIED, a->q_flags))
151 return EX_OK;
152 e->e_to = a->q_paddr;
153
154 /* on first call, locate the database */
155 if (!UdbInitialized)
156 {
157 extern int _udbx_init();
158
159 if (_udbx_init() == EX_TEMPFAIL)
160 return EX_TEMPFAIL;
161 }
162
163 /* short circuit the process if no chance of a match */
164 if (UdbSpec == NULL || UdbSpec[0] == '\0')
165 return EX_OK;
166
167 /* if name is too long, assume it won't match */
168 if (strlen(a->q_user) > sizeof keybuf - 12)
169 return EX_OK;
170
171 /* if name begins with a colon, it indicates our metadata */
172 if (a->q_user[0] == ':')
173 return EX_OK;
174
175 /* build actual database key */
176 (void) strcpy(keybuf, a->q_user);
177 (void) strcat(keybuf, ":maildrop");
178 keylen = strlen(keybuf);
179
180 breakout = FALSE;
181 for (up = UdbEnts; !breakout; up++)
182 {
183 char *user;
184
185 /*
186 ** Select action based on entry type.
187 **
188 ** On dropping out of this switch, "class" should
189 ** explain the type of the data, and "user" should
190 ** contain the user information.
191 */
192
193 switch (up->udb_type)
194 {
195 case UDB_DBFETCH:
196 key.data = keybuf;
197 key.size = keylen;
198 if (tTd(28, 80))
d747e748
JH
199 printf("udbexpand: trying %s (%d)\n",
200 keybuf, keylen);
6f14531a
RG
201 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
202 if (i > 0 || info.size <= 0)
203 {
204 if (tTd(28, 2))
d747e748
JH
205 printf("udbexpand: no match on %s (%d)\n",
206 keybuf, keylen);
6f14531a
RG
207 continue;
208 }
209 if (tTd(28, 80))
210 printf("udbexpand: match %.*s: %.*s\n",
211 key.size, key.data, info.size, info.data);
212
213 naddrs = 0;
214 a->q_flags &= ~QSELFREF;
215 while (i == 0 && key.size == keylen &&
216 bcmp(key.data, keybuf, keylen) == 0)
217 {
218 if (bitset(EF_VRFYONLY, e->e_flags))
219 {
220 a->q_flags |= QVERIFIED;
221 e->e_nrcpts++;
222 return EX_OK;
223 }
224
225 breakout = TRUE;
226 if (info.size < sizeof buf)
227 user = buf;
228 else
229 user = xalloc(info.size + 1);
230 bcopy(info.data, user, info.size);
231 user[info.size] = '\0';
232
233 message("expanded to %s", user);
234#ifdef LOG
235 if (LogLevel >= 10)
236 syslog(LOG_INFO, "%s: expand %s => %s",
237 e->e_id, e->e_to, user);
238#endif
239 AliasLevel++;
240 naddrs += sendtolist(user, a, sendq, e);
241 AliasLevel--;
242
243 if (user != buf)
244 free(user);
245
246 /* get the next record */
247 i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
248 }
249
250 /* if nothing ever matched, try next database */
251 if (!breakout)
252 continue;
253
254 if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
255 {
256 if (tTd(28, 5))
257 {
258 printf("udbexpand: QDONTSEND ");
259 printaddr(a, FALSE);
260 }
261 a->q_flags |= QDONTSEND;
262 }
263 if (i < 0)
264 {
265 syserr("udbexpand: db-get %.*s stat %d",
266 key.size, key.data, i);
267 return EX_TEMPFAIL;
268 }
269
270 /*
271 ** If this address has a -request address, reflect
272 ** it into the envelope.
273 */
274
275 (void) strcpy(keybuf, a->q_user);
276 (void) strcat(keybuf, ":mailsender");
277 keylen = strlen(keybuf);
278 key.data = keybuf;
279 key.size = keylen;
280 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
281 if (i != 0 || info.size <= 0)
282 break;
283 a->q_owner = xalloc(info.size + 1);
284 bcopy(info.data, a->q_owner, info.size);
285 a->q_owner[info.size] = '\0';
286 break;
287
288 case UDB_REMOTE:
289 /* not yet implemented */
290 continue;
291
292 case UDB_FORWARD:
293 if (bitset(EF_VRFYONLY, e->e_flags))
294 return EX_OK;
295 i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
296 if (i < sizeof buf)
297 user = buf;
298 else
299 user = xalloc(i + 1);
300 (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost);
301 message("expanded to %s", user);
302 a->q_flags &= ~QSELFREF;
303 AliasLevel++;
304 naddrs = sendtolist(user, a, sendq, e);
305 AliasLevel--;
306 if (naddrs > 0 && !bitset(QSELFREF, a->q_flags))
307 {
308 if (tTd(28, 5))
309 {
310 printf("udbexpand: QDONTSEND ");
311 printaddr(a, FALSE);
312 }
313 a->q_flags |= QDONTSEND;
314 }
315 if (user != buf)
316 free(user);
317 breakout = TRUE;
318 break;
319
320 case UDB_EOLIST:
321 breakout = TRUE;
322 continue;
323
324 default:
325 /* unknown entry type */
326 continue;
327 }
328 }
329 return EX_OK;
330}
331\f/*
332** UDBSENDER -- return canonical external name of sender, given local name
333**
334** Parameters:
335** sender -- the name of the sender on the local machine.
336**
337** Returns:
338** The external name for this sender, if derivable from the
339** database.
340** NULL -- if nothing is changed from the database.
341**
342** Side Effects:
343** none.
344*/
345
346char *
347udbsender(sender)
348 char *sender;
d747e748
JH
349{
350 extern char *udbmatch();
351
352 return udbmatch(sender, "mailname");
353}
354
355
356char *
357udbmatch(user, field)
358 char *user;
359 char *field;
6f14531a
RG
360{
361 register char *p;
362 register struct udbent *up;
363 int i;
364 int keylen;
365 DBT key, info;
366 char keybuf[MAXKEY];
367
368 if (tTd(28, 1))
d747e748 369 printf("udbmatch(%s, %s)\n", user, field);
6f14531a
RG
370
371 if (!UdbInitialized)
372 {
373 if (_udbx_init() == EX_TEMPFAIL)
374 return NULL;
375 }
376
377 /* short circuit if no spec */
378 if (UdbSpec == NULL || UdbSpec[0] == '\0')
379 return NULL;
380
381 /* long names can never match and are a pain to deal with */
d747e748 382 if ((strlen(user) + strlen(field)) > sizeof keybuf - 4)
6f14531a
RG
383 return NULL;
384
385 /* names beginning with colons indicate metadata */
d747e748 386 if (user[0] == ':')
6f14531a
RG
387 return NULL;
388
389 /* build database key */
d747e748
JH
390 (void) strcpy(keybuf, user);
391 (void) strcat(keybuf, ":");
392 (void) strcat(keybuf, field);
6f14531a
RG
393 keylen = strlen(keybuf);
394
395 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
396 {
397 /*
398 ** Select action based on entry type.
399 */
400
401 switch (up->udb_type)
402 {
403 case UDB_DBFETCH:
404 key.data = keybuf;
405 key.size = keylen;
406 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
407 if (i != 0 || info.size <= 0)
408 {
409 if (tTd(28, 2))
d747e748
JH
410 printf("udbmatch: no match on %s (%d)\n",
411 keybuf, keylen);
6f14531a
RG
412 continue;
413 }
414
415 p = xalloc(info.size + 1);
416 bcopy(info.data, p, info.size);
417 p[info.size] = '\0';
418 if (tTd(28, 1))
d747e748 419 printf("udbmatch ==> %s\n", p);
6f14531a
RG
420 return p;
421 }
422 }
423
d747e748
JH
424 if (strcmp(field, "mailname") != 0)
425 return NULL;
426
6f14531a
RG
427 /*
428 ** Nothing yet. Search again for a default case. But only
429 ** use it if we also have a forward (:maildrop) pointer already
430 ** in the database.
431 */
432
433 /* build database key */
d747e748 434 (void) strcpy(keybuf, user);
6f14531a
RG
435 (void) strcat(keybuf, ":maildrop");
436 keylen = strlen(keybuf);
437
438 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
439 {
440 switch (up->udb_type)
441 {
442 case UDB_DBFETCH:
443 /* get the default case for this database */
444 if (up->udb_default == NULL)
445 {
446 key.data = ":default:mailname";
447 key.size = strlen(key.data);
448 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
449 if (i != 0 || info.size <= 0)
450 {
451 /* no default case */
452 up->udb_default = "";
453 continue;
454 }
455
456 /* save the default case */
457 up->udb_default = xalloc(info.size + 1);
458 bcopy(info.data, up->udb_default, info.size);
459 up->udb_default[info.size] = '\0';
460 }
461 else if (up->udb_default[0] == '\0')
462 continue;
463
464 /* we have a default case -- verify user:maildrop */
465 key.data = keybuf;
466 key.size = keylen;
467 i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
468 if (i != 0 || info.size <= 0)
469 {
470 /* nope -- no aliasing for this user */
471 continue;
472 }
473
474 /* they exist -- build the actual address */
d747e748
JH
475 p = xalloc(strlen(user) + strlen(up->udb_default) + 2);
476 (void) strcpy(p, user);
6f14531a
RG
477 (void) strcat(p, "@");
478 (void) strcat(p, up->udb_default);
479 if (tTd(28, 1))
d747e748 480 printf("udbmatch ==> %s\n", p);
6f14531a
RG
481 return p;
482 }
483 }
484
485 /* still nothing.... too bad */
486 return NULL;
487}
488\f/*
489** _UDBX_INIT -- parse the UDB specification, opening any valid entries.
490**
491** Parameters:
492** none.
493**
494** Returns:
495** EX_TEMPFAIL -- if it appeared it couldn't get hold of a
496** database due to a host being down or some similar
497** (recoverable) situation.
498** EX_OK -- otherwise.
499**
500** Side Effects:
501** Fills in the UdbEnts structure from UdbSpec.
502*/
503
504#define MAXUDBOPTS 27
505
506int
507_udbx_init()
508{
509 register char *p;
510 int i;
511 register struct udbent *up;
512 char buf[BUFSIZ];
513
514 if (UdbInitialized)
515 return EX_OK;
516
517# ifdef UDB_DEFAULT_SPEC
518 if (UdbSpec == NULL)
519 UdbSpec = UDB_DEFAULT_SPEC;
520# endif
521
522 p = UdbSpec;
523 up = UdbEnts;
524 while (p != NULL)
525 {
526 char *spec;
527 auto int rcode;
528 int nopts;
529 int nmx;
530 register struct hostent *h;
531 char *mxhosts[MAXMXHOSTS + 1];
532 struct option opts[MAXUDBOPTS + 1];
533
534 while (*p == ' ' || *p == '\t' || *p == ',')
535 p++;
536 if (*p == '\0')
537 break;
538 spec = p;
539 p = strchr(p, ',');
540 if (p != NULL)
541 *p++ = '\0';
542
543 /* extract options */
544 nopts = _udb_parsespec(spec, opts, MAXUDBOPTS);
545
546 /*
547 ** Decode database specification.
548 **
549 ** In the sendmail tradition, the leading character
550 ** defines the semantics of the rest of the entry.
551 **
552 ** +hostname -- send a datagram to the udb server
553 ** on host "hostname" asking for the
554 ** home mail server for this user.
555 ** *hostname -- similar to +hostname, except that the
556 ** hostname is searched as an MX record;
557 ** resulting hosts are searched as for
558 ** +mxhostname. If no MX host is found,
559 ** this is the same as +hostname.
560 ** @hostname -- forward email to the indicated host.
561 ** This should be the last in the list,
562 ** since it always matches the input.
563 ** /dbname -- search the named database on the local
564 ** host using the Berkeley db package.
565 */
566
567 switch (*spec)
568 {
569 case '+': /* search remote database */
570 case '*': /* search remote database (expand MX) */
571 if (*spec == '*')
572 {
c2714ef5 573#if NAMED_BIND
6f14531a
RG
574 nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode);
575#else
576 mxhosts[0] = spec + 1;
577 nmx = 1;
578 rcode = 0;
579#endif
580 if (tTd(28, 16))
581 {
582 int i;
583
584 printf("getmxrr(%s): %d", spec + 1, nmx);
585 for (i = 0; i <= nmx; i++)
586 printf(" %s", mxhosts[i]);
587 printf("\n");
588 }
589 }
590 else
591 {
592 nmx = 1;
593 mxhosts[0] = spec + 1;
594 }
595
596 for (i = 0; i < nmx; i++)
597 {
598 h = gethostbyname(mxhosts[i]);
599 if (h == NULL)
600 continue;
601 up->udb_type = UDB_REMOTE;
602 up->udb_addr.sin_family = h->h_addrtype;
603 bcopy(h->h_addr_list[0],
604 (char *) &up->udb_addr.sin_addr,
042b8fbf 605 sizeof up->udb_addr.sin_addr);
6f14531a
RG
606 up->udb_addr.sin_port = UdbPort;
607 up->udb_timeout = UdbTimeout;
608 up++;
609 }
610
611 /* set up a datagram socket */
612 if (UdbSock < 0)
613 {
614 UdbSock = socket(AF_INET, SOCK_DGRAM, 0);
615 (void) fcntl(UdbSock, F_SETFD, 1);
616 }
617 break;
618
619 case '@': /* forward to remote host */
620 up->udb_type = UDB_FORWARD;
621 up->udb_fwdhost = spec + 1;
622 up++;
623 break;
624
625 case '/': /* look up remote name */
626 up->udb_dbname = spec;
627 errno = 0;
628 up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL);
629 if (up->udb_dbp == NULL)
630 {
631 if (errno != ENOENT && errno != EACCES)
632 {
633#ifdef LOG
634 if (LogLevel > 2)
635 syslog(LOG_ERR, "dbopen(%s): %s",
636 spec, errstring(errno));
637#endif
638 up->udb_type = UDB_EOLIST;
639 goto tempfail;
640 }
641 break;
642 }
643 up->udb_type = UDB_DBFETCH;
644 up++;
645 break;
646 }
647 }
648 up->udb_type = UDB_EOLIST;
649
650 if (tTd(28, 4))
651 {
652 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
653 {
654 switch (up->udb_type)
655 {
656 case UDB_REMOTE:
657 printf("REMOTE: addr %s, timeo %d\n",
658 anynet_ntoa((SOCKADDR *) &up->udb_addr),
659 up->udb_timeout);
660 break;
661
662 case UDB_DBFETCH:
663 printf("FETCH: file %s\n",
664 up->udb_dbname);
665 break;
666
667 case UDB_FORWARD:
668 printf("FORWARD: host %s\n",
669 up->udb_fwdhost);
670 break;
671
672 default:
673 printf("UNKNOWN\n");
674 break;
675 }
676 }
677 }
678
679 UdbInitialized = TRUE;
680 errno = 0;
681 return EX_OK;
682
683 /*
684 ** On temporary failure, back out anything we've already done
685 */
686
687 tempfail:
688 for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++)
689 {
690 if (up->udb_type == UDB_DBFETCH)
691 {
692 (*up->udb_dbp->close)(up->udb_dbp);
693 }
694 }
695 return EX_TEMPFAIL;
696}
697
698int
699_udb_parsespec(udbspec, opt, maxopts)
700 char *udbspec;
701 struct option opt[];
702 int maxopts;
703{
704 register char *spec;
705 register char *spec_end;
706 register int optnum;
707
708 spec_end = strchr(udbspec, ':');
709 for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++)
710 {
711 register char *p;
712
713 while (isascii(*spec) && isspace(*spec))
714 spec++;
715 spec_end = strchr(spec, ':');
716 if (spec_end != NULL)
717 *spec_end++ = '\0';
718
719 opt[optnum].name = spec;
720 opt[optnum].val = NULL;
721 p = strchr(spec, '=');
722 if (p != NULL)
723 opt[optnum].val = ++p;
724 }
725 return optnum;
726}
727
728#else /* not USERDB */
729
730int
731udbexpand(a, sendq, e)
732 ADDRESS *a;
733 ADDRESS **sendq;
734 ENVELOPE *e;
735{
736 return EX_OK;
737}
738
739#endif /* USERDB */