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*51363Seric static char sccsid [] = "@(#)udb.c 5.5 (Berkeley) 10/13/91 (with USERDB)"; 1251360Seric #else 13*51363Seric static char sccsid [] = "@(#)udb.c 5.5 (Berkeley) 10/13/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 /* 28*51363Seric ** UDBEXPAND.C -- interface between sendmail and Berkeley User Data Base. 2950581Seric ** 30*51363Seric ** This depends on the 4.4BSD db package. 3150581Seric */ 3250581Seric 3351362Seric 3451360Seric struct udbent 3551360Seric { 3651360Seric char *udb_spec; /* string version of spec */ 3751360Seric int udb_type; /* type of entry */ 3851360Seric union 3951360Seric { 4051360Seric /* type UE_REMOTE -- do remote call for lookup */ 4151360Seric struct 4251360Seric { 4351360Seric struct sockaddr_in _udb_addr; /* address */ 4451360Seric int _udb_timeout; /* timeout */ 4551360Seric } udb_remote; 4651360Seric #define udb_addr udb_u.udb_remote._udb_addr 4751360Seric #define udb_timeout udb_u.udb_remote._udb_timeout 4851360Seric 4951360Seric /* type UE_FORWARD -- forward message to remote */ 5051360Seric struct 5151360Seric { 5251360Seric char *_udb_fwdhost; /* name of forward host */ 5351360Seric } udb_forward; 5451360Seric #define udb_fwdhost udb_u.udb_forward._udb_fwdhost 5551360Seric 5651360Seric /* type UE_LOOKUP -- lookup in local database */ 5751360Seric struct 5851360Seric { 5951360Seric char *_udb_dbname; /* pathname of database */ 6051360Seric DB *_udb_dbp; /* open database ptr */ 6151360Seric } udb_lookup; 6251360Seric #define udb_dbname udb_u.udb_lookup._udb_dbname 6351360Seric #define udb_dbp udb_u.udb_lookup._udb_dbp 6451360Seric } udb_u; 6551360Seric }; 6651360Seric 6751360Seric #define UDB_EOLIST 0 /* end of list */ 6851360Seric #define UDB_SKIP 1 /* skip this entry */ 6951360Seric #define UDB_REMOTE 2 /* look up in remote database */ 7051360Seric #define UDB_LOOKUP 3 /* look up in local database */ 7151360Seric #define UDB_FORWARD 4 /* forward to remote host */ 7251360Seric 7351360Seric #define MAXUDBENT 10 /* maximum number of UDB entries */ 7451360Seric 75*51363Seric 76*51363Seric struct option 77*51363Seric { 78*51363Seric char *name; 79*51363Seric char *val; 80*51363Seric }; 81*51363Seric /* 82*51363Seric ** UDBEXPAND -- look up user in database and expand 83*51363Seric ** 84*51363Seric ** Parameters: 85*51363Seric ** a -- address to expand. 86*51363Seric ** sendq -- pointer to head of sendq to put the expansions in. 87*51363Seric ** 88*51363Seric ** Returns: 89*51363Seric ** none. 90*51363Seric ** 91*51363Seric ** Side Effects: 92*51363Seric ** Modifies sendq. 93*51363Seric */ 94*51363Seric 95*51363Seric int UdbPort = 1616; 96*51363Seric int UdbTimeout = 10; 97*51363Seric 9851362Seric struct udbent UdbEnts[MAXUDBENT + 1]; 9951362Seric int UdbSock = -1; 10051360Seric 10150581Seric void 10250581Seric udbexpand(a, sendq) 10350581Seric register ADDRESS *a; 10450581Seric ADDRESS **sendq; 10550581Seric { 10650581Seric int i; 10750581Seric register char *p; 10850581Seric DBT key; 10950581Seric DBT info; 11051360Seric static bool firstcall = TRUE; 11151360Seric bool breakout; 11251360Seric register struct udbent *up; 11351362Seric int keylen; 11451362Seric char keybuf[128]; 11550581Seric char buf[8192]; 11650581Seric 11750581Seric if (tTd(28, 1)) 11850581Seric printf("expand(%s)\n", a->q_paddr); 11950581Seric 12050581Seric /* make certain we are supposed to send to this address */ 12151360Seric if (bitset(QDONTSEND, a->q_flags) || 12251360Seric UdbSpec == NULL || UdbSpec[0] == '\0') 12350581Seric return; 12450581Seric CurEnv->e_to = a->q_paddr; 12550581Seric 12651360Seric /* on first call, locate the database */ 12751360Seric if (firstcall) 12850581Seric { 12951362Seric extern void _udbx_init(); 13051362Seric 13151362Seric _udbx_init(); 13251360Seric firstcall = FALSE; 13351362Seric } 13450581Seric 13551362Seric /* if name is too long, assume it won't match */ 13651362Seric if (strlen(a->q_user) > sizeof keybuf - 12) 13751362Seric return; 13851360Seric 13951362Seric /* if name begins with a colon, it indicates our metadata */ 14051362Seric if (a->q_user[0] == ':') 14151362Seric return; 14251360Seric 14351362Seric /* build actual database key */ 14451362Seric (void) strcpy(keybuf, a->q_user); 14551362Seric (void) strcat(keybuf, ":maildrop"); 14651362Seric keylen = strlen(keybuf); 14751360Seric 14851360Seric breakout = FALSE; 14951362Seric for (up = UdbEnts; !breakout; up++) 15050581Seric { 15151360Seric char *user; 15251360Seric struct timeval timeout; 15351360Seric fd_set fdset; 15450581Seric 15551360Seric /* 15651360Seric ** Select action based on entry type. 15751360Seric ** 15851360Seric ** On dropping out of this switch, "class" should 15951360Seric ** explain the type of the data, and "user" should 16051360Seric ** contain the user information. 16151360Seric */ 16250581Seric 16351360Seric switch (up->udb_type) 16451360Seric { 16551360Seric case UDB_LOOKUP: 16651362Seric key.data = keybuf; 16751362Seric key.size = keylen; 16851362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 16951360Seric if (i != 0 || info.size <= 0) 17051360Seric { 17151360Seric if (i < 0) 17251360Seric syserr("udbexpand: db-get stat %s"); 17351360Seric if (tTd(28, 2)) 17451362Seric printf("expand: no match on %s\n", keybuf); 17551360Seric continue; 17651360Seric } 17750581Seric 17851362Seric /* there is at least one match -- start processing */ 17951362Seric breakout = TRUE; 18051362Seric do 18151362Seric { 18251362Seric if (info.size < sizeof buf) 18351362Seric user = buf; 18451362Seric else 18551362Seric user = xalloc(info.size + 1); 18651362Seric bcopy(info.data, user, info.size); 18751362Seric user[info.size] = '\0'; 18850581Seric 18951362Seric message(Arpa_Info, "expanded to %s", user); 19051362Seric AliasLevel++; 19151362Seric sendtolist(user, a, sendq); 19251362Seric AliasLevel--; 19351362Seric 19451362Seric if (user != buf) 19551362Seric free(user); 19651362Seric 19751362Seric /* get the next record */ 19851362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 19951362Seric } while (i == 0 && key.size == keylen && 20051362Seric bcmp(key.data, keybuf, keylen) == 0); 20151360Seric break; 20251360Seric 20351360Seric case UDB_REMOTE: 20451362Seric if (sendto(UdbSock, keybuf, keylen, 0, 20551360Seric (struct sockaddr *) &up->udb_addr, 20651362Seric sizeof up->udb_addr) < 0) 20751360Seric { 20851360Seric continue; 20951360Seric } 21051360Seric timeout.tv_sec = up->udb_timeout / 10; 21151360Seric timeout.tv_usec = (up->udb_timeout % 10) * 100000; 21251360Seric do 21351360Seric { 21451360Seric FD_ZERO(&fdset); 21551362Seric FD_SET(UdbSock, &fdset); 21651360Seric i = select(FD_SETSIZE, &fdset, NULL, NULL, &timeout); 21751362Seric } while (i > 0 && !FD_ISSET(UdbSock, &fdset)); 21851360Seric if (i <= 0) 21951360Seric continue; 22051362Seric i = recvfrom(UdbSock, buf, sizeof buf - 1, 0, NULL, NULL); 22151360Seric if (i < 0) 22251360Seric continue; 22351362Seric if (buf[0] != ' ' && buf[0] != '-') 22451362Seric continue; 22551362Seric breakout = TRUE; 22651362Seric while (buf[0] == ' ' || buf[0] == '-') 22751362Seric { 22851362Seric user = &buf[1]; 22951362Seric buf[i] = '\0'; 23051362Seric message(Arpa_Info, "expanded to %s", user); 23151362Seric AliasLevel++; 23251362Seric sendtolist(user, a, sendq); 23351362Seric AliasLevel--; 23451362Seric 23551362Seric /* try for next record */ 23651362Seric if (buf[0] == ' ') 23751362Seric break; 23851362Seric i = recvfrom(UdbSock, buf, sizeof buf - 1, 0, NULL, NULL); 23951362Seric if (i < 0) 24051362Seric break; 24151362Seric } 24251360Seric break; 24351360Seric 24451360Seric case UDB_FORWARD: 24551360Seric i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 24651360Seric if (i < sizeof buf) 24751360Seric user = buf; 24851360Seric else 24951360Seric user = xalloc(i + 1); 25051360Seric (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); 25151362Seric message(Arpa_Info, "expanded to %s", user); 25251362Seric AliasLevel++; 25351362Seric sendtolist(user, a, sendq); 25451362Seric AliasLevel--; 25551362Seric if (user != buf) 25651362Seric free(user); 25751362Seric breakout = TRUE; 25851360Seric break; 25951360Seric 26051360Seric case UDB_EOLIST: 26151360Seric breakout = TRUE; 26251360Seric continue; 26351360Seric 26451360Seric default: 26551360Seric /* unknown entry type */ 26651360Seric continue; 26751360Seric } 26851362Seric } 26951362Seric } 27051360Seric 271*51363Seric #define MAXUDBOPTS 27 272*51363Seric 27351362Seric void 27451362Seric _udbx_init() 27551362Seric { 27651362Seric register char *p; 27751362Seric int i; 27851362Seric register struct udbent *up; 27951362Seric char buf[8192]; 28051360Seric 28151362Seric p = UdbSpec; 28251362Seric up = UdbEnts; 28351362Seric for (;;) 28451362Seric { 28551362Seric char *spec; 28651362Seric auto int rcode; 287*51363Seric int nopts; 28851362Seric int nmx; 28951362Seric register struct hostent *h; 29051362Seric char *mxhosts[MAXMXHOSTS + 1]; 291*51363Seric struct option opts[MAXUDBOPTS + 1]; 29251362Seric 29351362Seric while (*p == ' ' || *p == '\t' || *p == ',') 29451362Seric p++; 29551362Seric if (*p == '\0') 29651362Seric break; 29751362Seric spec = p; 29851362Seric p = index(p, ','); 29951362Seric if (*p != '\0') 30051362Seric *p++ = '\0'; 301*51363Seric 302*51363Seric /* extract options */ 303*51363Seric nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); 304*51363Seric 305*51363Seric /* 306*51363Seric ** Decode database specification. 307*51363Seric ** 308*51363Seric ** In the sendmail tradition, the leading character 309*51363Seric ** defines the semantics of the rest of the entry. 310*51363Seric ** 311*51363Seric ** +hostname -- send a datagram to the udb server 312*51363Seric ** on host "hostname" asking for the 313*51363Seric ** home mail server for this user. 314*51363Seric ** *hostname -- similar to +hostname, except that the 315*51363Seric ** hostname is searched as an MX record; 316*51363Seric ** resulting hosts are searched as for 317*51363Seric ** +mxhostname. If no MX host is found, 318*51363Seric ** this is the same as +hostname. 319*51363Seric ** @hostname -- forward email to the indicated host. 320*51363Seric ** This should be the last in the list, 321*51363Seric ** since it always matches the input. 322*51363Seric ** /dbname -- search the named database on the local 323*51363Seric ** host using the Berkeley db package. 324*51363Seric */ 325*51363Seric 32651362Seric switch (*spec) 32751360Seric { 32851362Seric case '+': /* search remote database */ 329*51363Seric case '*': /* search remote database (expand MX) */ 330*51363Seric if (*spec == '*') 331*51363Seric { 332*51363Seric nmx = getmxrr(spec + 1, mxhosts, "", &rcode); 333*51363Seric if (tTd(28, 16)) 334*51363Seric { 335*51363Seric int i; 33651362Seric 337*51363Seric printf("getmxrr(%s): %d", spec + 1, nmx); 338*51363Seric for (i = 0; i <= nmx; i++) 339*51363Seric printf(" %s", mxhosts[i]); 340*51363Seric printf("\n"); 341*51363Seric } 342*51363Seric } 343*51363Seric else 34451362Seric { 345*51363Seric nmx = 1; 346*51363Seric mxhosts[0] = spec + 1; 34751362Seric } 34851362Seric 34951362Seric for (i = 0; i < nmx; i++) 35051362Seric { 35151362Seric h = gethostbyname(mxhosts[i]); 35251362Seric if (h == NULL) 35351362Seric continue; 35451362Seric up->udb_type = UDB_REMOTE; 35551362Seric up->udb_addr.sin_family = h->h_addrtype; 35651362Seric up->udb_addr.sin_len = h->h_length; 35751362Seric bcopy(h->h_addr_list[0], 35851362Seric (char *) &up->udb_addr.sin_addr, 35951362Seric h->h_length); 36051362Seric up->udb_addr.sin_port = UdbPort; 36151362Seric up->udb_timeout = UdbTimeout; 36251362Seric up++; 36351362Seric } 36451362Seric 36551362Seric /* set up a datagram socket */ 36651362Seric if (UdbSock < 0) 36751362Seric { 36851362Seric UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 36951362Seric (void) fcntl(UdbSock, F_SETFD, 1); 37051362Seric } 37151362Seric break; 37251362Seric 37351362Seric case '@': /* forward to remote host */ 37451362Seric up->udb_type = UDB_FORWARD; 37551362Seric up->udb_fwdhost = spec + 1; 37651362Seric up++; 37751362Seric break; 37851362Seric 37951362Seric case '/': /* look up remote name */ 38051362Seric up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); 38151362Seric if (up->udb_dbp == NULL) 38251362Seric break; 38351362Seric up->udb_type = UDB_LOOKUP; 38451362Seric up++; 38551362Seric break; 38651360Seric } 38751362Seric } 38851362Seric up->udb_type = UDB_EOLIST; 38951360Seric 39051362Seric if (tTd(28, 4)) 39151362Seric { 39251362Seric for (up = UdbEnts; ; up++) 39351360Seric { 39451362Seric switch (up->udb_type) 39551362Seric { 39651362Seric case UDB_EOLIST: 39751362Seric return; 39851362Seric 39951362Seric case UDB_REMOTE: 40051362Seric printf("REMOTE: addr %s, timeo %d\n", 40151362Seric inet_ntoa(up->udb_addr.sin_addr), 40251362Seric up->udb_timeout); 40351362Seric break; 40451362Seric 40551362Seric case UDB_LOOKUP: 40651362Seric printf("LOOKUP\n"); 40751362Seric break; 40851362Seric 40951362Seric case UDB_FORWARD: 41051362Seric printf("FORWARD: host %s\n", 41151362Seric up->udb_fwdhost); 41251362Seric break; 41351362Seric 41451362Seric default: 41551362Seric printf("UNKNOWN\n"); 41651362Seric break; 41751362Seric } 41851360Seric } 41950581Seric } 42051360Seric } 42150581Seric 422*51363Seric int 423*51363Seric _udb_parsespec(udbspec, opt, maxopts) 424*51363Seric char *udbspec; 425*51363Seric struct option opt[]; 426*51363Seric int maxopts; 427*51363Seric { 428*51363Seric register char *spec; 429*51363Seric register char *spec_end; 430*51363Seric register int optnum; 431*51363Seric 432*51363Seric spec_end = index(udbspec, ':'); 433*51363Seric for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 434*51363Seric { 435*51363Seric register char *p; 436*51363Seric 437*51363Seric while (isspace(*spec)) 438*51363Seric spec++; 439*51363Seric spec_end = index(spec, ':'); 440*51363Seric if (spec_end != NULL) 441*51363Seric *spec_end++ = '\0'; 442*51363Seric 443*51363Seric opt[optnum].name = spec; 444*51363Seric opt[optnum].val = NULL; 445*51363Seric p = index(spec, '='); 446*51363Seric if (p != NULL) 447*51363Seric opt[optnum].val = ++p; 448*51363Seric } 449*51363Seric return optnum; 450*51363Seric } 451*51363Seric 45251360Seric #else /* not USERDB */ 45351360Seric 45451360Seric void 45551360Seric udbexpand(a, sendq) 45651360Seric ADDRESS *a; 45751360Seric ADDRESS **sendq; 45851360Seric { 45951360Seric return; 46050581Seric } 46150581Seric 46250581Seric #endif /* USERDB */ 463