diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-12-28 13:32:26 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-12-28 21:01:38 +0200 |
commit | fc06935a34c7aa16e181b8224dcea20ab77f5952 (patch) | |
tree | ce8cd4b283823e7dcf3eb896a6caec33ba286dee | |
parent | 1b01a1679d920b0e78390bf417391cea46b059db (diff) | |
download | anubis-fc06935a34c7aa16e181b8224dcea20ab77f5952.tar.gz anubis-fc06935a34c7aa16e181b8224dcea20ab77f5952.tar.bz2 |
Initial implementation of SMTP command modification.
The special section SMTP is processed before transferring
each SMTP command to the remote server. This section can
contain `modify command' actions which alter the command.
* src/extern.h (smtp_command_rule): New extern.
* src/list.c (list_head_item, list_tail_item): New functions.
* src/list.h (list_head_item, list_tail_item): New protos.
* src/main.c (smtp_command_rule): New global.
(main): Set smtp_command_rule.
* src/message.c (message_modify_command): New function.
* src/rc-gram.y (inst_eval): Implement command modification.
* src/tunnel.c (transfer_command): Remove second argument.
Always work on the last command stored in the assoc array.
Call smtp_command_rule to eventually modify the command.
All callers updated.
* src/rcfile.c: New keyword "smtp-command-rule" can be
used in the CONTROL section to modify the name of the
SMTP command section.
(rcfile_call_section): Issue 'No such section' warning in verbose
mode only.
* src/tunnel.c (save_command): Use case-insensitive comparison.
Convert command verb part to upper case.
(process_command): Convert buf to lowercase after saving command.
* NEWS, doc/anubis.texi: Update.
-rw-r--r-- | NEWS | 14 | ||||
-rw-r--r-- | doc/anubis.texi | 81 | ||||
-rw-r--r-- | src/extern.h | 1 | ||||
-rw-r--r-- | src/headers.h | 2 | ||||
-rw-r--r-- | src/list.c | 14 | ||||
-rw-r--r-- | src/list.h | 2 | ||||
-rw-r--r-- | src/main.c | 2 | ||||
-rw-r--r-- | src/message.c | 28 | ||||
-rw-r--r-- | src/rc-gram.y | 54 | ||||
-rw-r--r-- | src/rcfile.c | 18 | ||||
-rw-r--r-- | src/tunnel.c | 91 |
11 files changed, 222 insertions, 85 deletions
@@ -14,6 +14,20 @@ Please send your bug reports to <bug-anubis@gnu.org>. Command line options take precedence over configuration file statements. +* SMTP section + +The new rule section "SMTP" is invoked each time an SMTP command +is received. This section may alter the command's argument, using the +"modify command", e.g.: + +BEGIN SMTP +regex :extended +modify command [ehlo] "foo.bar.net" +if command ["mail from:"] "<(.*)>(.*)" + modify command ["mail from:"] "<root@bar.net>\2" +fi +END + * New keywords: log-facility and log-tag * Guile output diff --git a/doc/anubis.texi b/doc/anubis.texi index 1aecd96..b9f47e0 100644 --- a/doc/anubis.texi +++ b/doc/anubis.texi @@ -108,8 +108,8 @@ Database URL Managing the Database -* Administrators:: The Administrator's View -* Users:: The User's View +* Administrators:: Administrator's View +* Users:: User's View Administrators @@ -152,11 +152,12 @@ Conditional Statements Action List -* Stop Action:: Stopping the Processing +* Stop Action:: Stopping Processing * Call Action:: Invoking Another Section * Adding Headers or Text:: How to add a new header or body line(s). * Removing Headers:: How to remove a message header line(s). * Modifying Messages:: How to modify a message contents on-the-fly. +* Modifying SMTP Commands:: * Inserting Files:: How to append text files to an outgoing message. * Mail Encryption:: How to encrypt a message on-the-fly. * External Processor:: How to process a message body using an external tool. @@ -1298,6 +1299,12 @@ Declares the name of command section for outgoing mail. Default is configuration file. @end deffn +@deffn Option smtp-command-rule @var{string} +Declares the name of command section for rewriting @acronym{SMTP} +commands. Default is @samp{SMTP}. This option is available only +for system configuration file. @xref{Modifying SMTP Commands}. +@end deffn + @deffn Option log-tag @var{string} Tag syslog messages with @var{string}. Default is @samp{anubis}. @end deffn @@ -2187,6 +2194,7 @@ can use for instance: @samp{add} or @samp{ADD} or @samp{AdD}, and so on. * Adding Headers or Text:: How to add a new header or body line(s). * Removing Headers:: How to remove a message header line(s). * Modifying Messages:: How to modify a message contents on-the-fly. +* Modifying SMTP Commands:: * Inserting Files:: How to append text files to an outgoing message. * Mail Encryption:: How to encrypt a message on-the-fly. * External Processor:: How to process a message body using an external tool. @@ -2387,6 +2395,73 @@ modify body :extended ["the old \([[:alnum:]]+\)"] "the new \1" @end smallexample @end deffn +@node Modifying SMTP Commands +@subsection Modifying SMTP Commands + +GNU Anubis makes it possible to modify arguments of +the @acronym{SMTP} commands. To do so, define a section +named @samp{SMTP}. Anubis will call this section each time +it receives an @acronym{SMTP} command. This section may contain +any statements allowed for @samp{RULE} section, plus the +following special flavors of the @samp{modify} statement: + +@deffn Command modify [@var{flags}] command @samp{[}@var{cmd}@samp{]} @var{value} +If the current @acronym{SMTP} command matches @var{cmd}, rewrite it by +using @var{value} as its argument. +@end deffn + +For example, this is how to force using @samp{my.host.org} as the +@samp{EHLO} argument: + +@smallexample +@group +BEGIN SMTP +modify command [ehlo] "my.host.org" +END +@end group +@end smallexample + +The following points are worth mentioning: + +@enumerate 1 +@item As usual, you may use conditional expressions to decide what +to modify and how. For example, the code below replaces the domain +part of each @samp{MAIL FROM} command with @samp{gnu.org}: + +@smallexample +BEGIN SMTP +if command ["mail from:"] "<(.*)@@(.*)>(.*)" + modify command ["mail from:"] "<\1@@gnu.org>\2" +fi +END +@end smallexample + +@item Each @samp{modify command} statement applies only if +the current command matches its @var{cmd} argument. In particular, +this means that you cannot modify already transferred @acronym{SMTP} +commands nor the commands to be transferred. For example, the +following code will not work: + +@smallexample +@group +BEGIN SMTP +# @r{Wrong!} +if command ["mail from:"] "<>(.*)" + modify command [ehlo] "domain.net" +fi +END +@end group +@end smallexample + +It is because by the time @samp{MAIL FROM} is received, the +@samp{EHLO} command has already been processed and send to +the server. +@end enumerate + +The final point to notice is that you may use another than the +default name for that section (if you really want to). To do +so, define the new name via the @samp{smtp-command-rule} option in the +@samp{CONTROL} section (@pxref{Basic Settings,,smtp-command-rule}). @node Inserting Files @subsection Inserting Files diff --git a/src/extern.h b/src/extern.h index a2c63aa..2e8611b 100644 --- a/src/extern.h +++ b/src/extern.h @@ -100,6 +100,7 @@ extern char **x_argv; extern char *incoming_mail_rule; extern char *outgoing_mail_rule; +extern char *smtp_command_rule; extern char *log_tag; extern int log_facility; diff --git a/src/headers.h b/src/headers.h index b3b80a7..2fb235b 100644 --- a/src/headers.h +++ b/src/headers.h @@ -419,6 +419,8 @@ void message_append_mime_header (MESSAGE, const char *); void message_remove_headers (MESSAGE, RC_REGEX *); void message_modify_headers (MESSAGE, RC_REGEX *, char *, char *); void message_modify_body (MESSAGE, RC_REGEX *, char *); +void message_modify_command (MESSAGE msg, RC_REGEX *regex, char *key, + char *value); void message_proc_body (MESSAGE msg, int (*proc) (char **, char *, void *), void *param); void message_external_proc (MESSAGE, char **); @@ -180,6 +180,20 @@ list_item (struct list *list, size_t n) return p->data; } +void * +list_head_item (struct list *list) +{ + struct list_entry *p = list->head; + return p ? p->data : NULL; +} + +void * +list_tail_item (struct list *list) +{ + struct list_entry *p = list->tail; + return p ? p->data : NULL; +} + size_t list_count (struct list * list) { @@ -28,6 +28,8 @@ ANUBIS_LIST list_create (void); void list_destroy (ANUBIS_LIST *, list_iterator_t, void *); void list_iterate (ANUBIS_LIST, list_iterator_t, void *); void *list_item (ANUBIS_LIST, size_t); +void *list_head_item (struct list *list); +void *list_tail_item (struct list *list); size_t list_count (ANUBIS_LIST); void list_append (ANUBIS_LIST, void *); void list_prepend (ANUBIS_LIST, void *); @@ -36,6 +36,7 @@ NET_STREAM remote_server; char *anubis_domain; /* Local domain for EHLO in authentication mode */ char *incoming_mail_rule; /* Name of section for incoming mail processing */ char *outgoing_mail_rule; /* Name of section for outgoing mail processing */ +char *smtp_command_rule; /* Name of section for rewriting SMTP commands */ void xalloc_die () @@ -88,6 +89,7 @@ main (int argc, char *argv[]) anubis_getlogin (&session.supervisor); assign_string (&incoming_mail_rule, "INCOMING"); assign_string (&outgoing_mail_rule, "RULE"); + assign_string (&smtp_command_rule, "SMTP"); /* Initialize various database formats diff --git a/src/message.c b/src/message.c index c6bf895..f06db54 100644 --- a/src/message.c +++ b/src/message.c @@ -207,6 +207,34 @@ message_modify_headers (MESSAGE msg, RC_REGEX *regex, char *key2, iterator_destroy (&itr); } +void +message_modify_command (MESSAGE msg, RC_REGEX *regex, char *key, + char *value) +{ + char **rv; + int rc; + ASSOC *asc = list_tail_item (msg->commands); + + if (!asc) + return; + + if (asc->key && anubis_regex_match (regex, asc->key, &rc, &rv)) + { + if (key) + { + free (asc->key); + if (rc) + asc->key = substitute (key, rv); + else + asc->key = strdup (key); + } + if (value) + asc->value = expand_ampersand (value, asc->value); + } + if (rc) + argcv_free (-1, rv); +} + const char * message_get_body (MESSAGE msg) diff --git a/src/rc-gram.y b/src/rc-gram.y index fe9ea2c..44b8b01 100644 --- a/src/rc-gram.y +++ b/src/rc-gram.y @@ -1,6 +1,6 @@ %{ /* - rcfile.y + rc-gram.y This file is part of GNU Anubis. Copyright (C) 2003, 2004, 2007, 2009 The Anubis Team. @@ -39,7 +39,7 @@ static void rc_section_print (RC_SECTION *); static void rc_asgn_destroy (RC_ASGN *); static void rc_bool_destroy (RC_BOOL *); static void rc_level_print (int, char *); - static RC_NODE *rc_node_create (enum rc_node_type, struct rc_loc *loc); +static RC_NODE *rc_node_create (enum rc_node_type, struct rc_loc *loc); static void rc_node_destroy (RC_NODE *); static void rc_node_print (RC_NODE *); static void rc_rule_destroy (RC_RULE *); @@ -105,7 +105,8 @@ static struct rc_secdef *rc_secdef; char *string; RC_SECTION *section; RC_STMT *stmt; - struct { + struct + { RC_STMT *head; RC_STMT *tail; } stmtlist; @@ -114,7 +115,8 @@ static struct rc_secdef *rc_secdef; RC_NODE *node; RC_REGEX *regex; int num; - struct { + struct + { int part; RC_REGEX *key; char *string; @@ -561,6 +563,12 @@ inst_stmt: STOP if (!is_prog_allowed (&@1.beg)) YYERROR; + if ($2.part == COMMAND) + { + parse_error (&@2.beg, _("command part is not allowed")); + YYERROR; + } + $$ = rc_stmt_create (rc_stmt_inst, &@1.beg); $$->v.inst.opcode = inst_add; $$->v.inst.part = $2.part; @@ -573,6 +581,12 @@ inst_stmt: STOP if (!is_prog_allowed (&@1.beg)) YYERROR; + if ($2.part == COMMAND) + { + parse_error (&@2.beg, _("command part is not allowed")); + YYERROR; + } + $$ = rc_stmt_create (rc_stmt_inst, &@1.beg); $$->v.inst.opcode = inst_remove; $$->v.inst.part = $2.part; @@ -602,10 +616,7 @@ inst_stmt: STOP $$->v.inst.part = $2.part; $$->v.inst.key = $2.key; if ($3 == NULL && anubis_regex_refcnt ($2.key)) - { - /* FIXME: Perhaps --line? */ - parse_error (&@2.end, _("missing replacement value")); - } + parse_error (&@2.end, _("missing replacement value")); $$->v.inst.key2 = $3; $$->v.inst.arg = NULL; } @@ -1495,25 +1506,34 @@ inst_eval (struct eval_env *env, RC_INST *inst) case inst_add: tracefile (&env->loc, _("ADD %s [%s] %s"), - (inst->part == BODY) ? "BODY" : "HEADER", + part_string (inst->part), VALID_STR (inst->key2), arg); if (inst->part == BODY) message_add_body (env->msg, inst->key2, arg); - else + else if (inst->part == HEADER) message_add_header (env->msg, inst->key2, arg); break; case inst_modify: tracefile (&env->loc, _("MODIFY %s [%s] [%s] %s"), - (inst->part == BODY) ? "BODY" : "HEADER", + part_string (inst->part), anubis_regex_source (inst->key), VALID_STR (inst->key2), arg); - - if (inst->part == BODY) - message_modify_body (env->msg, inst->key, arg); - else - message_modify_headers (env->msg, inst->key, - inst->key2, arg); + + switch (inst->part) + { + case BODY: + message_modify_body (env->msg, inst->key, arg); + break; + + case HEADER: + message_modify_headers (env->msg, inst->key, inst->key2, arg); + break; + + case COMMAND: + message_modify_command (env->msg, inst->key, inst->key2, arg); + break; + } break; case inst_remove: diff --git a/src/rcfile.c b/src/rcfile.c index 24ff667..5511296 100644 --- a/src/rcfile.c +++ b/src/rcfile.c @@ -254,10 +254,11 @@ process_rcfile (int method) #define KW_ESMTP_REQUIRE_ENCRYPTION 29 #define KW_INCOMING_MAIL_RULE 30 #define KW_OUTGOING_MAIL_RULE 31 -#define KW_HANG 32 -#define KW_ALLOW_HANG 33 -#define KW_LOG_FACILITY 34 -#define KW_LOG_TAG 35 +#define KW_SMTP_COMMAND_RULE 32 +#define KW_HANG 33 +#define KW_ALLOW_HANG 34 +#define KW_LOG_FACILITY 35 +#define KW_LOG_TAG 36 char ** list_to_argv (ANUBIS_LIST list) @@ -575,7 +576,11 @@ control_parser (EVAL_ENV env, int key, ANUBIS_LIST arglist, void *inv_data) case KW_OUTGOING_MAIL_RULE: outgoing_mail_rule = strdup (arg); break; - + + case KW_SMTP_COMMAND_RULE: + smtp_command_rule = strdup (arg); + break; + case KW_LOG_FACILITY: parse_log_facility (arg); break; @@ -633,6 +638,7 @@ static struct rc_kwdef init_kw[] = { { "mode", KW_MODE }, { "incoming-mail-rule", KW_INCOMING_MAIL_RULE }, { "outgoing-mail-rule", KW_OUTGOING_MAIL_RULE }, + { "smtp-command-rule", KW_SMTP_COMMAND_RULE }, { "log-facility", KW_LOG_FACILITY }, { "log-tag", KW_LOG_TAG }, { "ALLOW-HANG", KW_ALLOW_HANG }, @@ -901,7 +907,7 @@ rcfile_call_section (int method, char *name, void *data, MESSAGE msg) { RC_SECTION *sec = rc_section_lookup (parse_tree, name); if (!sec) - anubis_error (0, 0, _("No such section: %s"), name); + info (VERBOSE, _("No such section: %s"), name); rc_call_section (method, sec, anubis_rc_sections, data, msg); } diff --git a/src/tunnel.c b/src/tunnel.c index 67c61a1..1b6ecac 100644 --- a/src/tunnel.c +++ b/src/tunnel.c @@ -2,7 +2,8 @@ tunnel.c This file is part of GNU Anubis. - Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009 The Anubis Team. + Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008, + 2009 The Anubis Team. GNU Anubis is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the @@ -25,7 +26,7 @@ #define obstack_chunk_free free #include <obstack.h> -static int transfer_command (MESSAGE, char *); +static int transfer_command (MESSAGE); static int process_command (MESSAGE, char *); static void process_data (MESSAGE); static int handle_ehlo (ANUBIS_SMTP_REPLY ); @@ -358,7 +359,7 @@ smtp_session_transparent (void) if (process_command (msg, command)) continue; - if (transfer_command (msg, command) == 0) + if (transfer_command (msg) == 0) break; } free (command); @@ -402,7 +403,7 @@ smtp_session (void) if (process_command (msg, command)) continue; - if (transfer_command (msg, command) == 0) + if (transfer_command (msg) == 0) break; } free (command); @@ -414,7 +415,7 @@ smtp_session (void) *********************/ static void -save_command (MESSAGE msg, char *line) +save_command (MESSAGE msg, const char *line) { int i; ASSOC *asc = xmalloc (sizeof (*asc)); @@ -425,14 +426,14 @@ save_command (MESSAGE msg, char *line) if (i == 4) { char *expect = NULL; - if (memcmp (line, "mail", 4) == 0) + if (strncasecmp (line, "mail", 4) == 0) expect = " from:"; - else if (memcmp (line, "rcpt", 4) == 0) + else if (strncasecmp (line, "rcpt", 4) == 0) expect = " to:"; if (expect) { int n = strlen (expect); - if (strncmp (&line[i], expect, n) == 0) + if (strncasecmp (&line[i], expect, n) == 0) i += n; } } @@ -440,6 +441,8 @@ save_command (MESSAGE msg, char *line) asc->key = xmalloc (i + 1); memcpy (asc->key, line, i); asc->key[i] = 0; + make_uppercase (asc->key); + for (; line[i] && isspace ((u_char) line[i]); i++) ; @@ -560,17 +563,11 @@ handle_starttls (char *command) static int process_command (MESSAGE msg, char *command) { - char buf[LINEBUFFER + 1]; - - safe_strcpy (buf, command); /* make a back-up */ - make_lowercase (buf); - save_command (msg, buf); - - if (!strncmp (buf, "starttls", 8)) - return handle_starttls (buf); - else if (!strncmp (buf, "xdatabase", 9)) - return xdatabase (buf + 9); - + save_command (msg, command); + if (!strncasecmp (command, "starttls", 8)) + return handle_starttls (command); + else if (!strncasecmp (command, "xdatabase", 9)) + return xdatabase (command + 9); return 0; } @@ -701,55 +698,29 @@ handle_ehlo (ANUBIS_SMTP_REPLY reply) return 0; } -#define CMD_MAIL_FROM "mail from:" -#define CMD_RCPT_TO "rcpt to:" - -static void -strip_inter_ws (char *buf, size_t len, size_t pfxlen) -{ - if (isspace (buf[pfxlen])) - { - size_t i; - for (i = pfxlen; i < len && isspace (buf[i]); i++) - ; - memmove (buf + pfxlen, buf + i, len - i + 1); - } -} - static int -is_prefix(char *buf, char *pfx) -{ - while (*pfx) - { - int b = *buf++; - int c = *pfx++; - - if (!b || toupper (c) != toupper (b)) - return 0; - } - return 1; -} - -static int -transfer_command (MESSAGE msg, char *command) +transfer_command (MESSAGE msg) { char *buf = NULL; int rc = 1; /* OK */ - size_t len; ANUBIS_SMTP_REPLY reply = smtp_reply_new (); const char *rstr; - + ASSOC *asc; + char *command; + + rcfile_call_section (CF_CLIENT, smtp_command_rule, NULL, msg); + asc = list_tail_item (message_get_commands (msg)); + if (!asc->value) + command = strdup (asc->key); + else if (strcasecmp (asc->key, "mail from:") == 0 + || strcasecmp (asc->key, "rcpt to:") == 0) + asprintf (&command, "%s%s", asc->key, asc->value); + else + asprintf (&command, "%s %s", asc->key, asc->value); + assign_string (&buf, command); make_lowercase (buf); - len = strlen (command); - if (len > sizeof (CMD_MAIL_FROM) - && is_prefix (command, CMD_MAIL_FROM)) - strip_inter_ws (command, len, sizeof (CMD_MAIL_FROM) - 1); - else if (len > sizeof (CMD_RCPT_TO) - && is_prefix (command, CMD_RCPT_TO)) - strip_inter_ws (command, len, sizeof (CMD_RCPT_TO) - 1); - swrite (CLIENT, remote_server, command); swrite (CLIENT, remote_server, CRLF); @@ -759,6 +730,7 @@ transfer_command (MESSAGE msg, char *command) if (handle_ehlo (reply)) { smtp_reply_free (reply); + free (command); return 0; } } @@ -782,6 +754,7 @@ transfer_command (MESSAGE msg, char *command) } } free (buf); + free (command); smtp_reply_free (reply); return rc; } |