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