xref: /csrg-svn/usr.sbin/sendmail/src/udb.c (revision 51360)
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.3 (Berkeley) 10/11/91 (with USERDB)";
12 #else
13 static char sccsid [] = "@(#)udb.c	5.3 (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 struct udbent
42 {
43 	char	*udb_spec;		/* string version of spec */
44 	int	udb_type;		/* type of entry */
45 	union
46 	{
47 		/* type UE_REMOTE -- do remote call for lookup */
48 		struct
49 		{
50 			int		_udb_addrlen;	/* length of addr */
51 			struct sockaddr_in _udb_addr;	/* address */
52 			int		_udb_timeout;	/* timeout */
53 		} udb_remote;
54 #define udb_addrlen	udb_u.udb_remote._udb_addrlen
55 #define udb_addr	udb_u.udb_remote._udb_addr
56 #define udb_timeout	udb_u.udb_remote._udb_timeout
57 
58 		/* type UE_FORWARD -- forward message to remote */
59 		struct
60 		{
61 			char	*_udb_fwdhost;	/* name of forward host */
62 		} udb_forward;
63 #define udb_fwdhost	udb_u.udb_forward._udb_fwdhost
64 
65 		/* type UE_LOOKUP -- lookup in local database */
66 		struct
67 		{
68 			char	*_udb_dbname;	/* pathname of database */
69 			DB	*_udb_dbp;	/* open database ptr */
70 		} udb_lookup;
71 #define udb_dbname	udb_u.udb_lookup._udb_dbname
72 #define udb_dbp		udb_u.udb_lookup._udb_dbp
73 	} udb_u;
74 };
75 
76 #define UDB_EOLIST	0	/* end of list */
77 #define UDB_SKIP	1	/* skip this entry */
78 #define UDB_REMOTE	2	/* look up in remote database */
79 #define UDB_LOOKUP	3	/* look up in local database */
80 #define UDB_FORWARD	4	/* forward to remote host */
81 
82 #define MAXUDBENT	10	/* maximum number of UDB entries */
83 
84 
85 void
86 udbexpand(a, sendq)
87 	register ADDRESS *a;
88 	ADDRESS **sendq;
89 {
90 	int i;
91 	register char *p;
92 	auto char *class;
93 	auto char *list;
94 	DBT key;
95 	DBT info;
96 	register char *bp;
97 	static bool firstcall = TRUE;
98 	static int udbsock = -1;
99 	bool breakout;
100 	register struct udbent *up;
101 	struct udbent udbents[MAXUDBENT + 1];
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 		firstcall = FALSE;
117 		p = UdbSpec;
118 		up = udbents;
119 		for (;;)
120 		{
121 			char *spec;
122 			auto int rcode;
123 			int nmx;
124 			char *mxhosts[MAXMXHOSTS + 1];
125 
126 			while (*p == ' ' || *p == '\t' || *p == ',')
127 				p++;
128 			if (*p == '\0')
129 				break;
130 			spec = p;
131 			p = index(p, ',');
132 			if (*p != '\0')
133 				*p++ = '\0';
134 			switch (*spec)
135 			{
136 			  case '*':	/* search remote database */
137 				expand("\001j", buf, &buf[sizeof(buf) - 1], CurEnv);
138 				nmx = getmxrr(spec + 1, mxhosts, buf, &rcode);
139 				for (i = 0; i < nmx; i++)
140 				{
141 					register struct hostent *h;
142 
143 					h = gethostbyname(mxhosts[i]);
144 					if (h == NULL)
145 						continue;
146 					up->udb_type = UDB_REMOTE;
147 					up->udb_addr.sin_family = h->h_addrtype;
148 					up->udb_addrlen = h->h_length;
149 					bcopy(h->h_addr_list[0],
150 					      (char *) &up->udb_addr.sin_addr,
151 					      h->h_length);
152 					up++;
153 				}
154 
155 				/* set up a datagram socket */
156 				if (udbsock < 0)
157 				{
158 					udbsock = socket(AF_INET, SOCK_DGRAM, 0);
159 					(void) fcntl(udbsock, F_SETFD, 1);
160 				}
161 				break;
162 
163 			  case '@':	/* forward to remote host */
164 				up->udb_type = UDB_FORWARD;
165 				up->udb_fwdhost = spec + 1;
166 				up++;
167 				break;
168 
169 			  case '/':	/* look up remote name */
170 				up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL);
171 				if (up->udb_dbp == NULL)
172 					break;
173 				up->udb_type = UDB_LOOKUP;
174 				up++;
175 				break;
176 			}
177 		}
178 		up->udb_type = UDB_EOLIST;
179 	}
180 
181 	breakout = FALSE;
182 	for (up = udbents; !breakout; up++)
183 	{
184 		char *user;
185 		struct timeval timeout;
186 		fd_set fdset;
187 
188 		/*
189 		**  Select action based on entry type.
190 		**
191 		**	On dropping out of this switch, "class" should
192 		**	explain the type of the data, and "user" should
193 		**	contain the user information.
194 		*/
195 
196 		switch (up->udb_type)
197 		{
198 		  case UDB_LOOKUP:
199 			key.data = a->q_user;
200 			key.size = strlen(key.data);
201 			i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0);
202 			if (i != 0 || info.size <= 0)
203 			{
204 				if (i < 0)
205 					syserr("udbexpand: db-get stat %s");
206 				if (tTd(28, 2))
207 					printf("expand: no match on %s\n", key.data);
208 				continue;
209 			}
210 
211 			/* extract the class (first string) and data (second string) */
212 			class = info.data;
213 			i = strlen((char *) info.data) + 1;
214 			p = (char *) info.data + i;
215 			i = info.size - i;
216 
217 			/* use internal buffer if it will fit; otherwise malloc */
218 			if (i < sizeof buf)
219 				user = buf;
220 			else
221 				user = xalloc(i + 1);
222 			bcopy(p, user, i);
223 			user[i] = '\0';
224 			break;
225 
226 		  case UDB_REMOTE:
227 			if (sendto(udbsock, a->q_user, strlen(a->q_user), 0,
228 				   (struct sockaddr *) &up->udb_addr,
229 				   up->udb_addrlen) < 0)
230 			{
231 				continue;
232 			}
233 			timeout.tv_sec = up->udb_timeout / 10;
234 			timeout.tv_usec = (up->udb_timeout % 10) * 100000;
235 			do
236 			{
237 				FD_ZERO(&fdset);
238 				FD_SET(udbsock, &fdset);
239 				i = select(FD_SETSIZE, &fdset, NULL, NULL, &timeout);
240 			} while (i > 0 && !FD_ISSET(udbsock, &fdset));
241 			if (i <= 0)
242 				continue;
243 			i = recvfrom(udbsock, buf, sizeof buf - 1, 0, NULL, NULL);
244 			if (i < 0)
245 				continue;
246 			class = buf;
247 			user = &buf[strlen(buf)];
248 			buf[i] = '\0';
249 			break;
250 
251 		  case UDB_FORWARD:
252 			class = "forward";
253 			i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1;
254 			if (i < sizeof buf)
255 				user = buf;
256 			else
257 				user = xalloc(i + 1);
258 			(void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost);
259 			break;
260 
261 		  case UDB_EOLIST:
262 			breakout = TRUE;
263 			continue;
264 
265 		  default:
266 			/* unknown entry type */
267 			continue;
268 		}
269 
270 		if (tTd(28, 1))
271 			printf("Class %s: %s\n", class, user);
272 
273 		/* do special processing based on class */
274 		if (strcmp(class, "user") == 0 || strcmp(class, "forward") == 0)
275 		{
276 			message(Arpa_Info, "expanded to (%s) %s", class, user);
277 			AliasLevel++;
278 			sendtolist(user, a, sendq);
279 			AliasLevel--;
280 			breakout = TRUE;
281 		}
282 
283 		/* free memory if we allocated it */
284 		if (up->udb_type == UDB_FORWARD || up->udb_type == UDB_LOOKUP)
285 		{
286 			if (user != buf)
287 				free(user);
288 		}
289 	}
290 }
291 
292 #else /* not USERDB */
293 
294 void
295 udbexpand(a, sendq)
296 	ADDRESS *a;
297 	ADDRESS **sendq;
298 {
299 	return;
300 }
301 
302 #endif /* USERDB */
303