#include <sys_defs.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <msg.h>
#include <vstream.h>
#include <vstring.h>
#include <vstring_vstream.h>
#include <events.h>
#include <iostuff.h>
#include "mail_proto.h"
#include "mail_params.h"
#include "clnt_stream.h"
#include "resolve_clnt.h"
extern CLNT_STREAM *rewrite_clnt_stream;
static VSTRING *last_class;
static VSTRING *last_addr;
static RESOLVE_REPLY last_reply;
void resolve_clnt_init(RESOLVE_REPLY *reply)
{
reply->transport = vstring_alloc(100);
reply->nexthop = vstring_alloc(100);
reply->recipient = vstring_alloc(100);
reply->flags = 0;
}
void resolve_clnt(const char *class, const char *addr, RESOLVE_REPLY *reply)
{
char *myname = "resolve_clnt";
VSTREAM *stream;
if (last_addr == 0) {
last_class = vstring_alloc(10);
last_addr = vstring_alloc(100);
resolve_clnt_init(&last_reply);
}
#define STR vstring_str
if (addr == STR(reply->recipient))
msg_panic("%s: result clobbers input", myname);
#define IFSET(flag, text) ((reply->flags & (flag)) ? (text) : "")
if (*addr && strcmp(addr, STR(last_addr)) == 0
&& strcmp(class, STR(last_class)) == 0) {
vstring_strcpy(reply->transport, STR(last_reply.transport));
vstring_strcpy(reply->nexthop, STR(last_reply.nexthop));
vstring_strcpy(reply->recipient, STR(last_reply.recipient));
reply->flags = last_reply.flags;
if (msg_verbose)
msg_info("%s: cached: `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s",
myname, addr, STR(reply->transport),
STR(reply->nexthop), STR(reply->recipient),
IFSET(RESOLVE_FLAG_FINAL, "final"),
IFSET(RESOLVE_FLAG_ROUTED, "routed"),
IFSET(RESOLVE_FLAG_ERROR, "error"),
IFSET(RESOLVE_FLAG_FAIL, "fail"),
IFSET(RESOLVE_CLASS_LOCAL, "local"),
IFSET(RESOLVE_CLASS_ALIAS, "alias"),
IFSET(RESOLVE_CLASS_VIRTUAL, "virtual"),
IFSET(RESOLVE_CLASS_RELAY, "relay"),
IFSET(RESOLVE_CLASS_DEFAULT, "default"));
return;
}
if (rewrite_clnt_stream == 0)
rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE,
var_rewrite_service,
var_ipc_idle_limit,
var_ipc_ttl_limit);
for (;;) {
stream = clnt_stream_access(rewrite_clnt_stream);
errno = 0;
if (attr_print(stream, ATTR_FLAG_NONE,
ATTR_TYPE_STR, MAIL_ATTR_REQ, class,
ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr,
ATTR_TYPE_END) != 0
|| vstream_fflush(stream)
|| attr_scan(stream, ATTR_FLAG_STRICT,
ATTR_TYPE_STR, MAIL_ATTR_TRANSPORT, reply->transport,
ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, reply->nexthop,
ATTR_TYPE_STR, MAIL_ATTR_RECIP, reply->recipient,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, &reply->flags,
ATTR_TYPE_END) != 4) {
if (msg_verbose || (errno != EPIPE && errno != ENOENT))
msg_warn("problem talking to service %s: %m",
var_rewrite_service);
} else {
if (msg_verbose)
msg_info("%s: `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s",
myname, addr, STR(reply->transport),
STR(reply->nexthop), STR(reply->recipient),
IFSET(RESOLVE_FLAG_FINAL, "final"),
IFSET(RESOLVE_FLAG_ROUTED, "routed"),
IFSET(RESOLVE_FLAG_ERROR, "error"),
IFSET(RESOLVE_FLAG_FAIL, "fail"),
IFSET(RESOLVE_CLASS_LOCAL, "local"),
IFSET(RESOLVE_CLASS_ALIAS, "alias"),
IFSET(RESOLVE_CLASS_VIRTUAL, "virtual"),
IFSET(RESOLVE_CLASS_RELAY, "relay"),
IFSET(RESOLVE_CLASS_DEFAULT, "default"));
if (STR(reply->transport)[0] == 0)
msg_warn("%s: null transport result for: <%s>", myname, addr);
else if (STR(reply->recipient)[0] == 0 && *addr != 0)
msg_warn("%s: null recipient result for: <%s>", myname, addr);
else
break;
}
sleep(1);
clnt_stream_recover(rewrite_clnt_stream);
}
vstring_strcpy(last_class, class);
vstring_strcpy(last_addr, addr);
vstring_strcpy(last_reply.transport, STR(reply->transport));
vstring_strcpy(last_reply.nexthop, STR(reply->nexthop));
vstring_strcpy(last_reply.recipient, STR(reply->recipient));
last_reply.flags = reply->flags;
}
void resolve_clnt_free(RESOLVE_REPLY *reply)
{
reply->transport = vstring_free(reply->transport);
reply->nexthop = vstring_free(reply->nexthop);
reply->recipient = vstring_free(reply->recipient);
}
#ifdef TEST
#include <stdlib.h>
#include <msg_vstream.h>
#include <vstring_vstream.h>
#include <mail_conf.h>
static NORETURN usage(char *myname)
{
msg_fatal("usage: %s [-v] [address...]", myname);
}
static void resolve(char *addr, RESOLVE_REPLY *reply)
{
struct RESOLVE_FLAG_TABLE {
int flag;
const char *name;
};
struct RESOLVE_FLAG_TABLE resolve_flag_table[] = {
RESOLVE_FLAG_FINAL, "FLAG_FINAL",
RESOLVE_FLAG_ROUTED, "FLAG_ROUTED",
RESOLVE_FLAG_ERROR, "FLAG_ERROR",
RESOLVE_FLAG_FAIL, "FLAG_FAIL",
RESOLVE_CLASS_LOCAL, "CLASS_LOCAL",
RESOLVE_CLASS_ALIAS, "CLASS_ALIAS",
RESOLVE_CLASS_VIRTUAL, "CLASS_VIRTUAL",
RESOLVE_CLASS_RELAY, "CLASS_RELAY",
RESOLVE_CLASS_DEFAULT, "CLASS_DEFAULT",
0,
};
struct RESOLVE_FLAG_TABLE *fp;
resolve_clnt_query(addr, reply);
if (reply->flags & RESOLVE_FLAG_FAIL) {
vstream_printf("request failed\n");
} else {
vstream_printf("%-10s %s\n", "address", addr);
vstream_printf("%-10s %s\n", "transport", STR(reply->transport));
vstream_printf("%-10s %s\n", "nexthop", *STR(reply->nexthop) ?
STR(reply->nexthop) : "[none]");
vstream_printf("%-10s %s\n", "recipient", STR(reply->recipient));
vstream_printf("%-10s ", "flags");
for (fp = resolve_flag_table; fp->name; fp++) {
if (reply->flags & fp->flag) {
vstream_printf("%s ", fp->name);
reply->flags &= ~fp->flag;
}
}
if (reply->flags != 0)
vstream_printf("Unknown flag 0x%x", reply->flags);
vstream_printf("\n\n");
vstream_fflush(VSTREAM_OUT);
}
}
int main(int argc, char **argv)
{
RESOLVE_REPLY reply;
int ch;
msg_vstream_init(argv[0], VSTREAM_ERR);
mail_conf_read();
msg_info("using config files in %s", var_config_dir);
if (chdir(var_queue_dir) < 0)
msg_fatal("chdir %s: %m", var_queue_dir);
while ((ch = GETOPT(argc, argv, "v")) > 0) {
switch (ch) {
case 'v':
msg_verbose++;
break;
default:
usage(argv[0]);
}
}
resolve_clnt_init(&reply);
if (argc > optind) {
while (argv[optind]) {
resolve(argv[optind], &reply);
optind++;
}
} else {
VSTRING *buffer = vstring_alloc(1);
while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
resolve(STR(buffer), &reply);
}
vstring_free(buffer);
}
resolve_clnt_free(&reply);
exit(0);
}
#endif