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