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>
|
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
|
|
|
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
|
|
|
|
2017-08-08 15:34:45 +02:00
|
|
|
int start_new_session(struct smtp_session **sessions, int socket, int *num_connections, struct config *cfg){
|
2016-11-12 10:48:08 +01:00
|
|
|
char smtp_banner[SMALLBUFSIZE];
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
#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; 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];
|
|
|
|
|
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
|
|
|
|
2017-08-30 20:46:39 +02:00
|
|
|
session->fd = -1;
|
|
|
|
|
2016-11-12 10:48:08 +01:00
|
|
|
memset(session->buf, 0, SMALLBUFSIZE);
|
|
|
|
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){
|
|
|
|
syslog(LOG_PRIORITY, "disconnected from %s", sessions[slot]->remote_host);
|
|
|
|
|
2017-08-08 15:34:45 +02:00
|
|
|
close(sessions[slot]->net.socket);
|
2016-11-12 10:48:08 +01:00
|
|
|
|
|
|
|
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 {
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|