1/*++ 2/* NAME 3/* rewrite_clnt 3 4/* SUMMARY 5/* address rewrite service client 6/* SYNOPSIS 7/* #include <vstring.h> 8/* #include <rewrite_clnt.h> 9/* 10/* VSTRING *rewrite_clnt(ruleset, address, result) 11/* const char *ruleset; 12/* const char *address; 13/* 14/* VSTRING *rewrite_clnt_internal(ruleset, address, result) 15/* const char *ruleset; 16/* const char *address; 17/* VSTRING *result; 18/* DESCRIPTION 19/* This module implements a mail address rewriting client. 20/* 21/* rewrite_clnt() sends a rule set name and external-form address to the 22/* rewriting service and returns the resulting external-form address. 23/* In case of communication failure the program keeps trying until the 24/* mail system shuts down. 25/* 26/* rewrite_clnt_internal() performs the same functionality but takes 27/* input in internal (unquoted) form, and produces output in internal 28/* (unquoted) form. 29/* DIAGNOSTICS 30/* Warnings: communication failure. Fatal error: mail system is down. 31/* SEE ALSO 32/* mail_proto(3h) low-level mail component glue. 33/* LICENSE 34/* .ad 35/* .fi 36/* The Secure Mailer license must be distributed with this software. 37/* AUTHOR(S) 38/* Wietse Venema 39/* IBM T.J. Watson Research 40/* P.O. Box 704 41/* Yorktown Heights, NY 10598, USA 42/*--*/ 43 44/* System library. */ 45 46#include <sys_defs.h> 47#include <unistd.h> 48#include <errno.h> 49#include <string.h> 50 51/* Utility library. */ 52 53#include <msg.h> 54#include <vstring.h> 55#include <vstream.h> 56#include <vstring_vstream.h> 57#include <events.h> 58#include <iostuff.h> 59#include <quote_822_local.h> 60 61/* Global library. */ 62 63#include "mail_proto.h" 64#include "mail_params.h" 65#include "clnt_stream.h" 66#include "rewrite_clnt.h" 67 68/* Application-specific. */ 69 70 /* 71 * XXX this is shared with the resolver client to save a file descriptor. 72 */ 73CLNT_STREAM *rewrite_clnt_stream = 0; 74 75static time_t last_expire; 76static VSTRING *last_rule; 77static VSTRING *last_addr; 78static VSTRING *last_result; 79 80/* rewrite_clnt - rewrite address to (transport, next hop, recipient) */ 81 82VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result) 83{ 84 VSTREAM *stream; 85 int server_flags; 86 int count = 0; 87 88 /* 89 * One-entry cache. 90 */ 91 if (last_addr == 0) { 92 last_rule = vstring_alloc(10); 93 last_addr = vstring_alloc(100); 94 last_result = vstring_alloc(100); 95 } 96 97 /* 98 * Sanity check. An address must be in externalized form. The result must 99 * not clobber the input, because we may have to retransmit the query. 100 */ 101#define STR vstring_str 102 103 if (*addr == 0) 104 addr = ""; 105 if (addr == STR(result)) 106 msg_panic("rewrite_clnt: result clobbers input"); 107 108 /* 109 * Peek at the cache. 110 */ 111 if (time((time_t *) 0) < last_expire 112 && strcmp(addr, STR(last_addr)) == 0 113 && strcmp(rule, STR(last_rule)) == 0) { 114 vstring_strcpy(result, STR(last_result)); 115 if (msg_verbose) 116 msg_info("rewrite_clnt: cached: %s: %s -> %s", 117 rule, addr, vstring_str(result)); 118 return (result); 119 } 120 121 /* 122 * Keep trying until we get a complete response. The rewrite service is 123 * CPU bound and making the client asynchronous would just complicate the 124 * code. 125 */ 126 if (rewrite_clnt_stream == 0) 127 rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE, 128 var_rewrite_service, 129 var_ipc_idle_limit, 130 var_ipc_ttl_limit); 131 132 for (;;) { 133 stream = clnt_stream_access(rewrite_clnt_stream); 134 errno = 0; 135 count += 1; 136 if (attr_print(stream, ATTR_FLAG_NONE, 137 ATTR_TYPE_STR, MAIL_ATTR_REQ, REWRITE_ADDR, 138 ATTR_TYPE_STR, MAIL_ATTR_RULE, rule, 139 ATTR_TYPE_STR, MAIL_ATTR_ADDR, addr, 140 ATTR_TYPE_END) != 0 141 || vstream_fflush(stream) 142 || attr_scan(stream, ATTR_FLAG_STRICT, 143 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, &server_flags, 144 ATTR_TYPE_STR, MAIL_ATTR_ADDR, result, 145 ATTR_TYPE_END) != 2) { 146 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT)) 147 msg_warn("problem talking to service %s: %m", 148 var_rewrite_service); 149 } else { 150 if (msg_verbose) 151 msg_info("rewrite_clnt: %s: %s -> %s", 152 rule, addr, vstring_str(result)); 153 /* Server-requested disconnect. */ 154 if (server_flags != 0) 155 clnt_stream_recover(rewrite_clnt_stream); 156 break; 157 } 158 sleep(1); /* XXX make configurable */ 159 clnt_stream_recover(rewrite_clnt_stream); 160 } 161 162 /* 163 * Update the cache. 164 */ 165 vstring_strcpy(last_rule, rule); 166 vstring_strcpy(last_addr, addr); 167 vstring_strcpy(last_result, STR(result)); 168 last_expire = time((time_t *) 0) + 30; /* XXX make configurable */ 169 170 return (result); 171} 172 173/* rewrite_clnt_internal - rewrite from/to internal form */ 174 175VSTRING *rewrite_clnt_internal(const char *ruleset, const char *addr, VSTRING *result) 176{ 177 VSTRING *src = vstring_alloc(100); 178 VSTRING *dst = vstring_alloc(100); 179 180 /* 181 * Convert the address from internal address form to external RFC822 182 * form, then rewrite it. After rewriting, convert to internal form. 183 */ 184 quote_822_local(src, addr); 185 rewrite_clnt(ruleset, STR(src), dst); 186 unquote_822_local(result, STR(dst)); 187 vstring_free(src); 188 vstring_free(dst); 189 return (result); 190} 191 192#ifdef TEST 193 194#include <stdlib.h> 195#include <string.h> 196#include <msg_vstream.h> 197#include <split_at.h> 198#include <vstring_vstream.h> 199#include <mail_conf.h> 200#include <mail_params.h> 201 202static NORETURN usage(char *myname) 203{ 204 msg_fatal("usage: %s [-v] [rule address...]", myname); 205} 206 207static void rewrite(char *rule, char *addr, VSTRING *reply) 208{ 209 rewrite_clnt(rule, addr, reply); 210 vstream_printf("%-10s %s\n", "rule", rule); 211 vstream_printf("%-10s %s\n", "address", addr); 212 vstream_printf("%-10s %s\n\n", "result", STR(reply)); 213 vstream_fflush(VSTREAM_OUT); 214} 215 216int main(int argc, char **argv) 217{ 218 VSTRING *reply; 219 int ch; 220 char *rule; 221 char *addr; 222 223 msg_vstream_init(argv[0], VSTREAM_ERR); 224 225 mail_conf_read(); 226 msg_info("using config files in %s", var_config_dir); 227 if (chdir(var_queue_dir) < 0) 228 msg_fatal("chdir %s: %m", var_queue_dir); 229 230 while ((ch = GETOPT(argc, argv, "v")) > 0) { 231 switch (ch) { 232 case 'v': 233 msg_verbose++; 234 break; 235 default: 236 usage(argv[0]); 237 } 238 } 239 reply = vstring_alloc(1); 240 241 if (argc > optind) { 242 for (;;) { 243 if ((rule = argv[optind++]) == 0) 244 break; 245 if ((addr = argv[optind++]) == 0) 246 usage(argv[0]); 247 rewrite(rule, addr, reply); 248 } 249 } else { 250 VSTRING *buffer = vstring_alloc(1); 251 252 while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { 253 if ((addr = split_at(STR(buffer), ' ')) == 0 254 || *(rule = STR(buffer)) == 0) 255 usage(argv[0]); 256 rewrite(rule, addr, reply); 257 } 258 vstring_free(buffer); 259 } 260 vstring_free(reply); 261 exit(0); 262} 263 264#endif 265