|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T r
Length: 28952 (0x7118)
Types: TextFile
Names: »route.c«
└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
└─⟦2fafebccf⟧ »EurOpenD3/mail/smail3.1.19.tar.Z«
└─⟦bcd2bc73f⟧
└─⟦this⟧ »src/route.c«
/* @(#)route.c 3.17 12/17/88 21:14:11 */
/*
* Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*/
/*
* route.c:
* Compute route to target hosts and fill in the transport,
* next host and next address for a recipient address.
*
* external functions: route_remote_addrs, verify_remote_addrs,
* cache_routers, finish_routers,
* match_end_domain, route_driver_finish,
* find_router, find_route_driver,
* read_router_file, read_method_file
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "defs.h"
#include "smail.h"
#include "route.h"
#include "transport.h"
#include "addr.h"
#include "dys.h"
#include "log.h"
#include "exitcodes.h"
#include "parse.h"
#include "smailconf.h"
#ifndef DEPEND
# include "extern.h"
# include "debug.h"
# include "error.h"
#endif
/* variables exported from this file */
int cached_routers = FALSE; /* TRUE if cache_routers() called */
/* functions local to this file */
static void premunge_remote_addrs();
static void compute_transport();
static void compute_next_addr();
static char *rebuild_work_addr();
static char *router_read_method();
static char *router_driv_function();
\f
/*
* route_remote_addrs - route addrs to transport, next host and next_addr
*/
void
route_remote_addrs(in, out, retry, defer, fail)
struct addr *in; /* input local-form addrs */
struct addr **out; /* output resolved addrs */
struct addr **retry; /* addr structures to reparse */
struct addr **defer; /* addrs to defer to a later time */
struct addr **fail; /* unresolvable addrs */
{
struct router *rp; /* temp for stepping thru routers */
register struct addr *cur; /* temp for stepping through addrs */
struct addr *next; /* next value for cur */
DEBUG(DBG_ROUTE_HI, "route_remote_addrs called\n");
/* first munge the addr structure as required */
premunge_remote_addrs(in);
/*
* give the complete input list to each router in turn.
* router drivers must obey the rules about overriding
* of routes produced by previous routers. At the end
* this routine will cleanup the addr structures.
*/
for (rp = routers; rp; rp = rp->succ) {
struct addr *new_in; /* addrs to put through next router */
/* look up the router driver by name */
struct route_driver *driver = find_route_driver(rp->driver);
if (driver == NULL) {
/*
* ERR_109 - router driver not found
*
* DESCRIPTION
* A driver name was not in the table of router drivers.
*
* ACTIONS
* Defer all input addresses with configuration errors.
* Since it cannot be known if this router would have
* overridden previous routers, all input addresses must be
* deferred.
*
* RESOLUTION
* The postmaster must check the router configuration
* before deliver can be performed.
*/
insert_addr_list(in,
defer,
note_error(ERR_CONFERR|ERR_109,
xprintf(
"router %s: driver %s not found",
rp->name,
rp->driver)));
return;
}
/* call the driver */
new_in = NULL;
(*driver->driver)(rp, in, &new_in, defer, fail);
in = new_in;
}
/*
* now that all routers have had their chance we cleanup
* the input list to form the output list of resolved
* addresses, and note addresses that no router could
* route to.
*/
for (cur = in; cur; cur = next) {
next = cur->succ;
if (cur->match_count == -1 ||
(cur->flags & (ADDR_PARTLOCAL|ADDR_FULLMATCH)) == ADDR_PARTLOCAL)
{
/* no router could match the target, fail address */
exitvalue = EX_NOHOST; /* set exit status */
/*
* ERR_101 - unknown host
*
* DESCRIPTION
* The target in this addr structure was not matched by any
* of the routers.
*
* ACTIONS
* A message is sent to the owner of the address, or to the
* sender if the address has no owner.
*
* RESOLUTION
* The address owner or sender should determine the correct
* domain name. If the owner or sender believes the
* message was in error, he or she should send mail to the
* postmaster.
*/
cur->error = note_error(ERR_NSOWNER|ERR_101, "unknown host");
cur->succ = *fail;
*fail = cur;
continue;
}
if (cur->next_host == NULL) {
/* routed to local host, reparse */
(void) strcpy(cur->work_addr, cur->remainder);
cur->remainder = NULL;
cur->succ = *retry;
*retry = cur;
continue;
}
if (cur->transport == NULL) {
/* router did not compute a transport, we must */
compute_transport(cur);
if (cur->error && (cur->error->info & ERR_CONFERR)) {
/* defer configuration errors */
cur->succ = *defer;
*defer = cur;
continue;
}
}
if (cur->next_addr == NULL) {
/* apply rules from Section 3.3 for next_addr */
compute_next_addr(cur);
}
if (cur->error) {
/* got an error, fail the address */
cur->succ = *fail;
*fail = cur;
} else {
/* link into the output queue if things went okay */
if (cur->next_host) {
DEBUG3(DBG_ROUTE_LO, " routed %s --> %s at %s\n",
cur->in_addr, cur->next_addr, cur->next_host);
} else {
DEBUG2(DBG_ROUTE_LO,
" routed %s --> %s on the local host\n",
cur->in_addr, cur->next_addr);
}
cur->succ = *out;
*out = cur;
}
}
}
\f
/*
* verify_remote_addrs - perform quick verify of remote addresses
*
* form a list of okay (verified) addrs, plus deferred (not currently
* determinable) addrs and failed (not deliverable) addrs.
*/
void
verify_remote_addrs(in, okay, defer, fail)
struct addr *in; /* input remote addr list */
struct addr **okay; /* output list of verified addrs */
struct addr **defer; /* temporariliy unverifiable addrs */
struct addr **fail; /* unverified addrs */
{
struct router *rp; /* temp for stepping thru routers */
register struct addr *cur; /* temp for stepping through addrs */
DEBUG(DBG_ROUTE_HI, "verify_remote_addrs called\n");
/* first munge the addr structure as required */
premunge_remote_addrs(in);
/*
* give the complete input list to each router in turn.
* router drivers must obey the rules about overriding
* of routes produced by previous routers. At the end
* this routine will cleanup the addr structures.
*/
for (rp = routers; in && rp; rp = rp->succ) {
struct addr *retry; /* addrs to put through next router */
/* look up the router driver by name */
struct route_driver *driver = find_route_driver(rp->driver);
if (driver == NULL) {
/*
* ERR_109 - router driver not found
*
* DESCRIPTION
* A driver name was not in the table of router drivers.
*
* ACTIONS
* Defer all input addresses with configuration errors.
* Since it cannot be known if this router would have
* overridden previous routers, all input addresses must be
* deferred.
*
* RESOLUTION
* The postmaster must check the router configuration
* before delivery can be performed.
*/
insert_addr_list(in,
defer,
note_error(ERR_CONFERR|ERR_109,
xprintf(
"router %s: driver %s not found",
rp->name,
rp->driver)));
return;
}
/* call the driver */
retry = NULL;
(*driver->verify)(rp, in, &retry, okay, defer, fail);
in = retry;
}
/*
* some cleanup is required on the the addr list
*/
for (cur = in; cur; cur = cur->succ) {
if (cur->flags&ADDR_PUTDOT) {
/* a dot was removed from the end of the target, put it back */
cur->target[strlen(cur->target)] = '.';
} else if (cur->flags&ADDR_MOVEDOT) {
/* move a dot back to the end of the target, from the front */
(void) strcpy(cur->target, cur->target + 1);
cur->target[strlen(cur->target)] = '.';
}
}
}
\f
/*
* cache_routers - call cache entrypoints for all routers
*
* cache information used by router drivers. This can be called when
* it is determined that there will be an attempt to deliver more than
* one mail message, to increase the overall efficiency of the mailer.
*
* Daemons can call this periodically to recache stale data.
*/
void
cache_routers()
{
struct router *rp; /* temp for stepping thru routers */
struct route_driver *driver;
for (rp = routers; rp; rp = rp->succ) {
driver = find_route_driver(rp->driver);
if (driver && driver->cache) {
(*driver->cache)(rp);
}
}
cached_routers = TRUE;
}
#ifdef notyet
/*
* finish_routers - free resources used by all routers
*
* free information that was cached by routers or used by routers in
* the process of routing. Routers can cache data for efficiency, or
* can maintain state between invocations. This function is called
* when routers will no longer be needed, allowing routers to free any
* resources that they were using that will no longer be needed. For
* example, it is a good idea for routers to close any files that they
* opened, as file descriptors are a precious resource in some
* machines.
*/
void
finish_routers()
{
struct router *rp; /* temp for stepping thru routers */
struct route_driver *driver;
for (rp = routers; rp; rp = rp->succ) {
driver = find_route_driver(rp->driver);
if (driver && driver->finish) {
(*driver->finish)(rp);
}
}
cached_routers = FALSE;
}
#endif
\f
/*
* premunge_remote_addrs - pre-routing munging on remote addr structures
*
* do a premunge on the target, by taking a dot at the end and
* putting it at the front (if one is not already at the front).
* Also, initialize the flags field, to remove any extraneously
* set flags.
*/
static void
premunge_remote_addrs(list)
struct addr *list; /* list of remote addrs to premunge */
{
register struct addr *cur; /* current address being processed */
for (cur = list; cur; cur = cur->succ) {
register int len = strlen(cur->target);
cur->flags &= ~(ADDR_PUTDOT |
ADDR_MOVEDOT |
ADDR_ERROR |
ADDR_FINISHED |
ADDR_FULLMATCH |
ADDR_NOTUSER |
ADDR_ISUSER);
if (cur->target[len-1] == '.') {
if (cur->target[0] == '.') {
cur->target[len-1] = '\0';
cur->flags |= ADDR_PUTDOT;
} else {
/* have to move the target so that a dot can be inserted */
register char *p = cur->target;
while (--len) {
p[len] = p[len - 1];
}
p[0] = '.';
cur->flags |= ADDR_MOVEDOT;
}
}
}
}
\f
/*
* match_end_domain - try to match one of a list of domains against a target
*
* given a list of domains separated by colons, determine if the given
* target ends in one of those domains. If so, return a pointer to the
* `.' that precedes the domain that matched, else return NULL. The
* list is scanned from left to right, with the first match returned.
*/
char *
match_end_domain(domains, target)
char *domains; /* colon separated list of domains */
char *target; /* target to test against */
{
register char *cur; /* current domain being checked */
int len = strlen(target); /* length of target */
for (cur = strcolon(domains); cur; cur = strcolon((char *)NULL))
{
register int domlen = strlen(cur);
if (len > domlen &&
target[len - domlen - 1] == '.' &&
EQIC(target + len-domlen, cur))
{
return target + len-domlen - 1;
}
}
/* did not end in one of the domains */
return NULL;
}
/*
* route_driver_finish - fill in addr structures for a route driver
*
* route drivers may call this routine for each addr structure they
* have computed a route to. This routine completes the addr structure
* if it is determined that the computed route has precedence over any
* previously computed route. In the case that the computed route has
* precedence over future routers, the ADDR_FINISHED flag is set in
* the addr.flags struct element.
*/
void
route_driver_finish(rp, addr, match_count, next_host, route, transport)
struct router *rp; /* the calling router */
struct addr *addr; /* addr structure in question */
int match_count; /* target match count */
char *next_host; /* computed next_host value */
char *route; /* computed route value */
struct transport *transport; /* optional transport from driver */
{
if (addr->flags&ADDR_FINISHED) {
/*
* a router erroneously found a new route for a finished addr,
* ignore it
*/
return;
}
if (match_count > addr->match_count) {
if (strlen(addr->target) == match_count ||
(addr->target[0] == '.' &&
strlen(addr->target) == match_count - 1))
{
addr->flags |= ADDR_FINISHED|ADDR_FULLMATCH;
} else {
if (next_host == NULL) {
/* partial match to local host not considered a match */
return;
}
if (rp->flags&USE_ALWAYS) {
addr->flags |= ADDR_FINISHED;
}
}
/* this route has precedence over previous routes */
addr->router = rp;
addr->match_count = match_count;
if (addr->next_host) {
xfree(addr->next_host);
}
if (next_host) {
addr->next_host = COPY_STRING(next_host);
} else {
addr->next_host = NULL;
}
if (addr->route) {
xfree(addr->route);
}
if (route) {
addr->route = COPY_STRING(route);
} else {
addr->route = NULL;
}
addr->transport = transport;
if (addr->next_addr) {
/*
* cleanup after a previous router that computed the
* next_addr itself
*/
xfree(addr->next_addr);
addr->next_addr = NULL;
}
DEBUG3(DBG_ROUTE_LO, "%s: %s matched by %s:\n",
addr->in_addr, addr->target, addr->router->name);
}
}
/*
* compute_next_addr - compute the next_addr for a routed recipient address
*/
static void
compute_next_addr(addr)
struct addr *addr; /* address to be completed */
{
if (addr->error) {
/* ignore the addr if it already has a pending error condition */
return;
}
if (addr->next_addr) {
/* free a previously allocated next_addr */
xfree(addr->next_addr);
addr->next_addr = NULL;
}
if (addr->flags&ADDR_PUTDOT) {
/* a dot was removed from the end of the target, put it back */
addr->target[strlen(addr->target)] = '.';
} else if (addr->flags&ADDR_MOVEDOT) {
/* move a dot back to the end of the target, from the front */
(void) strcpy(addr->target, addr->target+1);
addr->target[strlen(addr->target)] = '.';
}
/*
* TODO: perhaps for STRICT_TPORT we should build an ARPA route-addr
*/
if ((addr->transport->flags&UUCP_ONLY) ||
addr->route && (addr->flags&ADDR_FULLMATCH) == 0 ||
addr->route != NULL && index(addr->route, '!'))
{
/* we need to construct a !-route to accomplish delivery */
char *error;
char *uucp_route = build_partial_uucp_route(addr->remainder, &error);
if (uucp_route == NULL) {
/* build_partial_uucp_route failed, fail the address */
/*
* ERR_108 - error building path
*
* DESCRIPTION
* build_partial_uucp_route() returned an error while
* attempting to build a UUCP-path from the remainder in
* the address. The specific error was returned in
* `error'.
*
* ACTIONS
* Notify the sender or the owner of the address of the
* problem.
*
* RESOLUTION
* The sender or owner should correct the supplied address
* to use acceptible addressing forms.
*/
addr->error = note_error(ERR_NSOWNER|ERR_108,
xprintf("error building path: %s",
error));
exitvalue = EX_DATAERR; /* set the exit status */
return;
}
if (addr->route == NULL) {
if (addr->flags&ADDR_FULLMATCH) {
addr->next_addr = uucp_route;
} else {
/* not a full match, so we need to add in the target */
addr->next_addr = xmalloc((unsigned)(strlen(uucp_route) +
strlen(addr->target) + 2));
(void) sprintf(addr->next_addr, "%s!%s",
addr->target, uucp_route);
xfree(uucp_route);
}
} else {
/* we need to prepend the route to build the next_addr */
addr->next_addr = xmalloc((unsigned)(strlen(addr->route) +
strlen(uucp_route) +
((addr->flags&ADDR_FULLMATCH)? 2:
strlen(addr->target) + 3)));
if (addr->flags&ADDR_FULLMATCH) {
(void) sprintf(addr->next_addr, "%s!%s",
addr->route, uucp_route);
xfree(uucp_route);
} else {
/*
* if we didn't match target completely we need to
* append it to the route, so that, presumably, a
* gateway will route it.
*/
(void) sprintf(addr->next_addr, "%s!%s!%s",
addr->route, addr->target, uucp_route);
xfree(uucp_route);
}
}
} else if (addr->route) {
char *error;
switch (parse_address(addr->remainder, (char **)NULL, &error)) {
case UUCP_ROUTE:
addr->next_addr = xprintf("%s!%s", addr->route, addr->remainder);
break;
case RFC_ROUTE:
case RFC_ENDROUTE:
addr->next_addr = xprintf("@%s,%s", addr->route, addr->remainder);
break;
case MAILBOX:
addr->next_addr = xprintf("@%s:%s", addr->route, addr->remainder);
break;
default:
addr->next_addr = xprintf("%s@%s", addr->remainder, addr->route);
break;
}
} else {
if (addr->flags&ADDR_FULLMATCH) {
addr->next_addr = COPY_STRING(addr->remainder);
} else {
addr->next_addr =
rebuild_work_addr((int)(addr->flags&ADDR_FORM_MASK),
addr->target, addr->remainder);
}
}
return;
}
\f
/*
* rebuild_work_addr - recompute work_addr from target and remainder
*
* we need to reconstruct the work_addr used to form the
* target and remainder. Remember that work_addr is
* modified in computing target and remainder.
*/
static char *
rebuild_work_addr(form, target, remainder)
int form; /* method used to parse address */
char *target; /* target from a work_addr */
char *remainder; /* remainder from the same work_addr */
{
register char *work_addr;
register int len = strlen(target) + strlen(remainder);
switch(form) {
case RFC_ROUTE:
/* "@target,remainder" */
work_addr = xmalloc(len + sizeof("@,"));
(void) sprintf(work_addr, "@%s,%s", target, remainder);
break;
case RFC_ENDROUTE:
/* "@target:remainder" '@' should still be there */
work_addr = xmalloc(len + sizeof("@:"));
(void) sprintf(work_addr, "@%s:%s", target, remainder);
break;
case MAILBOX:
/* "remainder@target" */
work_addr = xmalloc(len + sizeof("@"));
(void) sprintf(work_addr, "%s@%s", remainder, target);
break;
case UUCP_ROUTE:
/* "target!remainder" */
work_addr = xmalloc(len + sizeof("!"));
(void) sprintf(work_addr, "%s!%s", target, remainder);
break;
case BERKENET:
/* "target:remainder" */
work_addr = xmalloc(len + sizeof(":"));
(void) sprintf(work_addr, "%s:%s", target, remainder);
break;
case DECNET:
/* "target::remainder" */
work_addr = xmalloc(len + sizeof("::"));
(void) sprintf(work_addr, "%s::%s", target, remainder);
break;
case PCT_MAILBOX:
/* "remainder%target" */
work_addr = xmalloc(len + sizeof("%"));
(void) sprintf(work_addr, "%s%%%s", remainder, target);
break;
default:
/* internal error? */
write_log(LOG_PANIC,
"internal error: rebuild_work_addr: unknown form: target=%s, remainder=%s, in_addr=%s",
target, remainder, work_addr);
/* use @host:addr, just for fun */
work_addr = xmalloc(len + sizeof("@:"));
(void) sprintf(work_addr, "@%s:%s", target, remainder);
break;
}
return work_addr;
}
\f
/*
* compute_transport - compute the transport from any available defaults
*
* some router drivers do not assign transports themselves but rely
* on defaults in the router structure. These defaults can either be
* a single default transport or a table of transports associated with
* host names, where the special name `*' is a catchall.
*/
static void
compute_transport(addr)
struct addr *addr;
{
struct router *rp = addr->router;
DEBUG2(DBG_ROUTE_HI, "compute_transport called: host=%s, router=%s\n",
addr->next_host, rp->name);
/* no transport yet assigned, look around for defaults */
if (rp->method) {
/* there is a "method", table of possible transports */
struct method *mp;
mp = rp->method;
while (mp->host) {
if (EQIC(addr->next_host, mp->host) || EQ(mp->host, "*")) {
/* found the right transport, use it */
addr->transport = find_transport(mp->transport);
if (addr->transport == NULL) {
/* configuration error, the transport does not exist */
/*
* ERR_102 - method transport not found
*
* DESCRIPTION
* The transport to be used for the method was not
* found in the table of transports.
*
* ACTIONS
* Defer the message as a configuration error.
*
* RESOLUTION
* The postmaster needs to correct the eror in the
* method file.
*/
addr->error = note_error(ERR_CONFERR|ERR_102,
xprintf(
"router %s: method transport %s not found for host %s",
rp->name,
mp->transport,
mp->host));
return;
}
DEBUG2(DBG_ROUTE_MID, "use transport %s for %s\n",
addr->transport->name, addr->in_addr);
return;
}
mp++;
}
}
if (rp->default_transport) {
/* there is a default, use it */
addr->transport = find_transport(rp->default_transport);
if (addr->transport == NULL) {
/*
* ERR_103 - default transport not found
*
* DESCRIPTION
* The default transport for the matching router is not in the
* list of transports.
*
* ACTIONS
* Defer the message as a configuration error.
*
* RESOLUTION
* The postmaster should correct the router file.
*/
addr->error = note_error(ERR_CONFERR|ERR_103,
xprintf(
"router %s: default transport %s not found",
rp->name,
rp->default_transport));
return;
}
DEBUG2(DBG_ROUTE_MID, "use default transport %s for %s\n",
addr->transport->name, addr->in_addr);
return;
} else {
/*
* ERR_110 - no transport for router
*
* DESCRIPTION
* No method or default transport was given for the router that
* produced this address.
*
* ACTIONS
* Defer the address as a configuration error.
*
* RESOLUTION
* The postmaster must check the router configuration before
* delivery to the address can be performed.
*/
addr->error = note_error(ERR_CONFERR|ERR_110,
xprintf("router %s: no transport found",
rp->name));
}
}
\f
/*
* find_router - given a router's name, return the router structure
*
* return NULL if no router of that name exists.
*/
struct router *
find_router(name)
register char *name; /* search key */
{
register struct router *rp; /* temp for stepping thru routers */
/* loop through all the routers */
for (rp = routers; rp; rp = rp->succ) {
if (EQ(rp->name, name)) {
/* found the router in question */
return rp;
}
}
return NULL; /* router not found */
}
/*
* find_route_driver - given a driver's name, return the driver structure
*
* return NULL if driver does not exist.
*/
struct route_driver *
find_route_driver(name)
register char *name; /* search key */
{
register struct route_driver *rdp; /* pointer to table of drivers */
for (rdp = route_drivers; rdp->name; rdp++) {
if (EQ(rdp->name, name)) {
return rdp; /* found the driver */
}
}
return NULL; /* driver not found */
}
\f
/*
* read_router_file - read router file
*
* read the router file and build a router list describing the
* entries. Return an error message or NULL.
*/
char *
read_router_file()
{
FILE *f; /* open router file */
char *error; /* error from read_standard_file() */
struct stat statbuf;
static struct attr_table router_generic[] = {
{ "driver", t_string, NULL, NULL, OFFSET(router, driver) },
{ "transport", t_string, NULL, NULL,
OFFSET(router, default_transport) },
{ "method", t_proc, NULL, (tup *)router_read_method, 0 },
{ "always", t_boolean, NULL, NULL, USE_ALWAYS },
};
struct attr_table *end_router_generic = ENDTABLE(router_generic);
static struct router router_template = {
NULL, /* name */
"pathalias", /* driver, a reasonable default */
NULL, /* succ will be assigned */
0, /* flags */
NULL, /* method */
NULL, /* default_transport */
NULL, /* private */
};
/*
* try to open router file, note file stat if possible
*/
if (router_file == NULL || EQ(router_file, "-")) {
return NULL;
}
f = fopen(router_file, "r");
if (f == NULL) {
if (require_configs) {
return xprintf("%s: %s", router_file, strerrno());
}
add_config_stat(router_file, (struct stat *)NULL);
return NULL;
}
(void)fstat(fileno(f), &statbuf);
add_config_stat(router_file, &statbuf);
/* call read_standard_file to do the real work */
error = read_standard_file(f,
(char *)&router_template,
sizeof(struct router),
OFFSET(router, name),
OFFSET(router, flags),
OFFSET(router, succ),
router_generic,
end_router_generic,
router_driv_function,
(char **)&routers);
/* finish up */
(void) fclose(f);
/* return any error message */
if (error) {
return xprintf("%s: %s", router_file, error);
}
return NULL;
}
/*
* router_read_method - handle method attribute for routers
*
* the method attribute specifies a file which contains a method table
* associating hosts with transports. Use of this attribute causes
* the table to be read and turned into an in-core method table.
*/
static char *
router_read_method(struct_p, attr)
char *struct_p; /* passed router structure */
struct attribute *attr; /* parsed attribute */
{
struct router *rp = (struct router *)struct_p;
char *error;
if (attr->value == off) {
rp->method = NULL;
} else if (attr->value == on) {
return xprintf("%s: boolean form for non-boolean attribute",
attr->name);
} else {
int free_method_fn = FALSE;
char *method_fn = attr->value;
if (method_fn[0] != '/') {
if (method_dir == NULL) {
return xprintf("%s: %s not absolute and no method_dir",
attr->name, method_fn);
}
method_fn = xmalloc(strlen(method_dir) + strlen(method_fn) +
sizeof("/"));
(void) sprintf(method_fn, "%s/%s", method_dir, attr->value);
free_method_fn = TRUE;
}
rp->method = read_method_file(method_fn, &error);
if (rp->method == NULL) {
return xprintf("%s %s: %s", attr->name, attr->value, error);
}
if (free_method_fn) {
xfree(method_fn);
}
}
/* everything went okay */
return NULL;
}
/*
* read_method_file - read a method file and return an in-core method table
*
* return NULL if the method file could not be opened.
*/
struct method *
read_method_file(fn, error)
char *fn; /* name of method file */
char **error; /* store error message here */
{
FILE *f = fopen(fn, "r");
struct method *methods; /* method table */
int ct = 0; /* count of methods */
char *entry; /* text of file entry */
struct stat statbuf;
if (f == NULL) {
*error = xprintf("open failed: %s", strerrno());
return NULL;
}
(void)fstat(fileno(f), &statbuf);
add_config_stat(fn, &statbuf);
/* allocate space for at least the ending record */
methods = (struct method *)xmalloc(sizeof(*methods));
/* loop and read all of the table entries in the method file */
while (entry = read_entry(f)) {
char *error;
struct attribute *new;
new = parse_table(entry, &error);
if (new == NULL) {
return NULL;
}
ct++;
methods = (struct method *)
xrealloc((char *)methods, (ct + 1)*sizeof(*methods));
methods[ct - 1].host = new->name;
methods[ct - 1].transport = new->value;
}
/* zero out the ending record */
methods[ct].host = NULL;
methods[ct].transport = NULL;
return methods;
}
static char *
router_driv_function(struct_p, driver_attrs)
char *struct_p; /* passed router structure */
struct attribute *driver_attrs; /* driver-specific attributes */
{
struct router *rp = (struct router *)struct_p;
struct route_driver *drv;
if (rp->driver == NULL) {
return xprintf("router %s: no driver attribute", rp->name);
}
drv = find_route_driver(rp->driver);
if (drv == NULL) {
return xprintf("router %s: unknown driver: %s",
rp->name, rp->driver);
}
if (drv->builder) {
return (*drv->builder)(rp, driver_attrs);
}
return NULL;
}