udb.c revision 38032
138032Speter/* 238032Speter * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 338032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 438032Speter * Copyright (c) 1988, 1993 538032Speter * The Regents of the University of California. All rights reserved. 638032Speter * 738032Speter * By using this file, you agree to the terms and conditions set 838032Speter * forth in the LICENSE file which can be found at the top level of 938032Speter * the sendmail distribution. 1038032Speter * 1138032Speter */ 1238032Speter 1338032Speter#include "sendmail.h" 1438032Speter 1538032Speter#ifndef lint 1638032Speter#if USERDB 1738032Speterstatic char sccsid [] = "@(#)udb.c 8.66 (Berkeley) 6/18/98 (with USERDB)"; 1838032Speter#else 1938032Speterstatic char sccsid [] = "@(#)udb.c 8.66 (Berkeley) 6/18/98 (without USERDB)"; 2038032Speter#endif 2138032Speter#endif 2238032Speter 2338032Speter#if USERDB 2438032Speter 2538032Speter#include <errno.h> 2638032Speter 2738032Speter#ifdef NEWDB 2838032Speter# include <db.h> 2938032Speter# ifndef DB_VERSION_MAJOR 3038032Speter# define DB_VERSION_MAJOR 1 3138032Speter# endif 3238032Speter#else 3338032Speter# define DBT struct _data_base_thang_ 3438032SpeterDBT 3538032Speter{ 3638032Speter void *data; /* pointer to data */ 3738032Speter size_t size; /* length of data */ 3838032Speter}; 3938032Speter#endif 4038032Speter 4138032Speter/* 4238032Speter** UDB.C -- interface between sendmail and Berkeley User Data Base. 4338032Speter** 4438032Speter** This depends on the 4.4BSD db package. 4538032Speter*/ 4638032Speter 4738032Speter 4838032Speterstruct udbent 4938032Speter{ 5038032Speter char *udb_spec; /* string version of spec */ 5138032Speter int udb_type; /* type of entry */ 5238032Speter char *udb_default; /* default host for outgoing mail */ 5338032Speter union 5438032Speter { 5538032Speter /* type UE_REMOTE -- do remote call for lookup */ 5638032Speter struct 5738032Speter { 5838032Speter struct sockaddr_in _udb_addr; /* address */ 5938032Speter int _udb_timeout; /* timeout */ 6038032Speter } udb_remote; 6138032Speter#define udb_addr udb_u.udb_remote._udb_addr 6238032Speter#define udb_timeout udb_u.udb_remote._udb_timeout 6338032Speter 6438032Speter /* type UE_FORWARD -- forward message to remote */ 6538032Speter struct 6638032Speter { 6738032Speter char *_udb_fwdhost; /* name of forward host */ 6838032Speter } udb_forward; 6938032Speter#define udb_fwdhost udb_u.udb_forward._udb_fwdhost 7038032Speter 7138032Speter#ifdef NEWDB 7238032Speter /* type UE_FETCH -- lookup in local database */ 7338032Speter struct 7438032Speter { 7538032Speter char *_udb_dbname; /* pathname of database */ 7638032Speter DB *_udb_dbp; /* open database ptr */ 7738032Speter } udb_lookup; 7838032Speter#define udb_dbname udb_u.udb_lookup._udb_dbname 7938032Speter#define udb_dbp udb_u.udb_lookup._udb_dbp 8038032Speter#endif 8138032Speter } udb_u; 8238032Speter}; 8338032Speter 8438032Speter#define UDB_EOLIST 0 /* end of list */ 8538032Speter#define UDB_SKIP 1 /* skip this entry */ 8638032Speter#define UDB_REMOTE 2 /* look up in remote database */ 8738032Speter#define UDB_DBFETCH 3 /* look up in local database */ 8838032Speter#define UDB_FORWARD 4 /* forward to remote host */ 8938032Speter#define UDB_HESIOD 5 /* look up via hesiod */ 9038032Speter 9138032Speter#define MAXUDBENT 10 /* maximum number of UDB entries */ 9238032Speter 9338032Speter 9438032Speterstruct option 9538032Speter{ 9638032Speter char *name; 9738032Speter char *val; 9838032Speter}; 9938032Speter 10038032Speter#ifdef HESIOD 10138032Speterextern int hes_udb_get __P((DBT *, DBT *)); 10238032Speter#endif 10338032Speterextern int _udbx_init __P((ENVELOPE *)); 10438032Speter/* 10538032Speter** UDBEXPAND -- look up user in database and expand 10638032Speter** 10738032Speter** Parameters: 10838032Speter** a -- address to expand. 10938032Speter** sendq -- pointer to head of sendq to put the expansions in. 11038032Speter** aliaslevel -- the current alias nesting depth. 11138032Speter** e -- the current envelope. 11238032Speter** 11338032Speter** Returns: 11438032Speter** EX_TEMPFAIL -- if something "odd" happened -- probably due 11538032Speter** to accessing a file on an NFS server that is down. 11638032Speter** EX_OK -- otherwise. 11738032Speter** 11838032Speter** Side Effects: 11938032Speter** Modifies sendq. 12038032Speter*/ 12138032Speter 12238032Speterint UdbPort = 1616; 12338032Speterint UdbTimeout = 10; 12438032Speter 12538032Speterstruct udbent UdbEnts[MAXUDBENT + 1]; 12638032Speterint UdbSock = -1; 12738032Speterbool UdbInitialized = FALSE; 12838032Speter 12938032Speterint 13038032Speterudbexpand(a, sendq, aliaslevel, e) 13138032Speter register ADDRESS *a; 13238032Speter ADDRESS **sendq; 13338032Speter int aliaslevel; 13438032Speter register ENVELOPE *e; 13538032Speter{ 13638032Speter int i; 13738032Speter DBT key; 13838032Speter DBT info; 13938032Speter bool breakout; 14038032Speter register struct udbent *up; 14138032Speter int keylen; 14238032Speter int naddrs; 14338032Speter char keybuf[MAXKEY]; 14438032Speter 14538032Speter bzero(&key, sizeof key); 14638032Speter bzero(&info, sizeof info); 14738032Speter 14838032Speter if (tTd(28, 1)) 14938032Speter printf("udbexpand(%s)\n", a->q_paddr); 15038032Speter 15138032Speter /* make certain we are supposed to send to this address */ 15238032Speter if (bitset(QDONTSEND|QVERIFIED, a->q_flags)) 15338032Speter return EX_OK; 15438032Speter e->e_to = a->q_paddr; 15538032Speter 15638032Speter /* on first call, locate the database */ 15738032Speter if (!UdbInitialized) 15838032Speter { 15938032Speter if (_udbx_init(e) == EX_TEMPFAIL) 16038032Speter return EX_TEMPFAIL; 16138032Speter } 16238032Speter 16338032Speter /* short circuit the process if no chance of a match */ 16438032Speter if (UdbSpec == NULL || UdbSpec[0] == '\0') 16538032Speter return EX_OK; 16638032Speter 16738032Speter /* short circuit name begins with '\\' since it can't possibly match */ 16838032Speter if (a->q_user[0] == '\\') 16938032Speter return EX_OK; 17038032Speter 17138032Speter /* if name is too long, assume it won't match */ 17238032Speter if (strlen(a->q_user) > (SIZE_T) sizeof keybuf - 12) 17338032Speter return EX_OK; 17438032Speter 17538032Speter /* if name begins with a colon, it indicates our metadata */ 17638032Speter if (a->q_user[0] == ':') 17738032Speter return EX_OK; 17838032Speter 17938032Speter /* build actual database key */ 18038032Speter (void) strcpy(keybuf, a->q_user); 18138032Speter (void) strcat(keybuf, ":maildrop"); 18238032Speter keylen = strlen(keybuf); 18338032Speter 18438032Speter breakout = FALSE; 18538032Speter for (up = UdbEnts; !breakout; up++) 18638032Speter { 18738032Speter char *user; 18838032Speter int usersize; 18938032Speter int userleft; 19038032Speter char userbuf[MEMCHUNKSIZE]; 19138032Speter#if defined(HESIOD) && defined(HES_GETMAILHOST) 19238032Speter char pobuf[MAXNAME]; 19338032Speter#endif 19438032Speter#if defined(NEWDB) && DB_VERSION_MAJOR > 1 19538032Speter DBC *dbc = NULL; 19638032Speter#endif 19738032Speter 19838032Speter user = userbuf; 19938032Speter userbuf[0] = '\0'; 20038032Speter usersize = sizeof userbuf; 20138032Speter userleft = sizeof userbuf - 1; 20238032Speter 20338032Speter /* 20438032Speter ** Select action based on entry type. 20538032Speter ** 20638032Speter ** On dropping out of this switch, "class" should 20738032Speter ** explain the type of the data, and "user" should 20838032Speter ** contain the user information. 20938032Speter */ 21038032Speter 21138032Speter switch (up->udb_type) 21238032Speter { 21338032Speter#ifdef NEWDB 21438032Speter case UDB_DBFETCH: 21538032Speter key.data = keybuf; 21638032Speter key.size = keylen; 21738032Speter if (tTd(28, 80)) 21838032Speter printf("udbexpand: trying %s (%d) via db\n", 21938032Speter keybuf, keylen); 22038032Speter#if DB_VERSION_MAJOR < 2 22138032Speter i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 22238032Speter#else 22338032Speter i = 0; 22438032Speter if (dbc == NULL && 22538032Speter (errno = (*up->udb_dbp->cursor)(up->udb_dbp, 22638032Speter NULL, &dbc)) != 0) 22738032Speter i = -1; 22838032Speter if (i != 0 || dbc == NULL || 22938032Speter (errno = dbc->c_get(dbc, &key, 23038032Speter &info, DB_SET)) != 0) 23138032Speter i = 1; 23238032Speter#endif 23338032Speter if (i > 0 || info.size <= 0) 23438032Speter { 23538032Speter if (tTd(28, 2)) 23638032Speter printf("udbexpand: no match on %s (%d)\n", 23738032Speter keybuf, keylen); 23838032Speter#if DB_VERSION_MAJOR > 1 23938032Speter if (dbc != NULL) 24038032Speter { 24138032Speter (void) dbc->c_close(dbc); 24238032Speter dbc = NULL; 24338032Speter } 24438032Speter#endif 24538032Speter break; 24638032Speter } 24738032Speter if (tTd(28, 80)) 24838032Speter printf("udbexpand: match %.*s: %.*s\n", 24938032Speter (int) key.size, (char *) key.data, 25038032Speter (int) info.size, (char *) info.data); 25138032Speter 25238032Speter a->q_flags &= ~QSELFREF; 25338032Speter while (i == 0 && key.size == keylen && 25438032Speter bcmp(key.data, keybuf, keylen) == 0) 25538032Speter { 25638032Speter char *p; 25738032Speter 25838032Speter if (bitset(EF_VRFYONLY, e->e_flags)) 25938032Speter { 26038032Speter a->q_flags |= QVERIFIED; 26138032Speter#if DB_VERSION_MAJOR > 1 26238032Speter if (dbc != NULL) 26338032Speter { 26438032Speter (void) dbc->c_close(dbc); 26538032Speter dbc = NULL; 26638032Speter } 26738032Speter#endif 26838032Speter return EX_OK; 26938032Speter } 27038032Speter 27138032Speter breakout = TRUE; 27238032Speter if (info.size >= userleft - 1) 27338032Speter { 27438032Speter char *nuser; 27538032Speter int size = MEMCHUNKSIZE; 27638032Speter 27738032Speter if (info.size > MEMCHUNKSIZE) 27838032Speter size = info.size; 27938032Speter nuser = xalloc(usersize + size); 28038032Speter 28138032Speter bcopy(user, nuser, usersize); 28238032Speter if (user != userbuf) 28338032Speter free(user); 28438032Speter user = nuser; 28538032Speter usersize += size; 28638032Speter userleft += size; 28738032Speter } 28838032Speter p = &user[strlen(user)]; 28938032Speter if (p != user) 29038032Speter { 29138032Speter *p++ = ','; 29238032Speter userleft--; 29338032Speter } 29438032Speter bcopy(info.data, p, info.size); 29538032Speter p[info.size] = '\0'; 29638032Speter userleft -= info.size; 29738032Speter 29838032Speter /* get the next record */ 29938032Speter#if DB_VERSION_MAJOR < 2 30038032Speter i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 30138032Speter#else 30238032Speter i = 0; 30338032Speter if ((errno = dbc->c_get(dbc, &key, 30438032Speter &info, DB_NEXT)) != 0) 30538032Speter i = 1; 30638032Speter#endif 30738032Speter } 30838032Speter 30938032Speter#if DB_VERSION_MAJOR > 1 31038032Speter if (dbc != NULL) 31138032Speter { 31238032Speter (void) dbc->c_close(dbc); 31338032Speter dbc = NULL; 31438032Speter } 31538032Speter#endif 31638032Speter 31738032Speter /* if nothing ever matched, try next database */ 31838032Speter if (!breakout) 31938032Speter break; 32038032Speter 32138032Speter message("expanded to %s", user); 32238032Speter if (LogLevel >= 10) 32338032Speter sm_syslog(LOG_INFO, e->e_id, 32438032Speter "expand %.100s => %s", 32538032Speter e->e_to, 32638032Speter shortenstring(user, MAXSHORTSTR)); 32738032Speter naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); 32838032Speter if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) 32938032Speter { 33038032Speter if (tTd(28, 5)) 33138032Speter { 33238032Speter printf("udbexpand: QDONTSEND "); 33338032Speter printaddr(a, FALSE); 33438032Speter } 33538032Speter a->q_flags |= QDONTSEND; 33638032Speter } 33738032Speter if (i < 0) 33838032Speter { 33938032Speter syserr("udbexpand: db-get %.*s stat %d", 34038032Speter (int) key.size, (char *) key.data, i); 34138032Speter return EX_TEMPFAIL; 34238032Speter } 34338032Speter 34438032Speter /* 34538032Speter ** If this address has a -request address, reflect 34638032Speter ** it into the envelope. 34738032Speter */ 34838032Speter 34938032Speter bzero(&key, sizeof key); 35038032Speter bzero(&info, sizeof info); 35138032Speter (void) strcpy(keybuf, a->q_user); 35238032Speter (void) strcat(keybuf, ":mailsender"); 35338032Speter keylen = strlen(keybuf); 35438032Speter key.data = keybuf; 35538032Speter key.size = keylen; 35638032Speter 35738032Speter#if DB_VERSION_MAJOR < 2 35838032Speter i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 35938032Speter#else 36038032Speter i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, 36138032Speter &key, &info, 0); 36238032Speter#endif 36338032Speter if (i != 0 || info.size <= 0) 36438032Speter break; 36538032Speter a->q_owner = xalloc(info.size + 1); 36638032Speter bcopy(info.data, a->q_owner, info.size); 36738032Speter a->q_owner[info.size] = '\0'; 36838032Speter 36938032Speter /* announce delivery; NORECEIPT bit set later */ 37038032Speter if (e->e_xfp != NULL) 37138032Speter { 37238032Speter fprintf(e->e_xfp, 37338032Speter "Message delivered to mailing list %s\n", 37438032Speter a->q_paddr); 37538032Speter } 37638032Speter e->e_flags |= EF_SENDRECEIPT; 37738032Speter a->q_flags |= QDELIVERED|QEXPANDED; 37838032Speter break; 37938032Speter#endif 38038032Speter 38138032Speter#ifdef HESIOD 38238032Speter case UDB_HESIOD: 38338032Speter key.data = keybuf; 38438032Speter key.size = keylen; 38538032Speter if (tTd(28, 80)) 38638032Speter printf("udbexpand: trying %s (%d) via hesiod\n", 38738032Speter keybuf, keylen); 38838032Speter /* look up the key via hesiod */ 38938032Speter i = hes_udb_get(&key, &info); 39038032Speter if (i < 0) 39138032Speter { 39238032Speter syserr("udbexpand: hesiod-get %.*s stat %d", 39338032Speter (int) key.size, (char *) key.data, i); 39438032Speter return EX_TEMPFAIL; 39538032Speter } 39638032Speter else if (i > 0 || info.size <= 0) 39738032Speter { 39838032Speter#if HES_GETMAILHOST 39938032Speter struct hes_postoffice *hp; 40038032Speter#endif 40138032Speter 40238032Speter if (tTd(28, 2)) 40338032Speter printf("udbexpand: no match on %s (%d)\n", 40438032Speter (char *) keybuf, (int) keylen); 40538032Speter#if HES_GETMAILHOST 40638032Speter if (tTd(28, 8)) 40738032Speter printf(" ... trying hes_getmailhost(%s)\n", 40838032Speter a->q_user); 40938032Speter hp = hes_getmailhost(a->q_user); 41038032Speter if (hp == NULL) 41138032Speter { 41238032Speter if (hes_error() == HES_ER_NET) 41338032Speter { 41438032Speter syserr("udbexpand: hesiod-getmail %s stat %d", 41538032Speter a->q_user, hes_error()); 41638032Speter return EX_TEMPFAIL; 41738032Speter } 41838032Speter if (tTd(28, 2)) 41938032Speter printf("hes_getmailhost(%s): %d\n", 42038032Speter a->q_user, hes_error()); 42138032Speter break; 42238032Speter } 42338032Speter if (strlen(hp->po_name) + strlen(hp->po_host) > 42438032Speter sizeof pobuf - 2) 42538032Speter { 42638032Speter if (tTd(28, 2)) 42738032Speter printf("hes_getmailhost(%s): expansion too long: %.30s@%.30s\n", 42838032Speter a->q_user, 42938032Speter hp->po_name, 43038032Speter hp->po_host); 43138032Speter break; 43238032Speter } 43338032Speter info.data = pobuf; 43438032Speter snprintf(pobuf, sizeof pobuf, "%s@%s", 43538032Speter hp->po_name, hp->po_host); 43638032Speter info.size = strlen(info.data); 43738032Speter#else 43838032Speter break; 43938032Speter#endif 44038032Speter } 44138032Speter if (tTd(28, 80)) 44238032Speter printf("udbexpand: match %.*s: %.*s\n", 44338032Speter (int) key.size, (char *) key.data, 44438032Speter (int) info.size, (char *) info.data); 44538032Speter a->q_flags &= ~QSELFREF; 44638032Speter 44738032Speter if (bitset(EF_VRFYONLY, e->e_flags)) 44838032Speter { 44938032Speter a->q_flags |= QVERIFIED; 45038032Speter return EX_OK; 45138032Speter } 45238032Speter 45338032Speter breakout = TRUE; 45438032Speter if (info.size >= usersize) 45538032Speter user = xalloc(info.size + 1); 45638032Speter bcopy(info.data, user, info.size); 45738032Speter user[info.size] = '\0'; 45838032Speter 45938032Speter message("hesioded to %s", user); 46038032Speter if (LogLevel >= 10) 46138032Speter sm_syslog(LOG_INFO, e->e_id, 46238032Speter "hesiod %.100s => %s", 46338032Speter e->e_to, 46438032Speter shortenstring(user, MAXSHORTSTR)); 46538032Speter naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); 46638032Speter 46738032Speter if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) 46838032Speter { 46938032Speter if (tTd(28, 5)) 47038032Speter { 47138032Speter printf("udbexpand: QDONTSEND "); 47238032Speter printaddr(a, FALSE); 47338032Speter } 47438032Speter a->q_flags |= QDONTSEND; 47538032Speter } 47638032Speter 47738032Speter /* 47838032Speter ** If this address has a -request address, reflect 47938032Speter ** it into the envelope. 48038032Speter */ 48138032Speter 48238032Speter (void) strcpy(keybuf, a->q_user); 48338032Speter (void) strcat(keybuf, ":mailsender"); 48438032Speter keylen = strlen(keybuf); 48538032Speter key.data = keybuf; 48638032Speter key.size = keylen; 48738032Speter i = hes_udb_get(&key, &info); 48838032Speter if (i != 0 || info.size <= 0) 48938032Speter break; 49038032Speter a->q_owner = xalloc(info.size + 1); 49138032Speter bcopy(info.data, a->q_owner, info.size); 49238032Speter a->q_owner[info.size] = '\0'; 49338032Speter break; 49438032Speter#endif /* HESIOD */ 49538032Speter 49638032Speter case UDB_REMOTE: 49738032Speter /* not yet implemented */ 49838032Speter break; 49938032Speter 50038032Speter case UDB_FORWARD: 50138032Speter if (bitset(EF_VRFYONLY, e->e_flags)) 50238032Speter return EX_OK; 50338032Speter i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 50438032Speter if (i >= usersize) 50538032Speter { 50638032Speter usersize = i + 1; 50738032Speter user = xalloc(usersize); 50838032Speter } 50938032Speter (void) snprintf(user, usersize, "%s@%s", 51038032Speter a->q_user, up->udb_fwdhost); 51138032Speter message("expanded to %s", user); 51238032Speter a->q_flags &= ~QSELFREF; 51338032Speter naddrs = sendtolist(user, a, sendq, aliaslevel + 1, e); 51438032Speter if (naddrs > 0 && !bitset(QSELFREF, a->q_flags)) 51538032Speter { 51638032Speter if (tTd(28, 5)) 51738032Speter { 51838032Speter printf("udbexpand: QDONTSEND "); 51938032Speter printaddr(a, FALSE); 52038032Speter } 52138032Speter a->q_flags |= QDONTSEND; 52238032Speter } 52338032Speter breakout = TRUE; 52438032Speter break; 52538032Speter 52638032Speter case UDB_EOLIST: 52738032Speter breakout = TRUE; 52838032Speter break; 52938032Speter 53038032Speter default: 53138032Speter /* unknown entry type */ 53238032Speter break; 53338032Speter } 53438032Speter if (user != userbuf) 53538032Speter free(user); 53638032Speter } 53738032Speter return EX_OK; 53838032Speter} 53938032Speter/* 54038032Speter** UDBSENDER -- return canonical external name of sender, given local name 54138032Speter** 54238032Speter** Parameters: 54338032Speter** sender -- the name of the sender on the local machine. 54438032Speter** 54538032Speter** Returns: 54638032Speter** The external name for this sender, if derivable from the 54738032Speter** database. 54838032Speter** NULL -- if nothing is changed from the database. 54938032Speter** 55038032Speter** Side Effects: 55138032Speter** none. 55238032Speter*/ 55338032Speter 55438032Speterchar * 55538032Speterudbsender(sender) 55638032Speter char *sender; 55738032Speter{ 55838032Speter extern char *udbmatch __P((char *, char *)); 55938032Speter 56038032Speter return udbmatch(sender, "mailname"); 56138032Speter} 56238032Speter 56338032Speter 56438032Speterchar * 56538032Speterudbmatch(user, field) 56638032Speter char *user; 56738032Speter char *field; 56838032Speter{ 56938032Speter register char *p; 57038032Speter register struct udbent *up; 57138032Speter int i; 57238032Speter int keylen; 57338032Speter DBT key, info; 57438032Speter char keybuf[MAXKEY]; 57538032Speter 57638032Speter if (tTd(28, 1)) 57738032Speter printf("udbmatch(%s, %s)\n", user, field); 57838032Speter 57938032Speter if (!UdbInitialized) 58038032Speter { 58138032Speter if (_udbx_init(CurEnv) == EX_TEMPFAIL) 58238032Speter return NULL; 58338032Speter } 58438032Speter 58538032Speter /* short circuit if no spec */ 58638032Speter if (UdbSpec == NULL || UdbSpec[0] == '\0') 58738032Speter return NULL; 58838032Speter 58938032Speter /* short circuit name begins with '\\' since it can't possibly match */ 59038032Speter if (user[0] == '\\') 59138032Speter return NULL; 59238032Speter 59338032Speter /* long names can never match and are a pain to deal with */ 59438032Speter i = strlen(field); 59538032Speter if (i < sizeof "maildrop") 59638032Speter i = sizeof "maildrop"; 59738032Speter if ((strlen(user) + i) > sizeof keybuf - 4) 59838032Speter return NULL; 59938032Speter 60038032Speter /* names beginning with colons indicate metadata */ 60138032Speter if (user[0] == ':') 60238032Speter return NULL; 60338032Speter 60438032Speter /* build database key */ 60538032Speter (void) strcpy(keybuf, user); 60638032Speter (void) strcat(keybuf, ":"); 60738032Speter (void) strcat(keybuf, field); 60838032Speter keylen = strlen(keybuf); 60938032Speter 61038032Speter for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 61138032Speter { 61238032Speter /* 61338032Speter ** Select action based on entry type. 61438032Speter */ 61538032Speter 61638032Speter switch (up->udb_type) 61738032Speter { 61838032Speter#ifdef NEWDB 61938032Speter case UDB_DBFETCH: 62038032Speter bzero(&key, sizeof key); 62138032Speter bzero(&info, sizeof info); 62238032Speter key.data = keybuf; 62338032Speter key.size = keylen; 62438032Speter#if DB_VERSION_MAJOR < 2 62538032Speter i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 62638032Speter#else 62738032Speter i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, 62838032Speter &key, &info, 0); 62938032Speter#endif 63038032Speter if (i != 0 || info.size <= 0) 63138032Speter { 63238032Speter if (tTd(28, 2)) 63338032Speter printf("udbmatch: no match on %s (%d) via db\n", 63438032Speter keybuf, keylen); 63538032Speter continue; 63638032Speter } 63738032Speter 63838032Speter p = xalloc(info.size + 1); 63938032Speter bcopy(info.data, p, info.size); 64038032Speter p[info.size] = '\0'; 64138032Speter if (tTd(28, 1)) 64238032Speter printf("udbmatch ==> %s\n", p); 64338032Speter return p; 64438032Speter#endif 64538032Speter 64638032Speter#ifdef HESIOD 64738032Speter case UDB_HESIOD: 64838032Speter key.data = keybuf; 64938032Speter key.size = keylen; 65038032Speter i = hes_udb_get(&key, &info); 65138032Speter if (i != 0 || info.size <= 0) 65238032Speter { 65338032Speter if (tTd(28, 2)) 65438032Speter printf("udbmatch: no match on %s (%d) via hesiod\n", 65538032Speter keybuf, keylen); 65638032Speter continue; 65738032Speter } 65838032Speter 65938032Speter p = xalloc(info.size + 1); 66038032Speter bcopy(info.data, p, info.size); 66138032Speter p[info.size] = '\0'; 66238032Speter if (tTd(28, 1)) 66338032Speter printf("udbmatch ==> %s\n", p); 66438032Speter return p; 66538032Speter#endif /* HESIOD */ 66638032Speter } 66738032Speter } 66838032Speter 66938032Speter if (strcmp(field, "mailname") != 0) 67038032Speter return NULL; 67138032Speter 67238032Speter /* 67338032Speter ** Nothing yet. Search again for a default case. But only 67438032Speter ** use it if we also have a forward (:maildrop) pointer already 67538032Speter ** in the database. 67638032Speter */ 67738032Speter 67838032Speter /* build database key */ 67938032Speter (void) strcpy(keybuf, user); 68038032Speter (void) strcat(keybuf, ":maildrop"); 68138032Speter keylen = strlen(keybuf); 68238032Speter 68338032Speter for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 68438032Speter { 68538032Speter switch (up->udb_type) 68638032Speter { 68738032Speter#ifdef NEWDB 68838032Speter case UDB_DBFETCH: 68938032Speter /* get the default case for this database */ 69038032Speter if (up->udb_default == NULL) 69138032Speter { 69238032Speter bzero(&key, sizeof key); 69338032Speter bzero(&info, sizeof info); 69438032Speter key.data = ":default:mailname"; 69538032Speter key.size = strlen(key.data); 69638032Speter#if DB_VERSION_MAJOR < 2 69738032Speter i = (*up->udb_dbp->get)(up->udb_dbp, 69838032Speter &key, &info, 0); 69938032Speter#else 70038032Speter i = errno = (*up->udb_dbp->get)(up->udb_dbp, 70138032Speter NULL, &key, 70238032Speter &info, 0); 70338032Speter#endif 70438032Speter if (i != 0 || info.size <= 0) 70538032Speter { 70638032Speter /* no default case */ 70738032Speter up->udb_default = ""; 70838032Speter continue; 70938032Speter } 71038032Speter 71138032Speter /* save the default case */ 71238032Speter up->udb_default = xalloc(info.size + 1); 71338032Speter bcopy(info.data, up->udb_default, info.size); 71438032Speter up->udb_default[info.size] = '\0'; 71538032Speter } 71638032Speter else if (up->udb_default[0] == '\0') 71738032Speter continue; 71838032Speter 71938032Speter /* we have a default case -- verify user:maildrop */ 72038032Speter bzero(&key, sizeof key); 72138032Speter bzero(&info, sizeof info); 72238032Speter key.data = keybuf; 72338032Speter key.size = keylen; 72438032Speter#if DB_VERSION_MAJOR < 2 72538032Speter i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 72638032Speter#else 72738032Speter i = errno = (*up->udb_dbp->get)(up->udb_dbp, NULL, 72838032Speter &key, &info, 0); 72938032Speter#endif 73038032Speter if (i != 0 || info.size <= 0) 73138032Speter { 73238032Speter /* nope -- no aliasing for this user */ 73338032Speter continue; 73438032Speter } 73538032Speter 73638032Speter /* they exist -- build the actual address */ 73738032Speter p = xalloc(strlen(user) + strlen(up->udb_default) + 2); 73838032Speter (void) strcpy(p, user); 73938032Speter (void) strcat(p, "@"); 74038032Speter (void) strcat(p, up->udb_default); 74138032Speter if (tTd(28, 1)) 74238032Speter printf("udbmatch ==> %s\n", p); 74338032Speter return p; 74438032Speter#endif 74538032Speter 74638032Speter#ifdef HESIOD 74738032Speter case UDB_HESIOD: 74838032Speter /* get the default case for this database */ 74938032Speter if (up->udb_default == NULL) 75038032Speter { 75138032Speter key.data = ":default:mailname"; 75238032Speter key.size = strlen(key.data); 75338032Speter i = hes_udb_get(&key, &info); 75438032Speter 75538032Speter if (i != 0 || info.size <= 0) 75638032Speter { 75738032Speter /* no default case */ 75838032Speter up->udb_default = ""; 75938032Speter continue; 76038032Speter } 76138032Speter 76238032Speter /* save the default case */ 76338032Speter up->udb_default = xalloc(info.size + 1); 76438032Speter bcopy(info.data, up->udb_default, info.size); 76538032Speter up->udb_default[info.size] = '\0'; 76638032Speter } 76738032Speter else if (up->udb_default[0] == '\0') 76838032Speter continue; 76938032Speter 77038032Speter /* we have a default case -- verify user:maildrop */ 77138032Speter key.data = keybuf; 77238032Speter key.size = keylen; 77338032Speter i = hes_udb_get(&key, &info); 77438032Speter if (i != 0 || info.size <= 0) 77538032Speter { 77638032Speter /* nope -- no aliasing for this user */ 77738032Speter continue; 77838032Speter } 77938032Speter 78038032Speter /* they exist -- build the actual address */ 78138032Speter p = xalloc(strlen(user) + strlen(up->udb_default) + 2); 78238032Speter (void) strcpy(p, user); 78338032Speter (void) strcat(p, "@"); 78438032Speter (void) strcat(p, up->udb_default); 78538032Speter if (tTd(28, 1)) 78638032Speter printf("udbmatch ==> %s\n", p); 78738032Speter return p; 78838032Speter break; 78938032Speter#endif /* HESIOD */ 79038032Speter } 79138032Speter } 79238032Speter 79338032Speter /* still nothing.... too bad */ 79438032Speter return NULL; 79538032Speter} 79638032Speter/* 79738032Speter** UDB_MAP_LOOKUP -- look up arbitrary entry in user database map 79838032Speter** 79938032Speter** Parameters: 80038032Speter** map -- the map being queried. 80138032Speter** name -- the name to look up. 80238032Speter** av -- arguments to the map lookup. 80338032Speter** statp -- to get any error status. 80438032Speter** 80538032Speter** Returns: 80638032Speter** NULL if name not found in map. 80738032Speter** The rewritten name otherwise. 80838032Speter*/ 80938032Speter 81038032Speter/* ARGSUSED3 */ 81138032Speterchar * 81238032Speterudb_map_lookup(map, name, av, statp) 81338032Speter MAP *map; 81438032Speter char *name; 81538032Speter char **av; 81638032Speter int *statp; 81738032Speter{ 81838032Speter char *val; 81938032Speter char *key; 82038032Speter char keybuf[MAXNAME + 1]; 82138032Speter 82238032Speter if (tTd(28, 20) || tTd(38, 20)) 82338032Speter printf("udb_map_lookup(%s, %s)\n", map->map_mname, name); 82438032Speter 82538032Speter if (bitset(MF_NOFOLDCASE, map->map_mflags)) 82638032Speter { 82738032Speter key = name; 82838032Speter } 82938032Speter else 83038032Speter { 83138032Speter int keysize = strlen(name); 83238032Speter 83338032Speter if (keysize > sizeof keybuf - 1) 83438032Speter keysize = sizeof keybuf - 1; 83538032Speter bcopy(name, keybuf, keysize); 83638032Speter keybuf[keysize] = '\0'; 83738032Speter makelower(keybuf); 83838032Speter key = keybuf; 83938032Speter } 84038032Speter val = udbmatch(key, map->map_file); 84138032Speter if (val == NULL) 84238032Speter return NULL; 84338032Speter if (bitset(MF_MATCHONLY, map->map_mflags)) 84438032Speter return map_rewrite(map, name, strlen(name), NULL); 84538032Speter else 84638032Speter return map_rewrite(map, val, strlen(val), av); 84738032Speter} 84838032Speter/* 84938032Speter** _UDBX_INIT -- parse the UDB specification, opening any valid entries. 85038032Speter** 85138032Speter** Parameters: 85238032Speter** e -- the current envelope. 85338032Speter** 85438032Speter** Returns: 85538032Speter** EX_TEMPFAIL -- if it appeared it couldn't get hold of a 85638032Speter** database due to a host being down or some similar 85738032Speter** (recoverable) situation. 85838032Speter** EX_OK -- otherwise. 85938032Speter** 86038032Speter** Side Effects: 86138032Speter** Fills in the UdbEnts structure from UdbSpec. 86238032Speter*/ 86338032Speter 86438032Speter#define MAXUDBOPTS 27 86538032Speter 86638032Speterint 86738032Speter_udbx_init(e) 86838032Speter ENVELOPE *e; 86938032Speter{ 87038032Speter int ents = 0; 87138032Speter register char *p; 87238032Speter register struct udbent *up; 87338032Speter 87438032Speter if (UdbInitialized) 87538032Speter return EX_OK; 87638032Speter 87738032Speter# ifdef UDB_DEFAULT_SPEC 87838032Speter if (UdbSpec == NULL) 87938032Speter UdbSpec = UDB_DEFAULT_SPEC; 88038032Speter# endif 88138032Speter 88238032Speter p = UdbSpec; 88338032Speter up = UdbEnts; 88438032Speter while (p != NULL) 88538032Speter { 88638032Speter char *spec; 88738032Speter int l; 88838032Speter# if 0 88938032Speter auto int rcode; 89038032Speter int nmx; 89138032Speter int i; 89238032Speter register struct hostent *h; 89338032Speter char *mxhosts[MAXMXHOSTS + 1]; 89438032Speter# endif 89538032Speter struct option opts[MAXUDBOPTS + 1]; 89638032Speter extern int _udb_parsespec __P((char *, struct option [], int)); 89738032Speter 89838032Speter while (*p == ' ' || *p == '\t' || *p == ',') 89938032Speter p++; 90038032Speter if (*p == '\0') 90138032Speter break; 90238032Speter spec = p; 90338032Speter p = strchr(p, ','); 90438032Speter if (p != NULL) 90538032Speter *p++ = '\0'; 90638032Speter 90738032Speter if (ents >= MAXUDBENT) 90838032Speter { 90938032Speter syserr("Maximum number of UDB entries exceeded"); 91038032Speter break; 91138032Speter } 91238032Speter 91338032Speter /* extract options */ 91438032Speter (void) _udb_parsespec(spec, opts, MAXUDBOPTS); 91538032Speter 91638032Speter /* 91738032Speter ** Decode database specification. 91838032Speter ** 91938032Speter ** In the sendmail tradition, the leading character 92038032Speter ** defines the semantics of the rest of the entry. 92138032Speter ** 92238032Speter ** +hostname -- send a datagram to the udb server 92338032Speter ** on host "hostname" asking for the 92438032Speter ** home mail server for this user. 92538032Speter ** *hostname -- similar to +hostname, except that the 92638032Speter ** hostname is searched as an MX record; 92738032Speter ** resulting hosts are searched as for 92838032Speter ** +mxhostname. If no MX host is found, 92938032Speter ** this is the same as +hostname. 93038032Speter ** @hostname -- forward email to the indicated host. 93138032Speter ** This should be the last in the list, 93238032Speter ** since it always matches the input. 93338032Speter ** /dbname -- search the named database on the local 93438032Speter ** host using the Berkeley db package. 93538032Speter ** Hesiod -- search the named database with BIND 93638032Speter ** using the MIT Hesiod package. 93738032Speter */ 93838032Speter 93938032Speter switch (*spec) 94038032Speter { 94138032Speter#if 0 94238032Speter case '+': /* search remote database */ 94338032Speter case '*': /* search remote database (expand MX) */ 94438032Speter if (*spec == '*') 94538032Speter { 94638032Speter#if NAMED_BIND 94738032Speter nmx = getmxrr(spec + 1, mxhosts, FALSE, &rcode); 94838032Speter#else 94938032Speter mxhosts[0] = spec + 1; 95038032Speter nmx = 1; 95138032Speter rcode = 0; 95238032Speter#endif 95338032Speter if (tTd(28, 16)) 95438032Speter { 95538032Speter int i; 95638032Speter 95738032Speter printf("getmxrr(%s): %d", spec + 1, nmx); 95838032Speter for (i = 0; i <= nmx; i++) 95938032Speter printf(" %s", mxhosts[i]); 96038032Speter printf("\n"); 96138032Speter } 96238032Speter } 96338032Speter else 96438032Speter { 96538032Speter nmx = 1; 96638032Speter mxhosts[0] = spec + 1; 96738032Speter } 96838032Speter 96938032Speter for (i = 0; i < nmx; i++) 97038032Speter { 97138032Speter h = sm_gethostbyname(mxhosts[i]); 97238032Speter if (h == NULL) 97338032Speter continue; 97438032Speter up->udb_type = UDB_REMOTE; 97538032Speter up->udb_addr.sin_family = h->h_addrtype; 97638032Speter bcopy(h->h_addr_list[0], 97738032Speter (char *) &up->udb_addr.sin_addr, 97838032Speter INADDRSZ); 97938032Speter up->udb_addr.sin_port = UdbPort; 98038032Speter up->udb_timeout = UdbTimeout; 98138032Speter ents++; 98238032Speter up++; 98338032Speter } 98438032Speter 98538032Speter /* set up a datagram socket */ 98638032Speter if (UdbSock < 0) 98738032Speter { 98838032Speter UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 98938032Speter (void) fcntl(UdbSock, F_SETFD, 1); 99038032Speter } 99138032Speter break; 99238032Speter#endif 99338032Speter 99438032Speter case '@': /* forward to remote host */ 99538032Speter up->udb_type = UDB_FORWARD; 99638032Speter up->udb_fwdhost = spec + 1; 99738032Speter ents++; 99838032Speter up++; 99938032Speter break; 100038032Speter 100138032Speter#ifdef HESIOD 100238032Speter case 'h': /* use hesiod */ 100338032Speter case 'H': 100438032Speter if (strcasecmp(spec, "hesiod") != 0) 100538032Speter goto badspec; 100638032Speter up->udb_type = UDB_HESIOD; 100738032Speter ents++; 100838032Speter up++; 100938032Speter break; 101038032Speter#endif /* HESIOD */ 101138032Speter 101238032Speter#ifdef NEWDB 101338032Speter case '/': /* look up remote name */ 101438032Speter l = strlen(spec); 101538032Speter if (l > 3 && strcmp(&spec[l - 3], ".db") == 0) 101638032Speter { 101738032Speter up->udb_dbname = spec; 101838032Speter } 101938032Speter else 102038032Speter { 102138032Speter up->udb_dbname = xalloc(l + 4); 102238032Speter strcpy(up->udb_dbname, spec); 102338032Speter strcat(up->udb_dbname, ".db"); 102438032Speter } 102538032Speter errno = 0; 102638032Speter#if DB_VERSION_MAJOR < 2 102738032Speter up->udb_dbp = dbopen(up->udb_dbname, O_RDONLY, 102838032Speter 0644, DB_BTREE, NULL); 102938032Speter#else 103038032Speter up->udb_dbp = NULL; 103138032Speter errno = db_open(up->udb_dbname, DB_BTREE, DB_RDONLY, 103238032Speter 0644, NULL, NULL, &up->udb_dbp); 103338032Speter#endif 103438032Speter if (up->udb_dbp == NULL) 103538032Speter { 103638032Speter if (tTd(28, 1)) 103738032Speter { 103838032Speter int saveerrno = errno; 103938032Speter 104038032Speter#if DB_VERSION_MAJOR < 2 104138032Speter printf("dbopen(%s): %s\n", 104238032Speter#else 104338032Speter printf("db_open(%s): %s\n", 104438032Speter#endif 104538032Speter up->udb_dbname, 104638032Speter errstring(errno)); 104738032Speter errno = saveerrno; 104838032Speter } 104938032Speter if (errno != ENOENT && errno != EACCES) 105038032Speter { 105138032Speter if (LogLevel > 2) 105238032Speter sm_syslog(LOG_ERR, e->e_id, 105338032Speter#if DB_VERSION_MAJOR < 2 105438032Speter "dbopen(%s): %s", 105538032Speter#else 105638032Speter "db_open(%s): %s", 105738032Speter#endif 105838032Speter up->udb_dbname, 105938032Speter errstring(errno)); 106038032Speter up->udb_type = UDB_EOLIST; 106138032Speter if (up->udb_dbname != spec) 106238032Speter free(up->udb_dbname); 106338032Speter goto tempfail; 106438032Speter } 106538032Speter if (up->udb_dbname != spec) 106638032Speter free(up->udb_dbname); 106738032Speter break; 106838032Speter } 106938032Speter up->udb_type = UDB_DBFETCH; 107038032Speter ents++; 107138032Speter up++; 107238032Speter break; 107338032Speter#endif 107438032Speter 107538032Speter default: 107638032Speterbadspec: 107738032Speter syserr("Unknown UDB spec %s", spec); 107838032Speter break; 107938032Speter } 108038032Speter } 108138032Speter up->udb_type = UDB_EOLIST; 108238032Speter 108338032Speter if (tTd(28, 4)) 108438032Speter { 108538032Speter for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 108638032Speter { 108738032Speter switch (up->udb_type) 108838032Speter { 108938032Speter#if DAEMON 109038032Speter case UDB_REMOTE: 109138032Speter printf("REMOTE: addr %s, timeo %d\n", 109238032Speter anynet_ntoa((SOCKADDR *) &up->udb_addr), 109338032Speter up->udb_timeout); 109438032Speter break; 109538032Speter#endif 109638032Speter 109738032Speter case UDB_DBFETCH: 109838032Speter#ifdef NEWDB 109938032Speter printf("FETCH: file %s\n", 110038032Speter up->udb_dbname); 110138032Speter#else 110238032Speter printf("FETCH\n"); 110338032Speter#endif 110438032Speter break; 110538032Speter 110638032Speter case UDB_FORWARD: 110738032Speter printf("FORWARD: host %s\n", 110838032Speter up->udb_fwdhost); 110938032Speter break; 111038032Speter 111138032Speter case UDB_HESIOD: 111238032Speter printf("HESIOD\n"); 111338032Speter break; 111438032Speter 111538032Speter default: 111638032Speter printf("UNKNOWN\n"); 111738032Speter break; 111838032Speter } 111938032Speter } 112038032Speter } 112138032Speter 112238032Speter UdbInitialized = TRUE; 112338032Speter errno = 0; 112438032Speter return EX_OK; 112538032Speter 112638032Speter /* 112738032Speter ** On temporary failure, back out anything we've already done 112838032Speter */ 112938032Speter 113038032Speter tempfail: 113138032Speter#ifdef NEWDB 113238032Speter for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 113338032Speter { 113438032Speter if (up->udb_type == UDB_DBFETCH) 113538032Speter { 113638032Speter#if DB_VERSION_MAJOR < 2 113738032Speter (*up->udb_dbp->close)(up->udb_dbp); 113838032Speter#else 113938032Speter errno = (*up->udb_dbp->close)(up->udb_dbp, 0); 114038032Speter#endif 114138032Speter } 114238032Speter } 114338032Speter#endif 114438032Speter return EX_TEMPFAIL; 114538032Speter} 114638032Speter 114738032Speterint 114838032Speter_udb_parsespec(udbspec, opt, maxopts) 114938032Speter char *udbspec; 115038032Speter struct option opt[]; 115138032Speter int maxopts; 115238032Speter{ 115338032Speter register char *spec; 115438032Speter register char *spec_end; 115538032Speter register int optnum; 115638032Speter 115738032Speter spec_end = strchr(udbspec, ':'); 115838032Speter for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 115938032Speter { 116038032Speter register char *p; 116138032Speter 116238032Speter while (isascii(*spec) && isspace(*spec)) 116338032Speter spec++; 116438032Speter spec_end = strchr(spec, ':'); 116538032Speter if (spec_end != NULL) 116638032Speter *spec_end++ = '\0'; 116738032Speter 116838032Speter opt[optnum].name = spec; 116938032Speter opt[optnum].val = NULL; 117038032Speter p = strchr(spec, '='); 117138032Speter if (p != NULL) 117238032Speter opt[optnum].val = ++p; 117338032Speter } 117438032Speter return optnum; 117538032Speter} 117638032Speter 117738032Speter#ifdef HESIOD 117838032Speter 117938032Speterint 118038032Speterhes_udb_get(key, info) 118138032Speter DBT *key; 118238032Speter DBT *info; 118338032Speter{ 118438032Speter char *name, *type; 118538032Speter char **hp; 118638032Speter char kbuf[MAXKEY + 1]; 118738032Speter 118838032Speter if (strlen(key->data) >= (SIZE_T) sizeof kbuf) 118938032Speter return 0; 119038032Speter strcpy(kbuf, key->data); 119138032Speter name = kbuf; 119238032Speter type = strrchr(name, ':'); 119338032Speter if (type == NULL) 119438032Speter return 1; 119538032Speter *type++ = '\0'; 119638032Speter if (strchr(name, '@') != NULL) 119738032Speter return 1; 119838032Speter 119938032Speter if (tTd(28, 1)) 120038032Speter printf("hes_udb_get(%s, %s)\n", name, type); 120138032Speter 120238032Speter /* make the hesiod query */ 120338032Speter#ifdef HESIOD_INIT 120438032Speter if (HesiodContext == NULL && hesiod_init(&HesiodContext) != 0) 120538032Speter return -1; 120638032Speter hp = hesiod_resolve(HesiodContext, name, type); 120738032Speter#else 120838032Speter hp = hes_resolve(name, type); 120938032Speter#endif /* HESIOD_INIT */ 121038032Speter *--type = ':'; 121138032Speter#ifdef HESIOD_INIT 121238032Speter if (hp == NULL) 121338032Speter return 1; 121438032Speter if (*hp == NULL) 121538032Speter { 121638032Speter hesiod_free_list(HesiodContext, hp); 121738032Speter if (errno == ECONNREFUSED || errno == EMSGSIZE) 121838032Speter return -1; 121938032Speter return 1; 122038032Speter } 122138032Speter#else 122238032Speter if (hp == NULL || hp[0] == NULL) 122338032Speter { 122438032Speter /* network problem or timeout */ 122538032Speter if (hes_error() == HES_ER_NET) 122638032Speter return -1; 122738032Speter 122838032Speter return 1; 122938032Speter } 123038032Speter#endif /* HESIOD_INIT */ 123138032Speter else 123238032Speter { 123338032Speter /* 123438032Speter ** If there are multiple matches, just return the 123538032Speter ** first one. 123638032Speter ** 123738032Speter ** XXX These should really be returned; for example, 123838032Speter ** XXX it is legal for :maildrop to be multi-valued. 123938032Speter */ 124038032Speter 124138032Speter info->data = hp[0]; 124238032Speter info->size = (size_t) strlen(info->data); 124338032Speter } 124438032Speter 124538032Speter if (tTd(28, 80)) 124638032Speter printf("hes_udb_get => %s\n", *hp); 124738032Speter 124838032Speter return 0; 124938032Speter} 125038032Speter#endif /* HESIOD */ 125138032Speter 125238032Speter#else /* not USERDB */ 125338032Speter 125438032Speterint 125538032Speterudbexpand(a, sendq, aliaslevel, e) 125638032Speter ADDRESS *a; 125738032Speter ADDRESS **sendq; 125838032Speter int aliaslevel; 125938032Speter ENVELOPE *e; 126038032Speter{ 126138032Speter return EX_OK; 126238032Speter} 126338032Speter 126438032Speter#endif /* USERDB */ 1265