1 /* $NetBSD: rewrite_clnt.c,v 1.3 2022/10/08 16:12: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 /* Wietse Venema 46 /* Google, Inc. 47 /* 111 8th Avenue 48 /* New York, NY 10011, USA 49 /*--*/ 50 51 /* System library. */ 52 53 #include <sys_defs.h> 54 #include <unistd.h> 55 #include <errno.h> 56 #include <string.h> 57 58 /* Utility library. */ 59 60 #include <msg.h> 61 #include <vstring.h> 62 #include <vstream.h> 63 #include <vstring_vstream.h> 64 #include <events.h> 65 #include <iostuff.h> 66 #include <quote_822_local.h> 67 68 /* Global library. */ 69 70 #include "mail_proto.h" 71 #include "mail_params.h" 72 #include "clnt_stream.h" 73 #include "rewrite_clnt.h" 74 75 /* Application-specific. */ 76 77 /* 78 * XXX this is shared with the resolver client to save a file descriptor. 79 */ 80 CLNT_STREAM *rewrite_clnt_stream = 0; 81 82 static time_t last_expire; 83 static VSTRING *last_rule; 84 static VSTRING *last_addr; 85 static VSTRING *last_result; 86 87 /* rewrite_clnt_handshake - receive server protocol announcement */ 88 89 static int rewrite_clnt_handshake(VSTREAM *stream) 90 { 91 return (attr_scan(stream, ATTR_FLAG_STRICT, 92 RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TRIVIAL), 93 ATTR_TYPE_END)); 94 } 95 96 /* rewrite_clnt - rewrite address to (transport, next hop, recipient) */ 97 98 VSTRING *rewrite_clnt(const char *rule, const char *addr, VSTRING *result) 99 { 100 VSTREAM *stream; 101 int server_flags; 102 int count = 0; 103 104 /* 105 * One-entry cache. 106 */ 107 if (last_addr == 0) { 108 last_rule = vstring_alloc(10); 109 last_addr = vstring_alloc(100); 110 last_result = vstring_alloc(100); 111 } 112 113 /* 114 * Sanity check. An address must be in externalized form. The result must 115 * not clobber the input, because we may have to retransmit the query. 116 */ 117 #define STR vstring_str 118 119 if (*addr == 0) 120 addr = ""; 121 if (addr == STR(result)) 122 msg_panic("rewrite_clnt: result clobbers input"); 123 124 /* 125 * Peek at the cache. 126 */ 127 if (time((time_t *) 0) < last_expire 128 && strcmp(addr, STR(last_addr)) == 0 129 && strcmp(rule, STR(last_rule)) == 0) { 130 vstring_strcpy(result, STR(last_result)); 131 if (msg_verbose) 132 msg_info("rewrite_clnt: cached: %s: %s -> %s", 133 rule, addr, vstring_str(result)); 134 return (result); 135 } 136 137 /* 138 * Keep trying until we get a complete response. The rewrite service is 139 * CPU bound and making the client asynchronous would just complicate the 140 * code. 141 */ 142 if (rewrite_clnt_stream == 0) 143 rewrite_clnt_stream = clnt_stream_create(MAIL_CLASS_PRIVATE, 144 var_rewrite_service, 145 var_ipc_idle_limit, 146 var_ipc_ttl_limit, 147 rewrite_clnt_handshake); 148 149 for (;;) { 150 stream = clnt_stream_access(rewrite_clnt_stream); 151 errno = 0; 152 count += 1; 153 if (stream == 0 154 || attr_print(stream, ATTR_FLAG_NONE, 155 SEND_ATTR_STR(MAIL_ATTR_REQ, REWRITE_ADDR), 156 SEND_ATTR_STR(MAIL_ATTR_RULE, rule), 157 SEND_ATTR_STR(MAIL_ATTR_ADDR, addr), 158 ATTR_TYPE_END) != 0 159 || vstream_fflush(stream) 160 || attr_scan(stream, ATTR_FLAG_STRICT, 161 RECV_ATTR_INT(MAIL_ATTR_FLAGS, &server_flags), 162 RECV_ATTR_STR(MAIL_ATTR_ADDR, result), 163 ATTR_TYPE_END) != 2) { 164 if (msg_verbose || count > 1 || (errno && errno != EPIPE && errno != ENOENT)) 165 msg_warn("problem talking to service %s: %m", 166 var_rewrite_service); 167 } else { 168 if (msg_verbose) 169 msg_info("rewrite_clnt: %s: %s -> %s", 170 rule, addr, vstring_str(result)); 171 /* Server-requested disconnect. */ 172 if (server_flags != 0) 173 clnt_stream_recover(rewrite_clnt_stream); 174 break; 175 } 176 sleep(1); /* XXX make configurable */ 177 clnt_stream_recover(rewrite_clnt_stream); 178 } 179 180 /* 181 * Update the cache. 182 */ 183 vstring_strcpy(last_rule, rule); 184 vstring_strcpy(last_addr, addr); 185 vstring_strcpy(last_result, STR(result)); 186 last_expire = time((time_t *) 0) + 30; /* XXX make configurable */ 187 188 return (result); 189 } 190 191 /* rewrite_clnt_internal - rewrite from/to internal form */ 192 193 VSTRING *rewrite_clnt_internal(const char *ruleset, const char *addr, VSTRING *result) 194 { 195 VSTRING *src = vstring_alloc(100); 196 VSTRING *dst = vstring_alloc(100); 197 198 /* 199 * Convert the address from internal address form to external RFC822 200 * form, then rewrite it. After rewriting, convert to internal form. 201 */ 202 quote_822_local(src, addr); 203 rewrite_clnt(ruleset, STR(src), dst); 204 unquote_822_local(result, STR(dst)); 205 vstring_free(src); 206 vstring_free(dst); 207 return (result); 208 } 209 210 #ifdef TEST 211 212 #include <stdlib.h> 213 #include <string.h> 214 #include <msg_vstream.h> 215 #include <split_at.h> 216 #include <vstring_vstream.h> 217 #include <mail_conf.h> 218 #include <mail_params.h> 219 220 static NORETURN usage(char *myname) 221 { 222 msg_fatal("usage: %s [-v] [rule address...]", myname); 223 } 224 225 static void rewrite(char *rule, char *addr, VSTRING *reply) 226 { 227 rewrite_clnt(rule, addr, reply); 228 vstream_printf("%-10s %s\n", "rule", rule); 229 vstream_printf("%-10s %s\n", "address", addr); 230 vstream_printf("%-10s %s\n\n", "result", STR(reply)); 231 vstream_fflush(VSTREAM_OUT); 232 } 233 234 int main(int argc, char **argv) 235 { 236 VSTRING *reply; 237 int ch; 238 char *rule; 239 char *addr; 240 241 msg_vstream_init(argv[0], VSTREAM_ERR); 242 243 mail_conf_read(); 244 msg_info("using config files in %s", var_config_dir); 245 if (chdir(var_queue_dir) < 0) 246 msg_fatal("chdir %s: %m", var_queue_dir); 247 248 while ((ch = GETOPT(argc, argv, "v")) > 0) { 249 switch (ch) { 250 case 'v': 251 msg_verbose++; 252 break; 253 default: 254 usage(argv[0]); 255 } 256 } 257 reply = vstring_alloc(1); 258 259 if (argc > optind) { 260 for (;;) { 261 if ((rule = argv[optind++]) == 0) 262 break; 263 if ((addr = argv[optind++]) == 0) 264 usage(argv[0]); 265 rewrite(rule, addr, reply); 266 } 267 } else { 268 VSTRING *buffer = vstring_alloc(1); 269 270 while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { 271 if ((addr = split_at(STR(buffer), ' ')) == 0 272 || *(rule = STR(buffer)) == 0) 273 usage(argv[0]); 274 rewrite(rule, addr, reply); 275 } 276 vstring_free(buffer); 277 } 278 vstring_free(reply); 279 exit(0); 280 } 281 282 #endif 283