diff options
Diffstat (limited to 'src/global/resolve_clnt.c')
-rw-r--r-- | src/global/resolve_clnt.c | 408 |
1 files changed, 408 insertions, 0 deletions
diff --git a/src/global/resolve_clnt.c b/src/global/resolve_clnt.c new file mode 100644 index 0000000..ccd67b2 --- /dev/null +++ b/src/global/resolve_clnt.c @@ -0,0 +1,408 @@ +/*++ +/* NAME +/* resolve_clnt 3 +/* SUMMARY +/* address resolve service client (internal forms) +/* SYNOPSIS +/* #include <resolve_clnt.h> +/* +/* typedef struct { +/* .in +4 +/* VSTRING *transport; +/* VSTRING *nexthop +/* VSTRING *recipient; +/* int flags; +/* .in -4 +/* } RESOLVE_REPLY; +/* +/* void resolve_clnt_init(reply) +/* RESOLVE_REPLY *reply; +/* +/* void resolve_clnt_query_from(sender, address, reply) +/* const char *sender; +/* const char *address; +/* RESOLVE_REPLY *reply; +/* +/* void resolve_clnt_verify_from(sender, address, reply) +/* const char *sender; +/* const char *address; +/* RESOLVE_REPLY *reply; +/* +/* void resolve_clnt_free(reply) +/* RESOLVE_REPLY *reply; +/* DESCRIPTION +/* This module implements a mail address resolver client. +/* +/* resolve_clnt_init() initializes a reply data structure for use +/* by resolve_clnt_query(). The structure is destroyed by passing +/* it to resolve_clnt_free(). +/* +/* resolve_clnt_query_from() sends an internal-form recipient address +/* (user@domain) to the resolver daemon and returns the resulting +/* transport name, next_hop host name, and internal-form recipient +/* address. In case of communication failure the program keeps trying +/* until the mail system goes down. The internal-form sender +/* information is used for sender-dependent relayhost lookup. +/* Specify RESOLVE_NULL_FROM when the sender is unavailable. +/* +/* resolve_clnt_verify_from() implements an alternative version that can +/* be used for address verification. +/* +/* In the resolver reply, the flags member is the bit-wise OR of +/* zero or more of the following: +/* .IP RESOLVE_FLAG_FINAL +/* The recipient address resolves to a mail transport that performs +/* final delivery. The destination is local or corresponds to a hosted +/* domain that is handled by the local machine. This flag is currently +/* not used. +/* .IP RESOLVE_FLAG_ROUTED +/* After address resolution the recipient localpart contains further +/* routing information, so the resolved next-hop destination is not +/* the final destination. +/* .IP RESOLVE_FLAG_ERROR +/* The address resolved to something that has invalid syntax. +/* .IP RESOLVE_FLAG_FAIL +/* The request could not be completed. +/* .PP +/* In addition, the address domain class is returned by setting +/* one of the following flags (this is preliminary code awaiting +/* more permanent implementation of address domain class handling): +/* .IP RESOLVE_CLASS_LOCAL +/* The address domain matches $mydestination, $inet_interfaces +/* or $proxy_interfaces. +/* .IP RESOLVE_CLASS_ALIAS +/* The address domain matches $virtual_alias_domains (virtual +/* alias domains, where each address is redirected to a real +/* local or remote address). +/* .IP RESOLVE_CLASS_VIRTUAL +/* The address domain matches $virtual_mailbox_domains (true +/* virtual domains where each address can have its own mailbox). +/* .IP RESOLVE_CLASS_RELAY +/* The address domain matches $relay_domains, i.e. this is an +/* authorized mail relay destination. +/* .IP RESOLVE_CLASS_DEFAULT +/* The address matches none of the above. Access to this domain +/* should be limited to authorized senders only. +/* .PP +/* For convenience, the constant RESOLVE_CLASS_FINAL includes all +/* cases where the local machine is the final destination. +/* DIAGNOSTICS +/* Warnings: communication failure. Fatal error: mail system is down. +/* SEE ALSO +/* mail_proto(3h) low-level mail component glue. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstream.h> +#include <vstring.h> +#include <vstring_vstream.h> +#include <events.h> +#include <iostuff.h> + +/* Global library. */ + +#include "mail_proto.h" +#include "mail_params.h" +#include "clnt_stream.h" +#include "resolve_clnt.h" + +/* Application-specific. */ + + /* + * XXX this is shared with the rewrite client to save a file descriptor. + */ +extern CLNT_STREAM *rewrite_clnt_stream; + +static time_t last_expire; +static VSTRING *last_class; +static VSTRING *last_sender; +static VSTRING *last_addr; +static RESOLVE_REPLY last_reply; + +/* resolve_clnt_init - initialize 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; +} + +/* resolve_clnt_handshake - receive server protocol announcement */ + +static int resolve_clnt_handshake(VSTREAM *stream) +{ + return (attr_scan(stream, ATTR_FLAG_STRICT, + RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TRIVIAL), + ATTR_TYPE_END)); +} + +/* resolve_clnt - resolve address to (transport, next hop, recipient) */ + +void resolve_clnt(const char *class, const char *sender, + const char *addr, RESOLVE_REPLY *reply) +{ + const char *myname = "resolve_clnt"; + VSTREAM *stream; + int server_flags; + int count = 0; + + /* + * One-entry cache. + */ + if (last_addr == 0) { + last_class = vstring_alloc(10); + last_sender = vstring_alloc(10); + last_addr = vstring_alloc(100); + resolve_clnt_init(&last_reply); + } + + /* + * Sanity check. The result must not clobber the input because we may + * have to retransmit the request. + */ +#define STR vstring_str + + if (addr == STR(reply->recipient)) + msg_panic("%s: result clobbers input", myname); + + /* + * Peek at the cache. + */ +#define IFSET(flag, text) ((reply->flags & (flag)) ? (text) : "") + + if (time((time_t *) 0) < last_expire + && *addr && strcmp(addr, STR(last_addr)) == 0 + && strcmp(class, STR(last_class)) == 0 + && strcmp(sender, STR(last_sender)) == 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' -> `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s", + myname, sender, 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; + } + + /* + * Keep trying until we get a complete response. The resolve service is + * CPU bound; making the client asynchronous would just complicate the + * code. + */ + 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, + resolve_clnt_handshake); + + for (;;) { + stream = clnt_stream_access(rewrite_clnt_stream); + errno = 0; + count += 1; + if (stream == 0 + || attr_print(stream, ATTR_FLAG_NONE, + SEND_ATTR_STR(MAIL_ATTR_REQ, class), + SEND_ATTR_STR(MAIL_ATTR_SENDER, sender), + SEND_ATTR_STR(MAIL_ATTR_ADDR, addr), + ATTR_TYPE_END) != 0 + || vstream_fflush(stream) + || attr_scan(stream, ATTR_FLAG_STRICT, + RECV_ATTR_INT(MAIL_ATTR_FLAGS, &server_flags), + RECV_ATTR_STR(MAIL_ATTR_TRANSPORT, reply->transport), + RECV_ATTR_STR(MAIL_ATTR_NEXTHOP, reply->nexthop), + RECV_ATTR_STR(MAIL_ATTR_RECIP, reply->recipient), + RECV_ATTR_INT(MAIL_ATTR_FLAGS, &reply->flags), + ATTR_TYPE_END) != 5) { + if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT)) + msg_warn("problem talking to service %s: %m", + var_rewrite_service); + } else { + if (msg_verbose) + msg_info("%s: `%s' -> `%s' -> transp=`%s' host=`%s' rcpt=`%s' flags=%s%s%s%s class=%s%s%s%s%s", + myname, sender, 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")); + /* Server-requested disconnect. */ + if (server_flags != 0) + clnt_stream_recover(rewrite_clnt_stream); + 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); /* XXX make configurable */ + clnt_stream_recover(rewrite_clnt_stream); + } + + /* + * Update the cache. + */ + vstring_strcpy(last_class, class); + vstring_strcpy(last_sender, sender); + 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; + last_expire = time((time_t *) 0) + 30; /* XXX make configurable */ +} + +/* resolve_clnt_free - destroy reply */ + +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 <split_at.h> +#include <mail_conf.h> + +static NORETURN usage(char *myname) +{ + msg_fatal("usage: %s [-v] [address...]", myname); +} + +static void resolve(char *class, 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(class, RESOLVE_NULL_FROM, addr, reply); + if (reply->flags & RESOLVE_FLAG_FAIL) { + vstream_printf("request failed\n"); + } else { + vstream_printf("%-10s %s\n", "class", class); + 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; + char *addr; + 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] && argv[optind + 1]) { + resolve(argv[optind], argv[optind + 1], &reply); + optind += 2; + } + } else { + VSTRING *buffer = vstring_alloc(1); + + while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { + addr = split_at(STR(buffer), ' '); + if (*STR(buffer) == 0) + msg_fatal("need as input: class [address]"); + if (addr == 0) + addr = ""; + resolve(STR(buffer), addr, &reply); + } + vstring_free(buffer); + } + resolve_clnt_free(&reply); + exit(0); +} + +#endif |