* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988 Regents of the University of California.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)udb.c 5.21 (Berkeley) %G% (with USERDB)";
static char sccsid
[] = "@(#)udb.c 5.21 (Berkeley) %G% (without USERDB)";
** UDB.C -- interface between sendmail and Berkeley User Data Base.
** This depends on the 4.4BSD db package.
char *udb_spec
; /* string version of spec */
int udb_type
; /* type of entry */
char *udb_default
; /* default host for outgoing mail */
/* type UE_REMOTE -- do remote call for lookup */
struct sockaddr_in _udb_addr
; /* address */
int _udb_timeout
; /* timeout */
#define udb_addr udb_u.udb_remote._udb_addr
#define udb_timeout udb_u.udb_remote._udb_timeout
/* type UE_FORWARD -- forward message to remote */
char *_udb_fwdhost
; /* name of forward host */
#define udb_fwdhost udb_u.udb_forward._udb_fwdhost
/* type UE_FETCH -- lookup in local database */
char *_udb_dbname
; /* pathname of database */
DB
*_udb_dbp
; /* open database ptr */
#define udb_dbname udb_u.udb_lookup._udb_dbname
#define udb_dbp udb_u.udb_lookup._udb_dbp
#define UDB_EOLIST 0 /* end of list */
#define UDB_SKIP 1 /* skip this entry */
#define UDB_REMOTE 2 /* look up in remote database */
#define UDB_DBFETCH 3 /* look up in local database */
#define UDB_FORWARD 4 /* forward to remote host */
#define MAXUDBENT 10 /* maximum number of UDB entries */
** UDBEXPAND -- look up user in database and expand
** a -- address to expand.
** sendq -- pointer to head of sendq to put the expansions in.
** EX_TEMPFAIL -- if something "odd" happened -- probably due
** to accessing a file on an NFS server that is down.
struct udbent UdbEnts
[MAXUDBENT
+ 1];
bool UdbInitialized
= FALSE
;
register struct udbent
*up
;
printf("expand(%s)\n", a
->q_paddr
);
/* make certain we are supposed to send to this address */
if (bitset(QDONTSEND
, a
->q_flags
))
/* on first call, locate the database */
if (_udbx_init() == EX_TEMPFAIL
)
/* short circuit the process if no chance of a match */
if (UdbSpec
== NULL
|| UdbSpec
[0] == '\0')
/* if name is too long, assume it won't match */
if (strlen(a
->q_user
) > sizeof keybuf
- 12)
/* if name begins with a colon, it indicates our metadata */
/* build actual database key */
(void) strcpy(keybuf
, a
->q_user
);
(void) strcat(keybuf
, ":maildrop");
for (up
= UdbEnts
; !breakout
; up
++)
** Select action based on entry type.
** On dropping out of this switch, "class" should
** explain the type of the data, and "user" should
** contain the user information.
i
= (*up
->udb_dbp
->seq
)(up
->udb_dbp
, &key
, &info
, R_CURSOR
);
if (i
> 0 || info
.size
<= 0)
printf("expand: no match on %s\n", keybuf
);
while (i
== 0 && key
.size
== keylen
&&
bcmp(key
.data
, keybuf
, keylen
) == 0)
if (info
.size
< sizeof buf
)
user
= xalloc(info
.size
+ 1);
bcopy(info
.data
, user
, info
.size
);
message(Arpa_Info
, "expanded to %s", user
);
sendtolist(user
, a
, sendq
, e
);
/* get the next record */
i
= (*up
->udb_dbp
->seq
)(up
->udb_dbp
, &key
, &info
, R_NEXT
);
syserr("udbexpand: db-get stat %s");
/* not yet implemented */
i
= strlen(up
->udb_fwdhost
) + strlen(a
->q_user
) + 1;
(void) sprintf(user
, "%s@%s", a
->q_user
, up
->udb_fwdhost
);
message(Arpa_Info
, "expanded to %s", user
);
sendtolist(user
, a
, sendq
, e
);
** UDBSENDER -- return canonical external name of sender, given local name
** sender -- the name of the sender on the local machine.
** The external name for this sender, if derivable from the
** NULL -- if nothing is changed from the database.
register struct udbent
*up
;
printf("udbsender(%s)\n", sender
);
if (_udbx_init() == EX_TEMPFAIL
)
/* short circuit if no spec */
if (UdbSpec
== NULL
|| UdbSpec
[0] == '\0')
/* long names can never match and are a pain to deal with */
if (strlen(sender
) > sizeof keybuf
- 12)
/* names beginning with colons indicate metadata */
(void) strcpy(keybuf
, sender
);
(void) strcat(keybuf
, ":mailname");
for (up
= UdbEnts
; up
->udb_type
!= UDB_EOLIST
; up
++)
** Select action based on entry type.
i
= (*up
->udb_dbp
->get
)(up
->udb_dbp
, &key
, &info
, 0);
if (i
!= 0 || info
.size
<= 0)
printf("udbsender: no match on %s\n",
p
= xalloc(info
.size
+ 1);
bcopy(info
.data
, p
, info
.size
);
printf("udbsender ==> %s\n", p
);
** Nothing yet. Search again for a default case. But only
** use it if we also have a forward (:maildrop) pointer already
(void) strcpy(keybuf
, sender
);
(void) strcat(keybuf
, ":maildrop");
for (up
= UdbEnts
; up
->udb_type
!= UDB_EOLIST
; up
++)
/* get the default case for this database */
if (up
->udb_default
== NULL
)
key
.data
= ":default:mailname";
key
.size
= strlen(key
.data
);
i
= (*up
->udb_dbp
->get
)(up
->udb_dbp
, &key
, &info
, 0);
if (i
!= 0 || info
.size
<= 0)
/* save the default case */
up
->udb_default
= xalloc(info
.size
+ 1);
bcopy(info
.data
, up
->udb_default
, info
.size
);
up
->udb_default
[info
.size
] = '\0';
else if (up
->udb_default
[0] == '\0')
/* we have a default case -- verify user:maildrop */
i
= (*up
->udb_dbp
->get
)(up
->udb_dbp
, &key
, &info
, 0);
if (i
!= 0 || info
.size
<= 0)
/* nope -- no aliasing for this user */
/* they exist -- build the actual address */
p
= xalloc(strlen(sender
) + strlen(up
->udb_default
) + 2);
(void) strcpy(p
, sender
);
(void) strcat(p
, up
->udb_default
);
printf("udbsender ==> %s\n", p
);
/* still nothing.... too bad */
** _UDBX_INIT -- parse the UDB specification, opening any valid entries.
** EX_TEMPFAIL -- if it appeared it couldn't get hold of a
** database due to a host being down or some similar
** (recoverable) situation.
** Fills in the UdbEnts structure from UdbSpec.
register struct udbent
*up
;
UdbSpec
= UDB_DEFAULT_SPEC
;
register struct hostent
*h
;
char *mxhosts
[MAXMXHOSTS
+ 1];
struct option opts
[MAXUDBOPTS
+ 1];
while (*p
== ' ' || *p
== '\t' || *p
== ',')
nopts
= _udb_parsespec(spec
, opts
, MAXUDBOPTS
);
** Decode database specification.
** In the sendmail tradition, the leading character
** defines the semantics of the rest of the entry.
** +hostname -- send a datagram to the udb server
** on host "hostname" asking for the
** home mail server for this user.
** *hostname -- similar to +hostname, except that the
** hostname is searched as an MX record;
** resulting hosts are searched as for
** +mxhostname. If no MX host is found,
** this is the same as +hostname.
** @hostname -- forward email to the indicated host.
** This should be the last in the list,
** since it always matches the input.
** /dbname -- search the named database on the local
** host using the Berkeley db package.
case '+': /* search remote database */
case '*': /* search remote database (expand MX) */
nmx
= getmxrr(spec
+ 1, mxhosts
, "", &rcode
);
printf("getmxrr(%s): %d", spec
+ 1, nmx
);
for (i
= 0; i
<= nmx
; i
++)
printf(" %s", mxhosts
[i
]);
for (i
= 0; i
< nmx
; i
++)
h
= gethostbyname(mxhosts
[i
]);
up
->udb_type
= UDB_REMOTE
;
up
->udb_addr
.sin_family
= h
->h_addrtype
;
(char *) &up
->udb_addr
.sin_addr
,
up
->udb_addr
.sin_port
= UdbPort
;
up
->udb_timeout
= UdbTimeout
;
/* set up a datagram socket */
UdbSock
= socket(AF_INET
, SOCK_DGRAM
, 0);
(void) fcntl(UdbSock
, F_SETFD
, 1);
case '@': /* forward to remote host */
up
->udb_type
= UDB_FORWARD
;
up
->udb_fwdhost
= spec
+ 1;
case '/': /* look up remote name */
up
->udb_dbp
= dbopen(spec
, O_RDONLY
, 0644, DB_BTREE
, NULL
);
if (errno
!= ENOENT
&& errno
!= EACCES
)
up
->udb_type
= UDB_EOLIST
;
up
->udb_type
= UDB_DBFETCH
;
up
->udb_type
= UDB_EOLIST
;
for (up
= UdbEnts
; up
->udb_type
!= UDB_EOLIST
; up
++)
printf("REMOTE: addr %s, timeo %d\n",
inet_ntoa(up
->udb_addr
.sin_addr
),
printf("FETCH: file %s\n",
printf("FORWARD: host %s\n",
** On temporary failure, back out anything we've already done
for (up
= UdbEnts
; up
->udb_type
!= UDB_EOLIST
; up
++)
if (up
->udb_type
== UDB_DBFETCH
)
(*up
->udb_dbp
->close
)(up
->udb_dbp
);
_udb_parsespec(udbspec
, opt
, maxopts
)
spec_end
= strchr(udbspec
, ':');
for (optnum
= 0; optnum
< maxopts
&& (spec
= spec_end
) != NULL
; optnum
++)
spec_end
= strchr(spec
, ':');