From 43c1eafc483b70ace596cdd9a00378c35047feec Mon Sep 17 00:00:00 2001 From: SJ Date: Wed, 2 Dec 2015 21:49:51 +0100 Subject: [PATCH] improved the smtp engine to handle partial commands --- configure | 2 +- configure.in | 2 +- src/cfg.c | 4 - src/cfg.h | 6 - src/config.h | 4 +- src/session.c | 194 ++++++--------------------- src/smtp.c | 194 +++++++++++++++++++++++++++ src/smtp.h | 18 +++ unit_tests/Makefile.in | 7 +- unit_tests/smtp.c | 291 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 550 insertions(+), 172 deletions(-) create mode 100644 src/smtp.c create mode 100644 src/smtp.h create mode 100644 unit_tests/smtp.c diff --git a/configure b/configure index 5ee5e46a..48415326 100755 --- a/configure +++ b/configure @@ -4889,7 +4889,7 @@ echo; echo CFLAGS="$static -O2 -Wall -g" LIBS="$antispam_libs $sunos_libs " -OBJS="dirs.o base64.o misc.o counters.o cfg.o sig.o decoder.o hash.o parser.o parser_utils.o rules.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o imap.o pop3.o extract.o mydomains.o $objs" +OBJS="dirs.o base64.o misc.o counters.o cfg.o sig.o decoder.o hash.o parser.o parser_utils.o rules.o smtp.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o imap.o pop3.o extract.o mydomains.o $objs" ac_config_files="$ac_config_files Makefile src/Makefile etc/Makefile util/Makefile init.d/Makefile unit_tests/Makefile contrib/imap/Makefile" diff --git a/configure.in b/configure.in index a7b91eab..f66e21aa 100644 --- a/configure.in +++ b/configure.in @@ -556,7 +556,7 @@ echo; echo CFLAGS="$static -O2 -Wall -g" LIBS="$antispam_libs $sunos_libs " -OBJS="dirs.o base64.o misc.o counters.o cfg.o sig.o decoder.o hash.o parser.o parser_utils.o rules.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o imap.o pop3.o extract.o mydomains.o $objs" +OBJS="dirs.o base64.o misc.o counters.o cfg.o sig.o decoder.o hash.o parser.o parser_utils.o rules.o smtp.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o imap.o pop3.o extract.o mydomains.o $objs" AC_CONFIG_FILES([Makefile src/Makefile etc/Makefile util/Makefile init.d/Makefile unit_tests/Makefile contrib/imap/Makefile]) AC_OUTPUT diff --git a/src/cfg.c b/src/cfg.c index 47984645..02d9e729 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -96,10 +96,6 @@ struct _parse_rule config_parse_rules[] = { "pemfile", "string", (void*) string_parser, offsetof(struct __config, pemfile), "", MAXVAL-1}, { "pidfile", "string", (void*) string_parser, offsetof(struct __config, pidfile), PIDFILE, MAXVAL-1}, { "piler_header_field", "string", (void*) string_parser, offsetof(struct __config, piler_header_field), "X-piler-id:", MAXVAL-1}, - { "pilergetd_listen_addr", "string", (void*) string_parser, offsetof(struct __config, pilergetd_listen_addr), "127.0.0.1", MAXVAL-1}, - { "pilergetd_listen_port", "integer", (void*) int_parser, offsetof(struct __config, pilergetd_listen_port), "10091", sizeof(int)}, - { "pilergetd_password", "string", (void*) string_parser, offsetof(struct __config, pilergetd_password), "xxxxxxxxxx", MAXVAL-1}, - { "pilergetd_pidfile", "string", (void*) string_parser, offsetof(struct __config, pilergetd_pidfile), PILERGETD_PIDFILE, MAXVAL-1}, { "process_rcpt_to_addresses", "integer", (void*) int_parser, offsetof(struct __config, process_rcpt_to_addresses), "0", sizeof(int)}, { "queuedir", "string", (void*) string_parser, offsetof(struct __config, queuedir), QUEUE_DIR, MAXVAL-1}, { "server_id", "integer", (void*) int_parser, offsetof(struct __config, server_id), "0", sizeof(int)}, diff --git a/src/cfg.h b/src/cfg.h index 413c4de5..978254d3 100644 --- a/src/cfg.h +++ b/src/cfg.h @@ -15,16 +15,10 @@ struct __config { int hostid_len; char pidfile[MAXVAL]; - char pilergetd_pidfile[MAXVAL]; char listen_addr[MAXVAL]; int listen_port; - char pilergetd_listen_addr[MAXVAL]; - int pilergetd_listen_port; - - char pilergetd_password[MAXVAL]; - char clamd_addr[MAXVAL]; int clamd_port; char clamd_socket[MAXVAL]; diff --git a/src/config.h b/src/config.h index 45705b77..ae0e5908 100644 --- a/src/config.h +++ b/src/config.h @@ -10,11 +10,10 @@ #include "params.h" #define PROGNAME "piler" -#define PILERGETD_PROGNAME "pilergetd" #define VERSION "1.2.0-master" -#define BUILD 928 +#define BUILD 929 #define HOSTID "mailarchiver" @@ -25,7 +24,6 @@ #define CLAMD_SOCKET "/tmp/clamd" #define PIDFILE "/var/run/piler/piler.pid" -#define PILERGETD_PIDFILE "/var/run/piler/pilergetd.pid" #define QUARANTINELEN 255 #define TIMEOUT 60 #define TIMEOUT_USEC 500000 diff --git a/src/session.c b/src/session.c index fead864b..53af2b97 100644 --- a/src/session.c +++ b/src/session.c @@ -17,13 +17,13 @@ #include #include #include - +#include "smtp.h" int is_blocked_by_tcp_wrappers(int sd); int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){ - int i, ret, pos, n, inj=ERR, protocol_state, prevlen=0; + int i, ret, pos, readpos=0, result, n, inj=ERR, protocol_state, prevlen=0; char *p, *rcpt, buf[MAXBUFSIZE], puf[MAXBUFSIZE], resp[MAXBUFSIZE], prevbuf[MAXBUFSIZE], last2buf[2*MAXBUFSIZE+1]; char virusinfo[SMALLBUFSIZE], delay[SMALLBUFSIZE], tmpbuf[SMALLBUFSIZE]; char *arule = NULL; @@ -39,7 +39,6 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){ #ifdef HAVE_STARTTLS int starttls = 0; - char ssl_error[SMALLBUFSIZE]; #endif @@ -87,7 +86,7 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){ send(new_sd, buf, strlen(buf), 0); if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, buf); - while((n = recvtimeoutssl(new_sd, puf, MAXBUFSIZE, TIMEOUT, sdata.tls, data->ssl)) > 0){ + while((n = recvtimeoutssl(new_sd, &puf[readpos], sizeof(puf)-readpos, TIMEOUT, sdata.tls, data->ssl)) > 0){ pos = 0; /* accept mail data */ @@ -307,7 +306,7 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){ pos = 0; } - } /* PERIOD found */ + } /* pos > 0, PERIOD found */ else { ret = write(sdata.fd, puf, n); sdata.tot_len += ret; @@ -318,28 +317,37 @@ int handle_smtp_session(int new_sd, struct __data *data, struct __config *cfg){ continue; } - } /* SMTP DATA */ + } /* if protocol_state == SMTP_STATE_DATA */ AFTER_PERIOD: /* handle smtp commands */ - memset(resp, 0, MAXBUFSIZE); + memset(resp, 0, sizeof(resp)); p = &puf[pos]; + readpos = 0; + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: command=*%s*", sdata.ttmpfile, p); + + do { + p = split(p, '\n', buf, sizeof(buf)-1, &result); + + if(result == 0){ + if(strlen(buf) > 0){ + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: partial read: *%s*", sdata.ttmpfile, buf); + + snprintf(puf, sizeof(puf)-5, "%s", buf); + readpos = strlen(puf); + } + + break; + } - while((p = split_str(p, "\r\n", buf, MAXBUFSIZE-1))){ if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: got: %s", sdata.ttmpfile, buf); - if(strncasecmp(buf, SMTP_CMD_EHLO, strlen(SMTP_CMD_EHLO)) == 0 || strncasecmp(buf, LMTP_CMD_LHLO, strlen(LMTP_CMD_LHLO)) == 0){ - if(protocol_state == SMTP_STATE_INIT) protocol_state = SMTP_STATE_HELO; - - if(sdata.tls == 0) snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_250_EXTENSIONS, cfg->hostid, data->starttls); - else snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_250_EXTENSIONS, cfg->hostid, ""); - - strncat(resp, buf, MAXBUFSIZE-1); - + process_command_ehlo_lhlo(&sdata, data, &protocol_state, buf, &resp[0], sizeof(resp)-1, cfg); continue; /* FIXME: implement the ENHANCEDSTATUSCODE extensions */ @@ -348,198 +356,73 @@ AFTER_PERIOD: if(strncasecmp(buf, SMTP_CMD_HELO, strlen(SMTP_CMD_HELO)) == 0){ if(protocol_state == SMTP_STATE_INIT) protocol_state = SMTP_STATE_HELO; - - strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1); - + strncat(resp, SMTP_RESP_250_OK, sizeof(resp)-1); continue; } #ifdef HAVE_STARTTLS if(cfg->tls_enable > 0 && strncasecmp(buf, SMTP_CMD_STARTTLS, strlen(SMTP_CMD_STARTTLS)) == 0 && strlen(data->starttls) > 4 && sdata.tls == 0){ - if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: starttls request from client", sdata.ttmpfile); - - if(data->ctx){ - data->ssl = SSL_new(data->ctx); - if(data->ssl){ - - SSL_set_options(data->ssl, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); - - if(SSL_set_fd(data->ssl, new_sd) == 1){ - strncat(resp, SMTP_RESP_220_READY_TO_START_TLS, MAXBUFSIZE-1); - starttls = 1; - protocol_state = SMTP_STATE_INIT; - - continue; - } syslog(LOG_PRIORITY, "%s: SSL_set_fd() failed", sdata.ttmpfile); - } syslog(LOG_PRIORITY, "%s: SSL_new() failed", sdata.ttmpfile); - } syslog(LOG_PRIORITY, "%s: SSL ctx is null!", sdata.ttmpfile); - - - strncat(resp, SMTP_RESP_454_ERR_TLS_TEMP_ERROR, MAXBUFSIZE-1); + process_command_starttls(&sdata, data, &protocol_state, &starttls, buf, new_sd, &resp[0], sizeof(resp)-1, cfg); continue; } #endif if(strncasecmp(buf, SMTP_CMD_MAIL_FROM, strlen(SMTP_CMD_MAIL_FROM)) == 0){ - - if(protocol_state != SMTP_STATE_HELO && protocol_state != SMTP_STATE_PERIOD){ - strncat(resp, SMTP_RESP_503_ERR, MAXBUFSIZE-1); - } - else { - - if(protocol_state == SMTP_STATE_PERIOD){ - if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: initiated new transaction", sdata.ttmpfile); - - unlink(sdata.ttmpfile); - unlink(sdata.tmpframe); - - init_session_data(&sdata, cfg); - } - - protocol_state = SMTP_STATE_MAIL_FROM; - - snprintf(sdata.mailfrom, SMALLBUFSIZE-1, "%s\r\n", buf); - - memset(sdata.fromemail, 0, SMALLBUFSIZE); - extractEmail(sdata.mailfrom, sdata.fromemail); - - strncat(resp, SMTP_RESP_250_OK, strlen(SMTP_RESP_250_OK)); - - } - + process_command_mail_from(&sdata, &protocol_state, buf, &resp[0], sizeof(resp)-1, cfg); continue; } if(strncasecmp(buf, SMTP_CMD_RCPT_TO, strlen(SMTP_CMD_RCPT_TO)) == 0){ - - if(protocol_state == SMTP_STATE_MAIL_FROM || protocol_state == SMTP_STATE_RCPT_TO){ - if(strlen(buf) > SMALLBUFSIZE/2){ - strncat(resp, SMTP_RESP_550_ERR_TOO_LONG_RCPT_TO, MAXBUFSIZE-1); - continue; - } - - if(sdata.num_of_rcpt_to < MAX_RCPT_TO-1){ - extractEmail(buf, sdata.rcptto[sdata.num_of_rcpt_to]); - } - - protocol_state = SMTP_STATE_RCPT_TO; - - if(sdata.num_of_rcpt_to < MAX_RCPT_TO-1) sdata.num_of_rcpt_to++; - - - strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1); - } - else { - strncat(resp, SMTP_RESP_503_ERR, MAXBUFSIZE-1); - } - + process_command_rcpt_to(&sdata, &protocol_state, buf, &resp[0], sizeof(resp)-1, cfg); continue; } if(strncasecmp(buf, SMTP_CMD_DATA, strlen(SMTP_CMD_DATA)) == 0){ - memset(last2buf, 0, 2*MAXBUFSIZE+1); memset(prevbuf, 0, MAXBUFSIZE); inj = ERR; prevlen = 0; - if(protocol_state != SMTP_STATE_RCPT_TO){ - strncat(resp, SMTP_RESP_503_ERR, MAXBUFSIZE-1); - } - else { - sdata.fd = open(sdata.filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP); - if(sdata.fd == -1){ - syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, sdata.ttmpfile); - strncat(resp, SMTP_RESP_451_ERR, MAXBUFSIZE-1); - } - else { - protocol_state = SMTP_STATE_DATA; - strncat(resp, SMTP_RESP_354_DATA_OK, MAXBUFSIZE-1); - } - - } - + process_command_data(&sdata, &protocol_state, buf, &resp[0], sizeof(resp)-1, cfg); continue; } if(strncasecmp(buf, SMTP_CMD_QUIT, strlen(SMTP_CMD_QUIT)) == 0){ - - protocol_state = SMTP_STATE_FINISHED; - - snprintf(buf, MAXBUFSIZE-1, SMTP_RESP_221_GOODBYE, cfg->hostid); - strncat(resp, buf, MAXBUFSIZE-1); - - unlink(sdata.ttmpfile); - unlink(sdata.tmpframe); - if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sdata.ttmpfile); - + process_command_quit(&sdata, &protocol_state, buf, &resp[0], sizeof(resp)-1, cfg); continue; } if(strncasecmp(buf, SMTP_CMD_NOOP, strlen(SMTP_CMD_NOOP)) == 0){ - strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1); + strncat(resp, SMTP_RESP_250_OK, sizeof(resp)-1); continue; } if(strncasecmp(buf, SMTP_CMD_RESET, strlen(SMTP_CMD_RESET)) == 0){ - - strncat(resp, SMTP_RESP_250_OK, MAXBUFSIZE-1); - - if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sdata.ttmpfile); - unlink(sdata.ttmpfile); - unlink(sdata.tmpframe); - - init_session_data(&sdata, cfg); - - protocol_state = SMTP_STATE_HELO; - + process_command_reset(&sdata, &protocol_state, buf, &resp[0], sizeof(resp)-1, cfg); continue; } + /* by default send 502 command not implemented message */ syslog(LOG_PRIORITY, "%s: invalid command: *%s*", sdata.ttmpfile, buf); - strncat(resp, SMTP_RESP_502_ERR, MAXBUFSIZE-1); - } + strncat(resp, SMTP_RESP_502_ERR, sizeof(resp)-1); + } while(p); - /* now we can send our buffered response */ - if(strlen(resp) > 0){ - write1(new_sd, resp, strlen(resp), sdata.tls, data->ssl); - - if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata.ttmpfile, resp); - memset(resp, 0, MAXBUFSIZE); - - #ifdef HAVE_STARTTLS - if(starttls == 1 && sdata.tls == 0){ - - if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: waiting for ssl handshake", sdata.ttmpfile); - - rc = SSL_accept(data->ssl); - - if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: SSL_accept() finished", sdata.ttmpfile); - - if(rc == 1){ - sdata.tls = 1; - } - else { - ERR_error_string_n(ERR_get_error(), ssl_error, SMALLBUFSIZE); - syslog(LOG_PRIORITY, "%s: SSL_accept() failed, rc=%d, errorcode: %d, error text: %s\n", sdata.ttmpfile, rc, SSL_get_error(data->ssl, rc), ssl_error); - } - } - #endif - - + send_buffered_response(&sdata, data, &protocol_state, starttls, buf, new_sd, &resp[0], sizeof(resp)-1, cfg); + memset(resp, 0, sizeof(resp)); } + if(protocol_state == SMTP_STATE_FINISHED){ goto QUITTING; } @@ -610,3 +493,4 @@ int is_blocked_by_tcp_wrappers(int sd){ } #endif + diff --git a/src/smtp.c b/src/smtp.c new file mode 100644 index 00000000..7ed7b585 --- /dev/null +++ b/src/smtp.c @@ -0,0 +1,194 @@ +/* + * smtp.c, SJ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void process_command_ehlo_lhlo(struct session_data *sdata, struct __data *data, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg){ + char tmpbuf[MAXBUFSIZE]; + + if(*protocol_state == SMTP_STATE_INIT) *protocol_state = SMTP_STATE_HELO; + + if(sdata->tls == 0) snprintf(tmpbuf, sizeof(tmpbuf)-1, SMTP_RESP_250_EXTENSIONS, cfg->hostid, data->starttls); + else snprintf(tmpbuf, sizeof(tmpbuf)-1, SMTP_RESP_250_EXTENSIONS, cfg->hostid, ""); + + strncat(resp, tmpbuf, resplen); +} + + +#ifdef HAVE_STARTTLS +void process_command_starttls(struct session_data *sdata, struct __data *data, int *protocol_state, int *starttls, char *buf, int new_sd, char *resp, int resplen, struct __config *cfg){ + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: starttls request from client", sdata->ttmpfile); + + if(data->ctx){ + data->ssl = SSL_new(data->ctx); + if(data->ssl){ + + SSL_set_options(data->ssl, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); + + if(SSL_set_fd(data->ssl, new_sd) == 1){ + strncat(resp, SMTP_RESP_220_READY_TO_START_TLS, resplen); + *starttls = 1; + *protocol_state = SMTP_STATE_INIT; + return; + } syslog(LOG_PRIORITY, "%s: SSL_set_fd() failed", sdata->ttmpfile); + } syslog(LOG_PRIORITY, "%s: SSL_new() failed", sdata->ttmpfile); + } syslog(LOG_PRIORITY, "%s: SSL ctx is null!", sdata->ttmpfile); + + strncat(resp, SMTP_RESP_454_ERR_TLS_TEMP_ERROR, resplen); +} +#endif + + +void process_command_mail_from(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg){ + + if(*protocol_state != SMTP_STATE_HELO && *protocol_state != SMTP_STATE_PERIOD){ + strncat(resp, SMTP_RESP_503_ERR, resplen); + } + else { + if(*protocol_state == SMTP_STATE_PERIOD){ + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: initiated new transaction", sdata->ttmpfile); + + unlink(sdata->ttmpfile); + unlink(sdata->tmpframe); + + init_session_data(sdata, cfg); + } + + *protocol_state = SMTP_STATE_MAIL_FROM; + + snprintf(sdata->mailfrom, SMALLBUFSIZE-1, "%s\r\n", buf); + + memset(sdata->fromemail, 0, SMALLBUFSIZE); + extractEmail(sdata->mailfrom, sdata->fromemail); + + strncat(resp, SMTP_RESP_250_OK, strlen(SMTP_RESP_250_OK)); + + } + +} + + +void process_command_rcpt_to(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg){ + + if(*protocol_state == SMTP_STATE_MAIL_FROM || *protocol_state == SMTP_STATE_RCPT_TO){ + + if(strlen(buf) > SMALLBUFSIZE/2){ + strncat(resp, SMTP_RESP_550_ERR_TOO_LONG_RCPT_TO, resplen); + return; + } + + if(sdata->num_of_rcpt_to < MAX_RCPT_TO-1){ + extractEmail(buf, sdata->rcptto[sdata->num_of_rcpt_to]); + } + + *protocol_state = SMTP_STATE_RCPT_TO; + + if(sdata->num_of_rcpt_to < MAX_RCPT_TO-1) sdata->num_of_rcpt_to++; + + strncat(resp, SMTP_RESP_250_OK, resplen); + } + else { + strncat(resp, SMTP_RESP_503_ERR, resplen); + } +} + + +void process_command_data(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg){ + + if(*protocol_state != SMTP_STATE_RCPT_TO){ + strncat(resp, SMTP_RESP_503_ERR, resplen); + } + else { + sdata->fd = open(sdata->filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP); + if(sdata->fd == -1){ + syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, sdata->ttmpfile); + strncat(resp, SMTP_RESP_451_ERR, resplen); + } + else { + *protocol_state = SMTP_STATE_DATA; + strncat(resp, SMTP_RESP_354_DATA_OK, resplen-1); + } + } + +} + + +void process_command_quit(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg){ + char tmpbuf[MAXBUFSIZE]; + + *protocol_state = SMTP_STATE_FINISHED; + + snprintf(tmpbuf, sizeof(tmpbuf)-1, SMTP_RESP_221_GOODBYE, cfg->hostid); + strncat(resp, tmpbuf, resplen); + + unlink(sdata->ttmpfile); + unlink(sdata->tmpframe); + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sdata->ttmpfile); +} + + +void process_command_reset(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg){ + + strncat(resp, SMTP_RESP_250_OK, resplen); + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sdata->ttmpfile); + + unlink(sdata->ttmpfile); + unlink(sdata->tmpframe); + + init_session_data(sdata, cfg); + + *protocol_state = SMTP_STATE_HELO; +} + + +void send_buffered_response(struct session_data *sdata, struct __data *data, int *protocol_state, int starttls, char *buf, int new_sd, char *resp, int resplen, struct __config *cfg){ + int rc; +#ifdef HAVE_STARTTLS + char ssl_error[SMALLBUFSIZE]; +#endif + + write1(new_sd, resp, strlen(resp), sdata->tls, data->ssl); + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sdata->ttmpfile, resp); + memset(resp, 0, MAXBUFSIZE); + +#ifdef HAVE_STARTTLS + if(starttls == 1 && sdata->tls == 0){ + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: waiting for ssl handshake", sdata->ttmpfile); + + rc = SSL_accept(data->ssl); + + if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: SSL_accept() finished", sdata->ttmpfile); + + if(rc == 1){ + sdata->tls = 1; + } + else { + ERR_error_string_n(ERR_get_error(), ssl_error, SMALLBUFSIZE); + syslog(LOG_PRIORITY, "%s: SSL_accept() failed, rc=%d, errorcode: %d, error text: %s\n", sdata->ttmpfile, rc, SSL_get_error(data->ssl, rc), ssl_error); + } + } +#endif +} + + diff --git a/src/smtp.h b/src/smtp.h new file mode 100644 index 00000000..708f384c --- /dev/null +++ b/src/smtp.h @@ -0,0 +1,18 @@ +/* + * smtp.h, SJ + */ + +#ifndef _SMTP_H + #define _SMTP_H + +void process_command_ehlo_lhlo(struct session_data *sdata, struct __data *data, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg); +void process_command_starttls(struct session_data *sdata, struct __data *data, int *protocol_state, int *starttls, char *buf, int new_sd, char *resp, int resplen, struct __config *cfg); +void process_command_mail_from(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg); +void process_command_rcpt_to(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg); +void process_command_data(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg); +void process_command_quit(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg); +void process_command_reset(struct session_data *sdata, int *protocol_state, char *buf, char *resp, int resplen, struct __config *cfg); + +void send_buffered_response(struct session_data *sdata, struct __data *data, int *protocol_state, int starttls, char *buf, int new_sd, char *resp, int resplen, struct __config *cfg); + +#endif /* _SMTP_H */ diff --git a/unit_tests/Makefile.in b/unit_tests/Makefile.in index 9be77d13..5c4ba671 100644 --- a/unit_tests/Makefile.in +++ b/unit_tests/Makefile.in @@ -24,13 +24,16 @@ RUNNING_GROUP = `@id_bin@ -gn $(RUNNING_USER)` INSTALL = @INSTALL@ -all: check_parser_utils +all: check_parser_utils smtp check_parser_utils: check_parser_utils.c ../src/libpiler.a $(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< -lpiler $(LIBS) $(LIBDIR) +smtp: smtp.c ../src/libpiler.a + $(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< -lpiler $(LIBS) $(LIBDIR) + clean: - rm -f check_parser_utils + rm -f check_parser_utils smtp distclean: clean rm -f Makefile diff --git a/unit_tests/smtp.c b/unit_tests/smtp.c new file mode 100644 index 00000000..1bf5700c --- /dev/null +++ b/unit_tests/smtp.c @@ -0,0 +1,291 @@ +/* + * smtp.c, SJ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/piler.h" + + +extern char *optarg; +extern int optind; + +char *testmessage = "From: aaa@aaa.fu\nTo: bela@aaa.fu\nMessage-Id: ajajajaja\nSubject: this is a test\n\nAaaaaa."; + + +int connect_to_smtp_server(char *server, int port, int timeout, int use_ssl, struct __data *data); + + +void usage(){ + printf("\nusage: smtp\n\n"); + printf(" -s SMTP server\n"); + printf(" -p SMTP port (25)\n"); + printf(" -t Timeout in sec (10)\n"); + + exit(0); +} + + +void send_smtp_command(int sd, char *cmd, char *buf, int buflen, int timeout, int use_ssl, struct __data *data){ + + printf("sent: %s", cmd); + write1(sd, cmd, strlen(cmd), use_ssl, data->ssl); + recvtimeoutssl(sd, buf, buflen, timeout, use_ssl, data->ssl); + printf("rcvd: %s", buf); + +} + + +static void test_smtp_commands_one_at_a_time(char *server, int port, int timeout){ + int sd, use_ssl = 0; + char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE]; + struct __data data; + + sd = connect_to_smtp_server(server, port, timeout, use_ssl, &data); + + send_smtp_command(sd, "HELO aaaa.fu\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "HELO"); + + send_smtp_command(sd, "MAIL FROM: \r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL"); + + send_smtp_command(sd, "RCPT TO: \r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "RCPT"); + + send_smtp_command(sd, "DATA\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "354 ", 4) == 0 && "DATA"); + + snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\n", testmessage); + + send_smtp_command(sd, sendbuf, recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "PERIOD"); + + send_smtp_command(sd, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "221 ", 4) == 0 && "QUIT"); + + close(sd); +} + + +static void test_smtp_commands_pipelining(char *server, int port, int timeout){ + int sd, use_ssl = 0; + char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE]; + struct __data data; + + sd = connect_to_smtp_server(server, port, timeout, use_ssl, &data); + + send_smtp_command(sd, "HELO aaaa.fu\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "HELO"); + + send_smtp_command(sd, "MAIL FROM: \r\nRCPT TO: \r\nDATA\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL"); + + snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\nQUIT\r\n", testmessage); + + send_smtp_command(sd, sendbuf, recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "QUIT"); + + close(sd); +} + + +static void test_smtp_commands_with_reset_command(char *server, int port, int timeout){ + int sd, use_ssl = 0; + char recvbuf[MAXBUFSIZE]; + struct __data data; + + sd = connect_to_smtp_server(server, port, timeout, use_ssl, &data); + + send_smtp_command(sd, "HELO aaaa.fu\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "HELO"); + + send_smtp_command(sd, "MAIL FROM: \r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL"); + + send_smtp_command(sd, "RSET\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "RSET"); + + send_smtp_command(sd, "RCPT TO: \r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "503 ", 4) == 0 && "RCPT"); + + send_smtp_command(sd, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "221 ", 4) == 0 && "QUIT"); + + close(sd); +} + + +static void test_smtp_commands_partial_command(char *server, int port, int timeout){ + int sd, use_ssl = 0; + char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE]; + struct __data data; + + sd = connect_to_smtp_server(server, port, timeout, use_ssl, &data); + + send_smtp_command(sd, "HELO aaaa.fu\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "HELO"); + + write1(sd, "M", 1, use_ssl, data.ssl); + printf("sent: M\n"); + + send_smtp_command(sd, "AIL FROM: \r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL"); + + send_smtp_command(sd, "RCPT TO: \r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "RCPT"); + + send_smtp_command(sd, "DATA\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "354 ", 4) == 0 && "DATA"); + + snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\n", testmessage); + + send_smtp_command(sd, sendbuf, recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "PERIOD"); + + send_smtp_command(sd, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "221 ", 4) == 0 && "QUIT"); + + close(sd); +} + + +static void test_smtp_commands_partial_command_pipelining(char *server, int port, int timeout){ + int sd, use_ssl = 0; + char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE]; + struct __data data; + + sd = connect_to_smtp_server(server, port, timeout, use_ssl, &data); + + send_smtp_command(sd, "HELO aaaa.fu\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "HELO"); + + write1(sd, "M", 1, use_ssl, data.ssl); + printf("sent: M\n"); + + send_smtp_command(sd, "AIL FROM: \r\nRCPT TO: \r\nDATA\r\n", recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL"); + + snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\nQUIT\r\n", testmessage); + + send_smtp_command(sd, sendbuf, recvbuf, sizeof(recvbuf)-1, timeout, use_ssl, &data); + assert(strncmp(recvbuf, "250 ", 4) == 0 && "QUIT"); + + close(sd); +} + + +int connect_to_smtp_server(char *server, int port, int timeout, int use_ssl, struct __data *data){ + int rc, sd = -1; + char port_string[8], buf[MAXBUFSIZE]; + struct addrinfo hints, *res; + + snprintf(port_string, sizeof(port_string)-1, "%d", port); + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if((rc = getaddrinfo(server, port_string, &hints, &res)) != 0){ + printf("getaddrinfo for '%s': %s\n", server, gai_strerror(rc)); + return sd; + } + + if((sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1){ + printf("cannot create socket\n"); + goto ENDE; + } + + if(connect(sd, res->ai_addr, res->ai_addrlen) == -1){ + printf("connect()\n"); + goto ENDE; + } + + recvtimeoutssl(sd, buf, sizeof(buf), timeout, use_ssl, data->ssl); + printf("rcvd: %s", buf); + +ENDE: + freeaddrinfo(res); + + return sd; +} + + +int main(int argc, char **argv){ + int c, port=25, timeout = 10; + char *server=NULL; + + while(1){ + +#ifdef _GNU_SOURCE + static struct option long_options[] = + { + {"server", required_argument, 0, 's' }, + {"port", required_argument, 0, 'p' }, + {"timeout", required_argument, 0, 't' }, + {"help", no_argument, 0, 'h' }, + {0,0,0,0} + }; + + int option_index = 0; + + c = getopt_long(argc, argv, "c:s:p:t:h?", long_options, &option_index); +#else + c = getopt(argc, argv, "c:s:p:t:h?"); +#endif + + + if(c == -1) break; + + switch(c){ + + case 's' : + server = optarg; + break; + + case 'p' : + port = atoi(optarg); + break; + + case 't' : + timeout = atoi(optarg); + break; + + case 'h' : + case '?' : + usage(); + break; + + + default : + break; + } + } + + if(!server) usage(); + + + test_smtp_commands_one_at_a_time(server, port, timeout); + test_smtp_commands_pipelining(server, port, timeout); + test_smtp_commands_with_reset_command(server, port, timeout); + test_smtp_commands_partial_command(server, port, timeout); + test_smtp_commands_partial_command_pipelining(server, port, timeout); + + + + return 0; +} + +