piler-smtp refactor

Signed-off-by: Janos SUTO <sj@acts.hu>
This commit is contained in:
Janos SUTO 2024-03-22 06:02:08 +01:00
parent 3d70a25d35
commit 1377cbdae6
18 changed files with 638 additions and 254 deletions

View File

@ -263,3 +263,12 @@ archive_address=
; rules. In other words if you decide to use the acl file, then
; everyone is not explicitly permitted is denied.
smtp_access_list=0
; max message size in bytes
; piler-smtp will reject any message that's bigger than this number
max_message_size=50000000
; max memory in bytes piler-smtp uses for buffering messages
; when this limit is exceeded, no new emails will be accepted
; until the used memory decreases below this level
max_smtp_memory=500000000

View File

@ -26,21 +26,21 @@ void reset_bdat_counters(struct smtp_session *session){
}
void get_bdat_size_to_read(struct smtp_session *session, char *buf){
void get_bdat_size_to_read(struct smtp_session *session){
char *p;
session->bdat_bytes_to_read = 0;
session->protocol_state = SMTP_STATE_BDAT;
p = strcasestr(buf, " LAST");
p = strcasestr(session->buf, " LAST");
if(p){
*p = '\0';
}
// determine the size to be read
p = strchr(buf, ' ');
p = strchr(session->buf, ' ');
if(p){
session->bdat_bytes_to_read = atoi(p);
if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_INFO, "fd=%d: BDAT len=%d", session->net.socket, session->bdat_bytes_to_read);

View File

@ -23,6 +23,11 @@ int int_parser(char *src, int *target){
return 0;
};
int uint64_parser(char *src, uint64 *target){
*target = strtoull(src, (char**)NULL, 10);
return 0;
}
struct _parse_rule {
char *name;
char *type;
@ -63,7 +68,9 @@ struct _parse_rule config_parse_rules[] =
{ "listen_port", "integer", (void*) int_parser, offsetof(struct config, listen_port), "25", sizeof(int)},
{ "locale", "string", (void*) string_parser, offsetof(struct config, locale), "", MAXVAL-1},
{ "max_connections", "integer", (void*) int_parser, offsetof(struct config, max_connections), "64", sizeof(int)},
{ "max_message_size", "integer", (void*) int_parser, offsetof(struct config, max_message_size), "50000000", sizeof(int)},
{ "max_requests_per_child", "integer", (void*) int_parser, offsetof(struct config, max_requests_per_child), "10000", sizeof(int)},
{ "max_smtp_memory", "uint64", (void*) uint64_parser, offsetof(struct config, max_smtp_memory), "500000000", sizeof(uint64)},
{ "memcached_servers", "string", (void*) string_parser, offsetof(struct config, memcached_servers), "127.0.0.1", MAXVAL-1},
{ "memcached_to_db_interval", "integer", (void*) int_parser, offsetof(struct config, memcached_to_db_interval), "900", sizeof(int)},
{ "memcached_ttl", "integer", (void*) int_parser, offsetof(struct config, memcached_ttl), "86400", sizeof(int)},
@ -217,6 +224,7 @@ struct config read_config(char *configfile){
void print_config_item(struct config *cfg, struct _parse_rule *rules, int i){
int j;
float f;
uint64 u;
char *p, buf[MAXVAL];
p = (char*)cfg + rules[i].offset;
@ -225,6 +233,10 @@ void print_config_item(struct config *cfg, struct _parse_rule *rules, int i){
memcpy((char*)&j, p, sizeof(int));
printf("%s=%d\n", rules[i].name, j);
}
else if(strcmp(rules[i].type, "uint64") == 0){
memcpy((char*)&u, p, sizeof(uint64));
printf("%s=%llu\n", rules[i].name, u);
}
else if(strcmp(rules[i].type, "float") == 0){
memcpy((char*)&f, p, sizeof(float));
printf("%s=%.4f\n", rules[i].name, f);

View File

@ -110,6 +110,9 @@ struct config {
int debug;
int smtp_access_list;
int max_message_size;
uint64 max_smtp_memory;
};

View File

@ -9,6 +9,8 @@
#include "piler-config.h"
#include "params.h"
typedef unsigned long long uint64;
#define BUILD 1001
#define HOSTID "mailarchiver"
@ -30,6 +32,7 @@
#define SMALLBUFSIZE 512
#define BIGBUFSIZE 131072
#define REALLYBIGBUFSIZE 524288
#define SMTPBUFSIZE 2048000
#define TINYBUFSIZE 128
#define MAXVAL 256
#define RANDOM_POOL "/dev/urandom"

View File

@ -403,7 +403,6 @@ struct smtp_session {
char ttmpfile[SMALLBUFSIZE];
char mailfrom[SMALLBUFSIZE];
char rcptto[MAX_RCPT_TO][SMALLBUFSIZE];
char buf[MAXBUFSIZE];
char remote_host[INET6_ADDRSTRLEN+1];
char nullbyte;
time_t lasttime;
@ -411,13 +410,17 @@ struct smtp_session {
int slot;
int fd;
int bad;
int buflen;
int last_data_char;
int tot_len;
int bdat_bytes_to_read;
int num_of_rcpt_to;
struct config *cfg;
struct net net;
int max_message_size;
char *buf;
int buflen;
int bufsize;
int too_big;
int mail_size;
};
struct tls_protocol {

View File

@ -781,3 +781,29 @@ int append_string_to_buffer(char **buffer, char *str){
return 0;
}
int get_size_from_smtp_mail_from(char *s){
int size=0;
char *p;
p = strcasestr(s, "SIZE=");
if(p){
p += strlen("SIZE=");
char *q = p;
for(; *q; q++){
if(isspace(*q)) break;
}
// We extract max. 9 characters, which is just under 1GB
// and not overflowing an int variable
if(q - p <= 9){
char c = *q;
*q = '\0';
size = atoi(p);
*q = c;
}
}
return size;
}

View File

@ -55,5 +55,6 @@ int init_ssl_to_server(struct data *data);
#endif
int append_string_to_buffer(char **buffer, char *str);
int get_size_from_smtp_mail_from(char *s);
#endif /* _MISC_H */

View File

@ -33,6 +33,7 @@ extern int optind;
struct epoll_event event, *events=NULL;
int num_connections = 0;
int listenerfd = -1;
int loglevel = 1;
char *configfile = CONFIG_FILE;
struct config cfg;
@ -48,6 +49,7 @@ void usage(){
printf(" -d Fork to the background\n");
printf(" -v Return the version and build number\n");
printf(" -V Return the version and some build parameters\n");
printf(" -L <log level> Set the log level: 1-5\n");
exit(0);
}
@ -138,16 +140,20 @@ int main(int argc, char **argv){
int client_len = sizeof(struct sockaddr_storage);
ssize_t readlen;
struct sockaddr_storage client_address;
char readbuf[BIGBUFSIZE];
char readbuf[REALLYBIGBUFSIZE];
int efd;
while((i = getopt(argc, argv, "c:dvVh")) > 0){
while((i = getopt(argc, argv, "c:L:dvVh")) > 0){
switch(i){
case 'c' :
configfile = optarg;
break;
case 'L':
loglevel = atoi(optarg);
break;
case 'd' :
daemonise = 1;
break;
@ -321,6 +327,7 @@ int main(int argc, char **argv){
break;
}
readbuf[readlen] = '\0';
handle_data(session, &readbuf[0], readlen, &cfg);
if(session->protocol_state == SMTP_STATE_BDAT && session->bad == 1){

View File

@ -9,6 +9,36 @@
int get_session_slot(struct smtp_session **sessions, int max_connections);
void init_smtp_session(struct smtp_session *session, int slot, int sd, char *client_addr, struct config *cfg);
#define GOT_CRLF_DOT_CRLF(p) *p == '\r' && *(p+1) == '\n' && *(p+2) == '.' && *(p+3) == '\r' && *(p+4) == '\n' ? 1 : 0
uint64 get_sessions_total_memory(struct smtp_session **sessions, int max_connections){
uint64 total = 0;
for(int i=0; i<max_connections; i++){
if(sessions[i]) total += sessions[i]->bufsize;
}
return total;
}
/*
* If the sending party sets the email size when it sends the "mail from"
* part in the smtp transaction, eg. MAIL FROM:<jajaja@akakak.lo> size=509603
* then piler-smtp could know the email size in advance and could do
* a better estimate on the allowed number of smtp sessions.
*/
uint64 get_sessions_total_expected_mail_size(struct smtp_session **sessions, int max_connections){
uint64 total = 0;
for(int i=0; i<max_connections; i++){
if(sessions[i]) total += sessions[i]->mail_size;
}
return total;
}
int start_new_session(struct smtp_session **sessions, int socket, int *num_connections, struct smtp_acl *smtp_acl[], char *client_addr, struct config *cfg){
int slot;
@ -31,6 +61,21 @@ int start_new_session(struct smtp_session **sessions, int socket, int *num_conne
return -1;
}
/*
* We are under the max_smtp_memory threshold
*/
uint64 expected_total_mail_size = get_sessions_total_expected_mail_size(sessions, cfg->max_connections);
uint64 total_memory = get_sessions_total_memory(sessions, cfg->max_connections);
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "DEBUG: total smtp memory allocated: %llu, expected total size: %llu", total_memory, expected_total_mail_size);
if(total_memory > cfg->max_smtp_memory || expected_total_mail_size > cfg->max_smtp_memory){
syslog(LOG_PRIORITY, "ERROR: too much memory consumption: %llu", total_memory);
send(socket, SMTP_RESP_451_ERR_TOO_MANY_REQUESTS, strlen(SMTP_RESP_451_ERR_TOO_MANY_REQUESTS), 0);
return -1;
}
slot = get_session_slot(sessions, cfg->max_connections);
if(slot >= 0 && sessions[slot] == NULL){
@ -84,11 +129,8 @@ struct smtp_session *get_session_by_socket(struct smtp_session **sessions, int m
void init_smtp_session(struct smtp_session *session, int slot, int sd, char *client_addr, struct config *cfg){
int i;
session->slot = slot;
session->buflen = 0;
session->protocol_state = SMTP_STATE_INIT;
session->cfg = cfg;
@ -100,27 +142,24 @@ void init_smtp_session(struct smtp_session *session, int slot, int sd, char *cli
session->net.ssl = NULL;
session->nullbyte = 0;
session->last_data_char = 0;
session->fd = -1;
memset(session->mailfrom, 0, SMALLBUFSIZE);
session->num_of_rcpt_to = 0;
for(i=0; i<MAX_RCPT_TO; i++) memset(session->rcptto[i], 0, SMALLBUFSIZE);
memset(session->buf, 0, MAXBUFSIZE);
snprintf(session->remote_host, sizeof(session->remote_host)-1, "%s", client_addr);
reset_bdat_counters(session);
session->buf = NULL;
session->buflen = 0;
session->bufsize = 0;
time(&(session->lasttime));
reset_smtp_session(session);
}
void free_smtp_session(struct smtp_session *session){
if(session){
if(session->buf != NULL){
free(session->buf);
}
if(session->net.use_ssl == 1){
SSL_shutdown(session->net.ssl);
SSL_free(session->net.ssl);
@ -160,71 +199,126 @@ void tear_down_session(struct smtp_session **sessions, int slot, int *num_connec
}
inline int get_last_newline_position(char *buf, int buflen){
int i;
for(i=buflen; i>0; i--){
if(*(buf+i) == '\n'){
i++;
break;
}
}
return i;
}
void flush_buffer(struct smtp_session *session){
// In the DATA phase skip the 1st character if it's a dot (.)
// and there are more characters before the trailing CR-LF
//
// See https://www.ietf.org/rfc/rfc5321.html#section-4.5.2 for more
for(int i=0; i<session->buflen; i++){
if(*(session->buf+i) == '\n' && *(session->buf+i+1) == '.' && *(session->buf+i+2) == '.'){
int dst = i + 2;
int src = dst + 1;
int l = session->buflen - src;
memmove(session->buf + dst, session->buf + src, l);
session->buflen -= 1;
}
}
// Exclude the trailing \r\n.\r\n sequence
session->buflen -= 5;
if(write(session->fd, session->buf, session->buflen) != session->buflen){
session->bad = 1;
syslog(LOG_PRIORITY, "ERROR (line: %d) %s: failed to write %d bytes", __LINE__, __func__, session->buflen);
}
session->tot_len = session->buflen;
}
void handle_data(struct smtp_session *session, char *readbuf, int readlen, struct config *cfg){
int puflen, rc, nullbyte;
char *p, copybuf[BIGBUFSIZE+MAXBUFSIZE], puf[MAXBUFSIZE];
// Update lasttime if we have something to process
time(&(session->lasttime));
// if there's something in the saved buffer, then let's merge them
if(session->protocol_state == SMTP_STATE_BDAT){
process_bdat(session, readbuf, readlen, cfg);
return;
}
int remaininglen = readlen + session->buflen;
// realloc memory if the new chunk doesn't fit in
if(session->buflen > 0){
memset(copybuf, 0, sizeof(copybuf));
if(session->buflen + readlen + 10 > session->bufsize){
// Handle if the current memory allocation for this email is above the max_message_size threshold
memcpy(copybuf, session->buf, session->buflen);
memcpy(&copybuf[session->buflen], readbuf, readlen);
if(session->buflen > cfg->max_message_size){
if(session->too_big == 0) syslog(LOG_PRIORITY, "ERROR: too big email: %d vs %d", session->buflen, cfg->max_message_size);
session->bad = 1;
session->too_big = 1;
}
if(session->bad == 0){
char *q = realloc(session->buf, session->bufsize + SMTPBUFSIZE);
if(q){
session->buf = q;
memset(session->buf+session->bufsize, 0, SMTPBUFSIZE);
session->bufsize += SMTPBUFSIZE;
} else {
syslog(LOG_PRIORITY, "ERROR: realloc %s %s %d", session->ttmpfile, __func__, __LINE__);
session->bad = 1;
}
}
}
// process smtp command
if(session->protocol_state != SMTP_STATE_DATA){
// We got ~2 MB of garbage and no valid smtp command
// Terminate the connection
if(session->buflen + readlen > SMTPBUFSIZE - 10){
session->bad = 1;
}
// We are at the beginning of the smtp transaction
if(session->bad == 1){
write1(&(session->net), SMTP_RESP_451_ERR, strlen(SMTP_RESP_451_ERR));
syslog(LOG_PRIORITY, "ERROR: sent 451 temp error back to client %s", session->ttmpfile);
return;
}
//printf("got %d *%s*\n", readlen, readbuf);
memcpy(session->buf + session->buflen, readbuf, readlen);
session->buflen += readlen;
int pos = get_last_newline_position(session->buf, session->buflen);
if(pos < readlen) return; // no complete command
process_smtp_command(session, cfg);
memset(session->buf, 0, session->bufsize);
session->buflen = 0;
memset(session->buf, 0, MAXBUFSIZE);
p = &copybuf[0];
}
else {
readbuf[readlen] = 0;
p = readbuf;
return;
}
if(session->bad == 0){
memcpy(session->buf + session->buflen, readbuf, readlen);
session->buflen += readlen;
do {
puflen = read_one_line(p, remaininglen, '\n', puf, sizeof(puf)-1, &rc, &nullbyte);
p += puflen;
remaininglen -= puflen;
if(nullbyte){
session->nullbyte = 1;
char *p = session->buf + session->buflen - 5;
if(session->buflen >= 5 && GOT_CRLF_DOT_CRLF(p)){
flush_buffer(session);
process_command_period(session);
}
// complete line: rc == OK and puflen > 0
// incomplete line with something in the buffer: rc == ERR and puflen > 0
if(puflen > 0){
// Update lasttime if we have a line to process
time(&(session->lasttime));
// Save incomplete line to buffer
if(rc == ERR){
memcpy(session->buf, puf, puflen);
session->buflen = puflen;
}
// We have a complete line to process
if(rc == OK){
if(session->protocol_state == SMTP_STATE_BDAT){
process_bdat(session, puf, puflen, cfg);
}
else if(session->protocol_state == SMTP_STATE_DATA){
sig_block(SIGALRM);
process_data(session, puf, puflen);
sig_unblock(SIGALRM);
}
else {
process_smtp_command(session, puf, cfg);
}
}
}
} while(puflen > 0);
} else if(strstr(readbuf, "\r\n.\r\n")){
process_command_period(session);
}
}

View File

@ -15,59 +15,59 @@
#include "smtp.h"
void process_smtp_command(struct smtp_session *session, char *buf, struct config *cfg){
void process_smtp_command(struct smtp_session *session, struct config *cfg){
char response[SMALLBUFSIZE];
if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "got on fd=%d: *%s*", session->net.socket, buf);
if(session->cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "got on fd=%d: *%s*", session->net.socket, session->buf);
if(strncasecmp(buf, SMTP_CMD_HELO, strlen(SMTP_CMD_HELO)) == 0){
if(strncasecmp(session->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));
if(strncasecmp(session->buf, SMTP_CMD_EHLO, strlen(SMTP_CMD_EHLO)) == 0 ||
strncasecmp(session->buf, LMTP_CMD_LHLO, strlen(LMTP_CMD_LHLO)) == 0){
process_command_ehlo_lhlo(session, response, sizeof(response), cfg);
return;
}
if(strncasecmp(buf, SMTP_CMD_HELP, strlen(SMTP_CMD_HELP)) == 0){
if(strncasecmp(session->buf, SMTP_CMD_HELP, strlen(SMTP_CMD_HELP)) == 0){
send_smtp_response(session, SMTP_RESP_221_PILER_SMTP_OK);
return;
}
if(strncasecmp(buf, SMTP_CMD_MAIL_FROM, strlen(SMTP_CMD_MAIL_FROM)) == 0){
process_command_mail_from(session, buf);
if(strncasecmp(session->buf, SMTP_CMD_MAIL_FROM, strlen(SMTP_CMD_MAIL_FROM)) == 0){
process_command_mail_from(session);
return;
}
if(strncasecmp(buf, SMTP_CMD_RCPT_TO, strlen(SMTP_CMD_RCPT_TO)) == 0){
process_command_rcpt_to(session, buf, cfg);
if(strncasecmp(session->buf, SMTP_CMD_RCPT_TO, strlen(SMTP_CMD_RCPT_TO)) == 0){
process_command_rcpt_to(session, cfg);
return;
}
if(strncasecmp(buf, SMTP_CMD_DATA, strlen(SMTP_CMD_DATA)) == 0){
if(strncasecmp(session->buf, SMTP_CMD_DATA, strlen(SMTP_CMD_DATA)) == 0){
process_command_data(session, cfg);
return;
}
/* Support only BDAT xxxx LAST command */
if(session->cfg->enable_chunking == 1 && strncasecmp(buf, SMTP_CMD_BDAT, strlen(SMTP_CMD_BDAT)) == 0 && strcasestr(buf, "LAST")){
get_bdat_size_to_read(session, buf);
if(session->cfg->enable_chunking == 1 && strncasecmp(session->buf, SMTP_CMD_BDAT, strlen(SMTP_CMD_BDAT)) == 0 && strcasestr(session->buf, "LAST")){
get_bdat_size_to_read(session);
return;
}
if(strncasecmp(buf, SMTP_CMD_QUIT, strlen(SMTP_CMD_QUIT)) == 0){
if(strncasecmp(session->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){
if(strncasecmp(session->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->net.use_ssl == 0){
if(session->cfg->tls_enable == 1 && strncasecmp(session->buf, SMTP_CMD_STARTTLS, strlen(SMTP_CMD_STARTTLS)) == 0 && session->net.use_ssl == 0){
process_command_starttls(session);
return;
}
@ -76,41 +76,6 @@ void process_smtp_command(struct smtp_session *session, char *buf, struct config
}
void process_data(struct smtp_session *session, char *buf, int buflen){
if(session->last_data_char == '\n' && strcmp(buf, ".\r\n") == 0){
process_command_period(session);
}
else {
// write line to file
int written=0, n_writes=0;
// In the DATA phase skip the 1st character if it's a dot (.)
// and there are more characters before the trailing CR-LF
//
// See https://www.ietf.org/rfc/rfc5321.html#section-4.5.2 for more.
int dotstuff = 0;
if(*buf == '.' && buflen > 1 && *(buf+1) != '\r' && *(buf+1) != '\n') dotstuff = 1;
while(written < buflen) {
int len = write(session->fd, buf+dotstuff+written, buflen-dotstuff-written);
n_writes++;
if(len > 0){
if(len != buflen-dotstuff) syslog(LOG_PRIORITY, "WARN: partial write: %d/%d bytes (round: %d)", len, buflen-dotstuff, n_writes);
written += len + dotstuff;
session->tot_len += len;
dotstuff = 0;
}
else syslog(LOG_PRIORITY, "ERROR (line: %d) process_data(): written %d bytes", __LINE__, len);
}
}
session->last_data_char = buf[buflen-1];
}
void wait_for_ssl_accept(struct smtp_session *session){
int rc;
@ -152,7 +117,7 @@ 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_ehlo_lhlo(struct smtp_session *session, char *buf, int buflen, struct config *cfg){
char extensions[SMALLBUFSIZE];
memset(extensions, 0, sizeof(extensions));
@ -163,7 +128,9 @@ void process_command_ehlo_lhlo(struct smtp_session *session, char *buf, int bufl
if(session->net.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(buf, buflen-1, SMTP_RESP_250_EXTENSIONS, session->cfg->hostid, extensions);
//#define SMTP_RESP_250_EXTENSIONS "250-%s\r\n%s250-SIZE %d\r\n250 8BITMIME\r\n"
snprintf(buf, buflen-1, SMTP_RESP_250_EXTENSIONS, session->cfg->hostid, cfg->max_message_size, extensions);
send_smtp_response(session, buf);
}
@ -234,26 +201,29 @@ void process_command_starttls(struct smtp_session *session){
}
void process_command_mail_from(struct smtp_session *session, char *buf){
void process_command_mail_from(struct smtp_session *session){
memset(session->mailfrom, 0, SMALLBUFSIZE);
if(session->protocol_state != SMTP_STATE_HELO && session->protocol_state != SMTP_STATE_PERIOD && session->protocol_state != SMTP_STATE_BDAT){
send(session->net.socket, SMTP_RESP_503_ERR, strlen(SMTP_RESP_503_ERR), 0);
}
else {
memset(&(session->ttmpfile[0]), 0, SMALLBUFSIZE);
make_random_string((unsigned char*)&(session->ttmpfile[0]), QUEUE_ID_LEN);
session->protocol_state = SMTP_STATE_MAIL_FROM;
extractEmail(buf, session->mailfrom);
extractEmail(session->buf, session->mailfrom);
reset_bdat_counters(session);
session->tot_len = 0;
int mailsize = get_size_from_smtp_mail_from(session->buf);
reset_smtp_session(session);
session->mail_size = mailsize;
send_smtp_response(session, SMTP_RESP_250_OK);
}
}
void process_command_rcpt_to(struct smtp_session *session, char *buf, struct config *cfg){
void process_command_rcpt_to(struct smtp_session *session, struct config *cfg){
if(session->protocol_state == SMTP_STATE_MAIL_FROM || session->protocol_state == SMTP_STATE_RCPT_TO){
@ -262,7 +232,7 @@ void process_command_rcpt_to(struct smtp_session *session, char *buf, struct con
session->protocol_state = SMTP_STATE_RCPT_TO;
if(session->num_of_rcpt_to < MAX_RCPT_TO){
extractEmail(buf, session->rcptto[session->num_of_rcpt_to]);
extractEmail(session->buf, session->rcptto[session->num_of_rcpt_to]);
// Check if we should accept archive_address only
if(cfg->archive_address[0] && !strstr(cfg->archive_address, session->rcptto[session->num_of_rcpt_to])){
@ -322,13 +292,23 @@ void process_command_period(struct smtp_session *session){
syslog(LOG_PRIORITY, "received: %s, from=%s, size=%d, client=%s, fd=%d, fsync=%ld", session->ttmpfile, session->mailfrom, session->tot_len, session->remote_host, session->net.socket, tvdiff(tv2, tv1));
move_email(session);
if(session->bad == 1 || session->too_big == 1){
snprintf(buf, sizeof(buf)-1, SMTP_RESP_451_ERR);
unlink(session->ttmpfile);
syslog(LOG_PRIORITY, "ERROR: problem in processing, removing %s", session->ttmpfile);
if(session->too_big)
snprintf(buf, sizeof(buf)-1, "%s", SMTP_RESP_552_ERR_TOO_BIG_EMAIL);
else
snprintf(buf, sizeof(buf)-1, "%s", SMTP_RESP_451_ERR);
} else {
move_email(session);
snprintf(buf, sizeof(buf)-1, "250 OK <%s>\r\n", session->ttmpfile);
}
snprintf(buf, sizeof(buf)-1, "250 OK <%s>\r\n", session->ttmpfile);
session->buflen = 0;
session->last_data_char = 0;
memset(session->buf, 0, sizeof(session->buf));
if(session->buf){
memset(session->buf, 0, session->bufsize);
session->buflen = 0;
}
send_smtp_response(session, buf);
}
@ -344,15 +324,32 @@ void process_command_quit(struct smtp_session *session, char *buf, int buflen){
void process_command_reset(struct smtp_session *session){
reset_smtp_session(session);
send_smtp_response(session, SMTP_RESP_250_OK);
session->tot_len = 0;
session->fd = -1;
session->protocol_state = SMTP_STATE_HELO;
session->last_data_char = 0;
}
void reset_smtp_session(struct smtp_session *session){
session->tot_len = 0;
session->mail_size = 0;
session->too_big = 0;
session->bad = 0;
session->fd = -1;
session->num_of_rcpt_to = 0;
for(int i=0; i<MAX_RCPT_TO; i++) memset(session->rcptto[i], 0, SMALLBUFSIZE);
reset_bdat_counters(session);
time(&(session->lasttime));
memset(&(session->ttmpfile[0]), 0, SMALLBUFSIZE);
make_random_string((unsigned char *)&(session->ttmpfile[0]), QUEUE_ID_LEN);
make_random_string((unsigned char*)&(session->ttmpfile[0]), QUEUE_ID_LEN);
if(session->buf){
memset(session->buf, 0, session->bufsize);
}
session->buflen = 0;
}

View File

@ -7,22 +7,24 @@
#include <piler.h>
void process_smtp_command(struct smtp_session *session, char *buf, struct config *cfg);
void process_smtp_command(struct smtp_session *session, struct config *cfg);
void process_data(struct smtp_session *session, char *buf, int buflen);
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_ehlo_lhlo(struct smtp_session *session, char *buf, int buflen, struct config *cfg);
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, struct config *cfg);
void process_command_mail_from(struct smtp_session *session);
void process_command_rcpt_to(struct smtp_session *session, struct config *cfg);
void process_command_data(struct smtp_session *session, struct config *cfg);
void process_command_period(struct smtp_session *session);
void process_command_starttls(struct smtp_session *session);
void reset_bdat_counters(struct smtp_session *session);
void get_bdat_size_to_read(struct smtp_session *session, char *buf);
void get_bdat_size_to_read(struct smtp_session *session);
void process_bdat(struct smtp_session *session, char *readbuf, int readlen, struct config *cfg);
void reset_smtp_session(struct smtp_session *session);
#endif

View File

@ -38,7 +38,7 @@
#define SMTP_RESP_220_READY_TO_START_TLS "220 Ready to start TLS\r\n"
#define SMTP_RESP_221_GOODBYE "221 %s Goodbye\r\n"
#define SMTP_RESP_250_OK "250 Ok\r\n"
#define SMTP_RESP_250_EXTENSIONS "250-%s\r\n250-PIPELINING\r\n%s250-SIZE\r\n250 8BITMIME\r\n"
#define SMTP_RESP_250_EXTENSIONS "250-%s\r\n250-SIZE %d\r\n%s250 8BITMIME\r\n"
#define SMTP_EXTENSION_STARTTLS "250-STARTTLS\r\n"
#define SMTP_EXTENSION_CHUNKING "250-CHUNKING\r\n"
@ -51,6 +51,7 @@
#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_451_ERR_TOO_MANY_REQUESTS "451 Too many requests, 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"
@ -59,6 +60,7 @@
#define SMTP_RESP_550_ERR_INVALID_RECIPIENT "550 Invalid 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 "550 Service currently unavailable\r\n"
#define SMTP_RESP_552_ERR_TOO_BIG_EMAIL "552 Too big email\r\n"
// LMTP commands

View File

@ -5,12 +5,12 @@
#ifndef _TAI_H
#define _TAI_H
#include "config.h"
#define TAI_PACK 8
#define TAIA_PACK 16
#define TIMESTAMP 25
typedef unsigned long long uint64;
struct tai {
uint64 x;
};

View File

@ -6,17 +6,17 @@ case1() {
setup
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/Inbox" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/Inbox2" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/Levelszemet" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/Levelszemet2" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/spam0" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/spam1" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/spam2" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/journal" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/deduptest" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/special" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu extra@addr.ess another@extra.addr -p 25 -t 20 --dir "$EML_DIR/virus" --socket --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/Inbox" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/Inbox2" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/Levelszemet" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/Levelszemet2" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/spam0" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/spam1" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/spam2" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/journal" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/deduptest" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu -p 25 -t 20 --dir "$EML_DIR/special" --no-counter
"$SMTP_SOURCE_PROG" -s $SMTP_HOST -r archive@cust1.acts.hu extra@addr.ess another@extra.addr -p 25 -t 20 --dir "$EML_DIR/virus" --no-counter
wait_until_emails_are_processed "piler1" 3020

View File

@ -24,6 +24,13 @@ setup_mysql() {
mysql -u piler -ppiler123 piler1 < ../util/db-mysql.sql
}
run_smtp_tests() {
mkdir -p /var/piler/store/00/piler /var/piler/tmp /var/piler/manticore
chown -R piler:piler /var/piler/
../src/piler-smtp -L 5 -d
./smtp -s 127.0.0.1
}
if [[ -v BUILD_NUMBER ]]; then
setup_mysql
fi
@ -37,3 +44,5 @@ fi
./check_hash
./check_decoder
./check_attachments
if [[ -v BUILD_NUMBER ]]; then run_smtp_tests; fi

View File

@ -4,11 +4,13 @@
#include "test.h"
extern char *optarg;
extern int optind;
char *testmessage = "From: aaa@aaa.fu\nTo: bela@aaa.fu\nMessage-Id: ajajajaja\nSubject: this is a test\n\nAaaaaa.";
char *testmessage = "From: aaa@aaa.fu\r\nTo: bela@aaa.fu\r\nMessage-Id: ajajajaja\r\nSubject: this is a test\r\n\r\nAaaaaa";
char *testmessage2 = " and the last line\r\n";
char *recipient = "aaa@worker0";
int helo = 0; // 0=HELO, 1=EHLO
@ -16,12 +18,24 @@ void usage(){
printf("\nusage: smtp\n\n");
printf(" -s <smtp server> SMTP server\n");
printf(" -p <smtp port> SMTP port (25)\n");
printf(" -r <recipient> Envelope recipient\n");
printf(" -t <timeout> Timeout in sec (10)\n");
exit(0);
}
int countreplies(char *s){
int replies = 0;
for(; *s; s++){
if(*s == '\n') replies++;
}
return replies;
}
void connect_to_smtp_server(char *server, int port, struct data *data){
int rc;
char port_string[8], buf[MAXBUFSIZE];
@ -53,14 +67,15 @@ void connect_to_smtp_server(char *server, int port, struct data *data){
}
recvtimeoutssl(data->net, buf, sizeof(buf));
printf("rcvd: %s", buf);
ENDE:
freeaddrinfo(res);
}
void send_smtp_command(struct net *net, char *cmd, char *buf, int buflen){
void send_smtp_command(struct net *net, char *cmd, char *buf, int buflen, int expectedreplies){
int tot=0;
if(net == NULL || cmd == NULL) return;
if(net->socket == -1){
@ -68,25 +83,32 @@ void send_smtp_command(struct net *net, char *cmd, char *buf, int buflen){
return;
}
printf("sent: %s", cmd);
write1(net, cmd, strlen(cmd));
recvtimeoutssl(net, buf, buflen);
printf("rcvd: %s", buf);
while(1){
tot += recvtimeoutssl(net, buf+tot, buflen);
if(countreplies(buf) == expectedreplies) break;
}
}
void send_helo_command(struct net *net){
int replies=1;
char recvbuf[MAXBUFSIZE];
if(helo == 0){
send_smtp_command(net, "HELO aaaa.fu\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "HELO");
send_smtp_command(net, "HELO aaaa.fu\r\n", recvbuf, sizeof(recvbuf)-1, replies);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
}
else {
send_smtp_command(net, "EHLO aaaa.fu\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250-", 4) == 0 && "EHLO");
if(net->use_ssl == 0) assert(strstr(recvbuf, "250-STARTTLS") && "STARTTLS");
else assert(strstr(recvbuf, "250-STARTTLS") == NULL && "STARTTLS");
//replies = 6;
replies = 5;
if(net->use_ssl == 1) replies--;
send_smtp_command(net, "EHLO aaaa.fu\r\n", recvbuf, sizeof(recvbuf)-1, replies);
ASSERT(strncmp(recvbuf, "250-", 4) == 0, recvbuf);
if(net->use_ssl == 0){ ASSERT(strstr(recvbuf, "250-STARTTLS"), recvbuf); }
else { ASSERT(strstr(recvbuf, "250-STARTTLS") == NULL, recvbuf); }
}
}
@ -94,209 +116,392 @@ void send_helo_command(struct net *net){
static void test_smtp_commands_one_at_a_time(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL");
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "RCPT TO: <archive@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "RCPT");
snprintf(sendbuf, sizeof(sendbuf)-1, "RCPT TO: <%s>\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "354 ", 4) == 0 && "DATA");
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "354 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\n", testmessage);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "PERIOD");
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "221 ", 4) == 0 && "QUIT");
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "221 ", 4) == 0, recvbuf);
close(data->net->socket);
TEST_FOOTER();
}
static void test_smtp_commands_pipelining(char *server, int port, struct data *data){
static void test_smtp_commands_one_at_a_time_data_in_2_parts(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\nRCPT TO: <archive@aaa.fu>\r\nDATA\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL");
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "RCPT TO: <%s>\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "354 ", 4) == 0, recvbuf);
write1(data->net, testmessage, strlen(testmessage));
snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\n", testmessage2);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "221 ", 4) == 0, recvbuf);
close(data->net->socket);
TEST_FOOTER();
}
/*static void test_smtp_commands_pipelining(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
snprintf(sendbuf, sizeof(sendbuf)-1, "MAIL FROM: <sender@aaa.fu>\r\nRCPT TO: <%s>\r\nDATA\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 3);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\nQUIT\r\n", testmessage);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "QUIT");
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 2);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
close(data->net->socket);
}
TEST_FOOTER();
}*/
static void test_smtp_commands_with_reset_command(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE];
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL");
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "RSET\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "RSET");
send_smtp_command(data->net, "RSET\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "RCPT TO: <archive@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "503 ", 4) == 0 && "RCPT");
snprintf(sendbuf, sizeof(sendbuf)-1, "RCPT TO: <%s>\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "503 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "221 ", 4) == 0 && "QUIT");
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "221 ", 4) == 0, recvbuf);
close(data->net->socket);
TEST_FOOTER();
}
static void test_smtp_commands_partial_command(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
write1(data->net, "M", 1);
printf("sent: M\n");
send_smtp_command(data->net, "AIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL");
send_smtp_command(data->net, "AIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "RCPT TO: <archive@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "RCPT");
snprintf(sendbuf, sizeof(sendbuf)-1, "RCPT TO: <%s>\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "354 ", 4) == 0 && "DATA");
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "354 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\n", testmessage);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "PERIOD");
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "221 ", 4) == 0 && "QUIT");
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "221 ", 4) == 0, recvbuf);
close(data->net->socket);
TEST_FOOTER();
}
static void test_smtp_commands_partial_command_pipelining(char *server, int port, struct data *data){
/*static void test_smtp_commands_partial_command_pipelining(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
write1(data->net, "M", 1);
printf("sent: M\n");
send_smtp_command(data->net, "AIL FROM: <sender@aaa.fu>\r\nRCPT TO: <archive@aaa.fu>\r\nDATA\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL");
snprintf(sendbuf, sizeof(sendbuf)-1, "AIL FROM: <sender@aaa.fu>\r\nRCPT TO: <%s>\r\nDATA\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 3);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\nQUIT\r\n", testmessage);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "QUIT");
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 2);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
close(data->net->socket);
}
TEST_FOOTER();
}*/
static void test_smtp_commands_starttls(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
send_smtp_command(data->net, "STARTTLS\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "220 ", 4) == 0 && "STARTTLS");
send_smtp_command(data->net, "STARTTLS\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "220 ", 4) == 0, recvbuf);
init_ssl_to_server(data);
data->net->use_ssl = 1;
send_helo_command(data->net);
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL");
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "RCPT TO: <archive@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "RCPT");
snprintf(sendbuf, sizeof(sendbuf)-1, "RCPT TO: <%s>\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "354 ", 4) == 0 && "DATA");
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "354 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\n", testmessage);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "PERIOD");
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "221 ", 4) == 0 && "QUIT");
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "221 ", 4) == 0, recvbuf);
close(data->net->socket);
TEST_FOOTER();
}
static void test_smtp_commands_period_command_in_2_parts(char *server, int port, char *part1, char *part2, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\nRCPT TO: <archive@aaa.fu>\r\nDATA\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL");
// As long as pipelining support is not reintroduced
//
/*snprintf(sendbuf, sizeof(sendbuf)-1, "MAIL FROM: <sender@aaa.fu>\r\nRCPT TO: <%s>\r\nDATA\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 3);
if(!strstr(recvbuf, "354 ")) recvtimeoutssl(data->net, recvbuf, sizeof(recvbuf)-1);
ASSERT(strstr(recvbuf, "354 "), recvbuf);*/
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "RCPT TO: <%s>\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "354 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "%s%s", testmessage, part1);
write1(data->net, sendbuf, strlen(sendbuf));
snprintf(sendbuf, sizeof(sendbuf), "%s", part2);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "QUIT\r\n");
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "QUIT");
ASSERT(strncmp(recvbuf, "221 ", 4) == 0, recvbuf);
close(data->net->socket);
TEST_FOOTER();
}
static void test_smtp_commands_period_command_in_its_own_packet(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\nRCPT TO: <archive@aaa.fu>\r\nDATA\r\n", recvbuf, sizeof(recvbuf)-1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "MAIL");
// As long as pipelining support is not reintroduced
//
/*snprintf(sendbuf, sizeof(sendbuf)-1, "MAIL FROM: <sender@aaa.fu>\r\nRCPT TO: <%s>\r\nDATA\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 3);
if(!strstr(recvbuf, "354 ")) recvtimeoutssl(data->net, recvbuf, sizeof(recvbuf)-1);
ASSERT(strstr(recvbuf, "354 "), recvbuf);*/
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "RCPT TO: <%s>\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "354 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "%s", testmessage);
write1(data->net, sendbuf, strlen(sendbuf));
snprintf(sendbuf, sizeof(sendbuf), "\r\n.\r\n");
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "QUIT\r\n");
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
assert(strncmp(recvbuf, "250 ", 4) == 0 && "QUIT");
ASSERT(strncmp(recvbuf, "221 ", 4) == 0, recvbuf);
close(data->net->socket);
TEST_FOOTER();
}
static void test_smtp_commands_with_partial_data_lines(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
int yes=1;
TEST_HEADER();
connect_to_smtp_server(server, port, data);
setsockopt(data->net->socket, IPPROTO_TCP, TCP_NODELAY, &yes, (socklen_t)sizeof(int));
send_helo_command(data->net);
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "RCPT TO: <%s>\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "DATA\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "354 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "From: aaa@aaa.fu\r\nTo: bela@aaa.fu\r\nMessage-Id: ajajajaja\r\nSubject: this is a test\r\n\r\nAaaaaa");
write1(data->net, sendbuf, strlen(sendbuf)); sleep(2);
snprintf(sendbuf, sizeof(sendbuf)-1, "jjdkdjkd dkd some garbage.");
write1(data->net, sendbuf, strlen(sendbuf)); sleep(2);
snprintf(sendbuf, sizeof(sendbuf)-1, "\r\nSome shit again au aua ua au aua uuu");
write1(data->net, sendbuf, strlen(sendbuf)); sleep(2);
snprintf(sendbuf, sizeof(sendbuf)-1, ".\r\nAnd the last line.\r\n.\r\n");
write1(data->net, sendbuf, strlen(sendbuf));
snprintf(sendbuf, sizeof(sendbuf)-1, "%s\r\n.\r\nQUIT\r\n", testmessage);
recvtimeoutssl(data->net, recvbuf, sizeof(recvbuf));
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
close(data->net->socket);
TEST_FOOTER();
}
static void test_smtp_bdat_last_one_at_a_time(char *server, int port, struct data *data){
char recvbuf[MAXBUFSIZE], sendbuf[MAXBUFSIZE];
TEST_HEADER();
connect_to_smtp_server(server, port, data);
send_helo_command(data->net);
send_smtp_command(data->net, "MAIL FROM: <sender@aaa.fu>\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "RCPT TO: <%s>\r\n", recipient);
send_smtp_command(data->net, sendbuf, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
snprintf(sendbuf, sizeof(sendbuf)-1, "BDAT %ld LAST\r\n", strlen(testmessage)+strlen(testmessage2));
write1(data->net, sendbuf, strlen(sendbuf));
write1(data->net, testmessage, strlen(testmessage));
send_smtp_command(data->net, testmessage2, recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "250 ", 4) == 0, recvbuf);
send_smtp_command(data->net, "QUIT\r\n", recvbuf, sizeof(recvbuf)-1, 1);
ASSERT(strncmp(recvbuf, "221 ", 4) == 0, recvbuf);
close(data->net->socket);
TEST_FOOTER();
}
@ -317,6 +522,7 @@ int main(int argc, char **argv){
{
{"server", required_argument, 0, 's' },
{"port", required_argument, 0, 'p' },
{"rcpt", required_argument, 0, 'r' },
{"timeout", required_argument, 0, 't' },
{"lhlo", no_argument, 0, 'l' },
{"help", no_argument, 0, 'h' },
@ -325,9 +531,9 @@ int main(int argc, char **argv){
int option_index = 0;
int c = getopt_long(argc, argv, "c:s:p:t:lh?", long_options, &option_index);
int c = getopt_long(argc, argv, "c:s:p:t:r:lh?", long_options, &option_index);
#else
int c = getopt(argc, argv, "c:s:p:t:lh?");
int c = getopt(argc, argv, "c:s:p:t:r:lh?");
#endif
@ -343,6 +549,10 @@ int main(int argc, char **argv){
port = atoi(optarg);
break;
case 'r' :
recipient = optarg;
break;
case 't' :
net.timeout = atoi(optarg);
break;
@ -357,7 +567,7 @@ int main(int argc, char **argv){
break;
default :
default :
break;
}
}
@ -367,21 +577,26 @@ int main(int argc, char **argv){
data.net = &net;
test_smtp_commands_one_at_a_time(server, port, &data);
test_smtp_commands_pipelining(server, port, &data);
test_smtp_commands_one_at_a_time_data_in_2_parts(server, port, &data);
////test_smtp_commands_pipelining(server, port, &data);
test_smtp_commands_with_reset_command(server, port, &data);
test_smtp_commands_partial_command(server, port, &data);
test_smtp_commands_partial_command_pipelining(server, port, &data);
////test_smtp_commands_partial_command_pipelining(server, port, &data);
test_smtp_commands_period_command_in_2_parts(server, port, "\r", "\n.\r\n", &data);
test_smtp_commands_period_command_in_2_parts(server, port, "\r\n", ".\r\n", &data);
test_smtp_commands_period_command_in_2_parts(server, port, "\r\n.", "\r\n", &data);
test_smtp_commands_period_command_in_2_parts(server, port, "\r\n.\r", "\n", &data);
test_smtp_commands_period_command_in_its_own_packet(server, port, &data);
helo = 1; // we must use EHLO to get the STARTTLS in the response
test_smtp_commands_starttls(server, port, &data);
test_smtp_commands_with_partial_data_lines(server, port, &data);
helo = 1; // we must use EHLO to get the STARTTLS in the response
test_smtp_commands_starttls(server, port, &data);
data.net->use_ssl = 0;
test_smtp_bdat_last_one_at_a_time(server, port, &data);
return 0;
}

View File

@ -8,6 +8,7 @@
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <locale.h>
#include <getopt.h>