From f67c28af2aaf03b60e5bbf606fc694b04ccc68c5 Mon Sep 17 00:00:00 2001 From: "cebka@mailsupport.rambler.ru" Date: Sat, 20 Sep 2008 04:13:49 +0400 Subject: [PATCH] * Place all protocol logic in separate file - simplify protocol parsing logic - add compatibility with sa-spamd - TODO: add protocol output for all commands --- configure | 4 +- main.h | 3 + protocol.c | 254 +++++++++++++++++++++++++++++++++++++++++++++++++++++ protocol.h | 25 ++++++ worker.c | 103 +--------------------- 5 files changed, 288 insertions(+), 101 deletions(-) create mode 100644 protocol.c create mode 100644 protocol.h diff --git a/configure b/configure index a8a5ddd32..8150e03d4 100755 --- a/configure +++ b/configure @@ -21,7 +21,7 @@ YACC_OUTPUT="cfg_yacc.c" LEX_OUTPUT="cfg_lex.c" CONFIG="config.h" -SOURCES="upstream.c cfg_utils.c memcached.c main.c util.c worker.c fstring.c url.c perl.c mem_pool.c plugins/surbl.c ${LEX_OUTPUT} ${YACC_OUTPUT}" +SOURCES="upstream.c cfg_utils.c memcached.c main.c util.c worker.c fstring.c url.c perl.c protocol.c mem_pool.c plugins/surbl.c ${LEX_OUTPUT} ${YACC_OUTPUT}" MODULES="surbl" CFLAGS="$CFLAGS -W -Wpointer-arith -Wno-unused-parameter" @@ -30,7 +30,7 @@ CFLAGS="$CFLAGS -Wunused-value -ggdb -I${LOCALBASE}/include" CFLAGS="$CFLAGS " LDFLAGS="$LDFLAGS -L/usr/lib -L${LOCALBASE}/lib" OPT_FLAGS="-O -pipe -fno-omit-frame-pointer" -DEPS="config.h cfg_file.h memcached.h util.h main.h upstream.h fstring.h url.h perl.h mem_pool.h ${LEX_OUTPUT} ${YACC_OUTPUT}" +DEPS="config.h cfg_file.h memcached.h util.h main.h upstream.h fstring.h url.h perl.h mem_pool.h protocol.h ${LEX_OUTPUT} ${YACC_OUTPUT}" EXEC=rspamd USER=postfix GROUP=postfix diff --git a/main.h b/main.h index c7e39c38f..e4f7fc600 100644 --- a/main.h +++ b/main.h @@ -23,6 +23,7 @@ #include "mem_pool.h" #include "url.h" #include "memcached.h" +#include "protocol.h" #include #include @@ -112,6 +113,8 @@ struct worker_task { WAIT_FILTER, } state; size_t content_length; + enum rspamd_protocol proto; + enum rspamd_command cmd; char *helo; char *from; char *rcpt; diff --git a/protocol.c b/protocol.c new file mode 100644 index 000000000..12fdfcf25 --- /dev/null +++ b/protocol.c @@ -0,0 +1,254 @@ +#include +#include +#include +#include +#include "main.h" + +/* + * Just check if the passed message is spam or not and reply as + * described below + */ +#define MSG_CMD_CHECK "check" +/* + * Check if message is spam or not, and return score plus list + * of symbols hit + */ +#define MSG_CMD_SYMBOLS "symbols" +/* + * Check if message is spam or not, and return score plus report + */ +#define MSG_CMD_REPORT "report" +/* + * Check if message is spam or not, and return score plus report + * if the message is spam + */ +#define MSG_CMD_REPORT_IFSPAM "report_ifspam" +/* + * Ignore this message -- client opened connection then changed + */ +#define MSG_CMD_SKIP "skip" +/* + * Return a confirmation that spamd is alive + */ +#define MSG_CMD_PING "ping" +/* + * Process this message as described above and return modified message + */ +#define MSG_CMD_PROCESS "process" + +/* + * spamassassin greeting: + */ +#define SPAMC_GREETING "SPAMC" +/* + * rspamd greeting: + */ +#define RSPAMC_GREETING "RSPAMC" +/* + * Headers + */ +#define CONTENT_LENGTH_HEADER "Content-Length" +#define HELO_HEADER "Helo" +#define FROM_HEADER "From" +#define IP_ADDR_HEADER "IP" +#define NRCPT_HEADER "Recipient-Number" +#define RCPT_HEADER "Rcpt" + +static int +parse_command (struct worker_task *task, char *line) +{ + char *token; + + token = strsep (&line, " "); + if (line == NULL || token == NULL) { + return -1; + } + + switch (token[0]) { + case 'c': + case 'C': + /* check */ + if (strcasecmp (token + 1, MSG_CMD_CHECK + 1) == 0) { + task->cmd = CMD_CHECK; + } + else { + msg_debug ("parse_command: bad comand: %s", token); + return -1; + } + break; + case 's': + case 'S': + /* symbols, skip */ + if (strcasecmp (token + 1, MSG_CMD_SYMBOLS + 1) == 0) { + task->cmd = CMD_SYMBOLS; + } + else if (strcasecmp (token + 1, MSG_CMD_SKIP + 1) == 0) { + task->cmd = CMD_SKIP; + } + else { + msg_debug ("parse_command: bad comand: %s", token); + return -1; + } + break; + case 'p': + case 'P': + /* ping, process */ + if (strcasecmp (token + 1, MSG_CMD_PING + 1) == 0) { + task->cmd = CMD_PING; + } + else if (strcasecmp (token + 1, MSG_CMD_PROCESS + 1) == 0) { + task->cmd = CMD_PROCESS; + } + else { + msg_debug ("parse_command: bad comand: %s", token); + return -1; + } + break; + case 'r': + case 'R': + /* report, report_ifspam */ + if (strcasecmp (token + 1, MSG_CMD_REPORT + 1) == 0) { + task->cmd = CMD_REPORT; + } + else if (strcasecmp (token + 1, MSG_CMD_REPORT_IFSPAM + 1) == 0) { + task->cmd = CMD_REPORT_IFSPAM; + } + else { + msg_debug ("parse_command: bad comand: %s", token); + return -1; + } + break; + default: + msg_debug ("parse_command: bad comand: %s", token); + return -1; + } + + if (strncasecmp (line, RSPAMC_GREETING, sizeof (RSPAMC_GREETING) - 1) == 0) { + task->proto = RSPAMC_PROTO; + } + else if (strncasecmp (line, SPAMC_GREETING, sizeof (SPAMC_GREETING) -1) == 0) { + task->proto = SPAMC_PROTO; + } + else { + msg_debug ("parse_command: bad protocol version: %s", line); + return -1; + } + task->state = READ_HEADER; + return 0; +} + +static int +parse_header (struct worker_task *task, char *line) +{ + char *headern, *err; + headern = strsep (&line, ":"); + + /* Check end of headers */ + if (*line == '\r' && *(line + 1) == '\n') { + task->state = READ_MESSAGE; + return 0; + } + + if (line == NULL || headern == NULL) { + return -1; + } + /* Eat whitespaces */ + g_strstrip (line); + g_strstrip (headern); + + switch (headern[0]) { + case 'c': + case 'C': + /* content-length */ + if (strncasecmp (headern, CONTENT_LENGTH_HEADER, sizeof (CONTENT_LENGTH_HEADER) - 1) == 0) { + task->content_length = strtoul (line, &err, 10); + task->msg = g_malloc (sizeof (f_str_buf_t)); + task->msg->buf = fstralloc (task->content_length); + if (task->msg->buf == NULL) { + msg_err ("read_socket: cannot allocate memory for message buffer"); + return -1; + } + } + else { + msg_info ("parse_header: wrong header: %s", headern); + return -1; + } + break; + case 'h': + case 'H': + /* helo */ + if (strncasecmp (headern, HELO_HEADER, sizeof (HELO_HEADER) - 1) == 0) { + task->helo = g_strdup (line); + } + else { + msg_info ("parse_header: wrong header: %s", headern); + return -1; + } + break; + case 'f': + case 'F': + /* from */ + if (strncasecmp (headern, FROM_HEADER, sizeof (FROM_HEADER) - 1) == 0) { + task->from = g_strdup (line); + } + else { + msg_info ("parse_header: wrong header: %s", headern); + return -1; + } + break; + case 'r': + case 'R': + /* rcpt */ + if (strncasecmp (headern, RCPT_HEADER, sizeof (RCPT_HEADER) - 1) == 0) { + task->rcpt = g_strdup (line); + } + else { + msg_info ("parse_header: wrong header: %s", headern); + return -1; + } + break; + case 'n': + case 'N': + /* nrcpt */ + if (strncasecmp (headern, NRCPT_HEADER, sizeof (NRCPT_HEADER) - 1) == 0) { + task->nrcpt = strtoul (line, &err, 10); + } + else { + msg_info ("parse_header: wrong header: %s", headern); + return -1; + } + break; + case 'i': + case 'I': + /* ip_addr */ + if (strncasecmp (headern, IP_ADDR_HEADER, sizeof (IP_ADDR_HEADER) - 1) == 0) { + if (!inet_aton (line, &task->from_addr)) { + msg_info ("parse_header: bad ip header: '%s'", line); + return -1; + } + } + else { + msg_info ("parse_header: wrong header: %s", headern); + return -1; + } + break; + default: + msg_info ("parse_header: wrong header: %s", headern); + return -1; + } + + return 0; +} + +int +read_rspamd_input_line (struct worker_task *task, char *line) +{ + switch (task->state) { + case READ_COMMAND: + return parse_command (task, line); + break; + case READ_HEADER: + return parse_header (task, line); + break; + } +} diff --git a/protocol.h b/protocol.h new file mode 100644 index 000000000..17a7a9315 --- /dev/null +++ b/protocol.h @@ -0,0 +1,25 @@ +#ifndef RSPAMD_PROTOCOL_H +#define RSPAMD_PROTOCOL_H + +#include "config.h" + +struct worker_task; + +enum rspamd_protocol { + SPAMC_PROTO, + RSPAMC_PROTO, +}; + +enum rspamd_command { + CMD_CHECK, + CMD_SYMBOLS, + CMD_REPORT, + CMD_REPORT_IFSPAM, + CMD_SKIP, + CMD_PING, + CMD_PROCESS, +}; + +int read_rspamd_input_line (struct worker_task *task, char *line); + +#endif diff --git a/worker.c b/worker.c index ea22ffbe2..7c3ce6b95 100644 --- a/worker.c +++ b/worker.c @@ -23,6 +23,7 @@ #include "util.h" #include "main.h" +#include "protocol.h" #include "upstream.h" #include "cfg_file.h" #include "url.h" @@ -415,108 +416,12 @@ read_socket (struct bufferevent *bev, void *arg) switch (task->state) { case READ_COMMAND: - s = evbuffer_readline (EVBUFFER_INPUT (bev)); - if (s != NULL) { - msg_info ("read_socket: got command %s", s); - free (s); - task->state = READ_HEADER; - } - break; case READ_HEADER: s = evbuffer_readline (EVBUFFER_INPUT (bev)); - if (s != NULL) { - msg_info ("read_socket: got header %s", s); - if (strncasecmp (s, CONTENT_LENGTH_HEADER, sizeof (CONTENT_LENGTH_HEADER) - 1) == 0) { - task->content_length = atoi (s + sizeof (CONTENT_LENGTH_HEADER) - 1); - msg_info ("read_socket: parsed content-length: %ld", (long int)task->content_length); - task->msg = malloc (sizeof (f_str_buf_t)); - if (task->msg == NULL) { - msg_err ("read_socket: cannot allocate memory"); - bufferevent_disable (bev, EV_READ); - bufferevent_free (bev); - free (task); - } - task->msg->buf = fstralloc (task->content_length); - if (task->msg->buf == NULL) { - msg_err ("read_socket: cannot allocate memory for message buffer"); - bufferevent_disable (bev, EV_READ); - bufferevent_free (bev); - free (task->msg); - free (task); - } - task->msg->pos = task->msg->buf->begin; - update_buf_size (task->msg); - } - else if (strncasecmp (s, HELO_HEADER, sizeof (HELO_HEADER) - 1) == 0) { - c = rindex (s, '\r'); - if (c != NULL) { - task->helo = malloc (c - (s + sizeof (HELO_HEADER) - 1)); - if (task->helo) { - strlcpy (task->helo, s + sizeof (HELO_HEADER) - 1, (c - (s + sizeof (HELO_HEADER) - 1))); - } - else { - msg_err ("read_socket: malloc failed for HELO header: %m"); - } - } - else { - msg_err ("read_socket: header " HELO_HEADER " has invalid format, ignored"); - } - } - else if (strncasecmp (s, FROM_HEADER, sizeof (FROM_HEADER) - 1) == 0) { - c = rindex (s, '\r'); - if (c != NULL) { - task->from = malloc (c - (s + sizeof (FROM_HEADER) - 1)); - if (task->from) { - strlcpy (task->from, s + sizeof (FROM_HEADER) - 1, (c - (s + sizeof (FROM_HEADER) - 1))); - } - else { - msg_err ("read_socket: malloc failed for FROM header: %m"); - } - } - else { - msg_err ("read_socket: header " FROM_HEADER " has invalid format, ignored"); - } - } - else if (strncasecmp (s, RCPT_HEADER, sizeof (RCPT_HEADER) - 1) == 0) { - c = rindex (s, '\r'); - if (c != NULL) { - task->rcpt = malloc (c - (s + sizeof (RCPT_HEADER) - 1)); - if (task->rcpt) { - strlcpy (task->rcpt, s + sizeof (RCPT_HEADER) - 1, (c - (s + sizeof (RCPT_HEADER) - 1))); - } - else { - msg_err ("read_socket: malloc failed for RCPT header: %m"); - } - } - else { - msg_err ("read_socket: header " RCPT_HEADER " has invalid format, ignored"); - } - } - else if (strncasecmp (s, NRCPT_HEADER, sizeof (NRCPT_HEADER) - 1) == 0) { - task->nrcpt = atoi (s + sizeof (NRCPT_HEADER) - 1); - } - else if (strncasecmp (s, IP_ADDR_HEADER, sizeof (IP_ADDR_HEADER) - 1) == 0) { - c = rindex (s, '\r'); - if (c != NULL) { - *c = 0; - if (!inet_aton (s + sizeof (IP_ADDR_HEADER) - 1, &task->from_addr)) { - msg_info ("read_socket: bad ip header: '%s'", s); - } - } - else { - msg_err ("read_socket: header " IP_ADDR_HEADER " has invalid format, ignored"); - } - } - else if (strlen (s) == 0 || (*s == '\r' && *(s+1) == '\n')) { - if (task->content_length != 0) { - task->state = READ_MESSAGE; - } - else { - task->state = WRITE_ERROR; - } - } - free (s); + if (read_rspamd_input_line (task, s) != 0) { + task->state = WRITE_ERROR; } + free (s); break; case READ_MESSAGE: r = bufferevent_read (bev, task->msg->pos, task->msg->free); -- 2.39.5