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*51923Seric static char sccsid [] = "@(#)udb.c 5.14 (Berkeley) 12/14/91 (with USERDB)"; 1251360Seric #else 13*51923Seric static char sccsid [] = "@(#)udb.c 5.14 (Berkeley) 12/14/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> 23*51923Seric #include <errno.h> 2451360Seric #include <fcntl.h> 2551360Seric #include <netdb.h> 2650581Seric #include <db.h> 2750581Seric 2850581Seric /* 2951363Seric ** UDBEXPAND.C -- interface between sendmail and Berkeley User Data Base. 3050581Seric ** 3151363Seric ** This depends on the 4.4BSD db package. 3250581Seric */ 3350581Seric 3451362Seric 3551360Seric struct udbent 3651360Seric { 3751360Seric char *udb_spec; /* string version of spec */ 3851360Seric int udb_type; /* type of entry */ 3951360Seric union 4051360Seric { 4151360Seric /* type UE_REMOTE -- do remote call for lookup */ 4251360Seric struct 4351360Seric { 4451360Seric struct sockaddr_in _udb_addr; /* address */ 4551360Seric int _udb_timeout; /* timeout */ 4651360Seric } udb_remote; 4751360Seric #define udb_addr udb_u.udb_remote._udb_addr 4851360Seric #define udb_timeout udb_u.udb_remote._udb_timeout 4951360Seric 5051360Seric /* type UE_FORWARD -- forward message to remote */ 5151360Seric struct 5251360Seric { 5351360Seric char *_udb_fwdhost; /* name of forward host */ 5451360Seric } udb_forward; 5551360Seric #define udb_fwdhost udb_u.udb_forward._udb_fwdhost 5651360Seric 5751360Seric /* type UE_LOOKUP -- lookup in local database */ 5851360Seric struct 5951360Seric { 6051360Seric char *_udb_dbname; /* pathname of database */ 6151360Seric DB *_udb_dbp; /* open database ptr */ 6251360Seric } udb_lookup; 6351360Seric #define udb_dbname udb_u.udb_lookup._udb_dbname 6451360Seric #define udb_dbp udb_u.udb_lookup._udb_dbp 6551360Seric } udb_u; 6651360Seric }; 6751360Seric 6851360Seric #define UDB_EOLIST 0 /* end of list */ 6951360Seric #define UDB_SKIP 1 /* skip this entry */ 7051360Seric #define UDB_REMOTE 2 /* look up in remote database */ 7151360Seric #define UDB_LOOKUP 3 /* look up in local database */ 7251360Seric #define UDB_FORWARD 4 /* forward to remote host */ 7351360Seric 7451360Seric #define MAXUDBENT 10 /* maximum number of UDB entries */ 7551360Seric 7651363Seric 7751363Seric struct option 7851363Seric { 7951363Seric char *name; 8051363Seric char *val; 8151363Seric }; 8251363Seric /* 8351363Seric ** UDBEXPAND -- look up user in database and expand 8451363Seric ** 8551363Seric ** Parameters: 8651363Seric ** a -- address to expand. 8751363Seric ** sendq -- pointer to head of sendq to put the expansions in. 8851363Seric ** 8951363Seric ** Returns: 90*51923Seric ** EX_TEMPFAIL -- if something "odd" happened -- probably due 91*51923Seric ** to accessing a file on an NFS server that is down. 92*51923Seric ** EX_OK -- otherwise. 9351363Seric ** 9451363Seric ** Side Effects: 9551363Seric ** Modifies sendq. 9651363Seric */ 9751363Seric 9851363Seric int UdbPort = 1616; 9951363Seric int UdbTimeout = 10; 10051363Seric 10151362Seric struct udbent UdbEnts[MAXUDBENT + 1]; 10251362Seric int UdbSock = -1; 10351360Seric 104*51923Seric int 10550581Seric udbexpand(a, sendq) 10650581Seric register ADDRESS *a; 10750581Seric ADDRESS **sendq; 10850581Seric { 10950581Seric int i; 11050581Seric register char *p; 11150581Seric DBT key; 11250581Seric DBT info; 11351360Seric static bool firstcall = TRUE; 11451360Seric bool breakout; 11551360Seric register struct udbent *up; 11651362Seric int keylen; 11751362Seric char keybuf[128]; 11850581Seric char buf[8192]; 11950581Seric 12050581Seric if (tTd(28, 1)) 12150581Seric printf("expand(%s)\n", a->q_paddr); 12250581Seric 12350581Seric /* make certain we are supposed to send to this address */ 12451909Seric if (bitset(QDONTSEND, a->q_flags)) 125*51923Seric return EX_OK; 12650581Seric CurEnv->e_to = a->q_paddr; 12750581Seric 12851360Seric /* on first call, locate the database */ 12951360Seric if (firstcall) 13050581Seric { 131*51923Seric extern int _udbx_init(); 13251362Seric 133*51923Seric if (_udbx_init() == EX_TEMPFAIL) 134*51923Seric return EX_TEMPFAIL; 13551360Seric firstcall = FALSE; 13651362Seric } 13750581Seric 13851909Seric /* short circuit the process if no chance of a match */ 13951909Seric if (UdbSpec == NULL || UdbSpec[0] == '\0') 140*51923Seric return EX_OK; 14151909Seric 14251362Seric /* if name is too long, assume it won't match */ 14351362Seric if (strlen(a->q_user) > sizeof keybuf - 12) 144*51923Seric return EX_OK; 14551360Seric 14651362Seric /* if name begins with a colon, it indicates our metadata */ 14751362Seric if (a->q_user[0] == ':') 148*51923Seric return EX_OK; 14951360Seric 15051362Seric /* build actual database key */ 15151362Seric (void) strcpy(keybuf, a->q_user); 15251362Seric (void) strcat(keybuf, ":maildrop"); 15351362Seric keylen = strlen(keybuf); 15451360Seric 15551360Seric breakout = FALSE; 15651362Seric for (up = UdbEnts; !breakout; up++) 15750581Seric { 15851360Seric char *user; 15951360Seric struct timeval timeout; 16051360Seric fd_set fdset; 16150581Seric 16251360Seric /* 16351360Seric ** Select action based on entry type. 16451360Seric ** 16551360Seric ** On dropping out of this switch, "class" should 16651360Seric ** explain the type of the data, and "user" should 16751360Seric ** contain the user information. 16851360Seric */ 16950581Seric 17051360Seric switch (up->udb_type) 17151360Seric { 17251360Seric case UDB_LOOKUP: 17351362Seric key.data = keybuf; 17451362Seric key.size = keylen; 17551362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 176*51923Seric if (i > 0 || info.size <= 0) 17751360Seric { 17851360Seric if (tTd(28, 2)) 17951362Seric printf("expand: no match on %s\n", keybuf); 18051360Seric continue; 18151360Seric } 18250581Seric 18351830Seric while (i == 0 && key.size == keylen && 18451830Seric bcmp(key.data, keybuf, keylen) == 0) 18551362Seric { 18651830Seric breakout = TRUE; 18751362Seric if (info.size < sizeof buf) 18851362Seric user = buf; 18951362Seric else 19051362Seric user = xalloc(info.size + 1); 19151362Seric bcopy(info.data, user, info.size); 19251362Seric user[info.size] = '\0'; 19350581Seric 19451362Seric message(Arpa_Info, "expanded to %s", user); 19551362Seric AliasLevel++; 19651362Seric sendtolist(user, a, sendq); 19751362Seric AliasLevel--; 19851362Seric 19951362Seric if (user != buf) 20051362Seric free(user); 20151362Seric 20251362Seric /* get the next record */ 20351362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 20451830Seric } 205*51923Seric if (i < 0) 206*51923Seric { 207*51923Seric syserr("udbexpand: db-get stat %s"); 208*51923Seric return EX_TEMPFAIL; 209*51923Seric } 21051360Seric break; 21151360Seric 21251360Seric case UDB_REMOTE: 21351741Seric /* not yet implemented */ 21451741Seric continue; 21551362Seric 21651360Seric case UDB_FORWARD: 21751360Seric i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 21851360Seric if (i < sizeof buf) 21951360Seric user = buf; 22051360Seric else 22151360Seric user = xalloc(i + 1); 22251360Seric (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); 22351362Seric message(Arpa_Info, "expanded to %s", user); 22451362Seric AliasLevel++; 22551362Seric sendtolist(user, a, sendq); 22651362Seric AliasLevel--; 22751362Seric if (user != buf) 22851362Seric free(user); 22951362Seric breakout = TRUE; 23051360Seric break; 23151360Seric 23251360Seric case UDB_EOLIST: 23351360Seric breakout = TRUE; 23451360Seric continue; 23551360Seric 23651360Seric default: 23751360Seric /* unknown entry type */ 23851360Seric continue; 23951360Seric } 24051362Seric } 241*51923Seric return EX_OK; 24251362Seric } 24351360Seric 24451363Seric #define MAXUDBOPTS 27 24551363Seric 246*51923Seric int 24751362Seric _udbx_init() 24851362Seric { 24951362Seric register char *p; 25051362Seric int i; 25151362Seric register struct udbent *up; 25251362Seric char buf[8192]; 25351360Seric 25451908Seric # ifdef UDB_DEFAULT_SPEC 25551908Seric if (UdbSpec == NULL) 25651908Seric UdbSpec = UDB_DEFAULT_SPEC; 25751908Seric # endif 25851908Seric 25951362Seric p = UdbSpec; 26051362Seric up = UdbEnts; 26151762Seric while (p != NULL) 26251362Seric { 26351362Seric char *spec; 26451362Seric auto int rcode; 26551363Seric int nopts; 26651362Seric int nmx; 26751362Seric register struct hostent *h; 26851362Seric char *mxhosts[MAXMXHOSTS + 1]; 26951363Seric struct option opts[MAXUDBOPTS + 1]; 27051362Seric 27151362Seric while (*p == ' ' || *p == '\t' || *p == ',') 27251362Seric p++; 27351362Seric if (*p == '\0') 27451362Seric break; 27551362Seric spec = p; 27651362Seric p = index(p, ','); 27751761Seric if (p != NULL) 27851362Seric *p++ = '\0'; 27951363Seric 28051363Seric /* extract options */ 28151363Seric nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); 28251363Seric 28351363Seric /* 28451363Seric ** Decode database specification. 28551363Seric ** 28651363Seric ** In the sendmail tradition, the leading character 28751363Seric ** defines the semantics of the rest of the entry. 28851363Seric ** 28951363Seric ** +hostname -- send a datagram to the udb server 29051363Seric ** on host "hostname" asking for the 29151363Seric ** home mail server for this user. 29251363Seric ** *hostname -- similar to +hostname, except that the 29351363Seric ** hostname is searched as an MX record; 29451363Seric ** resulting hosts are searched as for 29551363Seric ** +mxhostname. If no MX host is found, 29651363Seric ** this is the same as +hostname. 29751363Seric ** @hostname -- forward email to the indicated host. 29851363Seric ** This should be the last in the list, 29951363Seric ** since it always matches the input. 30051363Seric ** /dbname -- search the named database on the local 30151363Seric ** host using the Berkeley db package. 30251363Seric */ 30351363Seric 30451362Seric switch (*spec) 30551360Seric { 30651362Seric case '+': /* search remote database */ 30751363Seric case '*': /* search remote database (expand MX) */ 30851363Seric if (*spec == '*') 30951363Seric { 31051363Seric nmx = getmxrr(spec + 1, mxhosts, "", &rcode); 31151363Seric if (tTd(28, 16)) 31251363Seric { 31351363Seric int i; 31451362Seric 31551363Seric printf("getmxrr(%s): %d", spec + 1, nmx); 31651363Seric for (i = 0; i <= nmx; i++) 31751363Seric printf(" %s", mxhosts[i]); 31851363Seric printf("\n"); 31951363Seric } 32051363Seric } 32151363Seric else 32251362Seric { 32351363Seric nmx = 1; 32451363Seric mxhosts[0] = spec + 1; 32551362Seric } 32651362Seric 32751362Seric for (i = 0; i < nmx; i++) 32851362Seric { 32951362Seric h = gethostbyname(mxhosts[i]); 33051362Seric if (h == NULL) 33151362Seric continue; 33251362Seric up->udb_type = UDB_REMOTE; 33351362Seric up->udb_addr.sin_family = h->h_addrtype; 33451362Seric bcopy(h->h_addr_list[0], 33551362Seric (char *) &up->udb_addr.sin_addr, 33651362Seric h->h_length); 33751362Seric up->udb_addr.sin_port = UdbPort; 33851362Seric up->udb_timeout = UdbTimeout; 33951362Seric up++; 34051362Seric } 34151362Seric 34251362Seric /* set up a datagram socket */ 34351362Seric if (UdbSock < 0) 34451362Seric { 34551362Seric UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 34651362Seric (void) fcntl(UdbSock, F_SETFD, 1); 34751362Seric } 34851362Seric break; 34951362Seric 35051362Seric case '@': /* forward to remote host */ 35151362Seric up->udb_type = UDB_FORWARD; 35251362Seric up->udb_fwdhost = spec + 1; 35351362Seric up++; 35451362Seric break; 35551362Seric 35651362Seric case '/': /* look up remote name */ 35751831Seric up->udb_dbname = spec; 358*51923Seric errno = 0; 35951362Seric up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); 36051362Seric if (up->udb_dbp == NULL) 361*51923Seric { 362*51923Seric if (errno != ENOENT && errno != EACCES) 363*51923Seric return EX_TEMPFAIL; 36451362Seric break; 365*51923Seric } 36651362Seric up->udb_type = UDB_LOOKUP; 36751362Seric up++; 36851362Seric break; 36951360Seric } 37051362Seric } 37151362Seric up->udb_type = UDB_EOLIST; 37251360Seric 37351362Seric if (tTd(28, 4)) 37451362Seric { 37551362Seric for (up = UdbEnts; ; up++) 37651360Seric { 37751362Seric switch (up->udb_type) 37851362Seric { 37951362Seric case UDB_EOLIST: 380*51923Seric return EX_OK; 38151362Seric 38251362Seric case UDB_REMOTE: 38351362Seric printf("REMOTE: addr %s, timeo %d\n", 38451362Seric inet_ntoa(up->udb_addr.sin_addr), 38551362Seric up->udb_timeout); 38651362Seric break; 38751362Seric 38851362Seric case UDB_LOOKUP: 38951830Seric printf("LOOKUP: file %s\n", 39051830Seric up->udb_dbname); 39151362Seric break; 39251362Seric 39351362Seric case UDB_FORWARD: 39451362Seric printf("FORWARD: host %s\n", 39551362Seric up->udb_fwdhost); 39651362Seric break; 39751362Seric 39851362Seric default: 39951362Seric printf("UNKNOWN\n"); 40051362Seric break; 40151362Seric } 40251360Seric } 40350581Seric } 40451360Seric } 40550581Seric 40651363Seric int 40751363Seric _udb_parsespec(udbspec, opt, maxopts) 40851363Seric char *udbspec; 40951363Seric struct option opt[]; 41051363Seric int maxopts; 41151363Seric { 41251363Seric register char *spec; 41351363Seric register char *spec_end; 41451363Seric register int optnum; 41551363Seric 41651363Seric spec_end = index(udbspec, ':'); 41751363Seric for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 41851363Seric { 41951363Seric register char *p; 42051363Seric 42151363Seric while (isspace(*spec)) 42251363Seric spec++; 42351363Seric spec_end = index(spec, ':'); 42451363Seric if (spec_end != NULL) 42551363Seric *spec_end++ = '\0'; 42651363Seric 42751363Seric opt[optnum].name = spec; 42851363Seric opt[optnum].val = NULL; 42951363Seric p = index(spec, '='); 43051363Seric if (p != NULL) 43151363Seric opt[optnum].val = ++p; 43251363Seric } 43351363Seric return optnum; 43451363Seric } 43551363Seric 43651360Seric #else /* not USERDB */ 43751360Seric 438*51923Seric int 43951360Seric udbexpand(a, sendq) 44051360Seric ADDRESS *a; 44151360Seric ADDRESS **sendq; 44251360Seric { 443*51923Seric return EX_OK; 44450581Seric } 44550581Seric 44650581Seric #endif /* USERDB */ 447