1 /* $NetBSD: rewrite.c,v 1.1.1.1 2009/06/23 10:08:58 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* rewrite 3 6 /* SUMMARY 7 /* mail address rewriter 8 /* SYNOPSIS 9 /* #include "trivial-rewrite.h" 10 /* 11 /* void rewrite_init(void) 12 /* 13 /* void rewrite_proto(stream) 14 /* VSTREAM *stream; 15 /* 16 /* void rewrite_addr(context, addr, result) 17 /* RWR_CONTEXT *context; 18 /* char *addr; 19 /* VSTRING *result; 20 /* 21 /* void rewrite_tree(context, tree) 22 /* RWR_CONTEXT *context; 23 /* TOK822 *tree; 24 /* 25 /* RWR_CONTEXT local_context; 26 /* RWR_CONTEXT remote_context; 27 /* DESCRIPTION 28 /* This module implements the trivial address rewriting engine. 29 /* 30 /* rewrite_init() initializes data structures that are private 31 /* to this module. It should be called once before using the 32 /* actual rewriting routines. 33 /* 34 /* rewrite_proto() implements the client-server protocol: read 35 /* one rule set name and one address in external (quoted) form, 36 /* reply with the rewritten address in external form. 37 /* 38 /* rewrite_addr() rewrites an address string to another string. 39 /* Both input and output are in external (quoted) form. 40 /* 41 /* rewrite_tree() rewrites a parse tree with a single address to 42 /* another tree. A tree is a dummy node on top of a token list. 43 /* 44 /* local_context and remote_context provide domain names for 45 /* completing incomplete address forms. 46 /* STANDARDS 47 /* DIAGNOSTICS 48 /* Problems and transactions are logged to the syslog daemon. 49 /* BUGS 50 /* SEE ALSO 51 /* LICENSE 52 /* .ad 53 /* .fi 54 /* The Secure Mailer license must be distributed with this software. 55 /* AUTHOR(S) 56 /* Wietse Venema 57 /* IBM T.J. Watson Research 58 /* P.O. Box 704 59 /* Yorktown Heights, NY 10598, USA 60 /*--*/ 61 62 /* System library. */ 63 64 #include <sys_defs.h> 65 #include <stdlib.h> 66 #include <string.h> 67 68 /* Utility library. */ 69 70 #include <msg.h> 71 #include <vstring.h> 72 #include <vstream.h> 73 #include <vstring_vstream.h> 74 #include <split_at.h> 75 76 /* Global library. */ 77 78 #include <mail_params.h> 79 #include <mail_proto.h> 80 #include <resolve_local.h> 81 #include <tok822.h> 82 #include <mail_conf.h> 83 84 /* Application-specific. */ 85 86 #include "trivial-rewrite.h" 87 88 RWR_CONTEXT local_context = { 89 VAR_MYORIGIN, &var_myorigin, 90 VAR_MYDOMAIN, &var_mydomain, 91 }; 92 93 RWR_CONTEXT remote_context = { 94 VAR_REM_RWR_DOMAIN, &var_remote_rwr_domain, 95 VAR_REM_RWR_DOMAIN, &var_remote_rwr_domain, 96 }; 97 98 static VSTRING *ruleset; 99 static VSTRING *address; 100 static VSTRING *result; 101 102 /* rewrite_tree - rewrite address according to rule set */ 103 104 void rewrite_tree(RWR_CONTEXT *context, TOK822 *tree) 105 { 106 TOK822 *colon; 107 TOK822 *domain; 108 TOK822 *bang; 109 TOK822 *local; 110 111 /* 112 * XXX If you change this module, quote_822_local.c, or tok822_parse.c, 113 * be sure to re-run the tests under "make rewrite_clnt_test" and "make 114 * resolve_clnt_test" in the global directory. 115 */ 116 117 /* 118 * Sanity check. 119 */ 120 if (tree->head == 0) 121 msg_panic("rewrite_tree: empty tree"); 122 123 /* 124 * An empty address is a special case. 125 */ 126 if (tree->head == tree->tail 127 && tree->tail->type == TOK822_QSTRING 128 && VSTRING_LEN(tree->tail->vstr) == 0) 129 return; 130 131 /* 132 * Treat a lone @ as if it were an empty address. 133 */ 134 if (tree->head == tree->tail 135 && tree->tail->type == '@') { 136 tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); 137 tok822_sub_append(tree, tok822_alloc(TOK822_QSTRING, "")); 138 return; 139 } 140 141 /* 142 * Strip source route. 143 */ 144 if (tree->head->type == '@' 145 && (colon = tok822_find_type(tree->head, ':')) != 0 146 && colon != tree->tail) 147 tok822_free_tree(tok822_sub_keep_after(tree, colon)); 148 149 /* 150 * Optionally, transform address forms without @. 151 */ 152 if ((domain = tok822_rfind_type(tree->tail, '@')) == 0) { 153 154 /* 155 * Swap domain!user to user@domain. 156 */ 157 if (var_swap_bangpath != 0 158 && (bang = tok822_find_type(tree->head, '!')) != 0) { 159 tok822_sub_keep_before(tree, bang); 160 local = tok822_cut_after(bang); 161 tok822_free(bang); 162 tok822_sub_prepend(tree, tok822_alloc('@', (char *) 0)); 163 if (local) 164 tok822_sub_prepend(tree, local); 165 } 166 167 /* 168 * Promote user%domain to user@domain. 169 */ 170 else if (var_percent_hack != 0 171 && (domain = tok822_rfind_type(tree->tail, '%')) != 0) { 172 domain->type = '@'; 173 } 174 175 /* 176 * Append missing @origin 177 */ 178 else if (var_append_at_myorigin != 0 179 && REW_PARAM_VALUE(context->origin) != 0 180 && REW_PARAM_VALUE(context->origin)[0] != 0) { 181 domain = tok822_sub_append(tree, tok822_alloc('@', (char *) 0)); 182 tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->origin), 183 (TOK822 **) 0)); 184 } 185 } 186 187 /* 188 * Append missing .domain, but leave broken forms ending in @ alone. This 189 * merely makes diagnostics more accurate by leaving bogus addresses 190 * alone. 191 */ 192 if (var_append_dot_mydomain != 0 193 && REW_PARAM_VALUE(context->domain) != 0 194 && REW_PARAM_VALUE(context->domain)[0] != 0 195 && (domain = tok822_rfind_type(tree->tail, '@')) != 0 196 && domain != tree->tail 197 && tok822_find_type(domain, TOK822_DOMLIT) == 0 198 && tok822_find_type(domain, '.') == 0) { 199 tok822_sub_append(tree, tok822_alloc('.', (char *) 0)); 200 tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->domain), 201 (TOK822 **) 0)); 202 } 203 204 /* 205 * Strip trailing dot at end of domain, but not dot-dot or @-dot. This 206 * merely makes diagnostics more accurate by leaving bogus addresses 207 * alone. 208 */ 209 if (tree->tail->type == '.' 210 && tree->tail->prev 211 && tree->tail->prev->type != '.' 212 && tree->tail->prev->type != '@') 213 tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); 214 } 215 216 /* rewrite_proto - read request and send reply */ 217 218 int rewrite_proto(VSTREAM *stream) 219 { 220 RWR_CONTEXT *context; 221 TOK822 *tree; 222 223 if (attr_scan(stream, ATTR_FLAG_STRICT, 224 ATTR_TYPE_STR, MAIL_ATTR_RULE, ruleset, 225 ATTR_TYPE_STR, MAIL_ATTR_ADDR, address, 226 ATTR_TYPE_END) != 2) 227 return (-1); 228 229 if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_LOCAL) == 0) 230 context = &local_context; 231 else if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_REMOTE) == 0) 232 context = &remote_context; 233 else { 234 msg_warn("unknown context: %s", vstring_str(ruleset)); 235 return (-1); 236 } 237 238 /* 239 * Sanity check. An address is supposed to be in externalized form. 240 */ 241 if (*vstring_str(address) == 0) { 242 msg_warn("rewrite_addr: null address"); 243 vstring_strcpy(result, vstring_str(address)); 244 } 245 246 /* 247 * Convert the address from externalized (quoted) form to token list, 248 * rewrite it, and convert back. 249 */ 250 else { 251 tree = tok822_scan_addr(vstring_str(address)); 252 rewrite_tree(context, tree); 253 tok822_externalize(result, tree, TOK822_STR_DEFL); 254 tok822_free_tree(tree); 255 } 256 if (msg_verbose) 257 msg_info("`%s' `%s' -> `%s'", vstring_str(ruleset), 258 vstring_str(address), vstring_str(result)); 259 260 attr_print(stream, ATTR_FLAG_NONE, 261 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, server_flags, 262 ATTR_TYPE_STR, MAIL_ATTR_ADDR, vstring_str(result), 263 ATTR_TYPE_END); 264 265 if (vstream_fflush(stream) != 0) { 266 msg_warn("write rewrite reply: %m"); 267 return (-1); 268 } 269 return (0); 270 } 271 272 /* rewrite_init - module initializations */ 273 274 void rewrite_init(void) 275 { 276 ruleset = vstring_alloc(100); 277 address = vstring_alloc(100); 278 result = vstring_alloc(100); 279 } 280