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