first demo of the new architecture

Change-Id: Id76636f30173465fbd0e8516d99017b4336de4db
Signed-off-by: SJ <sj@acts.hu>
This commit is contained in:
SJ 2016-10-23 22:04:55 +02:00
parent 6783f9eec5
commit 5f5585d519
18 changed files with 982 additions and 270 deletions

4
configure vendored
View File

@ -4645,7 +4645,7 @@ fi
if test "$os" = "FreeBSD"; then if test "$os" = "FreeBSD"; then
defs="$defs -DFREEBSD" defs="$defs -DFREEBSD"
antispam_libs="-lz -lm -lcrypto -lssl" antispam_libs="-lz -lm -lcrypto -lssl -liconv"
MAKE="gmake" MAKE="gmake"
fi fi
@ -4866,7 +4866,7 @@ echo; echo
CFLAGS="$static -O2 -Wall -g" CFLAGS="$static -O2 -Wall -g"
LIBS="$antispam_libs $sunos_libs " 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" ac_config_files="$ac_config_files Makefile src/Makefile etc/Makefile util/Makefile init.d/Makefile unit_tests/Makefile contrib/imap/Makefile"

View File

@ -544,7 +544,7 @@ echo; echo
CFLAGS="$static -O2 -Wall -g" CFLAGS="$static -O2 -Wall -g"
LIBS="$antispam_libs $sunos_libs " 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_CONFIG_FILES([Makefile src/Makefile etc/Makefile util/Makefile init.d/Makefile unit_tests/Makefile contrib/imap/Makefile])
AC_OUTPUT AC_OUTPUT

View File

@ -33,7 +33,7 @@ MAKE = `which make`
INSTALL = @INSTALL@ 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 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
ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION) 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 pilerget: pilerget.c libpiler.a
$(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< -lpiler $(LIBS) $(LIBDIR) $(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)) (cd $(DESTDIR)$(libdir) && ln -sf libpiler.so.$(LIBPILER_VERSION) libpiler.so.$(PILER_VERSION))
$(INSTALL) -m 0755 piler $(DESTDIR)$(sbindir) $(INSTALL) -m 0755 piler $(DESTDIR)$(sbindir)
$(INSTALL) -m 0755 piler-smtp $(DESTDIR)$(sbindir)
$(INSTALL) -m 0755 pilerconf $(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) pilerget $(DESTDIR)$(bindir)
$(INSTALL) -m 6755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) pileraget $(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) $(INSTALL) -m 6755 -o $(RUNNING_USER) -g $(RUNNING_GROUP) pilertest $(DESTDIR)$(bindir)
clean: 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 distclean: clean
rm -f Makefile rm -f Makefile

136
src/bdat.c Normal file
View File

@ -0,0 +1,136 @@
/*
* bdat.c, SJ
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <syslog.h>
#include <time.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <piler.h>
#include <smtp.h>
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; i<session->bdat_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;
}
}
}

View File

@ -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)}, { "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}, { "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)}, { "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}, { "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)}, { "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)}, { "tls_enable", "integer", (void*) int_parser, offsetof(struct __config, tls_enable), "0", sizeof(int)},

View File

@ -48,6 +48,7 @@ struct __config {
int verbosity; int verbosity;
char locale[MAXVAL]; char locale[MAXVAL];
int smtp_timeout;
int helper_timeout; int helper_timeout;
int extract_attachments; int extract_attachments;

View File

@ -11,9 +11,9 @@
#define PROGNAME "piler" #define PROGNAME "piler"
#define VERSION "1.2.0" #define VERSION "1.3.0-master"
#define BUILD 952 #define BUILD 975
#define HOSTID "mailarchiver" #define HOSTID "mailarchiver"

View File

@ -22,6 +22,7 @@
#include <openssl/sha.h> #include <openssl/sha.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <netinet/in.h>
#include "tai.h" #include "tai.h"
#include "config.h" #include "config.h"
@ -64,9 +65,6 @@
#define RULE_NO_MATCH -100 #define RULE_NO_MATCH -100
typedef void signal_func (int);
struct child { struct child {
pid_t pid; pid_t pid;
int serial; int serial;
@ -386,6 +384,26 @@ struct session_ctx {
struct counters *counters; 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 */ #endif /* _DEFS_H */

View File

@ -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){ void check_and_create_directories(struct __config *cfg, uid_t uid, gid_t gid){
char *p, s[SMALLBUFSIZE]; char *p, s[SMALLBUFSIZE];
int i;
p = strrchr(cfg->workdir, '/'); p = strrchr(cfg->workdir, '/');
if(p){ if(p){
@ -45,6 +46,11 @@ void check_and_create_directories(struct __config *cfg, uid_t uid, gid_t gid){
*p = '/'; *p = '/';
} }
for(i=0; i<cfg->number_of_worker_processes; i++){
snprintf(s, sizeof(s)-1, "%s/%d", cfg->workdir, i);
createdir(s, uid, gid, 0700);
}
} }

View File

@ -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; n<buflen; n++){
if(*(buf + n) == c){
return n;
}
}
return -1;
}
/* /*
* count a character in buffer * count a character in buffer
*/ */
@ -627,6 +642,17 @@ int can_i_write_current_directory(){
} }
void move_email(struct smtp_session *session){
char buf[SMALLBUFSIZE];
snprintf(buf, sizeof(buf)-1, "%d/%s", session->ttmpfile[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 #ifndef _GNU_SOURCE
char *strcasestr(const char *s, const char *find){ char *strcasestr(const char *s, const char *find){
char c, sc; char c, sc;
@ -647,4 +673,3 @@ char *strcasestr(const char *s, const char *find){
return((char*)s); return((char*)s);
} }
#endif #endif

View File

@ -20,6 +20,7 @@ void get_extractor_list();
void __fatal(char *s); void __fatal(char *s);
long tvdiff(struct timeval a, struct timeval b); long tvdiff(struct timeval a, struct timeval b);
int searchStringInBuffer(char *s, int len1, char *what, int len2); 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); int countCharacterInBuffer(char *p, char c);
void replaceCharacterInBuffer(char *p, char from, char to); void replaceCharacterInBuffer(char *p, char from, char to);
char *split(char *str, int ch, char *buf, int buflen, int *result); 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(); int can_i_write_current_directory();
void move_email(struct smtp_session *session);
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
char *strcasestr(const char *s, const char *find); char *strcasestr(const char *s, const char *find);
#endif #endif

472
src/piler-smtp.c Normal file
View File

@ -0,0 +1,472 @@
/*
* piler-smtp.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <pwd.h>
#include <sys/ioctl.h>
#include <signal.h>
#include <poll.h>
#include <netinet/in.h>
#include <locale.h>
#include <errno.h>
#include <sys/time.h>
#include <time.h>
#include <syslog.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <piler.h>
#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 <len>
// 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; i<numfds; i++){
close(poll_set[i].fd);
free_smtp_session(sessions[i]);
}
syslog(LOG_PRIORITY, "%s has been terminated", PROGNAME);
//unlink(cfg.pidfile);
ERR_free_strings();
exit(1);
}
void fatal(char *s){
syslog(LOG_PRIORITY, "%s", s);
p_clean_exit();
}
void tear_down_client(int n){
int i;
close(poll_set[n].fd);
poll_set[n].events = 0;
syslog(LOG_PRIORITY, "disconnected from %s", sessions[n]->remote_host);
// new code
free_smtp_session(sessions[n]);
sessions[n] = NULL;
for(i=n; i<numfds; i++){
poll_set[i] = poll_set[i+1];
sessions[i] = sessions[i+1];
}
numfds--;
}
void check_for_client_timeout(){
time_t now;
int i;
if(numfds > 1){
time(&now);
for(i=1; i<numfds; i++){
if(now - sessions[i]->lasttime >= 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;
}

View File

@ -102,16 +102,24 @@ static void child_sighup_handler(int sig){
static void child_main(struct child *ptr){ static void child_main(struct child *ptr){
char s[INET6_ADDRSTRLEN]; struct import import;
struct sockaddr_storage client_addr; struct session_data sdata;
socklen_t addr_size; int tot_msgs = 0;
struct session_ctx sctx; 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; 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){ while(1){
if(received_sighup == 1){ if(received_sighup == 1){
@ -119,35 +127,30 @@ static void child_main(struct child *ptr){
break; 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); 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){ sleep(2);
if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "child (pid: %d, serial: %d) served enough: %d", getpid(), ptr->messages, ptr->serial); }
break; 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 #ifdef HAVE_MEMCACHED
memcached_shutdown(&(data.memc)); 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); 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)); fprintf(stderr, "getaddrinfo for '%s': %s\n", cfg.listen_addr, gai_strerror(rc));
return 1; return 1;
} }
@ -466,4 +470,3 @@ int main(int argc, char **argv){
return 0; return 0;
} }

View File

@ -19,6 +19,7 @@
#include <rules.h> #include <rules.h>
#include <sql.h> #include <sql.h>
#include <import.h> #include <import.h>
#include <smtp.h>
#include <config.h> #include <config.h>
#include <unistd.h> #include <unistd.h>
@ -29,6 +30,8 @@
int read_key(struct __config *cfg); int read_key(struct __config *cfg);
void insert_offset(struct session_data *sdata, int server_id); 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 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); 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); int is_blocked_by_tcp_wrappers(int sd);
void send_response_to_data(struct session_ctx *sctx, char *rcptto); void send_response_to_data(struct session_ctx *sctx, char *rcptto);
void process_written_file(struct session_ctx *sctx); void process_written_file(struct session_ctx *sctx);
void process_data(struct session_ctx *sctx);
#endif /* _PILER_H */ #endif /* _PILER_H */

View File

@ -5,6 +5,8 @@
#ifndef _SIG_H #ifndef _SIG_H
#define _SIG_H #define _SIG_H
typedef void signal_func (int);
void sig_block(int sig); void sig_block(int sig);
void sig_unblock(int sig); void sig_unblock(int sig);
void sig_catch(int sig, void (*f)()); void sig_catch(int sig, void (*f)());

View File

@ -1,285 +1,319 @@
/*
* smtp.c, SJ
*/
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <syslog.h> #include <syslog.h>
#include <time.h> #include <unistd.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <piler.h> #include <piler.h>
#include <smtp.h> #include "smtp.h"
void process_command_ehlo_lhlo(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen){ void process_smtp_command(struct smtp_session *session, char *buf){
char tmpbuf[MAXBUFSIZE]; 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]; char extensions[SMALLBUFSIZE];
memset(extensions, 0, sizeof(extensions)); 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 tls is not started, but it's enabled in the config
if(sctx->cfg->enable_chunking == 1) strncat(extensions, SMTP_EXTENSION_CHUNKING, sizeof(extensions)-strlen(extensions)-2); 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){ if(SSL_CTX_set_cipher_list(session->ctx, session->cfg->cipher_list) == 0){
sctx->data->ssl = SSL_new(sctx->data->ctx); syslog(LOG_PRIORITY, "failed to set cipher list: '%s'", session->cfg->cipher_list);
if(sctx->data->ssl){ 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){ if(SSL_CTX_use_certificate_file(session->ctx, session->cfg->pemfile, SSL_FILETYPE_PEM) != 1){
strncat(resp, SMTP_RESP_220_READY_TO_START_TLS, resplen); syslog(LOG_PRIORITY, "cannot load certificate from %s", session->cfg->pemfile);
*starttls = 1; return 0;
*protocol_state = SMTP_STATE_INIT; }
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; return;
} syslog(LOG_PRIORITY, "%s: SSL_set_fd() failed", sctx->sdata->ttmpfile); } syslog(LOG_PRIORITY, "%s: SSL_set_fd() failed", session->ttmpfile);
} syslog(LOG_PRIORITY, "%s: SSL_new() failed", sctx->sdata->ttmpfile); } syslog(LOG_PRIORITY, "%s: SSL_new() failed", session->ttmpfile);
} syslog(LOG_PRIORITY, "%s: SSL ctx is null!", sctx->sdata->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){ if(session->protocol_state != SMTP_STATE_HELO && session->protocol_state != SMTP_STATE_PERIOD && session->protocol_state != SMTP_STATE_BDAT){
strncat(resp, SMTP_RESP_503_ERR, resplen); send(session->socket, SMTP_RESP_503_ERR, strlen(SMTP_RESP_503_ERR), 0);
} }
else { else {
if(*protocol_state == SMTP_STATE_PERIOD || *protocol_state == SMTP_STATE_BDAT){ create_id(&(session->ttmpfile[0]), 15);
if(sctx->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: initiated new transaction", sctx->sdata->ttmpfile); session->protocol_state = SMTP_STATE_MAIL_FROM;
unlink(sctx->sdata->ttmpfile); extractEmail(buf, session->mailfrom);
unlink(sctx->sdata->tmpframe);
init_session_data(sctx->sdata, sctx->cfg); reset_bdat_counters(session);
} session->tot_len = 0;
*protocol_state = SMTP_STATE_MAIL_FROM; send_smtp_response(session, SMTP_RESP_250_OK);
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);
} }
} }
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){ if(session->protocol_state == SMTP_STATE_MAIL_FROM || session->protocol_state == SMTP_STATE_RCPT_TO){
strncat(resp, SMTP_RESP_503_ERR, resplen);
// 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 { else {
sctx->sdata->fd = open(sctx->sdata->filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP); send_smtp_response(session, SMTP_RESP_503_ERR);
if(sctx->sdata->fd == -1){ }
syslog(LOG_PRIORITY, "%s: %s", ERR_OPEN_TMP_FILE, sctx->sdata->ttmpfile); }
strncat(resp, SMTP_RESP_451_ERR, resplen);
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 { else {
*protocol_state = SMTP_STATE_DATA; session->protocol_state = SMTP_STATE_DATA;
strncat(resp, SMTP_RESP_354_DATA_OK, resplen-1); 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){ session->protocol_state = SMTP_STATE_PERIOD;
int n, expected_bdat_len;
char puf[MAXBUFSIZE];
if(*protocol_state != SMTP_STATE_RCPT_TO){ // TODO: add some error handling
strncat(resp, SMTP_RESP_503_ERR, resplen);
return;
}
sctx->bdat_rounds = 0; fsync(session->fd);
sctx->bdat_last_round = 0; close(session->fd);
while(sctx->bdat_last_round != 1){ session->fd = -1;
sctx->bdat_rounds++;
expected_bdat_len = 0;
if(sctx->bdat_rounds == 1){ syslog(LOG_PRIORITY, "received: %s, from=%s, size=%d", session->ttmpfile, session->mailfrom, session->tot_len);
expected_bdat_len = extract_bdat_command(sctx, buf);
sctx->sdata->fd = open(sctx->sdata->filename, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP); move_email(session);
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--;
}
}
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); session->buflen = 0;
close(sctx->sdata->fd); memset(session->buf, 0, SMALLBUFSIZE);
send_smtp_response(session, buf);
} }
int extract_bdat_command(struct session_ctx *sctx, char *buf){ void process_command_quit(struct smtp_session *session, char *buf, int buflen){
int expected_bdat_len=0; session->protocol_state = SMTP_STATE_FINISHED;
char *p;
// determine if this is the last BDAT command snprintf(buf, buflen-1, SMTP_RESP_221_GOODBYE, session->cfg->hostid);
p = strcasestr(buf, " LAST"); send_smtp_response(session, buf);
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;
} }
int read_bdat_data(struct session_ctx *sctx, int expected_bdat_len){ void process_command_reset(struct smtp_session *session){
int n, read_bdat_len=0, written_bdat_len=0; send_smtp_response(session, SMTP_RESP_250_OK);
char puf[MAXBUFSIZE];
while(read_bdat_len < expected_bdat_len){ session->tot_len = 0;
if((n = recvtimeoutssl(sctx->new_sd, &puf[0], sizeof(puf), TIMEOUT, sctx->sdata->tls, sctx->data->ssl)) > 0){ session->fd = -1;
read_bdat_len += n; session->protocol_state = SMTP_STATE_HELO;
written_bdat_len += write(sctx->sdata->fd, puf, n);
}
}
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);
}
}
}

View File

@ -5,18 +5,24 @@
#ifndef _SMTP_H #ifndef _SMTP_H
#define _SMTP_H #define _SMTP_H
void process_command_ehlo_lhlo(struct session_ctx *sctx, int *protocol_state, char *resp, int resplen); #include <piler.h>
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);
int read_bdat_data(struct session_ctx *sctx, int expected_bdat_len); void process_smtp_command(struct smtp_session *session, char *buf);
int extract_bdat_command(struct session_ctx *sctx, 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

View File

@ -48,6 +48,7 @@
#define SMTP_RESP_421_ERR "421 %s Error: timed out\r\n" #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_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_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_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_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" #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_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_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 "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_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_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" #define SMTP_RESP_550_ERR_YOU_ARE_BANNED_BY_LOCAL_POLICY "550 You are banned by local policy\r\n"