From 5f5585d519e81237e0c689d113bc36bc0570d7b3 Mon Sep 17 00:00:00 2001 From: SJ Date: Sun, 23 Oct 2016 22:04:55 +0200 Subject: [PATCH] first demo of the new architecture Change-Id: Id76636f30173465fbd0e8516d99017b4336de4db Signed-off-by: SJ --- configure | 4 +- configure.in | 2 +- src/Makefile.in | 7 +- src/bdat.c | 136 ++++++++++++++ src/cfg.c | 1 + src/cfg.h | 1 + src/config.h | 4 +- src/defs.h | 26 ++- src/dirs.c | 6 + src/misc.c | 27 ++- src/misc.h | 3 + src/piler-smtp.c | 472 +++++++++++++++++++++++++++++++++++++++++++++++ src/piler.c | 67 +++---- src/piler.h | 4 +- src/sig.h | 2 + src/smtp.c | 458 ++++++++++++++++++++++++--------------------- src/smtp.h | 30 +-- src/smtpcodes.h | 2 +- 18 files changed, 982 insertions(+), 270 deletions(-) create mode 100644 src/bdat.c create mode 100644 src/piler-smtp.c diff --git a/configure b/configure index 74a295d1..8bee6e37 100755 --- a/configure +++ b/configure @@ -4645,7 +4645,7 @@ fi if test "$os" = "FreeBSD"; then defs="$defs -DFREEBSD" - antispam_libs="-lz -lm -lcrypto -lssl" + antispam_libs="-lz -lm -lcrypto -lssl -liconv" MAKE="gmake" fi @@ -4866,7 +4866,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 smtp.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o import_maildir.o import_mailbox.o import_pop3.o import_imap.o import_gui.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 bdat.o message.o attachment.o digest.o store.o archive.o tai.o import.o import_maildir.o import_mailbox.o import_pop3.o import_imap.o import_gui.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 156f6dbd..000184d5 100644 --- a/configure.in +++ b/configure.in @@ -544,7 +544,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 smtp.o session.o message.o attachment.o digest.o store.o archive.o tai.o import.o import_maildir.o import_mailbox.o import_pop3.o import_imap.o import_gui.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 bdat.o message.o attachment.o digest.o store.o archive.o tai.o import.o import_maildir.o import_mailbox.o import_pop3.o import_imap.o import_gui.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/Makefile.in b/src/Makefile.in index 4c5ef87a..fc139378 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -33,7 +33,7 @@ MAKE = `which make` INSTALL = @INSTALL@ -all: libpiler.a piler pilerconf pilerget pileraget pilerimport pilerexport pilerpurge reindex test +all: libpiler.a piler pilerconf pilerget pileraget pilerimport pilerexport pilerpurge reindex test piler-smtp install: install-piler @@ -47,6 +47,8 @@ libpiler.a: $(OBJS) $(SQL_OBJS) ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION) +piler-smtp: piler-smtp.c libpiler.a + $(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< cfg.o misc.o tai.o smtp.o dirs.o sig.o bdat.o $(LIBS) $(LIBDIR) pilerget: pilerget.c libpiler.a $(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< -lpiler $(LIBS) $(LIBDIR) @@ -83,6 +85,7 @@ install-piler: (cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION)) $(INSTALL) -m 0755 piler $(DESTDIR)$(sbindir) + $(INSTALL) -m 0755 piler-smtp $(DESTDIR)$(sbindir) $(INSTALL) -m 0755 pilerconf $(DESTDIR)$(sbindir) $(INSTALL) -m 6755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) pilerget $(DESTDIR)$(bindir) $(INSTALL) -m 6755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) pileraget $(DESTDIR)$(bindir) @@ -93,7 +96,7 @@ install-piler: $(INSTALL) -m 6755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) pilertest $(DESTDIR)$(bindir) clean: - rm -f *.o *.a libpiler.so* piler pilerconf pilerget pileraget pilerimport pilerexport pilerpurge pilertest reindex + rm -f *.o *.a libpiler.so* piler pilerconf pilerget pileraget pilerimport pilerexport pilerpurge pilertest reindex piler-smtp distclean: clean rm -f Makefile diff --git a/src/bdat.c b/src/bdat.c new file mode 100644 index 00000000..14e90294 --- /dev/null +++ b/src/bdat.c @@ -0,0 +1,136 @@ +/* + * bdat.c, SJ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +void reset_bdat_counters(struct smtp_session *session){ + session->bdat_rounds = 0; + session->bdat_last_round = 0; + session->bdat_bytes_to_read = 0; + session->bad = 0; +} + + +void get_bdat_size_to_read(struct smtp_session *session, char *buf){ + char *p; + + session->bdat_rounds++; + session->bdat_bytes_to_read = 0; + + session->protocol_state = SMTP_STATE_BDAT; + + // determine if this is the last BDAT command + + p = strcasestr(buf, " LAST"); + if(p){ + session->bdat_last_round = 1; + syslog(LOG_INFO, "%s: BDAT LAST", session->ttmpfile); + *p = '\0'; + } + + // determine the size to be read + + p = strchr(buf, ' '); + if(p){ + session->bdat_bytes_to_read = atoi(p); + if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: BDAT len=%d", session->ttmpfile, session->bdat_bytes_to_read); + } + + if(!p || session->bdat_bytes_to_read <= 0){ + session->bdat_bytes_to_read = 0; + syslog(LOG_INFO, "%s: malformed BDAT command", session->ttmpfile); + } +} + + +void process_bdat(struct smtp_session *session, char *readbuf, int readlen){ + int i; + char buf[SMALLBUFSIZE]; + + if(readlen <= 0) return; + + //printf("readbuf in process_bdat (%d): *%s*\n", readlen, readbuf); + + if(session->bdat_rounds == 1){ + session->fd = open(session->ttmpfile, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP); + if(session->fd == -1){ + syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, session->ttmpfile); + } + } + + session->bdat_bytes_to_read -= readlen; + + if(session->fd != -1){ + write(session->fd, readbuf, readlen); + session->tot_len += readlen; + + if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: wrote %d bytes, %d bytes to go", session->ttmpfile, readlen, session->bdat_bytes_to_read); + } + + + if(session->bdat_bytes_to_read < 0){ + // malformed data from client: we got more data then had been told in BDAT argument + syslog(LOG_PRIORITY, "ERROR: invalid BDAT data. Expected %d, got %d bytes", session->bdat_bytes_to_read + readlen, readlen); + session->bad = 1; + + close(session->fd); + unlink(session->ttmpfile); + } + + if(session->bdat_bytes_to_read == 0){ + + if(session->bdat_last_round == 1){ + if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: read all bdat data in %d rounds", session->ttmpfile, session->bdat_rounds); + + // send back the smtp answers + for(i=0; ibdat_rounds; i++){ + if(session->fd == -1){ + send_smtp_response(session, SMTP_RESP_421_ERR_WRITE_FAILED); + } + else { + if(i == 0){ + fsync(session->fd); + close(session->fd); + + move_email(session); + + snprintf(buf, sizeof(buf)-1, "250 OK <%s>\r\n", session->ttmpfile); + send_smtp_response(session, buf); + syslog(LOG_PRIORITY, "received: %s, from=%s, size=%d", session->ttmpfile, session->mailfrom, session->tot_len); + } + else send_smtp_response(session, SMTP_RESP_250_BDAT); + } + } + + // technically we are not in the PERIOD state, but it's good enough + // to quit the BDAT processing state + session->protocol_state = SMTP_STATE_PERIOD; + + session->fd = -1; + } + + else { + // this is not the last BDAT round, let's go back + // after the rcpt state, and wait for the next + // BDAT command + session->protocol_state = SMTP_STATE_RCPT_TO; + } + } +} diff --git a/src/cfg.c b/src/cfg.c index 1ce2ddd0..11f70a88 100644 --- a/src/cfg.c +++ b/src/cfg.c @@ -101,6 +101,7 @@ struct _parse_rule config_parse_rules[] = { "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)}, + { "smtp_timeout", "integer", (void*) int_parser, offsetof(struct __config, smtp_timeout), "60", sizeof(int)}, { "spam_header_line", "string", (void*) string_parser, offsetof(struct __config, spam_header_line), "", MAXVAL-1}, { "syslog_recipients", "integer", (void*) int_parser, offsetof(struct __config, syslog_recipients), "0", sizeof(int)}, { "tls_enable", "integer", (void*) int_parser, offsetof(struct __config, tls_enable), "0", sizeof(int)}, diff --git a/src/cfg.h b/src/cfg.h index 8689d9ca..bd1a3e66 100644 --- a/src/cfg.h +++ b/src/cfg.h @@ -48,6 +48,7 @@ struct __config { int verbosity; char locale[MAXVAL]; + int smtp_timeout; int helper_timeout; int extract_attachments; diff --git a/src/config.h b/src/config.h index 4ac5f4c2..0458a194 100644 --- a/src/config.h +++ b/src/config.h @@ -11,9 +11,9 @@ #define PROGNAME "piler" -#define VERSION "1.2.0" +#define VERSION "1.3.0-master" -#define BUILD 952 +#define BUILD 975 #define HOSTID "mailarchiver" diff --git a/src/defs.h b/src/defs.h index 3f56adf1..4107f046 100644 --- a/src/defs.h +++ b/src/defs.h @@ -22,6 +22,7 @@ #include #include +#include #include "tai.h" #include "config.h" @@ -64,9 +65,6 @@ #define RULE_NO_MATCH -100 -typedef void signal_func (int); - - struct child { pid_t pid; int serial; @@ -386,6 +384,26 @@ struct session_ctx { struct counters *counters; }; +struct smtp_session { + char ttmpfile[SMALLBUFSIZE]; + char mailfrom[SMALLBUFSIZE]; + char buf[SMALLBUFSIZE]; + char remote_host[INET6_ADDRSTRLEN]; + time_t lasttime; + int protocol_state; + int fd; + int bad; + int buflen; + int tot_len; + int bdat_rounds; + int bdat_last_round; + int bdat_bytes_to_read; + int socket; + struct __config *cfg; + SSL_CTX *ctx; + SSL *ssl; + int use_ssl; + int starttls; +}; #endif /* _DEFS_H */ - diff --git a/src/dirs.c b/src/dirs.c index e4067884..b83e8d96 100644 --- a/src/dirs.c +++ b/src/dirs.c @@ -18,6 +18,7 @@ void createdir(char *path, uid_t uid, gid_t gid, mode_t mode); void check_and_create_directories(struct __config *cfg, uid_t uid, gid_t gid){ char *p, s[SMALLBUFSIZE]; + int i; p = strrchr(cfg->workdir, '/'); if(p){ @@ -45,6 +46,11 @@ void check_and_create_directories(struct __config *cfg, uid_t uid, gid_t gid){ *p = '/'; } + for(i=0; inumber_of_worker_processes; i++){ + snprintf(s, sizeof(s)-1, "%s/%d", cfg->workdir, i); + createdir(s, uid, gid, 0700); + } + } diff --git a/src/misc.c b/src/misc.c index c006a8cc..542e3e23 100644 --- a/src/misc.c +++ b/src/misc.c @@ -107,6 +107,21 @@ int searchStringInBuffer(char *s, int len1, char *what, int len2){ } +int search_char_backward(char *buf, int buflen, char c){ + int n, m; + + m = buflen - 1 - 5; + if(m < 0) m = 0; + + for(n=m; nttmpfile[RND_STR_LEN-1] % session->cfg->number_of_worker_processes, session->ttmpfile); + + if(rename(session->ttmpfile, buf)){ + syslog(LOG_PRIORITY, "ERROR: couldn't rename %s to %s", session->ttmpfile, buf); + } +} + + #ifndef _GNU_SOURCE char *strcasestr(const char *s, const char *find){ char c, sc; @@ -647,4 +673,3 @@ char *strcasestr(const char *s, const char *find){ return((char*)s); } #endif - diff --git a/src/misc.h b/src/misc.h index 89a40500..0412e2d4 100644 --- a/src/misc.h +++ b/src/misc.h @@ -20,6 +20,7 @@ void get_extractor_list(); void __fatal(char *s); long tvdiff(struct timeval a, struct timeval b); int searchStringInBuffer(char *s, int len1, char *what, int len2); +int search_char_backward(char *buf, int buflen, char c); int countCharacterInBuffer(char *p, char c); void replaceCharacterInBuffer(char *p, char from, char to); char *split(char *str, int ch, char *buf, int buflen, int *result); @@ -46,6 +47,8 @@ void *get_in_addr(struct sockaddr *sa); int can_i_write_current_directory(); +void move_email(struct smtp_session *session); + #ifndef _GNU_SOURCE char *strcasestr(const char *s, const char *find); #endif diff --git a/src/piler-smtp.c b/src/piler-smtp.c new file mode 100644 index 00000000..e6b341cf --- /dev/null +++ b/src/piler-smtp.c @@ -0,0 +1,472 @@ +/* + * piler-smtp.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define POLL_SIZE 256 + +extern char *optarg; +extern int optind; + +struct pollfd poll_set[POLL_SIZE]; +int timeout = 20; // checking for timeout this often [sec] +int numfds = 0; +int listenerfd = -1; + +char *configfile = CONFIG_FILE; +struct __config cfg; +struct passwd *pwd; +struct smtp_session *session, *sessions[POLL_SIZE]; + + +void handle_data(struct smtp_session *session, char *readbuf, int readlen){ + char *p, puf[MAXBUFSIZE]; + int result; + + // process BDAT stuff + + if(session->protocol_state == SMTP_STATE_BDAT){ + if(session->bad == 1){ + // something bad happened in the BDAT processing + return; + } + + process_bdat(session, readbuf, readlen); + } + + // process DATA + + else if(session->protocol_state == SMTP_STATE_DATA){ + process_data(session, readbuf, readlen); + } + + // process other SMTP commands + + else { + //printf("len=%d, buf=*%s*\n\n\n", readlen, readbuf); + + if(session->buflen > 0){ + snprintf(puf, sizeof(puf)-1, "%s%s", session->buf, readbuf); + snprintf(readbuf, BIGBUFSIZE-1, "%s", puf); + + session->buflen = 0; + memset(session->buf, 0, SMALLBUFSIZE); + } + + p = readbuf; + + do { + memset(puf, 0, sizeof(puf)); + p = split(p, '\n', puf, sizeof(puf)-1, &result); + + if(puf[0] == '\0') continue; + + if(result == 1){ + process_smtp_command(session, puf); + + // if chunking is enabled and we have data after BDAT + // then process the rest + + if(session->cfg->enable_chunking == 1 && p && session->protocol_state == SMTP_STATE_BDAT){ + process_bdat(session, p, strlen(p)); + break; + } + } + else { + snprintf(session->buf, SMALLBUFSIZE-1, "%s", puf); + session->buflen = strlen(puf); + } + } while(p); + + } + +} + + +void init_smtp_session(struct smtp_session *session, int fd_index, int sd){ + session->socket = sd; + session->buflen = 0; + session->protocol_state = SMTP_STATE_INIT; + + session->cfg = &cfg; + + session->use_ssl = 0; // use SSL/TLS + session->starttls = 0; // SSL/TLS communication is active (1) or not (0) + session->ctx = NULL; + session->ssl = NULL; + + memset(session->buf, 0, SMALLBUFSIZE); + memset(session->remote_host, 0, INET6_ADDRSTRLEN); + + reset_bdat_counters(session); + + time(&(session->lasttime)); +} + + +void free_smtp_session(struct smtp_session *session){ + + if(session){ + //printf("freeing session\n"); + + if(session->use_ssl == 1){ + //printf("shutdown ssl\n"); + SSL_shutdown(session->ssl); + SSL_free(session->ssl); + } + + if(session->ctx) SSL_CTX_free(session->ctx); + + free(session); + } +} + + + +void p_clean_exit(){ + int i; + + if(listenerfd != -1) close(listenerfd); + + for(i=1; iremote_host); + + // new code + free_smtp_session(sessions[n]); + sessions[n] = NULL; + + for(i=n; i 1){ + time(&now); + + for(i=1; ilasttime >= cfg.smtp_timeout){ + syslog(LOG_PRIORITY, "client %s timeout", sessions[i]->remote_host); + tear_down_client(i); + } + } + } + + alarm(timeout); +} + + +#ifdef HAVE_LIBWRAP +int is_blocked_by_tcp_wrappers(int sd){ + struct request_info req; + + request_init(&req, RQ_DAEMON, PROGNAME, RQ_FILE, sd, 0); + + fromhost(&req); + + if(!hosts_access(&req)){ + send(sd, SMTP_RESP_550_ERR_YOU_ARE_BANNED_BY_LOCAL_POLICY, strlen(SMTP_RESP_550_ERR_YOU_ARE_BANNED_BY_LOCAL_POLICY), 0); + syslog(LOG_PRIORITY, "denied connection from %s by tcp_wrappers", eval_client(&req)); + return 1; + } + + return 0; +} +#endif + + +int create_listener_socket(char *listen_addr, int listen_port){ + int rc, sd, yes=1; + char port_string[8]; + struct addrinfo hints, *res; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + snprintf(port_string, sizeof(port_string)-1, "%d", listen_port); + + if((rc = getaddrinfo(listen_addr, port_string, &hints, &res)) != 0){ + syslog(LOG_PRIORITY, "getaddrinfo for '%s': %s", listen_addr, gai_strerror(rc)); + return -1; + } + + if((sd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1){ + syslog(LOG_PRIORITY, "socket() error"); + return -1; + } + + if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){ + syslog(LOG_PRIORITY, "setsockopt() error"); + close(sd); + return -1; + } + + if(ioctl(sd, FIONBIO, (char *)&yes) == -1){ + syslog(LOG_PRIORITY, "ioctl() failed"); + close(sd); + return -1; + } + + if(bind(sd, res->ai_addr, res->ai_addrlen) == -1){ + syslog(LOG_PRIORITY, "cannot bind to port: %s:%d", listen_addr, listen_port); + close(sd); + return -1; + } + + freeaddrinfo(res); + + if(listen(sd, cfg.backlog) == -1){ + syslog(LOG_PRIORITY, "listen() error"); + close(sd); + return -1; + } + + return sd; +} + + +void start_new_session(int socket, struct sockaddr_storage client_address, int fd_index){ + char smtp_banner[SMALLBUFSIZE], remote_host[INET6_ADDRSTRLEN]; + + // Uh-oh! We have enough connections to serve already + if(numfds >= POLL_SIZE){ + inet_ntop(client_address.ss_family, get_in_addr((struct sockaddr*)&client_address), remote_host, sizeof(remote_host)); + syslog(LOG_PRIORITY, "too many connections (%d), cannot accept %s", numfds, remote_host); + send(socket, SMTP_RESP_421_ERR_ALL_PORTS_ARE_BUSY, strlen(SMTP_RESP_421_ERR_ALL_PORTS_ARE_BUSY), 0); + close(socket); + return; + } + +#ifdef HAVE_LIBWRAP + if(is_blocked_by_tcp_wrappers(socket) == 1){ + close(socket); + return; + } +#endif + + sessions[numfds] = malloc(sizeof(struct smtp_session)); + + if(sessions[numfds] == NULL){ + syslog(LOG_PRIORITY, "malloc error()"); + send(socket, SMTP_RESP_421_ERR_TMP, strlen(SMTP_RESP_421_ERR_TMP), 0); + close(socket); + return; + } + + + init_smtp_session(sessions[numfds], fd_index, socket); + + snprintf(smtp_banner, sizeof(smtp_banner)-1, SMTP_RESP_220_BANNER, cfg.hostid); + send(socket, smtp_banner, strlen(smtp_banner), 0); + + inet_ntop(client_address.ss_family, get_in_addr((struct sockaddr*)&client_address), sessions[numfds]->remote_host, INET6_ADDRSTRLEN); + + syslog(LOG_PRIORITY, "connected from %s", sessions[numfds]->remote_host); + + poll_set[numfds].fd = socket; + poll_set[numfds].events = POLLIN|POLLHUP; + + numfds++; +} + + +void initialise_configuration(){ + cfg = read_config(configfile); + + if(strlen(cfg.username) > 1){ + pwd = getpwnam(cfg.username); + if(!pwd) fatal(ERR_NON_EXISTENT_USER); + } + + if(getuid() == 0 && pwd){ + check_and_create_directories(&cfg, pwd->pw_uid, pwd->pw_gid); + } + + if(chdir(cfg.workdir)){ + syslog(LOG_PRIORITY, "workdir: *%s*", cfg.workdir); + fatal(ERR_CHDIR); + } + + setlocale(LC_MESSAGES, cfg.locale); + setlocale(LC_CTYPE, cfg.locale); + + syslog(LOG_PRIORITY, "reloaded config: %s", configfile); +} + + +int main(int argc, char **argv){ + int listenerfd, client_sockfd; + int i, daemonise=0; + int client_len = sizeof(struct sockaddr_storage); + int readlen; + int bytes_to_read; + struct sockaddr_storage client_address; + char readbuf[BIGBUFSIZE]; + + while((i = getopt(argc, argv, "c:dvVh")) > 0){ + switch(i){ + + case 'c' : + configfile = optarg; + break; + + case 'd' : + daemonise = 1; + break; + + case 'v' : + case 'V' : + printf("%s build %d\n", VERSION, get_build()); + return 0; + + case 'h' : + default : + __fatal("usage: ..."); + } + } + + (void) openlog("piler-poll", LOG_PID, LOG_MAIL); + + memset(sessions, '\0', sizeof(sessions)); + memset(poll_set, '\0', sizeof(poll_set)); + + initialise_configuration(); + + listenerfd = create_listener_socket(cfg.listen_addr, cfg.listen_port); + if(listenerfd == -1){ + syslog(LOG_PRIORITY, "create_listener_socket() error"); + exit(1); + } + + if(drop_privileges(pwd)) fatal(ERR_SETUID); + + set_signal_handler(SIGTERM, p_clean_exit); + set_signal_handler(SIGALRM, check_for_client_timeout); + set_signal_handler(SIGHUP, initialise_configuration); + + alarm(timeout); + + poll_set[0].fd = listenerfd; + poll_set[0].events = POLLIN; + numfds = 1; + + SSL_library_init(); + SSL_load_error_strings(); + + srand(getpid()); + +#if HAVE_DAEMON == 1 + if(daemonise == 1 && daemon(1, 0) == -1) fatal(ERR_DAEMON); +#endif + + for(;;){ + int fd_index; + + poll(poll_set, numfds, -1); + + for(fd_index = 0; fd_index < numfds; fd_index++){ + if(poll_set[fd_index].revents & POLLIN){ + + // process new connection + + if(poll_set[fd_index].fd == listenerfd){ + client_sockfd = accept(listenerfd, (struct sockaddr *)&client_address, (socklen_t *)&client_len); + start_new_session(client_sockfd, client_address, fd_index); + } + + // handle data from an existing connection + + else { + ioctl(poll_set[fd_index].fd, FIONREAD, &bytes_to_read); + + if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "got %d bytes to read", bytes_to_read); + + if(bytes_to_read == 0){ + tear_down_client(fd_index); + } + else { + session = sessions[fd_index]; + + time(&(session->lasttime)); + + // readbuf must be large enough to hold 'bytes_to_read' data + // I think there shouldn't be more than MTU size data to be + // read from the socket at a time + memset(readbuf, 0, sizeof(readbuf)); + + if(session->use_ssl == 1) + readlen = SSL_read(session->ssl, (char*)&readbuf[0], sizeof(readbuf)-1); + else + readlen = recv(poll_set[fd_index].fd, &readbuf[0], sizeof(readbuf)-1, 0); + + if(readlen < 1) break; + + readbuf[readlen] = '\0'; // we need either this or memset(readbuf, ...) above + + handle_data(session, &readbuf[0], readlen); + + if(session->protocol_state == SMTP_STATE_BDAT && session->bad == 1) tear_down_client(fd_index); + } + } + } + } + } + + return 0; +} diff --git a/src/piler.c b/src/piler.c index e59d84f1..029197b2 100644 --- a/src/piler.c +++ b/src/piler.c @@ -102,16 +102,24 @@ static void child_sighup_handler(int sig){ static void child_main(struct child *ptr){ - char s[INET6_ADDRSTRLEN]; - struct sockaddr_storage client_addr; - socklen_t addr_size; - struct session_ctx sctx; + struct import import; + struct session_data sdata; + int tot_msgs = 0; + char dir[TINYBUFSIZE]; + + /* open directory, then process its files, then sleep 2 sec, and repeat */ + + import.total_messages = import.total_size = import.processed_messages = import.batch_processing_limit = 0; + import.remove_after_import = 1; + import.extra_recipient = import.move_folder = NULL; + + data.import = &import; ptr->messages = 0; - sctx.data = &data; - sctx.cfg = &cfg; - if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child (pid: %d, serial: %d) started main()", getpid(), ptr->serial); + snprintf(dir, sizeof(dir)-1, "%d", ptr->serial); + + if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child (pid: %d, serial: %d) started main() working on '%s'", getpid(), ptr->serial, dir); while(1){ if(received_sighup == 1){ @@ -119,35 +127,30 @@ static void child_main(struct child *ptr){ break; } - ptr->status = READY; - - addr_size = sizeof(client_addr); - sctx.new_sd = accept(sd, (struct sockaddr *)&client_addr, &addr_size); - - if(sctx.new_sd == -1) continue; - - ptr->status = BUSY; - - inet_ntop(client_addr.ss_family, get_in_addr((struct sockaddr *)&client_addr), s, sizeof(s)-1); - - syslog(LOG_PRIORITY, "connection from %s", s); - - data.child_serial = ptr->serial; - sig_block(SIGHUP); - ptr->messages += handle_smtp_session(&sctx); - sig_unblock(SIGHUP); - close(sctx.new_sd); + if(open_database(&sdata, &cfg) == OK){ + import_from_maildir(dir, &sdata, &data, &tot_msgs, &cfg); + ptr->messages += tot_msgs; + close_database(&sdata); - if(cfg.max_requests_per_child > 0 && ptr->messages >= cfg.max_requests_per_child){ - if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child (pid: %d, serial: %d) served enough: %d", getpid(), ptr->messages, ptr->serial); - break; + sleep(2); + } + else { + syslog(LOG_PRIORITY, "ERROR: cannot open database"); + sleep(10); } - } + sig_unblock(SIGHUP); - ptr->status = UNDEF; + // TODO: do we want to quit after processing a certain number of messages? + + //if(cfg.max_requests_per_child > 0 && ptr->messages >= cfg.max_requests_per_child){ + // if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child (pid: %d, serial: %d) served enough: %d", getpid(), ptr->messages, ptr->serial); + // break; + //} + + } #ifdef HAVE_MEMCACHED memcached_shutdown(&(data.memc)); @@ -409,7 +412,8 @@ int main(int argc, char **argv){ snprintf(port_string, sizeof(port_string)-1, "%d", cfg.listen_port); - if((rc = getaddrinfo(cfg.listen_addr, port_string, &hints, &res)) != 0){ + //if((rc = getaddrinfo(cfg.listen_addr, port_string, &hints, &res)) != 0){ + if((rc = getaddrinfo("127.0.0.1", "5678", &hints, &res)) != 0){ fprintf(stderr, "getaddrinfo for '%s': %s\n", cfg.listen_addr, gai_strerror(rc)); return 1; } @@ -466,4 +470,3 @@ int main(int argc, char **argv){ return 0; } - diff --git a/src/piler.h b/src/piler.h index 920952a5..d20fbf54 100644 --- a/src/piler.h +++ b/src/piler.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -29,6 +30,8 @@ int read_key(struct __config *cfg); void insert_offset(struct session_data *sdata, int server_id); +void tear_down_client(int n); + int do_av_check(struct session_data *sdata, char *virusinfo, struct __data *data, struct __config *cfg); int make_digests(struct session_data *sdata, struct __config *cfg); @@ -69,7 +72,6 @@ int is_email_address_on_my_domains(char *email, struct __data *data); int is_blocked_by_tcp_wrappers(int sd); void send_response_to_data(struct session_ctx *sctx, char *rcptto); void process_written_file(struct session_ctx *sctx); -void process_data(struct session_ctx *sctx); #endif /* _PILER_H */ diff --git a/src/sig.h b/src/sig.h index 86e1e3a8..103fb0a2 100644 --- a/src/sig.h +++ b/src/sig.h @@ -5,6 +5,8 @@ #ifndef _SIG_H #define _SIG_H +typedef void signal_func (int); + void sig_block(int sig); void sig_unblock(int sig); void sig_catch(int sig, void (*f)()); diff --git a/src/smtp.c b/src/smtp.c index 8490d5fb..91164adb 100644 --- a/src/smtp.c +++ b/src/smtp.c @@ -1,285 +1,319 @@ -/* - * smtp.c, SJ - */ - #include #include #include -#include #include #include #include +#include +#include #include -#include -#include #include -#include +#include #include #include #include -#include +#include "smtp.h" -void process_command_ehlo_lhlo(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen){ - char tmpbuf[MAXBUFSIZE]; +void process_smtp_command(struct smtp_session *session, char *buf){ + char response[SMALLBUFSIZE]; + + if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "processing command: *%s*", buf); + + if(strncasecmp(buf, SMTP_CMD_HELO, strlen(SMTP_CMD_HELO)) == 0){ + process_command_helo(session, response, sizeof(response)); + return; + } + + if(strncasecmp(buf, SMTP_CMD_EHLO, strlen(SMTP_CMD_EHLO)) == 0 || + strncasecmp(buf, LMTP_CMD_LHLO, strlen(LMTP_CMD_LHLO)) == 0){ + process_command_ehlo_lhlo(session, response, sizeof(response)); + return; + } + + if(strncasecmp(buf, SMTP_CMD_MAIL_FROM, strlen(SMTP_CMD_MAIL_FROM)) == 0){ + process_command_mail_from(session, buf); + return; + } + + if(strncasecmp(buf, SMTP_CMD_RCPT_TO, strlen(SMTP_CMD_RCPT_TO)) == 0){ + process_command_rcpt_to(session, buf); + return; + } + + if(strncasecmp(buf, SMTP_CMD_DATA, strlen(SMTP_CMD_DATA)) == 0){ + process_command_data(session); + return; + } + + if(session->cfg->enable_chunking == 1 && strncasecmp(buf, SMTP_CMD_BDAT, strlen(SMTP_CMD_BDAT)) == 0){ + get_bdat_size_to_read(session, buf); + return; + } + + if(strncasecmp(buf, SMTP_CMD_QUIT, strlen(SMTP_CMD_QUIT)) == 0){ + process_command_quit(session, response, sizeof(response)); + return; + } + + if(strncasecmp(buf, SMTP_CMD_RESET, strlen(SMTP_CMD_RESET)) == 0){ + process_command_reset(session); + return; + } + + if(session->cfg->tls_enable == 1 && strncasecmp(buf, SMTP_CMD_STARTTLS, strlen(SMTP_CMD_STARTTLS)) == 0 && session->use_ssl == 0){ + process_command_starttls(session); + return; + } + + send_smtp_response(session, SMTP_RESP_502_ERR); +} + + +void process_data(struct smtp_session *session, char *readbuf, int readlen){ + char puf[SMALLBUFSIZE+BIGBUFSIZE]; + int n, pos, len; + + memset(puf, 0, sizeof(puf)); + + if(session->buflen > 0){ + memcpy(&puf[0], session->buf, session->buflen); + memcpy(&puf[session->buflen], readbuf, readlen); + len = session->buflen + readlen; + } + else { + memcpy(&puf[0], readbuf, readlen); + len = readlen; + } + + pos = searchStringInBuffer(&puf[0], len, SMTP_CMD_PERIOD, 5); + + if(pos > 0){ + //write(session->fd, puf, pos+2); + //session->tot_len += pos+2; + write(session->fd, puf, pos); + session->tot_len += pos; + process_command_period(session); + } + else { + n = search_char_backward(&puf[0], len, '\r'); + + if(n == -1 || len - n > 4){ + write(session->fd, puf, len); + session->tot_len += len; + } + else { + write(session->fd, puf, n); + session->tot_len += n; + + snprintf(session->buf, SMALLBUFSIZE-1, "%s", &puf[n]); + session->buflen = len - n; + } + } +} + + +void send_smtp_response(struct smtp_session *session, char *buf){ + int rc; + char ssl_error[SMALLBUFSIZE]; + + write1(session->socket, buf, strlen(buf), session->use_ssl, session->ssl); + + if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "sent: %s", buf); + + if(session->starttls == 1 && session->use_ssl == 0){ + + if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "waiting for ssl handshake"); + + rc = SSL_accept(session->ssl); + + if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "SSL_accept() finished"); + + if(rc == 1){ + session->use_ssl = 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", session->ttmpfile, rc, SSL_get_error(session->ssl, rc), ssl_error); + } + } +} + + +void process_command_helo(struct smtp_session *session, char *buf, int buflen){ + if(session->protocol_state == SMTP_STATE_INIT) session->protocol_state = SMTP_STATE_HELO; + + snprintf(buf, buflen-1, "220 %s ESMTP\r\n", session->cfg->hostid); + send_smtp_response(session, buf); +} + + +void process_command_ehlo_lhlo(struct smtp_session *session, char *buf, int buflen){ char extensions[SMALLBUFSIZE]; memset(extensions, 0, sizeof(extensions)); - if(*protocol_state == SMTP_STATE_INIT) *protocol_state = SMTP_STATE_HELO; + if(session->protocol_state == SMTP_STATE_INIT) session->protocol_state = SMTP_STATE_HELO; - if(sctx->sdata->tls == 0) snprintf(extensions, sizeof(extensions)-1, "%s", sctx->data->starttls); - if(sctx->cfg->enable_chunking == 1) strncat(extensions, SMTP_EXTENSION_CHUNKING, sizeof(extensions)-strlen(extensions)-2); + // if tls is not started, but it's enabled in the config + if(session->use_ssl == 0 && session->cfg->tls_enable == 1) snprintf(extensions, sizeof(extensions)-1, "%s", SMTP_EXTENSION_STARTTLS); + if(session->cfg->enable_chunking == 1) strncat(extensions, SMTP_EXTENSION_CHUNKING, sizeof(extensions)-strlen(extensions)-2); - snprintf(tmpbuf, sizeof(tmpbuf)-1, SMTP_RESP_250_EXTENSIONS, sctx->cfg->hostid, extensions); + snprintf(buf, buflen-1, SMTP_RESP_250_EXTENSIONS, session->cfg->hostid, extensions); - strncat(resp, tmpbuf, resplen-strlen(resp)); + send_smtp_response(session, buf); } -void process_command_starttls(struct session_ctx *sctx, int *protocol_state, int *starttls, char *resp, int resplen){ +int init_ssl(struct smtp_session *session){ + session->ctx = SSL_CTX_new(TLSv1_server_method()); - if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: starttls request from client", sctx->sdata->ttmpfile); + if(session->ctx == NULL){ + syslog(LOG_PRIORITY, "%s: SSL ctx is null!", session->ttmpfile); + return 0; + } - if(sctx->data->ctx){ - sctx->data->ssl = SSL_new(sctx->data->ctx); - if(sctx->data->ssl){ + if(SSL_CTX_set_cipher_list(session->ctx, session->cfg->cipher_list) == 0){ + syslog(LOG_PRIORITY, "failed to set cipher list: '%s'", session->cfg->cipher_list); + return 0; + } - SSL_set_options(sctx->data->ssl, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); + if(SSL_CTX_use_PrivateKey_file(session->ctx, session->cfg->pemfile, SSL_FILETYPE_PEM) != 1){ + syslog(LOG_PRIORITY, "cannot load private key from %s", session->cfg->pemfile); + return 0; + } - if(SSL_set_fd(sctx->data->ssl, sctx->new_sd) == 1){ - strncat(resp, SMTP_RESP_220_READY_TO_START_TLS, resplen); - *starttls = 1; - *protocol_state = SMTP_STATE_INIT; + if(SSL_CTX_use_certificate_file(session->ctx, session->cfg->pemfile, SSL_FILETYPE_PEM) != 1){ + syslog(LOG_PRIORITY, "cannot load certificate from %s", session->cfg->pemfile); + return 0; + } + + return 1; +} + + +void process_command_starttls(struct smtp_session *session){ + if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "starttls request from client"); + + if(init_ssl(session) == 1){ + + session->ssl = SSL_new(session->ctx); + if(session->ssl){ + + SSL_set_options(session->ssl, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3); + + if(SSL_set_fd(session->ssl, session->socket) == 1){ + session->starttls = 1; + send_smtp_response(session, SMTP_RESP_220_READY_TO_START_TLS); + session->protocol_state = SMTP_STATE_INIT; + session->use_ssl = 1; return; - } syslog(LOG_PRIORITY, "%s: SSL_set_fd() failed", sctx->sdata->ttmpfile); - } syslog(LOG_PRIORITY, "%s: SSL_new() failed", sctx->sdata->ttmpfile); - } syslog(LOG_PRIORITY, "%s: SSL ctx is null!", sctx->sdata->ttmpfile); + } syslog(LOG_PRIORITY, "%s: SSL_set_fd() failed", session->ttmpfile); + } syslog(LOG_PRIORITY, "%s: SSL_new() failed", session->ttmpfile); + } syslog(LOG_PRIORITY, "%s: SSL ctx is null!", session->ttmpfile); - strncat(resp, SMTP_RESP_454_ERR_TLS_TEMP_ERROR, resplen); + send_smtp_response(session, SMTP_RESP_454_ERR_TLS_TEMP_ERROR); } -void process_command_mail_from(struct session_ctx *sctx, int *protocol_state, char *buf, char *resp, int resplen){ +void process_command_mail_from(struct smtp_session *session, char *buf){ + memset(session->mailfrom, 0, SMALLBUFSIZE); - if(*protocol_state != SMTP_STATE_HELO && *protocol_state != SMTP_STATE_PERIOD && *protocol_state != SMTP_STATE_BDAT){ - strncat(resp, SMTP_RESP_503_ERR, resplen); + if(session->protocol_state != SMTP_STATE_HELO && session->protocol_state != SMTP_STATE_PERIOD && session->protocol_state != SMTP_STATE_BDAT){ + send(session->socket, SMTP_RESP_503_ERR, strlen(SMTP_RESP_503_ERR), 0); } else { - if(*protocol_state == SMTP_STATE_PERIOD || *protocol_state == SMTP_STATE_BDAT){ - if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: initiated new transaction", sctx->sdata->ttmpfile); + create_id(&(session->ttmpfile[0]), 15); + session->protocol_state = SMTP_STATE_MAIL_FROM; - unlink(sctx->sdata->ttmpfile); - unlink(sctx->sdata->tmpframe); + extractEmail(buf, session->mailfrom); - init_session_data(sctx->sdata, sctx->cfg); - } + reset_bdat_counters(session); + session->tot_len = 0; - *protocol_state = SMTP_STATE_MAIL_FROM; - - snprintf(sctx->sdata->mailfrom, SMALLBUFSIZE-1, "%s\r\n", buf); - - memset(sctx->sdata->fromemail, 0, SMALLBUFSIZE); - extractEmail(sctx->sdata->mailfrom, sctx->sdata->fromemail); - - strncat(resp, SMTP_RESP_250_OK, strlen(SMTP_RESP_250_OK)); - - } - -} - - -void process_command_rcpt_to(struct session_ctx *sctx, int *protocol_state, char *buf, char *resp, int resplen){ - - 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(sctx->sdata->num_of_rcpt_to < MAX_RCPT_TO-1){ - extractEmail(buf, sctx->sdata->rcptto[sctx->sdata->num_of_rcpt_to]); - } - - *protocol_state = SMTP_STATE_RCPT_TO; - - if(sctx->sdata->num_of_rcpt_to < MAX_RCPT_TO-1) sctx->sdata->num_of_rcpt_to++; - - strncat(resp, SMTP_RESP_250_OK, resplen); - } - else { - strncat(resp, SMTP_RESP_503_ERR, resplen); + send_smtp_response(session, SMTP_RESP_250_OK); } } -void process_command_data(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen){ +void process_command_rcpt_to(struct smtp_session *session, char *buf){ - if(*protocol_state != SMTP_STATE_RCPT_TO){ - strncat(resp, SMTP_RESP_503_ERR, resplen); + if(session->protocol_state == SMTP_STATE_MAIL_FROM || session->protocol_state == SMTP_STATE_RCPT_TO){ + + // For now, we are not interested in the envelope recipients + + session->protocol_state = SMTP_STATE_RCPT_TO; + send_smtp_response(session, SMTP_RESP_250_OK); } else { - sctx->sdata->fd = open(sctx->sdata->filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP); - if(sctx->sdata->fd == -1){ - syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, sctx->sdata->ttmpfile); - strncat(resp, SMTP_RESP_451_ERR, resplen); + send_smtp_response(session, SMTP_RESP_503_ERR); + } +} + + +void process_command_data(struct smtp_session *session){ + session->tot_len = 0; + + if(session->protocol_state != SMTP_STATE_RCPT_TO){ + send_smtp_response(session, SMTP_RESP_503_ERR); + } + else { + session->fd = open(session->ttmpfile, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP); + if(session->fd == -1){ + syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, session->ttmpfile); + send_smtp_response(session, SMTP_RESP_451_ERR); } else { - *protocol_state = SMTP_STATE_DATA; - strncat(resp, SMTP_RESP_354_DATA_OK, resplen-1); + session->protocol_state = SMTP_STATE_DATA; + send_smtp_response(session, SMTP_RESP_354_DATA_OK); } } } +void process_command_period(struct smtp_session *session){ + char buf[SMALLBUFSIZE]; -void process_command_bdat(struct session_ctx *sctx, int *protocol_state, char *buf, char *resp, int resplen){ - int n, expected_bdat_len; - char puf[MAXBUFSIZE]; + session->protocol_state = SMTP_STATE_PERIOD; - if(*protocol_state != SMTP_STATE_RCPT_TO){ - strncat(resp, SMTP_RESP_503_ERR, resplen); - return; - } + // TODO: add some error handling - sctx->bdat_rounds = 0; - sctx->bdat_last_round = 0; + fsync(session->fd); + close(session->fd); - while(sctx->bdat_last_round != 1){ - sctx->bdat_rounds++; - expected_bdat_len = 0; + session->fd = -1; - if(sctx->bdat_rounds == 1){ - expected_bdat_len = extract_bdat_command(sctx, buf); + syslog(LOG_PRIORITY, "received: %s, from=%s, size=%d", session->ttmpfile, session->mailfrom, session->tot_len); - sctx->sdata->fd = open(sctx->sdata->filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP); - if(sctx->sdata->fd == -1){ - syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, sctx->sdata->ttmpfile); - strncat(resp, SMTP_RESP_451_ERR, resplen); - return; - } - else { - *protocol_state = SMTP_STATE_BDAT; - } - } - else if(sctx->bdat_last_round != 1){ - if((n = recvtimeoutssl(sctx->new_sd, &puf[0], sizeof(puf), TIMEOUT, sctx->sdata->tls, sctx->data->ssl)) > 0){ - expected_bdat_len = extract_bdat_command(sctx, puf); - if(expected_bdat_len <= 0 && sctx->bdat_rounds > 0) sctx->bdat_rounds--; - } - } + move_email(session); - if(expected_bdat_len > 0) sctx->sdata->tot_len += read_bdat_data(sctx, expected_bdat_len); - } + snprintf(buf, sizeof(buf)-1, "250 OK <%s>\r\n", session->ttmpfile); - fsync(sctx->sdata->fd); - close(sctx->sdata->fd); + session->buflen = 0; + memset(session->buf, 0, SMALLBUFSIZE); + + send_smtp_response(session, buf); } -int extract_bdat_command(struct session_ctx *sctx, char *buf){ - int expected_bdat_len=0; - char *p; +void process_command_quit(struct smtp_session *session, char *buf, int buflen){ + session->protocol_state = SMTP_STATE_FINISHED; - // determine if this is the last BDAT command + snprintf(buf, buflen-1, SMTP_RESP_221_GOODBYE, session->cfg->hostid); - p = strcasestr(buf, " LAST"); - if(p){ - sctx->bdat_last_round = 1; - syslog(LOG_INFO, "%s: BDAT LAST", sctx->sdata->ttmpfile); - *p = '\0'; - } - - // determine the size to be read - - p = strchr(buf, ' '); - if(p){ - expected_bdat_len = atoi(p); - if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: BDAT len=%d", sctx->sdata->ttmpfile, expected_bdat_len); - } - - if(!p || expected_bdat_len <= 0){ - syslog(LOG_INFO, "%s: malformed BDAT command", sctx->sdata->ttmpfile); - return -1; - } - - return expected_bdat_len; + send_smtp_response(session, buf); } -int read_bdat_data(struct session_ctx *sctx, int expected_bdat_len){ - int n, read_bdat_len=0, written_bdat_len=0; - char puf[MAXBUFSIZE]; +void process_command_reset(struct smtp_session *session){ + send_smtp_response(session, SMTP_RESP_250_OK); - while(read_bdat_len < expected_bdat_len){ - if((n = recvtimeoutssl(sctx->new_sd, &puf[0], sizeof(puf), TIMEOUT, sctx->sdata->tls, sctx->data->ssl)) > 0){ - read_bdat_len += n; - written_bdat_len += write(sctx->sdata->fd, puf, n); - } - } + session->tot_len = 0; + session->fd = -1; + session->protocol_state = SMTP_STATE_HELO; - if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "%s: wrote %d bytes of BDAT data", sctx->sdata->ttmpfile, written_bdat_len); + reset_bdat_counters(session); - return written_bdat_len; + create_id(&(session->ttmpfile[0]), 15); } - -void process_command_quit(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen){ - char tmpbuf[MAXBUFSIZE]; - - *protocol_state = SMTP_STATE_FINISHED; - - snprintf(tmpbuf, sizeof(tmpbuf)-1, SMTP_RESP_221_GOODBYE, sctx->cfg->hostid); - strncat(resp, tmpbuf, resplen); - - unlink(sctx->sdata->ttmpfile); - unlink(sctx->sdata->tmpframe); - - if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sctx->sdata->ttmpfile); -} - - -void process_command_reset(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen){ - - strncat(resp, SMTP_RESP_250_OK, resplen); - - if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: removed", sctx->sdata->ttmpfile); - - unlink(sctx->sdata->ttmpfile); - unlink(sctx->sdata->tmpframe); - - init_session_data(sctx->sdata, sctx->cfg); - - *protocol_state = SMTP_STATE_HELO; -} - - -void send_buffered_response(struct session_ctx *sctx, int starttls, char *resp){ - int rc; - char ssl_error[SMALLBUFSIZE]; - - write1(sctx->new_sd, resp, strlen(resp), sctx->sdata->tls, sctx->data->ssl); - - if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: sent: %s", sctx->sdata->ttmpfile, resp); - memset(resp, 0, MAXBUFSIZE); - - if(starttls == 1 && sctx->sdata->tls == 0){ - - if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: waiting for ssl handshake", sctx->sdata->ttmpfile); - - rc = SSL_accept(sctx->data->ssl); - - if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: SSL_accept() finished", sctx->sdata->ttmpfile); - - if(rc == 1){ - sctx->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", sctx->sdata->ttmpfile, rc, SSL_get_error(sctx->data->ssl, rc), ssl_error); - } - } -} - - diff --git a/src/smtp.h b/src/smtp.h index 177cdee3..46fc2cd4 100644 --- a/src/smtp.h +++ b/src/smtp.h @@ -5,18 +5,24 @@ #ifndef _SMTP_H #define _SMTP_H -void process_command_ehlo_lhlo(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen); -void process_command_starttls(struct session_ctx *sctx, int *protocol_state, int *starttls, char *resp, int resplen); -void process_command_mail_from(struct session_ctx *sctx, int *protocol_state, char *buf, char *resp, int resplen); -void process_command_rcpt_to(struct session_ctx *sctx, int *protocol_state, char *buf, char *resp, int resplen); -void process_command_data(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen); -void process_command_bdat(struct session_ctx *sctx, int *protocol_state, char *buf, char *resp, int resplen); -void process_command_quit(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen); -void process_command_reset(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen); +#include -int read_bdat_data(struct session_ctx *sctx, int expected_bdat_len); -int extract_bdat_command(struct session_ctx *sctx, char *buf); +void process_smtp_command(struct smtp_session *session, char *buf); +void process_data(struct smtp_session *session, char *readbuf, int readlen); -void send_buffered_response(struct session_ctx *sctx, int starttls, char *resp); +void send_smtp_response(struct smtp_session *session, char *buf); +void process_command_helo(struct smtp_session *session, char *buf, int buflen); +void process_command_ehlo_lhlo(struct smtp_session *session, char *buf, int buflen); +void process_command_quit(struct smtp_session *session, char *buf, int buflen); +void process_command_reset(struct smtp_session *session); +void process_command_mail_from(struct smtp_session *session, char *buf); +void process_command_rcpt_to(struct smtp_session *session, char *buf); +void process_command_data(struct smtp_session *session); +void process_command_period(struct smtp_session *session); +void process_command_starttls(struct smtp_session *session); -#endif /* _SMTP_H */ +void reset_bdat_counters(struct smtp_session *session); +void get_bdat_size_to_read(struct smtp_session *session, char *buf); +void process_bdat(struct smtp_session *session, char *readbuf, int readlen); + +#endif diff --git a/src/smtpcodes.h b/src/smtpcodes.h index 141df0e4..0aa0d500 100644 --- a/src/smtpcodes.h +++ b/src/smtpcodes.h @@ -48,6 +48,7 @@ #define SMTP_RESP_421_ERR "421 %s Error: timed out\r\n" #define SMTP_RESP_421_ERR_TMP "421 %s service not available\r\n" #define SMTP_RESP_421_ERR_WRITE_FAILED "421 writing queue file failed\r\n" +#define SMTP_RESP_421_ERR_ALL_PORTS_ARE_BUSY "421 All server ports are busy\r\n" #define SMTP_RESP_450_ERR_CMD_NOT_IMPLEMENTED "450 command not implemented\r\n" #define SMTP_RESP_451_ERR "451 Error in processing, try again later\r\n" #define SMTP_RESP_454_ERR_TLS_TEMP_ERROR "454 TLS not available currently\r\n" @@ -56,7 +57,6 @@ #define SMTP_RESP_503_ERR "503 Bad command sequence\r\n" #define SMTP_RESP_530_ERR_MUST_ISSUE_STARTTLS_FIRST "530 MUST issue STARTTLS command first\r\n" #define SMTP_RESP_550_ERR "550 Access denied.\r\n" -#define SMTP_RESP_550_ERR_PREF "550 Access denied." #define SMTP_RESP_550_INVALID_RECIPIENT "550 Unknown recipient\r\n" #define SMTP_RESP_550_ERR_TOO_LONG_RCPT_TO "550 too long recipient\r\n" #define SMTP_RESP_550_ERR_YOU_ARE_BANNED_BY_LOCAL_POLICY "550 You are banned by local policy\r\n"