2016-11-12 10:48:08 +01:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2017-01-18 09:38:30 +01:00
# include <netdb.h>
2019-12-03 21:03:36 +01:00
# include <signal.h>
2016-11-12 10:48:08 +01:00
# include <piler.h>
int get_session_slot ( struct smtp_session * * sessions , int max_connections ) ;
2017-08-08 15:34:45 +02:00
void init_smtp_session ( struct smtp_session * session , int slot , int sd , struct config * cfg ) ;
2016-11-12 10:48:08 +01:00
2020-12-27 23:40:39 +01:00
int start_new_session ( struct smtp_session * * sessions , int socket , int * num_connections , struct smtp_acl * smtp_acl [ ] , char * client_addr , struct config * cfg ) {
2016-11-12 10:48:08 +01:00
int slot ;
/*
* We have enough connections to serve already
*/
if ( * num_connections > = cfg - > max_connections ) {
2017-08-08 15:34:45 +02:00
syslog ( LOG_PRIORITY , " ERROR: too many connections (%d), cannot accept socket %d " , * num_connections , socket ) ;
2016-11-12 10:48:08 +01:00
send ( socket , SMTP_RESP_421_ERR_ALL_PORTS_ARE_BUSY , strlen ( SMTP_RESP_421_ERR_ALL_PORTS_ARE_BUSY ) , 0 ) ;
close ( socket ) ;
return - 1 ;
}
2020-12-27 23:40:39 +01:00
// Check remote client against the allowed network ranges
if ( cfg - > smtp_access_list & & is_blocked_by_pilerscreen ( smtp_acl , client_addr , cfg ) ) {
send ( socket , SMTP_RESP_550_ERR , strlen ( SMTP_RESP_550_ERR ) , 0 ) ;
2016-11-12 10:48:08 +01:00
close ( socket ) ;
return - 1 ;
}
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 ) ;
2020-08-10 20:58:34 +02:00
char smtp_banner [ SMALLBUFSIZE ] ;
2016-11-12 10:48:08 +01:00
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 ; i < max_connections ; i + + ) {
if ( sessions [ i ] = = NULL ) return i ;
}
return - 1 ;
}
struct smtp_session * get_session_by_socket ( struct smtp_session * * sessions , int max_connections , int socket ) {
int i ;
for ( i = 0 ; i < max_connections ; i + + ) {
2017-08-08 15:34:45 +02:00
if ( sessions [ i ] & & sessions [ i ] - > net . socket = = socket ) return sessions [ i ] ;
2016-11-12 10:48:08 +01:00
}
return NULL ;
}
2017-08-08 15:34:45 +02:00
void init_smtp_session ( struct smtp_session * session , int slot , int sd , struct config * cfg ) {
2017-01-18 09:38:30 +01:00
struct sockaddr_in addr ;
socklen_t addr_size = sizeof ( struct sockaddr_in ) ;
char hbuf [ NI_MAXHOST ] , sbuf [ NI_MAXSERV ] ;
2018-02-20 15:59:21 +01:00
int i ;
2017-01-18 09:38:30 +01:00
2016-11-12 10:48:08 +01:00
session - > slot = slot ;
session - > buflen = 0 ;
session - > protocol_state = SMTP_STATE_INIT ;
session - > cfg = cfg ;
2017-08-08 15:34:45 +02:00
session - > net . socket = sd ;
session - > net . use_ssl = 0 ; // use SSL/TLS
session - > net . starttls = 0 ; // SSL/TLS communication is active (1) or not (0)
session - > net . ctx = NULL ;
session - > net . ssl = NULL ;
2016-11-12 10:48:08 +01:00
2018-03-17 17:32:32 +01:00
session - > last_data_char = 0 ;
2017-08-30 20:46:39 +02:00
session - > fd = - 1 ;
2018-02-20 15:59:21 +01:00
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 ) ;
2018-03-02 22:46:48 +01:00
memset ( session - > buf , 0 , MAXBUFSIZE ) ;
2016-11-12 10:48:08 +01:00
memset ( session - > remote_host , 0 , INET6_ADDRSTRLEN ) ;
reset_bdat_counters ( session ) ;
time ( & ( session - > lasttime ) ) ;
2017-01-18 09:38:30 +01:00
2017-08-08 15:34:45 +02:00
if ( getpeername ( session - > net . socket , ( struct sockaddr * ) & addr , & addr_size ) = = 0 & &
2017-01-18 09:38:30 +01:00
getnameinfo ( ( struct sockaddr * ) & addr , addr_size , hbuf , sizeof ( hbuf ) , sbuf , sizeof ( sbuf ) , NI_NUMERICHOST | NI_NUMERICSERV ) = = 0 ) {
snprintf ( session - > remote_host , INET6_ADDRSTRLEN - 1 , " %s " , hbuf ) ;
}
2016-11-12 10:48:08 +01:00
}
void free_smtp_session ( struct smtp_session * session ) {
if ( session ) {
2017-08-08 15:34:45 +02:00
if ( session - > net . use_ssl = = 1 ) {
SSL_shutdown ( session - > net . ssl ) ;
SSL_free ( session - > net . ssl ) ;
2016-11-12 10:48:08 +01:00
}
2017-08-08 15:34:45 +02:00
if ( session - > net . ctx ) SSL_CTX_free ( session - > net . ctx ) ;
2016-11-12 10:48:08 +01:00
free ( session ) ;
}
}
void tear_down_session ( struct smtp_session * * sessions , int slot , int * num_connections ) {
2018-05-13 16:48:20 +02:00
syslog ( LOG_PRIORITY , " disconnected from %s on fd=%d (%d active connections) " , sessions [ slot ] - > remote_host , sessions [ slot ] - > net . socket , ( * num_connections ) - 1 ) ;
2016-11-12 10:48:08 +01:00
2017-08-08 15:34:45 +02:00
close ( sessions [ slot ] - > net . socket ) ;
2016-11-12 10:48:08 +01:00
2018-05-13 14:26:21 +02:00
if ( sessions [ slot ] - > fd ! = - 1 ) {
syslog ( LOG_PRIORITY , " Removing %s " , sessions [ slot ] - > ttmpfile ) ;
close ( sessions [ slot ] - > fd ) ;
unlink ( sessions [ slot ] - > ttmpfile ) ;
sessions [ slot ] - > fd = - 1 ;
}
2016-11-12 10:48:08 +01:00
free_smtp_session ( sessions [ slot ] ) ;
sessions [ slot ] = NULL ;
( * num_connections ) - - ;
}
2018-02-20 15:59:21 +01:00
void handle_data ( struct smtp_session * session , char * readbuf , int readlen , struct config * cfg ) {
2018-03-04 19:50:37 +01:00
int puflen , rc ;
2018-03-02 22:46:48 +01:00
char * p , copybuf [ BIGBUFSIZE + MAXBUFSIZE ] , puf [ MAXBUFSIZE ] ;
2016-11-12 10:48:08 +01:00
2018-03-02 22:46:48 +01:00
// if there's something in the saved buffer, then let's merge them
2016-11-12 10:48:08 +01:00
2018-03-02 22:46:48 +01:00
if ( session - > buflen > 0 ) {
memset ( copybuf , 0 , sizeof ( copybuf ) ) ;
2016-11-12 10:48:08 +01:00
2018-03-02 22:46:48 +01:00
memcpy ( copybuf , session - > buf , session - > buflen ) ;
memcpy ( & copybuf [ session - > buflen ] , readbuf , readlen ) ;
2016-11-12 10:48:08 +01:00
2018-03-02 22:46:48 +01:00
session - > buflen = 0 ;
memset ( session - > buf , 0 , MAXBUFSIZE ) ;
2016-11-12 10:48:08 +01:00
2018-03-02 22:46:48 +01:00
p = & copybuf [ 0 ] ;
2016-11-12 10:48:08 +01:00
}
else {
2018-03-02 22:46:48 +01:00
p = readbuf ;
}
2016-11-12 10:48:08 +01:00
2018-03-02 22:46:48 +01:00
do {
puflen = read_one_line ( p , ' \n ' , puf , sizeof ( puf ) - 1 , & rc ) ;
p + = puflen ;
2016-11-12 10:48:08 +01:00
2018-03-04 19:50:37 +01:00
if ( puflen > 0 ) {
2019-12-15 15:08:16 +01:00
// Update lasttime if we have a line to process
time ( & ( session - > lasttime ) ) ;
2018-03-04 20:16:30 +01:00
// pass the puffer to process_data() only if there was an '\n'
// on the line or the puffer does not start with a period
if ( session - > protocol_state = = SMTP_STATE_DATA & & ( rc = = OK | | puf [ 0 ] ! = ' . ' ) ) {
2019-12-03 21:03:36 +01:00
sig_block ( SIGALRM ) ;
2018-03-02 22:46:48 +01:00
process_data ( session , puf , puflen ) ;
2019-12-03 21:03:36 +01:00
sig_unblock ( SIGALRM ) ;
2018-03-02 22:46:48 +01:00
}
2018-03-04 19:50:37 +01:00
else if ( session - > protocol_state = = SMTP_STATE_BDAT ) {
2018-03-02 22:46:48 +01:00
process_bdat ( session , puf , puflen , cfg ) ;
2016-11-12 10:48:08 +01:00
}
2018-03-04 19:50:37 +01:00
else if ( rc = = OK ) {
process_smtp_command ( session , puf , cfg ) ;
}
2016-11-12 10:48:08 +01:00
else {
2018-03-02 22:46:48 +01:00
snprintf ( session - > buf , MAXBUFSIZE - 1 , " %s " , puf ) ;
session - > buflen = puflen ;
2016-11-12 10:48:08 +01:00
}
2018-03-02 22:46:48 +01:00
}
2016-11-12 10:48:08 +01:00
2018-03-02 22:46:48 +01:00
} while ( puflen > 0 ) ;
2016-11-12 10:48:08 +01:00
}
2018-02-20 15:59:21 +01:00
2018-02-21 13:55:05 +01:00
void write_envelope_addresses ( struct smtp_session * session , struct config * cfg ) {
2018-02-20 15:59:21 +01:00
if ( session - > fd = = - 1 ) return ;
2020-08-10 20:58:34 +02:00
for ( int i = 0 ; i < session - > num_of_rcpt_to ; i + + ) {
char * p = strchr ( session - > rcptto [ i ] , ' @ ' ) ;
2018-02-21 13:55:05 +01:00
if ( p & & strncmp ( p + 1 , cfg - > hostid , cfg - > hostid_len ) ) {
2020-08-10 20:58:34 +02:00
char s [ SMALLBUFSIZE ] ;
2018-02-21 13:55:05 +01:00
snprintf ( s , sizeof ( s ) - 1 , " X-Piler-Envelope-To: %s \n " , session - > rcptto [ i ] ) ;
if ( write ( session - > fd , s , strlen ( s ) ) = = - 1 ) syslog ( LOG_PRIORITY , " ERROR: %s: cannot write envelope to address " , session - > ttmpfile ) ;
}
2018-02-20 15:59:21 +01:00
}
}