xref: /csrg-svn/usr.sbin/sendmail/src/udb.c (revision 51362)
150581Seric /*
250581Seric  * Copyright (c) 1983 Eric P. Allman
350581Seric  * Copyright (c) 1988 Regents of the University of California.
450581Seric  * All rights reserved.
550581Seric  *
650581Seric  * %sccs.include.redist.c%
750581Seric  */
850581Seric 
950581Seric #ifndef lint
1051360Seric #ifdef USERDB
11*51362Seric static char sccsid [] = "@(#)udb.c	5.4 (Berkeley) 10/11/91 (with USERDB)";
1251360Seric #else
13*51362Seric static char sccsid [] = "@(#)udb.c	5.4 (Berkeley) 10/11/91 (without USERDB)";
1450581Seric #endif
1551360Seric #endif
1650581Seric 
1750581Seric #include "sendmail.h"
1850581Seric 
1950581Seric #ifdef USERDB
2050581Seric 
2150581Seric #include <sys/file.h>
2251360Seric #include <sys/time.h>
2351360Seric #include <fcntl.h>
2451360Seric #include <netdb.h>
2550581Seric #include <db.h>
2650581Seric 
2750581Seric /*
2850581Seric **  UDBEXPAND -- look up user in database and expand
2950581Seric **
3050581Seric **	Parameters:
3150581Seric **		a -- address to expand.
3250581Seric **		sendq -- pointer to head of sendq to put the expansions in.
3350581Seric **
3450581Seric **	Returns:
3550581Seric **		none.
3650581Seric **
3750581Seric **	Side Effects:
3850581Seric **		Modifies sendq.
3950581Seric */
4050581Seric 
41*51362Seric int	UdbPort = 1616;
42*51362Seric int	UdbTimeout = 10;
43*51362Seric 
4451360Seric struct udbent
4551360Seric {
4651360Seric 	char	*udb_spec;		/* string version of spec */
4751360Seric 	int	udb_type;		/* type of entry */
4851360Seric 	union
4951360Seric 	{
5051360Seric 		/* type UE_REMOTE -- do remote call for lookup */
5151360Seric 		struct
5251360Seric 		{
5351360Seric 			struct sockaddr_in _udb_addr;	/* address */
5451360Seric 			int		_udb_timeout;	/* timeout */
5551360Seric 		} udb_remote;
5651360Seric #define udb_addr	udb_u.udb_remote._udb_addr
5751360Seric #define udb_timeout	udb_u.udb_remote._udb_timeout
5851360Seric 
5951360Seric 		/* type UE_FORWARD -- forward message to remote */
6051360Seric 		struct
6151360Seric 		{
6251360Seric 			char	*_udb_fwdhost;	/* name of forward host */
6351360Seric 		} udb_forward;
6451360Seric #define udb_fwdhost	udb_u.udb_forward._udb_fwdhost
6551360Seric 
6651360Seric 		/* type UE_LOOKUP -- lookup in local database */
6751360Seric 		struct
6851360Seric 		{
6951360Seric 			char	*_udb_dbname;	/* pathname of database */
7051360Seric 			DB	*_udb_dbp;	/* open database ptr */
7151360Seric 		} udb_lookup;
7251360Seric #define udb_dbname	udb_u.udb_lookup._udb_dbname
7351360Seric #define udb_dbp		udb_u.udb_lookup._udb_dbp
7451360Seric 	} udb_u;
7551360Seric };
7651360Seric 
7751360Seric #define UDB_EOLIST	0	/* end of list */
7851360Seric #define UDB_SKIP	1	/* skip this entry */
7951360Seric #define UDB_REMOTE	2	/* look up in remote database */
8051360Seric #define UDB_LOOKUP	3	/* look up in local database */
8151360Seric #define UDB_FORWARD	4	/* forward to remote host */
8251360Seric 
8351360Seric #define MAXUDBENT	10	/* maximum number of UDB entries */
8451360Seric 
85*51362Seric struct udbent	UdbEnts[MAXUDBENT + 1];
86*51362Seric int		UdbSock = -1;
8751360Seric 
8850581Seric void
8950581Seric udbexpand(a, sendq)
9050581Seric 	register ADDRESS *a;
9150581Seric 	ADDRESS **sendq;
9250581Seric {
9350581Seric 	int i;
9450581Seric 	register char *p;
9550581Seric 	DBT key;
9650581Seric 	DBT info;
9751360Seric 	static bool firstcall = TRUE;
9851360Seric 	bool breakout;
9951360Seric 	register struct udbent *up;
100*51362Seric 	int keylen;
101*51362Seric 	char keybuf[128];
10250581Seric 	char buf[8192];
10350581Seric 
10450581Seric 	if (tTd(28, 1))
10550581Seric 		printf("expand(%s)\n", a->q_paddr);
10650581Seric 
10750581Seric 	/* make certain we are supposed to send to this address */
10851360Seric 	if (bitset(QDONTSEND, a->q_flags) ||
10951360Seric 	    UdbSpec == NULL || UdbSpec[0] == '\0')
11050581Seric 		return;
11150581Seric 	CurEnv->e_to = a->q_paddr;
11250581Seric 
11351360Seric 	/* on first call, locate the database */
11451360Seric 	if (firstcall)
11550581Seric 	{
116*51362Seric 		extern void _udbx_init();
117*51362Seric 
118*51362Seric 		_udbx_init();
11951360Seric 		firstcall = FALSE;
120*51362Seric 	}
12150581Seric 
122*51362Seric 	/* if name is too long, assume it won't match */
123*51362Seric 	if (strlen(a->q_user) > sizeof keybuf - 12)
124*51362Seric 		return;
12551360Seric 
126*51362Seric 	/* if name begins with a colon, it indicates our metadata */
127*51362Seric 	if (a->q_user[0] == ':')
128*51362Seric 		return;
12951360Seric 
130*51362Seric 	/* build actual database key */
131*51362Seric 	(void) strcpy(keybuf, a->q_user);
132*51362Seric 	(void) strcat(keybuf, ":maildrop");
133*51362Seric 	keylen = strlen(keybuf);
13451360Seric 
13551360Seric 	breakout = FALSE;
136*51362Seric 	for (up = UdbEnts; !breakout; up++)
13750581Seric 	{
13851360Seric 		char *user;
13951360Seric 		struct timeval timeout;
14051360Seric 		fd_set fdset;
14150581Seric 
14251360Seric 		/*
14351360Seric 		**  Select action based on entry type.
14451360Seric 		**
14551360Seric 		**	On dropping out of this switch, "class" should
14651360Seric 		**	explain the type of the data, and "user" should
14751360Seric 		**	contain the user information.
14851360Seric 		*/
14950581Seric 
15051360Seric 		switch (up->udb_type)
15151360Seric 		{
15251360Seric 		  case UDB_LOOKUP:
153*51362Seric 			key.data = keybuf;
154*51362Seric 			key.size = keylen;
155*51362Seric 			i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR);
15651360Seric 			if (i != 0 || info.size <= 0)
15751360Seric 			{
15851360Seric 				if (i < 0)
15951360Seric 					syserr("udbexpand: db-get stat %s");
16051360Seric 				if (tTd(28, 2))
161*51362Seric 					printf("expand: no match on %s\n", keybuf);
16251360Seric 				continue;
16351360Seric 			}
16450581Seric 
165*51362Seric 			/* there is at least one match -- start processing */
166*51362Seric 			breakout = TRUE;
167*51362Seric 			do
168*51362Seric 			{
169*51362Seric 				if (info.size < sizeof buf)
170*51362Seric 					user = buf;
171*51362Seric 				else
172*51362Seric 					user = xalloc(info.size + 1);
173*51362Seric 				bcopy(info.data, user, info.size);
174*51362Seric 				user[info.size] = '\0';
17550581Seric 
176*51362Seric 				message(Arpa_Info, "expanded to %s", user);
177*51362Seric 				AliasLevel++;
178*51362Seric 				sendtolist(user, a, sendq);
179*51362Seric 				AliasLevel--;
180*51362Seric 
181*51362Seric 				if (user != buf)
182*51362Seric 					free(user);
183*51362Seric 
184*51362Seric 				/* get the next record */
185*51362Seric 				i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT);
186*51362Seric 			} while (i == 0 && key.size == keylen &&
187*51362Seric 					bcmp(key.data, keybuf, keylen) == 0);
18851360Seric 			break;
18951360Seric 
19051360Seric 		  case UDB_REMOTE:
191*51362Seric 			if (sendto(UdbSock, keybuf, keylen, 0,
19251360Seric 				   (struct sockaddr *) &up->udb_addr,
193*51362Seric 				   sizeof up->udb_addr) < 0)
19451360Seric 			{
19551360Seric 				continue;
19651360Seric 			}
19751360Seric 			timeout.tv_sec = up->udb_timeout / 10;
19851360Seric 			timeout.tv_usec = (up->udb_timeout % 10) * 100000;
19951360Seric 			do
20051360Seric 			{
20151360Seric 				FD_ZERO(&fdset);
202*51362Seric 				FD_SET(UdbSock, &fdset);
20351360Seric 				i = select(FD_SETSIZE, &fdset, NULL, NULL, &timeout);
204*51362Seric 			} while (i > 0 && !FD_ISSET(UdbSock, &fdset));
20551360Seric 			if (i <= 0)
20651360Seric 				continue;
207*51362Seric 			i = recvfrom(UdbSock, buf, sizeof buf - 1, 0, NULL, NULL);
20851360Seric 			if (i < 0)
20951360Seric 				continue;
210*51362Seric 			if (buf[0] != ' ' && buf[0] != '-')
211*51362Seric 				continue;
212*51362Seric 			breakout = TRUE;
213*51362Seric 			while (buf[0] == ' ' || buf[0] == '-')
214*51362Seric 			{
215*51362Seric 				user = &buf[1];
216*51362Seric 				buf[i] = '\0';
217*51362Seric 				message(Arpa_Info, "expanded to %s", user);
218*51362Seric 				AliasLevel++;
219*51362Seric 				sendtolist(user, a, sendq);
220*51362Seric 				AliasLevel--;
221*51362Seric 
222*51362Seric 				/* try for next record */
223*51362Seric 				if (buf[0] == ' ')
224*51362Seric 					break;
225*51362Seric 				i = recvfrom(UdbSock, buf, sizeof buf - 1, 0, NULL, NULL);
226*51362Seric 				if (i < 0)
227*51362Seric 					break;
228*51362Seric 			}
22951360Seric 			break;
23051360Seric 
23151360Seric 		  case UDB_FORWARD:
23251360Seric 			i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
23351360Seric 			if (i < sizeof buf)
23451360Seric 				user = buf;
23551360Seric 			else
23651360Seric 				user = xalloc(i + 1);
23751360Seric 			(void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost);
238*51362Seric 			message(Arpa_Info, "expanded to %s", user);
239*51362Seric 			AliasLevel++;
240*51362Seric 			sendtolist(user, a, sendq);
241*51362Seric 			AliasLevel--;
242*51362Seric 			if (user != buf)
243*51362Seric 				free(user);
244*51362Seric 			breakout = TRUE;
24551360Seric 			break;
24651360Seric 
24751360Seric 		  case UDB_EOLIST:
24851360Seric 			breakout = TRUE;
24951360Seric 			continue;
25051360Seric 
25151360Seric 		  default:
25251360Seric 			/* unknown entry type */
25351360Seric 			continue;
25451360Seric 		}
255*51362Seric 	}
256*51362Seric }
25751360Seric 
258*51362Seric void
259*51362Seric _udbx_init()
260*51362Seric {
261*51362Seric 	register char *p;
262*51362Seric 	int i;
263*51362Seric 	register struct udbent *up;
264*51362Seric 	char buf[8192];
26551360Seric 
266*51362Seric 	p = UdbSpec;
267*51362Seric 	up = UdbEnts;
268*51362Seric 	for (;;)
269*51362Seric 	{
270*51362Seric 		char *spec;
271*51362Seric 		auto int rcode;
272*51362Seric 		int nmx;
273*51362Seric 		register struct hostent *h;
274*51362Seric 		char *mxhosts[MAXMXHOSTS + 1];
275*51362Seric 
276*51362Seric 		while (*p == ' ' || *p == '\t' || *p == ',')
277*51362Seric 			p++;
278*51362Seric 		if (*p == '\0')
279*51362Seric 			break;
280*51362Seric 		spec = p;
281*51362Seric 		p = index(p, ',');
282*51362Seric 		if (*p != '\0')
283*51362Seric 			*p++ = '\0';
284*51362Seric 		switch (*spec)
28551360Seric 		{
286*51362Seric 		  case '+':	/* search remote database */
287*51362Seric 			h = gethostbyname(spec + 1);
288*51362Seric 			if (h == NULL)
289*51362Seric 				continue;
290*51362Seric 			up->udb_type = UDB_REMOTE;
291*51362Seric 			up->udb_addr.sin_family = h->h_addrtype;
292*51362Seric 			up->udb_addr.sin_len = h->h_length;
293*51362Seric 			bcopy(h->h_addr_list[0],
294*51362Seric 			      (char *) &up->udb_addr.sin_addr,
295*51362Seric 			      h->h_length);
296*51362Seric 			up->udb_addr.sin_port = UdbPort;
297*51362Seric 			up->udb_timeout = UdbTimeout;
298*51362Seric 			up++;
299*51362Seric 
300*51362Seric 			/* set up a datagram socket */
301*51362Seric 			if (UdbSock < 0)
302*51362Seric 			{
303*51362Seric 				UdbSock = socket(AF_INET, SOCK_DGRAM, 0);
304*51362Seric 				(void) fcntl(UdbSock, F_SETFD, 1);
305*51362Seric 			}
306*51362Seric 			break;
307*51362Seric 
308*51362Seric 		  case '*':	/* search remote database (expand MX) */
309*51362Seric 			nmx = getmxrr(spec + 1, mxhosts, "", &rcode);
310*51362Seric 			if (tTd(28, 16))
311*51362Seric 			{
312*51362Seric 				int i;
313*51362Seric 
314*51362Seric 				printf("getmxrr(%s): %d", spec + 1, nmx);
315*51362Seric 				for (i = 0; i <= nmx; i++)
316*51362Seric 					printf(" %s", mxhosts[i]);
317*51362Seric 				printf("\n");
318*51362Seric 			}
319*51362Seric 			for (i = 0; i < nmx; i++)
320*51362Seric 			{
321*51362Seric 				h = gethostbyname(mxhosts[i]);
322*51362Seric 				if (h == NULL)
323*51362Seric 					continue;
324*51362Seric 				up->udb_type = UDB_REMOTE;
325*51362Seric 				up->udb_addr.sin_family = h->h_addrtype;
326*51362Seric 				up->udb_addr.sin_len = h->h_length;
327*51362Seric 				bcopy(h->h_addr_list[0],
328*51362Seric 				      (char *) &up->udb_addr.sin_addr,
329*51362Seric 				      h->h_length);
330*51362Seric 				up->udb_addr.sin_port = UdbPort;
331*51362Seric 				up->udb_timeout = UdbTimeout;
332*51362Seric 				up++;
333*51362Seric 			}
334*51362Seric 
335*51362Seric 			/* set up a datagram socket */
336*51362Seric 			if (UdbSock < 0)
337*51362Seric 			{
338*51362Seric 				UdbSock = socket(AF_INET, SOCK_DGRAM, 0);
339*51362Seric 				(void) fcntl(UdbSock, F_SETFD, 1);
340*51362Seric 			}
341*51362Seric 			break;
342*51362Seric 
343*51362Seric 		  case '@':	/* forward to remote host */
344*51362Seric 			up->udb_type = UDB_FORWARD;
345*51362Seric 			up->udb_fwdhost = spec + 1;
346*51362Seric 			up++;
347*51362Seric 			break;
348*51362Seric 
349*51362Seric 		  case '/':	/* look up remote name */
350*51362Seric 			up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL);
351*51362Seric 			if (up->udb_dbp == NULL)
352*51362Seric 				break;
353*51362Seric 			up->udb_type = UDB_LOOKUP;
354*51362Seric 			up++;
355*51362Seric 			break;
35651360Seric 		}
357*51362Seric 	}
358*51362Seric 	up->udb_type = UDB_EOLIST;
35951360Seric 
360*51362Seric 	if (tTd(28, 4))
361*51362Seric 	{
362*51362Seric 		for (up = UdbEnts; ; up++)
36351360Seric 		{
364*51362Seric 			switch (up->udb_type)
365*51362Seric 			{
366*51362Seric 			  case UDB_EOLIST:
367*51362Seric 				return;
368*51362Seric 
369*51362Seric 			  case UDB_REMOTE:
370*51362Seric 				printf("REMOTE: addr %s, timeo %d\n",
371*51362Seric 					inet_ntoa(up->udb_addr.sin_addr),
372*51362Seric 					up->udb_timeout);
373*51362Seric 				break;
374*51362Seric 
375*51362Seric 			  case UDB_LOOKUP:
376*51362Seric 				printf("LOOKUP\n");
377*51362Seric 				break;
378*51362Seric 
379*51362Seric 			  case UDB_FORWARD:
380*51362Seric 				printf("FORWARD: host %s\n",
381*51362Seric 					up->udb_fwdhost);
382*51362Seric 				break;
383*51362Seric 
384*51362Seric 			  default:
385*51362Seric 				printf("UNKNOWN\n");
386*51362Seric 				break;
387*51362Seric 			}
38851360Seric 		}
38950581Seric 	}
39051360Seric }
39150581Seric 
39251360Seric #else /* not USERDB */
39351360Seric 
39451360Seric void
39551360Seric udbexpand(a, sendq)
39651360Seric 	ADDRESS *a;
39751360Seric 	ADDRESS **sendq;
39851360Seric {
39951360Seric 	return;
40050581Seric }
40150581Seric 
40250581Seric #endif /* USERDB */
403