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*51909Seric static char sccsid [] = "@(#)udb.c 5.13 (Berkeley) 12/12/91 (with USERDB)"; 1251360Seric #else 13*51909Seric static char sccsid [] = "@(#)udb.c 5.13 (Berkeley) 12/12/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 /* 2851363Seric ** UDBEXPAND.C -- interface between sendmail and Berkeley User Data Base. 2950581Seric ** 3051363Seric ** 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 7551363Seric 7651363Seric struct option 7751363Seric { 7851363Seric char *name; 7951363Seric char *val; 8051363Seric }; 8151363Seric /* 8251363Seric ** UDBEXPAND -- look up user in database and expand 8351363Seric ** 8451363Seric ** Parameters: 8551363Seric ** a -- address to expand. 8651363Seric ** sendq -- pointer to head of sendq to put the expansions in. 8751363Seric ** 8851363Seric ** Returns: 8951363Seric ** none. 9051363Seric ** 9151363Seric ** Side Effects: 9251363Seric ** Modifies sendq. 9351363Seric */ 9451363Seric 9551363Seric int UdbPort = 1616; 9651363Seric int UdbTimeout = 10; 9751363Seric 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 */ 121*51909Seric if (bitset(QDONTSEND, a->q_flags)) 12250581Seric return; 12350581Seric CurEnv->e_to = a->q_paddr; 12450581Seric 12551360Seric /* on first call, locate the database */ 12651360Seric if (firstcall) 12750581Seric { 12851362Seric extern void _udbx_init(); 12951362Seric 13051362Seric _udbx_init(); 13151360Seric firstcall = FALSE; 13251362Seric } 13350581Seric 134*51909Seric /* short circuit the process if no chance of a match */ 135*51909Seric if (UdbSpec == NULL || UdbSpec[0] == '\0') 136*51909Seric return; 137*51909Seric 13851362Seric /* if name is too long, assume it won't match */ 13951362Seric if (strlen(a->q_user) > sizeof keybuf - 12) 14051362Seric return; 14151360Seric 14251362Seric /* if name begins with a colon, it indicates our metadata */ 14351362Seric if (a->q_user[0] == ':') 14451362Seric return; 14551360Seric 14651362Seric /* build actual database key */ 14751362Seric (void) strcpy(keybuf, a->q_user); 14851362Seric (void) strcat(keybuf, ":maildrop"); 14951362Seric keylen = strlen(keybuf); 15051360Seric 15151360Seric breakout = FALSE; 15251362Seric for (up = UdbEnts; !breakout; up++) 15350581Seric { 15451360Seric char *user; 15551360Seric struct timeval timeout; 15651360Seric fd_set fdset; 15750581Seric 15851360Seric /* 15951360Seric ** Select action based on entry type. 16051360Seric ** 16151360Seric ** On dropping out of this switch, "class" should 16251360Seric ** explain the type of the data, and "user" should 16351360Seric ** contain the user information. 16451360Seric */ 16550581Seric 16651360Seric switch (up->udb_type) 16751360Seric { 16851360Seric case UDB_LOOKUP: 16951362Seric key.data = keybuf; 17051362Seric key.size = keylen; 17151362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 17251360Seric if (i != 0 || info.size <= 0) 17351360Seric { 17451360Seric if (i < 0) 17551360Seric syserr("udbexpand: db-get stat %s"); 17651360Seric if (tTd(28, 2)) 17751362Seric printf("expand: no match on %s\n", keybuf); 17851360Seric continue; 17951360Seric } 18050581Seric 18151830Seric while (i == 0 && key.size == keylen && 18251830Seric bcmp(key.data, keybuf, keylen) == 0) 18351362Seric { 18451830Seric breakout = TRUE; 18551362Seric if (info.size < sizeof buf) 18651362Seric user = buf; 18751362Seric else 18851362Seric user = xalloc(info.size + 1); 18951362Seric bcopy(info.data, user, info.size); 19051362Seric user[info.size] = '\0'; 19150581Seric 19251362Seric message(Arpa_Info, "expanded to %s", user); 19351362Seric AliasLevel++; 19451362Seric sendtolist(user, a, sendq); 19551362Seric AliasLevel--; 19651362Seric 19751362Seric if (user != buf) 19851362Seric free(user); 19951362Seric 20051362Seric /* get the next record */ 20151362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 20251830Seric } 20351360Seric break; 20451360Seric 20551360Seric case UDB_REMOTE: 20651741Seric /* not yet implemented */ 20751741Seric continue; 20851362Seric 20951360Seric case UDB_FORWARD: 21051360Seric i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 21151360Seric if (i < sizeof buf) 21251360Seric user = buf; 21351360Seric else 21451360Seric user = xalloc(i + 1); 21551360Seric (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); 21651362Seric message(Arpa_Info, "expanded to %s", user); 21751362Seric AliasLevel++; 21851362Seric sendtolist(user, a, sendq); 21951362Seric AliasLevel--; 22051362Seric if (user != buf) 22151362Seric free(user); 22251362Seric breakout = TRUE; 22351360Seric break; 22451360Seric 22551360Seric case UDB_EOLIST: 22651360Seric breakout = TRUE; 22751360Seric continue; 22851360Seric 22951360Seric default: 23051360Seric /* unknown entry type */ 23151360Seric continue; 23251360Seric } 23351362Seric } 23451362Seric } 23551360Seric 23651363Seric #define MAXUDBOPTS 27 23751363Seric 23851362Seric void 23951362Seric _udbx_init() 24051362Seric { 24151362Seric register char *p; 24251362Seric int i; 24351362Seric register struct udbent *up; 24451362Seric char buf[8192]; 24551360Seric 24651908Seric # ifdef UDB_DEFAULT_SPEC 24751908Seric if (UdbSpec == NULL) 24851908Seric UdbSpec = UDB_DEFAULT_SPEC; 24951908Seric # endif 25051908Seric 25151362Seric p = UdbSpec; 25251362Seric up = UdbEnts; 25351762Seric while (p != NULL) 25451362Seric { 25551362Seric char *spec; 25651362Seric auto int rcode; 25751363Seric int nopts; 25851362Seric int nmx; 25951362Seric register struct hostent *h; 26051362Seric char *mxhosts[MAXMXHOSTS + 1]; 26151363Seric struct option opts[MAXUDBOPTS + 1]; 26251362Seric 26351362Seric while (*p == ' ' || *p == '\t' || *p == ',') 26451362Seric p++; 26551362Seric if (*p == '\0') 26651362Seric break; 26751362Seric spec = p; 26851362Seric p = index(p, ','); 26951761Seric if (p != NULL) 27051362Seric *p++ = '\0'; 27151363Seric 27251363Seric /* extract options */ 27351363Seric nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); 27451363Seric 27551363Seric /* 27651363Seric ** Decode database specification. 27751363Seric ** 27851363Seric ** In the sendmail tradition, the leading character 27951363Seric ** defines the semantics of the rest of the entry. 28051363Seric ** 28151363Seric ** +hostname -- send a datagram to the udb server 28251363Seric ** on host "hostname" asking for the 28351363Seric ** home mail server for this user. 28451363Seric ** *hostname -- similar to +hostname, except that the 28551363Seric ** hostname is searched as an MX record; 28651363Seric ** resulting hosts are searched as for 28751363Seric ** +mxhostname. If no MX host is found, 28851363Seric ** this is the same as +hostname. 28951363Seric ** @hostname -- forward email to the indicated host. 29051363Seric ** This should be the last in the list, 29151363Seric ** since it always matches the input. 29251363Seric ** /dbname -- search the named database on the local 29351363Seric ** host using the Berkeley db package. 29451363Seric */ 29551363Seric 29651362Seric switch (*spec) 29751360Seric { 29851362Seric case '+': /* search remote database */ 29951363Seric case '*': /* search remote database (expand MX) */ 30051363Seric if (*spec == '*') 30151363Seric { 30251363Seric nmx = getmxrr(spec + 1, mxhosts, "", &rcode); 30351363Seric if (tTd(28, 16)) 30451363Seric { 30551363Seric int i; 30651362Seric 30751363Seric printf("getmxrr(%s): %d", spec + 1, nmx); 30851363Seric for (i = 0; i <= nmx; i++) 30951363Seric printf(" %s", mxhosts[i]); 31051363Seric printf("\n"); 31151363Seric } 31251363Seric } 31351363Seric else 31451362Seric { 31551363Seric nmx = 1; 31651363Seric mxhosts[0] = spec + 1; 31751362Seric } 31851362Seric 31951362Seric for (i = 0; i < nmx; i++) 32051362Seric { 32151362Seric h = gethostbyname(mxhosts[i]); 32251362Seric if (h == NULL) 32351362Seric continue; 32451362Seric up->udb_type = UDB_REMOTE; 32551362Seric up->udb_addr.sin_family = h->h_addrtype; 32651362Seric bcopy(h->h_addr_list[0], 32751362Seric (char *) &up->udb_addr.sin_addr, 32851362Seric h->h_length); 32951362Seric up->udb_addr.sin_port = UdbPort; 33051362Seric up->udb_timeout = UdbTimeout; 33151362Seric up++; 33251362Seric } 33351362Seric 33451362Seric /* set up a datagram socket */ 33551362Seric if (UdbSock < 0) 33651362Seric { 33751362Seric UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 33851362Seric (void) fcntl(UdbSock, F_SETFD, 1); 33951362Seric } 34051362Seric break; 34151362Seric 34251362Seric case '@': /* forward to remote host */ 34351362Seric up->udb_type = UDB_FORWARD; 34451362Seric up->udb_fwdhost = spec + 1; 34551362Seric up++; 34651362Seric break; 34751362Seric 34851362Seric case '/': /* look up remote name */ 34951831Seric up->udb_dbname = spec; 35051362Seric up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); 35151362Seric if (up->udb_dbp == NULL) 35251362Seric break; 35351362Seric up->udb_type = UDB_LOOKUP; 35451362Seric up++; 35551362Seric break; 35651360Seric } 35751362Seric } 35851362Seric up->udb_type = UDB_EOLIST; 35951360Seric 36051362Seric if (tTd(28, 4)) 36151362Seric { 36251362Seric for (up = UdbEnts; ; up++) 36351360Seric { 36451362Seric switch (up->udb_type) 36551362Seric { 36651362Seric case UDB_EOLIST: 36751362Seric return; 36851362Seric 36951362Seric case UDB_REMOTE: 37051362Seric printf("REMOTE: addr %s, timeo %d\n", 37151362Seric inet_ntoa(up->udb_addr.sin_addr), 37251362Seric up->udb_timeout); 37351362Seric break; 37451362Seric 37551362Seric case UDB_LOOKUP: 37651830Seric printf("LOOKUP: file %s\n", 37751830Seric up->udb_dbname); 37851362Seric break; 37951362Seric 38051362Seric case UDB_FORWARD: 38151362Seric printf("FORWARD: host %s\n", 38251362Seric up->udb_fwdhost); 38351362Seric break; 38451362Seric 38551362Seric default: 38651362Seric printf("UNKNOWN\n"); 38751362Seric break; 38851362Seric } 38951360Seric } 39050581Seric } 39151360Seric } 39250581Seric 39351363Seric int 39451363Seric _udb_parsespec(udbspec, opt, maxopts) 39551363Seric char *udbspec; 39651363Seric struct option opt[]; 39751363Seric int maxopts; 39851363Seric { 39951363Seric register char *spec; 40051363Seric register char *spec_end; 40151363Seric register int optnum; 40251363Seric 40351363Seric spec_end = index(udbspec, ':'); 40451363Seric for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 40551363Seric { 40651363Seric register char *p; 40751363Seric 40851363Seric while (isspace(*spec)) 40951363Seric spec++; 41051363Seric spec_end = index(spec, ':'); 41151363Seric if (spec_end != NULL) 41251363Seric *spec_end++ = '\0'; 41351363Seric 41451363Seric opt[optnum].name = spec; 41551363Seric opt[optnum].val = NULL; 41651363Seric p = index(spec, '='); 41751363Seric if (p != NULL) 41851363Seric opt[optnum].val = ++p; 41951363Seric } 42051363Seric return optnum; 42151363Seric } 42251363Seric 42351360Seric #else /* not USERDB */ 42451360Seric 42551360Seric void 42651360Seric udbexpand(a, sendq) 42751360Seric ADDRESS *a; 42851360Seric ADDRESS **sendq; 42951360Seric { 43051360Seric return; 43150581Seric } 43250581Seric 43350581Seric #endif /* USERDB */ 434