/*
smtp.c - MessageWall SMTP definitions
Copyright (C) 2002 Ian Gulliver
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include <firestring.h>
#include <firedns.h>
#include "messagewall.h"
#include "dbase.h"
#include "client.h"
#include "rfc822.h"
#include "mime.h"
#include "dnsbl.h"
#include "dnsdcc.h"
#include "rdns.h"
#include "rmx.h"
#include "auth.h"
#include "tls.h"
#include "smtp.h"
static const char tagstring[] = "$Id: smtp.c,v 1.92.2.3 2002/10/16 14:00:29 ian Exp $";
#define SMTP_RESET \
clients[client].have_from = 0; \
clients[client].num_to = 0;
const char *months[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
int smtp_parseline(int client) {
int i;
struct firestring_estr_t line;
/*
* find the newline
*/
i = firestring_estr_strstr(&clients[client].buffer,"\r\n",0);
if (i == -1) {
if (firestring_estr_strchr(&clients[client].buffer,'\n',0) != -1) {
/*
* bare LF
*/
fprintf(stderr,"{%d} (%d) SMTP/FATAL: client sent bare LF in command\n",process,client);
tls_client_write(client,SMTP_LF,sizeof(SMTP_LF) - 1);
smtp_clear(client);
}
return 1;
}
/*
* create the new line object
*/
line.a = line.l = i;
line.s = clients[client].buffer.s;
if (smtp_handle_line(client,&line) != 0)
return 1;
line.a = line.l = i;
line.s = clients[client].buffer.s;
/*
* fold the buffer back on itself
* or into the message buffer, if we've just entered the DATA phase
*/
if (clients[client].in_data == 1) {
firestring_estr_estrcat(&clients[client].message,&clients[client].buffer,line.l + 2);
clients[client].buffer.l = 0;
} else {
clients[client].buffer.l -= line.l + 2;
memmove(clients[client].buffer.s,&clients[client].buffer.s[line.l + 2],clients[client].buffer.l);
}
return 0;
}
int smtp_parsedata(int client) {
time(&clients[client].lasttalk);
for (; clients[client].message_point < clients[client].message.l - 2; clients[client].message_point++) {
if (clients[client].message.s[clients[client].message_point] == '\n') {
if (clients[client].message_point == 0 || clients[client].message.s[clients[client].message_point - 1] != '\r') {
if (clients[client].bare_lf == 0) {
fprintf(stderr,"{%d} (%d) SMTP/FATAL: client sent bare LF inside DATA\n",process,client);
tls_client_write(client,SMTP_LF,sizeof(SMTP_LF) - 1);
clients[client].bare_lf = 1;
}
}
} else if (sevenbit == 1 && (unsigned char) clients[client].message.s[clients[client].message_point] > 127) {
if (clients[client].bare_lf == 0) {
fprintf(stderr,"{%d} (%d) SMTP/FATAL: client sent 8bit data to server in 7bit mode\n",process,client);
tls_client_write(client,SMTP_7BIT,sizeof(SMTP_7BIT) - 1);
clients[client].bare_lf = 1;
}
} else if (clients[client].message.s[clients[client].message_point] == '.') {
if (clients[client].message_point != 0 && clients[client].message.s[clients[client].message_point - 1] != '\n')
continue;
if (clients[client].message.s[clients[client].message_point+1] == '\r' &&
clients[client].message.s[clients[client].message_point+2] == '\n') {
/*
* end of data section
*/
/*
* copy remaining message back into buffer, erroring if they've pipelined too much
* (pipelining isn't actually legal here, but who knows)
*/
if (clients[client].message.l - clients[client].message_point - 2 > SMTP_LINE_MAXLEN) {
if (clients[client].bare_lf == 0)
tls_client_write(client,SMTP_TOOLONG,sizeof(SMTP_TOOLONG) - 1);
smtp_clear(client);
return 0;
}
firestring_estr_estrcpy(&clients[client].buffer,&clients[client].message,clients[client].message_point + 3);
/*
* truncate message to end
* smtp messages always end with the newline that starts the delimeter
*/
clients[client].message.l = clients[client].message_point;
fprintf(stderr,"{%d} (%d) SMTP/STATUS: received message of %d bytes\n",process,client,clients[client].message.l);
return smtp_checks_gotmessage(client);
} else {
/*
* smtp quote
*/
memmove(&clients[client].message.s[clients[client].message_point],&clients[client].message.s[clients[client].message_point+1],clients[client].message.l - clients[client].message_point - 1);
clients[client].message.l--;
}
}
}
return 1;
}
int smtp_reject(int client, const char *facility, const char *logmessage, const char *clientmessage, int score, int hard, void *var1, void *var2) {
static char buffer[1024];
int i,j;
int code;
firestring_snprintf(buffer,1024,logmessage,var1,var2);
if (hard == 1) {
fprintf(stderr,"{%d} (%d) %s/REJECT: %s\n",process,client,facility,buffer);
i = firestring_snprintf(buffer,1024,clientmessage,var1,var2);
tls_client_write(client,buffer,i);
clients[client].in_data = 0;
clients[client].have_from = 0;
clients[client].num_to = 0;
clients[client].send_progress = 0;
smtp_message_clear(client);
return 1;
}
fprintf(stderr,"{%d} (%d) %s/WARNING: %s\n",process,client,facility,buffer);
clients[client].score += score;
clients[client].points[clients[client].num_warnings] = score;
firestring_estr_sprintf(&clients[client].warnings[clients[client].num_warnings++],&clientmessage[17],var1,var2);
if (clients[client].score >= clients[client].profile->reject_score || clients[client].num_warnings == SMTP_MAX_WARNINGS - 1) {
if (clients[client].score >= clients[client].profile->reject_score) {
fprintf(stderr,"{%d} (%d) %s/REJECT: Message score (%d) has reached or exceeded maximum (%d)\n",process,client,facility,clients[client].score,clients[client].profile->reject_score);
code = atoi(clientmessage);
i = firestring_snprintf(buffer,1024,"%d-MessageWall: Message score (%d) has reached or exceeded maximum (%d):\r\n",code,clients[client].score,clients[client].profile->reject_score);
tls_client_write(client,buffer,i);
} else {
fprintf(stderr,"{%d} (%d) %s/REJECT: Number of warnings (%d) has reached maximum\n",process,client,facility,clients[client].num_warnings);
i = firestring_snprintf(buffer,1024,"552-MessageWall: Number of warnings (%d) has reached maximum:\r\n",clients[client].num_warnings);
tls_client_write(client,buffer,i);
code = 552;
}
for (j = 0; j < clients[client].num_warnings; j++) {
i = firestring_snprintf(buffer,1024,"%d-%5d %e",code,clients[client].points[j],&clients[client].warnings[j]);
tls_client_write(client,buffer,i);
}
i = firestring_snprintf(buffer,1024,"%d MessageWall: This message is being rejected\r\n",code);
tls_client_write(client,buffer,i);
clients[client].in_data = 0;
clients[client].have_from = 0;
clients[client].num_to = 0;
clients[client].send_progress = 0;
smtp_message_clear(client);
return 1;
}
return 0;
}
int smtp_checks_gotmessage(int client) {
int numparts;
/*
* clear dnsdcc queries for client
*/
dnsdcc_clear_queries(client);
if (clients[client].bare_lf == 1) {
fprintf(stderr,"{%d} (%d) SMTP/REJECT: delayed reject from bare LF or 8bit error\n",process,client);
clients[client].in_data = 0;
clients[client].have_from = 0;
clients[client].num_to = 0;
clients[client].send_progress = 0;
return 0;
}
/*
* parse MIME parts
*/
numparts = -1;
if (mime_parse(client, 0,&numparts,-1,&clients[client].message,clients[client].profile) == 1)
return 0;
if (clients[client].parts[0].visible == 0) {
/*
* useless
*/
smtp_reject(client,"MIME","message contains no parts accepted at this address",SMTP_USELESS,1,1,NULL,NULL);
return 0;
}
/*
* to_cc check
*/
if (clients[client].profile->to_cc_check == 1 && rfc822_to_cc_check(&clients[client].parts[0].message,&clients[client].to[0]) != 0) {
if (smtp_reject(client,"RFC822","%e: envelope forward path not in To/CC",SMTP_TO_CC,clients[client].profile->to_cc_score,0,&clients[client].to[0],NULL) != 0)
return 0;
}
/*
* from check
*/
if (clients[client].profile->from_check == 1 && rfc822_from_check(&clients[client].parts[0].message,&clients[client].from) != 0) {
if (smtp_reject(client,"RFC822","%e: envelope reverse path not in From",SMTP_FROM,clients[client].profile->from_score,0,&clients[client].from,NULL) != 0)
return 0;
}
/*
* realname check
*/
if (clients[client].profile->realname_check == 1 && rfc822_realname_check(&clients[client].parts[0].message) != 0) {
struct firestring_estr_t *value;
value = rfc822_header_value(&clients[client].parts[0].message,"From:");
if (smtp_reject(client,"RFC822","%e: From: field does not contain a real name",SMTP_REALNAME,clients[client].profile->realname_score,0,value,NULL) != 0)
return 0;
}
/*
* header rejection checks
*/
if (rfc822_header_reject_check(client) == 1 || rfc822_header_rejecti_check(client) == 1)
return 0;
if (rmx_check_profile(client) != -1)
return smtp_checks_gotRMX(client,0);
else {
clients[client].send_progress = PROGRESS_WAITRMX;
return 0;
}
}
int smtp_checks_gotRMX(int client, int timedout) {
if (rmx_check_profile(client) != 0) {
if (timedout == 1) {
if (smtp_reject(client,"RMX","%e: reverse path domain has no MX or A records (temporary)",SMTP_RMX_TEMP,clients[client].profile->rmx_score,0,&clients[client].fromdomain,NULL) == 1)
return 0;
} else {
if (smtp_reject(client,"RMX","%e: reverse path domain has no MX or A records",SMTP_RMX,clients[client].profile->rmx_score,0,&clients[client].fromdomain,NULL) == 1)
return 0;
}
}
if (dnsbl_domain_check_profile(client,0) != -1)
return smtp_checks_gotDNSBL_DOMAIN(client);
else {
clients[client].send_progress = PROGRESS_WAITDNSBL_DOMAIN;
return 0;
}
}
int smtp_checks_gotDNSBL_DOMAIN(int client) {
if (dnsbl_domain_check_profile(client,1) == 1)
return 0;
if (rdns_check_profile(client) != -1)
return smtp_checks_gotRDNS(client,0);
else {
clients[client].send_progress = PROGRESS_WAITRDNS;
return 0;
}
}
int smtp_checks_gotRDNS(int client, int timedout) {
if (rdns_check_profile(client) != 0) {
if (timedout == 1) {
if (smtp_reject(client,"RDNS","%s: IP has no reverse DNS (temporary)",SMTP_RDNS_TEMP,clients[client].profile->rdns_score,0,firedns_ntoa4(&clients[client].ip),NULL) == 1)
return 0;
} else {
if (smtp_reject(client,"RDNS","%s: IP has no reverse DNS",SMTP_RDNS,clients[client].profile->rdns_score,0,firedns_ntoa4(&clients[client].ip),NULL) == 1)
return 0;
}
}
if (dnsbl_check_profile(client,0) != -1)
return smtp_checks_gotDNSBL(client);
else {
clients[client].send_progress = PROGRESS_WAITDNSBL;
return 0;
}
}
int smtp_checks_gotDNSBL(int client) {
if (dnsbl_check_profile(client,1) == 1)
return 0;
if (dnsdcc_check_profile(client,0) != -1) {
return smtp_checks_gotDNSDCC(client);
} else {
clients[client].send_progress = PROGRESS_WAITDNSDCC;
return 0;
}
}
int smtp_checks_gotDNSDCC(int client) {
if (dnsdcc_check_profile(client,1) == 1)
return 0;
clients[client].send_progress = 0;
return smtp_start_send(client);
}
int smtp_start_send(int client) {
int i,o;
/*
* reserve a backend
*/
i = smtp_get_backend();
if (i == -1) {
smtp_reject(client,"BACKEND","none available to receive message",SMTP_NO_BACKEND,1,1,NULL,NULL);
return 0;
}
/*
* build the message
*/
backends[i].message.l = 0;
o = 0;
mime_build_message(client,i,&o);
/*
* it passes, start sending it
*/
client_start(i,client);
clients[client].in_data = 0;
clients[client].have_from = 0;
return 0;
}
/*
* checks before allowing message transmission to begin
* (determine DATA response)
*/
int smtp_checks_gotDATA(int client) {
if (clients[client].profile->reject == 1) {
smtp_reject(client,"PROFILE","reject set in profile",SMTP_REJECT,1,1,NULL,NULL);
return 1;
}
return 0;
}
int smtp_handle_line(int client, struct firestring_estr_t *line) {
static struct firestring_estr_t outline = {0};
struct firestring_estr_t rcptdomain;
struct firestring_estr_t username, password;
int i;
int atsign;
int l;
int j;
if (outline.a == 0)
firestring_estr_alloc(&outline,SMTP_LINE_MAXLEN);
time(&clients[client].lasttalk);
if (clients[client].auth_status != AUTH_STATUS_NONE) {
switch (clients[client].auth_status) {
case AUTH_STATUS_LOGIN_USER:
if (mime_base64(line,&clients[client].username) == 1) {
clients[client].username.l = 0;
clients[client].auth_status = AUTH_STATUS_NONE;
fprintf(stderr,"{%d} (%d) AUTH/FAIL: Bad credential format\n",process,client);
tls_client_write(client,SMTP_AUTH_BADFORMAT,sizeof(SMTP_AUTH_BADFORMAT) - 1);
return 0;
}
clients[client].auth_status = AUTH_STATUS_LOGIN_PASS;
tls_client_write(client,SMTP_AUTH_LOGIN_PASS,sizeof(SMTP_AUTH_LOGIN_PASS) - 1);
break;
case AUTH_STATUS_LOGIN_PASS:
clients[client].auth_status = AUTH_STATUS_NONE;
if (mime_base64(line,&clients[client].password) == 1) {
clients[client].username.l = 0;
fprintf(stderr,"{%d} (%d) AUTH/FAIL: Bad credential format\n",process,client);
tls_client_write(client,SMTP_AUTH_BADFORMAT,sizeof(SMTP_AUTH_BADFORMAT) - 1);
return 0;
}
firestring_estr_0(&clients[client].username);
if (auth_check_password(&clients[client].username,&clients[client].password) == 0) {
fprintf(stderr,"{%d} (%d) AUTH/SUCCEED: Password accepted via LOGIN for '%s'\n",process,client,clients[client].username.s);
tls_client_write(client,SMTP_AUTH_SUCCEED,sizeof(SMTP_AUTH_SUCCEED) - 1);
clients[client].can_relay = 1;
} else {
clients[client].username.l = 0;
fprintf(stderr,"{%d} (%d) AUTH/FAIL: Password rejected via LOGIN for '%s'\n",process,client,clients[client].username.s);
tls_client_write(client,SMTP_AUTH_FAIL,sizeof(SMTP_AUTH_FAIL) - 1);
}
memset(clients[client].password.s,0,clients[client].password.l);
return 0;
break;
case AUTH_STATUS_PLAIN:
clients[client].auth_status = AUTH_STATUS_NONE;
/*
* ignore anything before the first nil
*/
auth_plain_jumpin:
if (mime_base64(line,&outline) == 1) {
fprintf(stderr,"{%d} (%d) AUTH/FAIL: Bad credential format\n",process,client);
tls_client_write(client,SMTP_AUTH_BADFORMAT,sizeof(SMTP_AUTH_BADFORMAT) - 1);
return 0;
}
i = firestring_estr_strchr(&outline,'\0',0);
if (i == -1) {
fprintf(stderr,"{%d} (%d) AUTH/FAIL: Bad credential format\n",process,client);
tls_client_write(client,SMTP_AUTH_BADFORMAT,sizeof(SMTP_AUTH_BADFORMAT) - 1);
return 0;
}
i++;
j = firestring_estr_strchr(&outline,'\0',i);
if (j == -1) {
fprintf(stderr,"{%d} (%d) AUTH/FAIL: Bad credential format\n",process,client);
tls_client_write(client,SMTP_AUTH_BADFORMAT,sizeof(SMTP_AUTH_BADFORMAT) - 1);
return 0;
}
username.l = username.a = j - i;
username.s = &outline.s[i];
j++;
password.l = password.a = outline.l - j;
password.s = &outline.s[j];
if (auth_check_password(&username,&password) == 0) {
firestring_estr_estrcpy(&clients[client].username,&username,0);
fprintf(stderr,"{%d} (%d) AUTH/SUCCEED: Password accepted via PLAIN for '%s'\n",process,client,username.s);
tls_client_write(client,SMTP_AUTH_SUCCEED,sizeof(SMTP_AUTH_SUCCEED) - 1);
clients[client].can_relay = 1;
} else {
fprintf(stderr,"{%d} (%d) AUTH/FAIL: Password rejected via PLAIN for '%s'\n",process,client,username.s);
tls_client_write(client,SMTP_AUTH_FAIL,sizeof(SMTP_AUTH_FAIL) - 1);
}
memset(password.s,0,password.l);
return 0;
break;
}
return 0;
}
if (firestring_estr_starts(line,"HELO ") == 0) {
SMTP_RESET
tls_client_write(client,SMTP_OK,sizeof(SMTP_OK) - 1);
} else if (firestring_estr_starts(line,"EHLO ") == 0) {
SMTP_RESET
firestring_estr_sprintf(&outline,"250-MessageWall: Hello\r\n"
"250-PIPELINING\r\n"
"%s"
"%s"
"%s"
"250 SIZE %l\r\n",
sevenbit == 1 ? "" : "250-8BITMIME\r\n",
relay_auth == NULL ? "" : "250-AUTH LOGIN PLAIN\r\n"
"250-AUTH=LOGIN PLAIN\r\n",
cert == NULL ? "" : "250-STARTTLS\r\n",
max_message_size);
tls_client_write(client,outline.s,outline.l);
} else if (firestring_estr_strcasecmp(line,"STARTTLS") == 0) {
if (cert != NULL && tls_start(client) == 0) {
fprintf(stderr,"{%d} (%d) TLS/STATUS: TLS negotiation started\n",process,client);
tls_client_write(client,SMTP_TLS_OK,sizeof(SMTP_TLS_OK) - 1);
} else {
fprintf(stderr,"{%d} (%d) TLS/STATUS: TLS startup failed\n",process,client);
tls_client_write(client,SMTP_TLS_ERROR,sizeof(SMTP_TLS_ERROR) - 1);
}
} else if (firestring_estr_strcasecmp(line,"AUTH LOGIN") == 0) {
tls_client_write(client,SMTP_AUTH_LOGIN_USER,sizeof(SMTP_AUTH_LOGIN_USER) - 1);
clients[client].auth_status = AUTH_STATUS_LOGIN_USER;
} else if (firestring_estr_starts(line,"AUTH PLAIN") == 0) {
if (line->l == 10) {
tls_client_write(client,SMTP_AUTH_PLAIN,sizeof(SMTP_AUTH_PLAIN) - 1);
clients[client].auth_status = AUTH_STATUS_PLAIN;
} else {
line->s += 10;
line->l -= 10;
goto auth_plain_jumpin;
}
} else if (firestring_estr_starts(line,"MAIL FROM:") == 0) {
if (clients[client].have_from != 0) {
tls_client_write(client,SMTP_OOO,sizeof(SMTP_OOO) - 1);
return 0;
}
smtp_message_clear(client);
/*
* find end of address
*/
j = firestring_estr_strchr(line,'<',10);
if (j == -1) {
fprintf(stderr,"{%d} (%d) SMTP/REJECT: no < in MAIL FROM line\n",process,client);
if (smtp_error(client) == 0) {
SMTP_RESET
tls_client_write(client,SMTP_INVALIDPATH,sizeof(SMTP_INVALIDPATH) - 1);
return 0;
} else
return 1;
}
j++;
l = firestring_estr_strchr(line,'>',j);
if (l == -1) {
fprintf(stderr,"{%d} (%d) SMTP/REJECT: no > in MAIL FROM line\n",process,client);
if (smtp_error(client) == 0) {
SMTP_RESET
tls_client_write(client,SMTP_INVALIDPATH,sizeof(SMTP_INVALIDPATH) - 1);
return 0;
} else
return 1;
}
/*
* validate character set
*/
atsign = 0;
clients[client].fromdomain.l = 0;
for (i = j; i < l; i++) {
/*
* RFC violation:
* restrict to one @ sign
*/
if (line->s[i] == '@') {
firestring_estr_estrcpy(&clients[client].fromdomain,line,i+1);
clients[client].fromdomain.l -= (line->l - l);
atsign++;
}
if (strchr(path_charset,line->s[i]) == NULL || atsign == 2) {
/*
* invalid character
*/
firestring_estr_0(line);
fprintf(stderr,"{%d} (%d) SMTP/REJECT: %s: invalid MAIL character (%c)\n",process,client,line->s,line->s[i]);
tls_client_write(client,SMTP_BADCHAR,sizeof(SMTP_BADCHAR) - 1);
return 0;
}
}
firestring_estr_0(&clients[client].fromdomain);
if (l - 11 > SMTP_PATH_MAXLEN || l - j > clients[client].from.a) {
fprintf(stderr,"{%d} (%d) SMTP/REJECT: MAIL path too long\n",process,client);
tls_client_write(client,SMTP_BADCHAR,sizeof(SMTP_BADCHAR) - 1);
return 0;
}
/*
* we just checked this size; this is safe
*/
memcpy(clients[client].from.s,&line->s[j],l - j); /* ITS4: ignore memcpy */
clients[client].from.l = l - j;
clients[client].have_from = 1;
tls_client_write(client,SMTP_OK,sizeof(SMTP_OK) - 1);
rmx_send_queries(client);
dnsbl_domain_send_queries(client);
} else if (firestring_estr_starts(line,"RCPT TO:") == 0 && firestring_estr_ends(line,">") == 0) {
if (clients[client].have_from == 0) {
tls_client_write(client,SMTP_OOO,sizeof(SMTP_OOO) - 1);
return 0;
}
/*
* find end of address
*/
j = firestring_estr_strchr(line,'<',8);
if (j == -1) {
/*
* no < sign
*/
fprintf(stderr,"{%d} (%d) SMTP/REJECT: no < in RCPT TO line\n",process,client);
if (smtp_error(client) == 0) {
SMTP_RESET
tls_client_write(client,SMTP_INVALIDPATH,sizeof(SMTP_INVALIDPATH) - 1);
return 0;
} else
return 1;
}
j++;
l = firestring_estr_strchr(line,'>',j);
if (l == -1) {
fprintf(stderr,"{%d} (%d) SMTP/REJECT: no > in RCPT TO line\n",process,client);
if (smtp_error(client) == 0) {
SMTP_RESET
tls_client_write(client,SMTP_INVALIDPATH,sizeof(SMTP_INVALIDPATH) - 1);
return 0;
} else
return 1;
}
/*
* check that we have recipient space
*/
if (clients[client].can_relay == 1) {
if (clients[client].num_to == max_rcpt) {
fprintf(stderr,"{%d} (%d) SMTP/REJECT: too many RCPT\n",process,client);
SMTP_RESET
tls_client_write(client,SMTP_MAXRCPT,sizeof(SMTP_MAXRCPT) - 1);
return 0;
}
} else {
if (clients[client].num_to == 1) {
fprintf(stderr,"{%d} (%d) SMTP/TEMPORARY: external host attempted multiple recipient delivery, asked for one at a time\n",process,client);
tls_client_write(client,SMTP_ONLYONE,sizeof(SMTP_ONLYONE) - 1);
return 0;
}
}
/*
* validate character set
*/
atsign = 0;
for (i = j; i < l; i++) {
/*
* RFC violation:
* restrict to one @ sign
*/
if (line->s[i] == '@')
atsign++;
if (strchr(path_charset,line->s[i]) == NULL || atsign == 2) {
/*
* invalid character
*/
firestring_estr_0(line);
fprintf(stderr,"{%d} (%d) SMTP/REJECT: %s: invalid RCPT character (%c)\n",process,client,line->s,line->s[i]);
tls_client_write(client,SMTP_BADCHAR,sizeof(SMTP_BADCHAR) - 1);
return 0;
}
}
/*
* one @ sign is required
*/
if (atsign != 1) {
fprintf(stderr,"{%d} (%d) SMTP/REJECT: too many @ signs in RCPT\n",process,client);
tls_client_write(client,SMTP_BADCHAR,sizeof(SMTP_BADCHAR) - 1);
return 0;
}
/*
* if client isn't permitted to relay, check for local domain
*/
if (clients[client].can_relay == 0) {
i = firestring_estr_strchr(line,'@',j);
rcptdomain.s = &line->s[i + 1];
rcptdomain.a = rcptdomain.l = l - i - 1;
if (dbase_domain_is_local(&rcptdomain) != 0) {
smtp_reject(client,"SMTP","relaying denied to '%e'",SMTP_NORELAY,1,1,&rcptdomain,NULL);
return 0;
}
}
if (l - j > SMTP_PATH_MAXLEN || l - j > clients[client].to[clients[client].num_to].a) {
fprintf(stderr,"{%d} (%d) SMTP/REJECT: RCPT path too long\n",process,client);
tls_client_write(client,SMTP_BADCHAR,sizeof(SMTP_BADCHAR) - 1);
return 0;
}
/*
* we just checked this length; this is safe
*/
memcpy(clients[client].to[clients[client].num_to].s,&line->s[j],l - j); /* ITS4: ignore memcpy */
clients[client].to[clients[client].num_to].l = l - j;
clients[client].num_to++;
tls_client_write(client,SMTP_OK,sizeof(SMTP_OK) - 1);
} else if (firestring_estr_strcasecmp(line,"DATA") == 0) {
struct firestring_estr_t blank = {0};
struct tm *timestruct;
time_t temptime;
if (clients[client].have_from == 0 || clients[client].num_to == 0) {
tls_client_write(client,SMTP_OOO,sizeof(SMTP_OOO) - 1);
return 0;
}
/*
* discover the profile that will apply to this message
*/
if (clients[client].can_relay == 1) {
clients[client].profile = profile_relay;
} else {
/*
* delivery to local user from outside
*/
unsigned int u;
int atsign;
struct messagewall_special_user_t *special_user;
atsign = firestring_estr_strchr(&clients[client].to[0],'@',0);
atsign++;
u = dbase_text_hash(&clients[client].to[0],0);
special_user = special_user_hash[u];
while (special_user != NULL) {
if (firestring_estr_estrcasecmp(&clients[client].to[0],&special_user->address,0) == 0)
break;
special_user = special_user->next;
}
if (special_user == NULL) {
/*
* no specific address match, check for domain match
*/
u = dbase_text_hash(&clients[client].to[0],atsign);
special_user = special_user_hash[u];
while (special_user != NULL) {
if (firestring_estr_estrcasecmp(&clients[client].to[0],&special_user->address,atsign) == 0)
break;
special_user = special_user->next;
}
}
if (special_user == NULL)
/*
* not a special user
*/
clients[client].profile = profile_default;
else
clients[client].profile = special_user->profile;
}
fprintf(stderr,"{%d} (%d) SMTP/STATUS: Message start: %s\n",process,client,clients[client].profile->name);
firestring_estr_0(&clients[client].from);
fprintf(stderr,"{%d} (%d) SMTP/INFO: From: %s\n",process,client,clients[client].from.s);
for (i = 0; i < clients[client].num_to; i++) {
firestring_estr_0(&clients[client].to[i]);
fprintf(stderr,"{%d} (%d) SMTP/INFO: To: %s\n",process,client,clients[client].to[i].s);
}
clients[client].num_warnings = 0;
clients[client].score = 0;
clients[client].bare_lf = 0;
/*
* run prechecks
*/
if (smtp_checks_gotDATA(client) != 0)
return 0;
clients[client].in_data = 1;
tls_client_write(client,SMTP_START_DATA,sizeof(SMTP_START_DATA) - 1);
time(&temptime);
timestruct = gmtime(&temptime);
clients[client].message_point = firestring_estr_sprintf(&clients[client].message,"Received: from [%s] %s%e%sby %s (MessageWall " MESSAGEWALL_VERSION ") with SMTP%s%s%s; %d %s %d %02d:%02d:%02d -0000\r\n",firedns_ntoa4(&clients[client].ip),
clients[client].username.l == 0 ? "" : "(authenticated as ",
clients[client].username.l == 0 ? &blank : &clients[client].username,
clients[client].username.l == 0 ? "" : ") ",
domain,
clients[client].ssl == NULL ? "" : " (encrypted with ",
clients[client].ssl == NULL ? "" : SSL_get_cipher(clients[client].ssl),
clients[client].ssl == NULL ? "" : ")",
timestruct->tm_mday,months[timestruct->tm_mon],1900 + timestruct->tm_year,timestruct->tm_hour,timestruct->tm_min,timestruct->tm_sec);
} else if (firestring_estr_strcasecmp(line,"RSET") == 0) {
SMTP_RESET
tls_client_write(client,SMTP_OK,sizeof(SMTP_OK) - 1);
} else if (firestring_estr_strcasecmp(line,"NOOP") == 0) {
tls_client_write(client,SMTP_OK,sizeof(SMTP_OK) - 1);
} else if (firestring_estr_starts(line,"VRFY ") == 0) {
fprintf(stderr,"{%d} (%d) SMTP/STATUS: VRFY from client (refused)\n",process,client);
tls_client_write(client,SMTP_VRFY,sizeof(SMTP_VRFY) - 1);
} else if (firestring_estr_strcasecmp(line,"QUIT") == 0) {
fprintf(stderr,"{%d} (%d) SMTP/FATAL: client QUIT\n",process,client);
firestring_estr_sprintf(&outline,"221 %s MessageWall: Goodbye\r\n",domain);
tls_client_write(client,outline.s,outline.l);
smtp_clear(client);
return 1;
} else {
firestring_estr_sprintf(&outline,"{%d} (%d) SMTP/STATUS: Invalid command: '%e'\n",process,client,line);
write(2,outline.s,outline.l);
if (smtp_error(client) == 0) {
firestring_estr_sprintf(&outline,"500 MessageWall: Unrecognized command\r\n");
tls_client_write(client,outline.s,outline.l);
} else
return 1;
}
return 0;
}
int smtp_get_backend() {
int i;
for (i = 0; i < max_backends; i++)
if (backends[i].state == BACKEND_STATE_IDLE)
return i;
return -1;
}
int smtp_error(int client) {
clients[client].errors++;
if (clients[client].errors > max_errors) {
fprintf(stderr,"{%d} (%d) SMTP/FATAL: too many errors\n",process,client);
tls_client_write(client,SMTP_ERRORS,sizeof(SMTP_ERRORS) - 1);
smtp_clear(client);
return 1;
}
return 0;
}
int smtp_clear(int client) {
int i;
smtp_message_clear(client);
if (clients[client].rdns_fd >= 0) {
firedns_getresult(clients[client].rdns_fd);
clients[client].rdns_fd = -1;
}
for (i = 0; i < dnsbls; i++) {
if (clients[client].dnsbl_fd[i] >= 0) {
firedns_getresult(clients[client].dnsbl_fd[i]);
clients[client].dnsbl_fd[i] = -1;
}
}
if (clients[client].ssl != NULL) {
SSL_free(clients[client].ssl);
clients[client].ssl = NULL;
}
if (clients[client].fd != -1) {
close(clients[client].fd);
clients[client].fd = -1;
}
return 0;
}
int smtp_message_clear(int client) {
int i,j;
if (clients[client].rmx_fd >= 0) {
firedns_getresult(clients[client].rmx_fd);
clients[client].rmx_fd = -1;
}
if (clients[client].rmx_a_fd >= 0) {
firedns_getresult(clients[client].rmx_a_fd);
clients[client].rmx_a_fd = -1;
}
for (i = 0; i < dnsbls_domain; i++) {
if (clients[client].dnsbl_domain_fd[i] >= 0) {
firedns_getresult(clients[client].dnsbl_domain_fd[i]);
clients[client].dnsbl_domain_fd[i] = -1;
}
}
for (i = 0; i < max_parts; i++) {
for (j = 0; j < dnsdccs; j++) {
if (clients[client].dnsdcc_fd[i][j] >= 0) {
firedns_getresult(clients[client].dnsdcc_fd[i][j]);
clients[client].dnsdcc_fd[i][j] = -1;
}
}
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1