diff --git a/configure b/configure index 8bee6e37..8bf81927 100755 --- a/configure +++ b/configure @@ -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 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" +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 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 000184d5..cc6f09bc 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 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" +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 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 fc139378..dd138fbe 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -48,7 +48,7 @@ libpiler.a: $(OBJS) $(SQL_OBJS) 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) + $(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< cfg.o misc.o tai.o smtp.o session.o dirs.o sig.o bdat.o $(LIBS) $(LIBDIR) pilerget: pilerget.c libpiler.a $(CC) $(CFLAGS) $(INCDIR) $(DEFS) -o $@ $< -lpiler $(LIBS) $(LIBDIR) diff --git a/src/piler-smtp.c b/src/piler-smtp.c index 69e22f72..b61e9903 100644 --- a/src/piler-smtp.c +++ b/src/piler-smtp.c @@ -41,110 +41,6 @@ struct passwd *pwd; struct smtp_session *session, **sessions=NULL; -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 slot, int sd){ - session->slot = slot; - - 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){ - - if(session->use_ssl == 1){ - SSL_shutdown(session->ssl); - SSL_free(session->ssl); - } - - if(session->ctx) SSL_CTX_free(session->ctx); - - free(session); - } -} - - - void p_clean_exit(){ int i; @@ -173,40 +69,6 @@ void fatal(char *s){ } -int get_session_slot(){ - int i; - - for(i=0; isocket == socket) return sessions[i]; - } - - return NULL; -} - - -void tear_down_client(int slot){ - syslog(LOG_PRIORITY, "disconnected from %s", sessions[slot]->remote_host); - - close(sessions[slot]->socket); - - free_smtp_session(sessions[slot]); - sessions[slot] = NULL; - - num_connections--; -} - - void check_for_client_timeout(){ time_t now; int i; @@ -217,7 +79,7 @@ void check_for_client_timeout(){ for(i=0; ilasttime >= cfg.smtp_timeout){ syslog(LOG_PRIORITY, "client %s timeout", sessions[i]->remote_host); - tear_down_client(sessions[i]->slot); + tear_down_session(sessions, sessions[i]->slot, &num_connections); } } } @@ -226,72 +88,6 @@ void check_for_client_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 start_new_session(int socket){ - char smtp_banner[SMALLBUFSIZE]; - int slot; - - // Uh-oh! We have enough connections to serve already - if(num_connections >= cfg.max_connections){ - syslog(LOG_PRIORITY, "too many connections (%d), cannot accept socket %d", num_connections, socket); - send(socket, SMTP_RESP_421_ERR_ALL_PORTS_ARE_BUSY, strlen(SMTP_RESP_421_ERR_ALL_PORTS_ARE_BUSY), 0); - close(socket); - return -1; - } - -#ifdef HAVE_LIBWRAP - if(is_blocked_by_tcp_wrappers(socket) == 1){ - close(socket); - return -1; - } -#endif - - slot = get_session_slot(); - - if(slot >= 0 && sessions[slot] == NULL){ - sessions[slot] = malloc(sizeof(struct smtp_session)); - if(sessions[slot]){ - init_smtp_session(sessions[slot], slot, socket); - snprintf(smtp_banner, sizeof(smtp_banner)-1, SMTP_RESP_220_BANNER, cfg.hostid); - send(socket, smtp_banner, strlen(smtp_banner), 0); - - num_connections++; - - return 0; - } - else { - syslog(LOG_PRIORITY, "ERROR: malloc() in start_new_session()"); - } - } - else { - syslog(LOG_PRIORITY, "ERROR: couldn't find a slot for the connection"); - } - - send(socket, SMTP_RESP_421_ERR_TMP, strlen(SMTP_RESP_421_ERR_TMP), 0); - close(socket); - - return -1; -} - - void initialise_configuration(){ cfg = read_config(configfile); @@ -320,6 +116,7 @@ int main(int argc, char **argv){ int listenerfd, client_sockfd; int i, n, daemonise=0; int client_len = sizeof(struct sockaddr_storage); + ssize_t readlen; struct sockaddr_storage client_address; char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; char readbuf[BIGBUFSIZE]; @@ -409,7 +206,6 @@ int main(int argc, char **argv){ if((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN))){ syslog(LOG_PRIORITY, "ERROR: epoll error"); close(events[i].data.fd); - // we have to tear_down_client as well if not the listening socket? continue; } @@ -447,7 +243,7 @@ int main(int argc, char **argv){ break; } - start_new_session(client_sockfd); + start_new_session(sessions, client_sockfd, &num_connections, &cfg); } continue; @@ -458,12 +254,8 @@ int main(int argc, char **argv){ else { int done = 0; - ssize_t count; - // should the following work here as well? - // ioctl(events[i].data.fd, FIONREAD, &bytes_to_read); - - session = get_session_by_socket(events[i].data.fd); + session = get_session_by_socket(sessions, cfg.max_connections, events[i].data.fd); if(session == NULL){ syslog(LOG_PRIORITY, "ERROR: cannot find session for this socket: %d", events[i].data.fd); close(events[i].data.fd); @@ -476,13 +268,13 @@ int main(int argc, char **argv){ memset(readbuf, 0, sizeof(readbuf)); if(session->use_ssl == 1) - count = SSL_read(session->ssl, (char*)&readbuf[0], sizeof(readbuf)-1); + readlen = SSL_read(session->ssl, (char*)&readbuf[0], sizeof(readbuf)-1); else - count = read(events[i].data.fd, (char*)&readbuf[0], sizeof(readbuf)-1); + readlen = read(events[i].data.fd, (char*)&readbuf[0], sizeof(readbuf)-1); - if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "got %ld bytes to read", count); + if(cfg.verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "got %ld bytes to read", readlen); - if(count == -1){ + if(readlen == -1){ /* If errno == EAGAIN, that means we have read all data. So go back to the main loop. */ if(errno != EAGAIN){ syslog(LOG_PRIORITY, "read"); @@ -490,27 +282,22 @@ int main(int argc, char **argv){ } break; } - else if(count == 0){ + else if(readlen == 0){ /* End of file. The remote has closed the connection. */ done = 1; break; } - handle_data(session, &readbuf[0], count); + handle_data(session, &readbuf[0], readlen); if(session->protocol_state == SMTP_STATE_BDAT && session->bad == 1){ - tear_down_client(session->slot); - done = 0; // to prevent the repeated tear down of connection + done = 1; break; } } if(done){ - printf("Closed connection on descriptor %d\n", events[i].data.fd); - - /* Closing the descriptor will make epoll remove it from the set of descriptors which are monitored. */ - //close(events[i].data.fd); - tear_down_client(session->slot); + tear_down_session(sessions, session->slot, &num_connections); } } diff --git a/src/piler.h b/src/piler.h index 150941c6..047ae814 100644 --- a/src/piler.h +++ b/src/piler.h @@ -67,6 +67,12 @@ int retrieve_file_from_archive(char *filename, int mode, char **buffer, FILE *de void load_mydomains(struct session_data *sdata, struct __data *data, struct __config *cfg); int is_email_address_on_my_domains(char *email, struct __data *data); +int start_new_session(struct smtp_session **sessions, int socket, int *num_connections, struct __config *cfg); +void tear_down_session(struct smtp_session **sessions, int slot, int *num_connections); +struct smtp_session *get_session_by_socket(struct smtp_session **sessions, int max_connections, int socket); +void handle_data(struct smtp_session *session, char *readbuf, int readlen); +void free_smtp_session(struct smtp_session *session); + void child_sighup_handler(int sig); void child_main(struct child *ptr); pid_t child_make(struct child *ptr); diff --git a/src/session.c b/src/session.c new file mode 100644 index 00000000..a7cc8497 --- /dev/null +++ b/src/session.c @@ -0,0 +1,215 @@ +#include +#include +#include +#include + + +int get_session_slot(struct smtp_session **sessions, int max_connections); +void init_smtp_session(struct smtp_session *session, int slot, int sd, struct __config *cfg); + + +#ifdef HAVE_LIBWRAP +int is_blocked_by_tcp_wrappers(int sd){ + struct request_info req; + + request_init(&req, RQ_DAEMON, "piler", 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 start_new_session(struct smtp_session **sessions, int socket, int *num_connections, struct __config *cfg){ + char smtp_banner[SMALLBUFSIZE]; + int slot; + + /* + * We have enough connections to serve already + */ + + if(*num_connections >= cfg->max_connections){ + syslog(LOG_PRIORITY, "too many connections (%d), cannot accept socket %d", *num_connections, socket); + send(socket, SMTP_RESP_421_ERR_ALL_PORTS_ARE_BUSY, strlen(SMTP_RESP_421_ERR_ALL_PORTS_ARE_BUSY), 0); + close(socket); + return -1; + } + +#ifdef HAVE_LIBWRAP + if(is_blocked_by_tcp_wrappers(socket) == 1){ + close(socket); + return -1; + } +#endif + + slot = get_session_slot(sessions, cfg->max_connections); + + if(slot >= 0 && sessions[slot] == NULL){ + sessions[slot] = malloc(sizeof(struct smtp_session)); + if(sessions[slot]){ + init_smtp_session(sessions[slot], slot, socket, cfg); + snprintf(smtp_banner, sizeof(smtp_banner)-1, SMTP_RESP_220_BANNER, cfg->hostid); + send(socket, smtp_banner, strlen(smtp_banner), 0); + + (*num_connections)++; + + return 0; + } + else { + syslog(LOG_PRIORITY, "ERROR: malloc() in start_new_session()"); + } + } + else { + syslog(LOG_PRIORITY, "ERROR: couldn't find a slot for the connection"); + } + + send(socket, SMTP_RESP_421_ERR_TMP, strlen(SMTP_RESP_421_ERR_TMP), 0); + close(socket); + + return -1; +} + + +int get_session_slot(struct smtp_session **sessions, int max_connections){ + int i; + + for(i=0; isocket == socket) return sessions[i]; + } + + return NULL; +} + + +void init_smtp_session(struct smtp_session *session, int slot, int sd, struct __config *cfg){ + session->slot = slot; + + 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){ + + if(session->use_ssl == 1){ + SSL_shutdown(session->ssl); + SSL_free(session->ssl); + } + + if(session->ctx) SSL_CTX_free(session->ctx); + + free(session); + } +} + + +void tear_down_session(struct smtp_session **sessions, int slot, int *num_connections){ + syslog(LOG_PRIORITY, "disconnected from %s", sessions[slot]->remote_host); + + close(sessions[slot]->socket); + + free_smtp_session(sessions[slot]); + sessions[slot] = NULL; + + (*num_connections)--; +} + + +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); + + } + +} +