/*++ /* NAME /* rewrite_clnt 3 /* SUMMARY /* address rewrite service client /* SYNOPSIS /* #include /* #include /* /* VSTRING *rewrite_clnt(ruleset, address, result) /* const char *ruleset; /* const char *address; /* /* VSTRING *rewrite_clnt_internal(ruleset, address, result) /* const char *ruleset; /* const char *address; /* VSTRING *result; /* DESCRIPTION /* This module implements a mail address rewriting client. /* /* rewrite_clnt() sends a rule set name and external-form address to the /* rewriting service and returns the resulting external-form address. /* In case of communication failure the program keeps trying until the /* mail system shuts down. /* /* rewrite_clnt_internal() performs the same functionality but takes /* input in internal (unquoted) form, and produces output in internal /* (unquoted) form. /* 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 #include #include #include /* Utility library. */ #include #include #include #include #include #include #include /* Global library. */ #include "mail_proto.h" #include "mail_params.h" #include "clnt_stream.h" #include "rewrite_clnt.h" /* Application-specific. */ /* * XXX this is shared with the resolver client to save a file descriptor. */ CLNT_STREAM *rewrite_clnt_stream = 0; static time_t last_expire; static VSTRING *last_rule; static VSTRING *last_addr; static VSTRING *last_result; /* rewrite_clnt_handshake - receive server protocol announcement */ static int rewrite_clnt_handshake(VSTREAM *stream) { return (attr_scan(stream, ATTR_FLAG_STRICT, RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TRIVIAL), ATTR_TYPE_END)); } /* rewrite_clnt - rewrite address to (transport, next hop, recipient) */ VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result) { VSTREAM *stream; int server_flags; int count = 0; /* * One-entry cache. */ if (last_addr == 0) { last_rule = vstring_alloc(10); last_addr = vstring_alloc(100); last_result = vstring_alloc(100); } /* * Sanity check. An address must be in externalized form. The result must * not clobber the input, because we may have to retransmit the query. */ #define STR vstring_str if (*addr == 0) addr = ""; if (addr == STR(result)) msg_panic("rewrite_clnt: result clobbers input"); /* * Peek at the cache. */ if (time((time_t *) 0) < last_expire && strcmp(addr, STR(last_addr)) == 0 && strcmp(rule, STR(last_rule)) == 0) { vstring_strcpy(result, STR(last_result)); if (msg_verbose) msg_info("rewrite_clnt: cached: %s: %s -> %s", rule, addr, vstring_str(result)); return (result); } /* * Keep trying until we get a complete response. The rewrite service is * CPU bound and 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, rewrite_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, REWRITE_ADDR), SEND_ATTR_STR(MAIL_ATTR_RULE, rule), 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_ADDR, result), ATTR_TYPE_END) != 2) { 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("rewrite_clnt: %s: %s -> %s", rule, addr, vstring_str(result)); /* Server-requested disconnect. */ if (server_flags != 0) clnt_stream_recover(rewrite_clnt_stream); break; } sleep(1); /* XXX make configurable */ clnt_stream_recover(rewrite_clnt_stream); } /* * Update the cache. */ vstring_strcpy(last_rule, rule); vstring_strcpy(last_addr, addr); vstring_strcpy(last_result, STR(result)); last_expire = time((time_t *) 0) + 30; /* XXX make configurable */ return (result); } /* rewrite_clnt_internal - rewrite from/to internal form */ VSTRING *rewrite_clnt_internal(const char *ruleset, const char *addr, VSTRING *result) { VSTRING *src = vstring_alloc(100); VSTRING *dst = vstring_alloc(100); /* * Convert the address from internal address form to external RFC822 * form, then rewrite it. After rewriting, convert to internal form. */ quote_822_local(src, addr); rewrite_clnt(ruleset, STR(src), dst); unquote_822_local(result, STR(dst)); vstring_free(src); vstring_free(dst); return (result); } #ifdef TEST #include #include #include #include #include #include #include static NORETURN usage(char *myname) { msg_fatal("usage: %s [-v] [rule address...]", myname); } static void rewrite(char *rule, char *addr, VSTRING *reply) { rewrite_clnt(rule, addr, reply); vstream_printf("%-10s %s\n", "rule", rule); vstream_printf("%-10s %s\n", "address", addr); vstream_printf("%-10s %s\n\n", "result", STR(reply)); vstream_fflush(VSTREAM_OUT); } int main(int argc, char **argv) { VSTRING *reply; int ch; char *rule; char *addr; 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]); } } reply = vstring_alloc(1); if (argc > optind) { for (;;) { if ((rule = argv[optind++]) == 0) break; if ((addr = argv[optind++]) == 0) usage(argv[0]); rewrite(rule, addr, reply); } } else { VSTRING *buffer = vstring_alloc(1); while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { if ((addr = split_at(STR(buffer), ' ')) == 0 || *(rule = STR(buffer)) == 0) usage(argv[0]); rewrite(rule, addr, reply); } vstring_free(buffer); } vstring_free(reply); exit(0); } #endif