1 /* $NetBSD: rewrite.c,v 1.2 2017/02/14 01:16:48 christos 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 #ifdef STRCASECMP_IN_STRINGS_H 69 #include <strings.h> 70 #endif 71 72 /* Utility library. */ 73 74 #include <msg.h> 75 #include <vstring.h> 76 #include <vstream.h> 77 #include <vstring_vstream.h> 78 #include <split_at.h> 79 80 /* Global library. */ 81 82 #include <mail_params.h> 83 #include <mail_proto.h> 84 #include <resolve_local.h> 85 #include <tok822.h> 86 #include <mail_conf.h> 87 88 /* Application-specific. */ 89 90 #include "trivial-rewrite.h" 91 92 RWR_CONTEXT local_context = { 93 VAR_MYORIGIN, &var_myorigin, 94 VAR_MYDOMAIN, &var_mydomain, 95 }; 96 97 RWR_CONTEXT remote_context = { 98 VAR_REM_RWR_DOMAIN, &var_remote_rwr_domain, 99 VAR_REM_RWR_DOMAIN, &var_remote_rwr_domain, 100 }; 101 102 static VSTRING *ruleset; 103 static VSTRING *address; 104 static VSTRING *result; 105 106 /* rewrite_tree - rewrite address according to rule set */ 107 108 void rewrite_tree(RWR_CONTEXT *context, TOK822 *tree) 109 { 110 TOK822 *colon; 111 TOK822 *domain; 112 TOK822 *bang; 113 TOK822 *local; 114 VSTRING *vstringval; 115 116 /* 117 * XXX If you change this module, quote_822_local.c, or tok822_parse.c, 118 * be sure to re-run the tests under "make rewrite_clnt_test" and "make 119 * resolve_clnt_test" in the global directory. 120 */ 121 122 /* 123 * Sanity check. 124 */ 125 if (tree->head == 0) 126 msg_panic("rewrite_tree: empty tree"); 127 128 /* 129 * An empty address is a special case. 130 */ 131 if (tree->head == tree->tail 132 && tree->tail->type == TOK822_QSTRING 133 && VSTRING_LEN(tree->tail->vstr) == 0) 134 return; 135 136 /* 137 * Treat a lone @ as if it were an empty address. 138 */ 139 if (tree->head == tree->tail 140 && tree->tail->type == '@') { 141 tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); 142 tok822_sub_append(tree, tok822_alloc(TOK822_QSTRING, "")); 143 return; 144 } 145 146 /* 147 * Strip source route. 148 */ 149 if (tree->head->type == '@' 150 && (colon = tok822_find_type(tree->head, ':')) != 0 151 && colon != tree->tail) 152 tok822_free_tree(tok822_sub_keep_after(tree, colon)); 153 154 /* 155 * Optionally, transform address forms without @. 156 */ 157 if ((domain = tok822_rfind_type(tree->tail, '@')) == 0) { 158 159 /* 160 * Swap domain!user to user@domain. 161 */ 162 if (var_swap_bangpath != 0 163 && (bang = tok822_find_type(tree->head, '!')) != 0) { 164 tok822_sub_keep_before(tree, bang); 165 local = tok822_cut_after(bang); 166 tok822_free(bang); 167 tok822_sub_prepend(tree, tok822_alloc('@', (char *) 0)); 168 if (local) 169 tok822_sub_prepend(tree, local); 170 } 171 172 /* 173 * Promote user%domain to user@domain. 174 */ 175 else if (var_percent_hack != 0 176 && (domain = tok822_rfind_type(tree->tail, '%')) != 0) { 177 domain->type = '@'; 178 } 179 180 /* 181 * Append missing @origin 182 */ 183 else if (var_append_at_myorigin != 0 184 && REW_PARAM_VALUE(context->origin) != 0 185 && REW_PARAM_VALUE(context->origin)[0] != 0) { 186 domain = tok822_sub_append(tree, tok822_alloc('@', (char *) 0)); 187 tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->origin), 188 (TOK822 **) 0)); 189 } 190 } 191 192 /* 193 * Append missing .domain, but leave broken forms ending in @ alone. This 194 * merely makes diagnostics more accurate by leaving bogus addresses 195 * alone. 196 * 197 * Backwards-compatibility warning: warn for "user@localhost" when there is 198 * no "localhost" in mydestination or in any other address class with an 199 * explicit domain list. 200 */ 201 if (var_append_dot_mydomain != 0 202 && REW_PARAM_VALUE(context->domain) != 0 203 && REW_PARAM_VALUE(context->domain)[0] != 0 204 && (domain = tok822_rfind_type(tree->tail, '@')) != 0 205 && domain != tree->tail 206 && tok822_find_type(domain, TOK822_DOMLIT) == 0 207 && tok822_find_type(domain, '.') == 0) { 208 if (warn_compat_break_app_dot_mydomain 209 && (vstringval = domain->next->vstr) != 0) { 210 if (strcasecmp(vstring_str(vstringval), "localhost") != 0) { 211 msg_info("using backwards-compatible default setting " 212 VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to " 213 "\"%s.%s\"", vstring_str(vstringval), 214 vstring_str(vstringval), var_mydomain); 215 } else if (resolve_class("localhost") == RESOLVE_CLASS_DEFAULT) { 216 msg_info("using backwards-compatible default setting " 217 VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to " 218 "\"%s.%s\"; please add \"localhost\" to " 219 "mydestination or other address class", 220 vstring_str(vstringval), vstring_str(vstringval), 221 var_mydomain); 222 } 223 } 224 tok822_sub_append(tree, tok822_alloc('.', (char *) 0)); 225 tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->domain), 226 (TOK822 **) 0)); 227 } 228 229 /* 230 * Strip trailing dot at end of domain, but not dot-dot or @-dot. This 231 * merely makes diagnostics more accurate by leaving bogus addresses 232 * alone. 233 */ 234 if (tree->tail->type == '.' 235 && tree->tail->prev 236 && tree->tail->prev->type != '.' 237 && tree->tail->prev->type != '@') 238 tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); 239 } 240 241 /* rewrite_proto - read request and send reply */ 242 243 int rewrite_proto(VSTREAM *stream) 244 { 245 RWR_CONTEXT *context; 246 TOK822 *tree; 247 248 if (attr_scan(stream, ATTR_FLAG_STRICT, 249 RECV_ATTR_STR(MAIL_ATTR_RULE, ruleset), 250 RECV_ATTR_STR(MAIL_ATTR_ADDR, address), 251 ATTR_TYPE_END) != 2) 252 return (-1); 253 254 if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_LOCAL) == 0) 255 context = &local_context; 256 else if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_REMOTE) == 0) 257 context = &remote_context; 258 else { 259 msg_warn("unknown context: %s", vstring_str(ruleset)); 260 return (-1); 261 } 262 263 /* 264 * Sanity check. An address is supposed to be in externalized form. 265 */ 266 if (*vstring_str(address) == 0) { 267 msg_warn("rewrite_addr: null address"); 268 vstring_strcpy(result, vstring_str(address)); 269 } 270 271 /* 272 * Convert the address from externalized (quoted) form to token list, 273 * rewrite it, and convert back. 274 */ 275 else { 276 tree = tok822_scan_addr(vstring_str(address)); 277 rewrite_tree(context, tree); 278 tok822_externalize(result, tree, TOK822_STR_DEFL); 279 tok822_free_tree(tree); 280 } 281 if (msg_verbose) 282 msg_info("`%s' `%s' -> `%s'", vstring_str(ruleset), 283 vstring_str(address), vstring_str(result)); 284 285 attr_print(stream, ATTR_FLAG_NONE, 286 SEND_ATTR_INT(MAIL_ATTR_FLAGS, server_flags), 287 SEND_ATTR_STR(MAIL_ATTR_ADDR, vstring_str(result)), 288 ATTR_TYPE_END); 289 290 if (vstream_fflush(stream) != 0) { 291 msg_warn("write rewrite reply: %m"); 292 return (-1); 293 } 294 return (0); 295 } 296 297 /* rewrite_init - module initializations */ 298 299 void rewrite_init(void) 300 { 301 ruleset = vstring_alloc(100); 302 address = vstring_alloc(100); 303 result = vstring_alloc(100); 304 } 305