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