Newer
Older
static BOOL smtp_splittag(char *in, char **name, char **tag)
{
char *last;
if(in==NULL)
return(FALSE);
*name=strtok_r(in, "#", &last);
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
if(*name) {
*tag=strtok_r(NULL, "", &last);
return(TRUE);
}
return(FALSE);
}
static uint smtp_matchuser(scfg_t *scfg, char *str, BOOL aliases, BOOL datdupe)
{
char *user=strdup(str);
char *name;
char *tag=NULL;
uint usernum=0;
if(!user)
return(0);
if(!smtp_splittag(user, &name, &tag))
goto end;
if(datdupe)
usernum=userdatdupe(scfg, 0, U_NAME, LEN_NAME, name, FALSE);
else
usernum=matchuser(scfg, name, aliases);
if(!usernum)
goto end;
if(checktag(scfg, tag, usernum))
usernum=UINT_MAX;
end:
free(user);
return(usernum);
}
static void smtp_thread(void* arg)
{
int i,j;
char path[MAX_PATH+1];
char value[INI_MAX_VALUE_LEN];
str_list_t sec_list;
char* section;
char buf[1024],*p,*tp,*cp;
char hdrfield[512];
char alias_buf[128];
char reverse_path[128];
char date[64];
char qwkid[32];
char rcpt_name[128];
char rcpt_addr[128];

rswindell
committed
char sender[128];
char sender_addr[128];
char user_name[LEN_ALIAS+1];
char user_pass[LEN_PASS+1];
char relay_list[MAX_PATH+1];
char domain_list[MAX_PATH+1];
char spam_bait[MAX_PATH+1];
char spam_block[MAX_PATH+1];
char host_name[128];
char host_ip[64];
char dnsbl_ip[64];

rswindell
committed
char* telegram_buf;
char* msgbuf;
char challenge[256];
char response[128];
char secret[64];
char md5_data[384];
char digest[MD5_DIGEST_SIZE];
char dest_host[128];
char* errmsg;
ushort dest_port;
ushort hfield_type;
ushort agent;
ulong hdr_lines=0;
ulong hdr_len=0;
ulong badcmds=0;

rswindell
committed
BOOL telegram=FALSE;
BOOL forward=FALSE;
BOOL no_forward=FALSE;
BOOL auth_login;
BOOL routed=FALSE;
BOOL dnsbl_recvhdr;
BOOL msg_handled;
uint subnum=INVALID_SUB;
char msgtxt_fname[MAX_PATH+1];
char newtxt_fname[MAX_PATH+1];
char logtxt_fname[MAX_PATH+1];
char rcptlst_fname[MAX_PATH+1];
ushort rcpt_count=0;
FILE* proc_err;
char proc_err_fname[MAX_PATH+1];
char session_id[MAX_PATH+1];
FILE* spy=NULL;
SOCKET socket;
HOSTENT* host;
int smb_error;
smb_t smb;
smbmsg_t msg;
smbmsg_t newmsg;
user_t user;
user_t relay_user;

rswindell
committed
node_t node;
client_t client;
smtp_t smtp=*(smtp_t*)arg;
SOCKADDR_IN server_addr;
IN_ADDR dnsbl_result;
JSRuntime* js_runtime=NULL;
JSContext* js_cx=NULL;
JSObject* js_glob=NULL;
enum {
SMTP_STATE_INITIAL
,SMTP_STATE_HELO
,SMTP_STATE_DATA_HEADER
,SMTP_STATE_DATA_BODY
} state = SMTP_STATE_INITIAL;
enum {
SMTP_CMD_NONE
,SMTP_CMD_MAIL
,SMTP_CMD_SEND
,SMTP_CMD_SOML
,SMTP_CMD_SAML
} cmd = SMTP_CMD_NONE;
SetThreadName("SMTP");
thread_up(TRUE /* setuid */);
free(arg);
socket=smtp.socket;
lprintf(LOG_DEBUG,"%04d SMTP Session thread started", socket);
if(startup->inbound_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->inbound_sound, NULL, SND_ASYNC|SND_FILENAME);
addr_len=sizeof(server_addr);
if((i=getsockname(socket, (struct sockaddr *)&server_addr,&addr_len))!=0) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%d) getting address/port"
sockprintf(socket,sys_error);
mail_close_socket(socket);
thread_down();
if((mailproc_to_match=alloca(sizeof(BOOL)*mailproc_count))==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR allocating memory for mailproc_to_match", socket);
sockprintf(socket,sys_error);
mail_close_socket(socket);
thread_down();
return;
}
memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count);
memset(&smb,0,sizeof(smb));

rswindell
committed
memset(&msg,0,sizeof(msg));
memset(&user,0,sizeof(user));
memset(&relay_user,0,sizeof(relay_user));
SAFECOPY(host_ip,inet_ntoa(smtp.client_addr.sin_addr));
lprintf(LOG_INFO,"%04d SMTP Connection accepted on port %u from: %s port %u"
,socket, BE_INT16(server_addr.sin_port), host_ip, ntohs(smtp.client_addr.sin_port));
if(startup->options&MAIL_OPT_NO_HOST_LOOKUP)
host=NULL;
else
host=gethostbyaddr ((char *)&smtp.client_addr.sin_addr
if(host!=NULL && host->h_name!=NULL)
else
strcpy(host_name,"<no name>");
if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP)) {
lprintf(LOG_INFO,"%04d SMTP Hostname: %s", socket, host_name);
for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++)
lprintf(LOG_INFO,"%04d SMTP HostAlias: %s", socket, host->h_aliases[i]);
#if 0
if(host!=NULL) {
ip=resolve_ip(host_name);
if(ip!=smtp.client_addr.sin_addr.s_addr) {
smtp.client_addr.sin_addr.s_addr=ip;
lprintf(LOG_WARNING,"%04d !SMTP DNS/IP ADDRESS MISMATCH: %s vs %s"
,socket, inet_ntoa(smtp.client_addr.sin_addr), host_ip);
sockprintf(socket,"550 DNS and IP address mismatch");
mail_close_socket(socket);
thread_down();
return;
}
}
#endif
active_clients++, update_clients();
SAFECOPY(hello_name,host_name);
sprintf(spam_bait,"%sspambait.cfg",scfg.ctrl_dir);
sprintf(spam_block,"%sspamblock.cfg",scfg.ctrl_dir);
if(smtp.client_addr.sin_addr.s_addr==server_addr.sin_addr.s_addr
|| smtp.client_addr.sin_addr.s_addr==htonl(IPv4_LOCALHOST)) {
/* local connection */
dnsbl_result.s_addr=0;
} else {
if(trashcan(&scfg,host_ip,"ip") || findstr(host_ip,spam_block)) {
lprintf(LOG_NOTICE,"%04d !SMTP CLIENT BLOCKED in ip.can: %s"
,socket, host_ip);
sockprintf(socket,"550 Access denied.");
mail_close_socket(socket);
thread_down();
if(active_clients)
active_clients--, update_clients();
return;
}
if(trashcan(&scfg,host_name,"host") || findstr(host_name,spam_block)) {
lprintf(LOG_NOTICE,"%04d !SMTP CLIENT BLOCKED in host.can: %s"
,socket, host_name);
sockprintf(socket,"550 Access denied.");
mail_close_socket(socket);
thread_down();
if(active_clients)
active_clients--, update_clients();
/* SPAM Filters (mail-abuse.org) */
dnsbl_result.s_addr = dns_blacklisted(socket,smtp.client_addr.sin_addr,host_name,dnsbl,dnsbl_ip);
if(dnsbl_result.s_addr) {
lprintf(LOG_NOTICE,"%04d !SMTP BLACKLISTED SERVER on %s: %s [%s] = %s"
,socket, dnsbl, host_name, dnsbl_ip, inet_ntoa(dnsbl_result));
if(startup->options&MAIL_OPT_DNSBL_REFUSE) {
SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
spamlog(&scfg, "SMTP", "SESSION REFUSED", str, host_name, dnsbl_ip, NULL, NULL);
sockprintf(socket
,"550 Mail from %s refused due to listing at %s"
,dnsbl_ip, dnsbl);
mail_close_socket(socket);
lprintf(LOG_NOTICE,"%04d !SMTP REFUSED SESSION from blacklisted server"
,socket);
thread_down();
if(active_clients)
active_clients--, update_clients();
return;
}
}
sprintf(smb.file,"%smail",scfg.data_dir);
if(smb_islocked(&smb)) {
lprintf(LOG_WARNING,"%04d !SMTP MAIL BASE LOCKED: %s"
,socket, smb.last_error);
sockprintf(socket,sys_unavail);
mail_close_socket(socket);
thread_down();
if(active_clients)
active_clients--, update_clients();
return;
}
srand(time(NULL) ^ (DWORD)GetCurrentThreadId()); /* seed random number generator */
rand(); /* throw-away first result */
SAFEPRINTF4(session_id,"%x%x%x%lx",getpid(),socket,rand(),clock());
SAFEPRINTF2(msgtxt_fname,"%sSBBS_SMTP.%s.msg", scfg.temp_dir, session_id);
SAFEPRINTF2(newtxt_fname,"%sSBBS_SMTP.%s.new", scfg.temp_dir, session_id);
SAFEPRINTF2(logtxt_fname,"%sSBBS_SMTP.%s.log", scfg.temp_dir, session_id);
SAFEPRINTF2(rcptlst_fname,"%sSBBS_SMTP.%s.lst", scfg.temp_dir, session_id);
rcptlst=fopen(rcptlst_fname,"w+");
if(rcptlst==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d creating recipient list: %s"
,socket, errno, rcptlst_fname);
sockprintf(socket,sys_error);

rswindell
committed
mail_close_socket(socket);
if(active_clients)
active_clients--, update_clients();
if(trashcan(&scfg,host_name,"smtpspy")
|| trashcan(&scfg,host_ip,"smtpspy")) {
sprintf(str,"%ssmtpspy.txt", scfg.logs_dir);
spy=fopen(str,"a");
}
/* Initialize client display */
client.size=sizeof(client);
client.time=time(NULL);
SAFECOPY(client.addr,host_ip);
SAFECOPY(client.host,host_name);
client.port=ntohs(smtp.client_addr.sin_port);
client.protocol="SMTP";
client.user="<unknown>";
client_on(socket,&client,FALSE /* update */);
SAFEPRINTF(str,"SMTP: %s",host_ip);
sockprintf(socket,"220 %s Synchronet SMTP Server %s-%s Ready"
,startup->host_name,revision,PLATFORM_DESC);
while(1) {
rd = sockreadline(socket, buf, sizeof(buf));
if(rd<0)
truncsp(buf);
if(spy!=NULL)
fprintf(spy,"%s\n",buf);
if(relay_user.number==0 && dnsbl_result.s_addr && startup->options&MAIL_OPT_DNSBL_THROTTLE)
mswait(DNSBL_THROTTLE_VALUE);
if(state>=SMTP_STATE_DATA_HEADER) {
if(!strcmp(buf,".")) {

rswindell
committed
state=SMTP_STATE_HELO; /* RESET state machine here in case of error */

rswindell
committed
if(msgtxt==NULL) {
lprintf(LOG_ERR,"%04d !SMTP NO MESSAGE TEXT FILE POINTER?", socket);
sockprintf(socket,"554 No message text");
continue;
}
if(ftell(msgtxt)<1) {
lprintf(LOG_ERR,"%04d !SMTP INVALID MESSAGE LENGTH: %ld (%lu lines)"
, socket, ftell(msgtxt), lines);
sockprintf(socket,"554 No message text");
lprintf(LOG_INFO,"%04d SMTP End of message (body: %lu lines, %lu bytes, header: %lu lines, %lu bytes)"
, socket, lines, ftell(msgtxt)-hdr_len, hdr_lines, hdr_len);
/* Twit-listing (sender's name and e-mail addresses) here */
sprintf(path,"%stwitlist.cfg",scfg.ctrl_dir);
if(fexist(path) && (findstr(sender,path) || findstr(sender_addr,path))) {
lprintf(LOG_NOTICE,"%04d !SMTP FILTERING TWIT-LISTED SENDER: %s <%s>"
,socket, sender, sender_addr);
SAFEPRINTF2(tmp,"Twit-listed sender: %s <%s>", sender, sender_addr);
spamlog(&scfg, "SMTP", "REFUSED", tmp, host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket, "554 Sender not allowed.");
continue;
}
if(telegram==TRUE) { /* Telegram */
const char* head="\1n\1h\1cInstant Message\1n from \1h\1y";
const char* tail="\1n:\r\n\1h";
rewind(msgtxt);
length=filelength(fileno(msgtxt));
p=strchr(sender_addr,'@');
if(p==NULL || resolve_ip(p+1)!=smtp.client_addr.sin_addr.s_addr)
/* Append real IP and hostname if different */
safe_snprintf(str,sizeof(str),"%s%s\r\n\1w[\1n%s\1h] (\1n%s\1h)%s"
,head,sender_addr,host_ip,host_name,tail);
else
safe_snprintf(str,sizeof(str),"%s%s%s",head,sender_addr,tail);
if((telegram_buf=(char*)malloc(length+strlen(str)+1))==NULL) {
lprintf(LOG_CRIT,"%04d !SMTP ERROR allocating %lu bytes of memory for telegram from %s"
,socket,length+strlen(str)+1,sender_addr);
sockprintf(socket, insuf_stor);
continue;
}
strcpy(telegram_buf,str); /* can't use SAFECOPY here */
if(fread(telegram_buf+strlen(str),1,length,msgtxt)!=length) {
lprintf(LOG_ERR,"%04d !SMTP ERROR reading %lu bytes from telegram file"
,socket,length);
sockprintf(socket, insuf_stor);
free(telegram_buf);
continue;
}
telegram_buf[length+strlen(str)]=0; /* Need ASCIIZ */
/* Send telegram to users */
sec_list=iniReadSectionList(rcptlst,NULL); /* Each section is a recipient */
for(rcpt_count=0; sec_list!=NULL
&& sec_list[rcpt_count]!=NULL
&& rcpt_count<startup->max_recipients; rcpt_count++) {
section=sec_list[rcpt_count];
SAFECOPY(rcpt_name,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENT),"unknown",value));
usernum=iniReadInteger(rcptlst,section ,smb_hfieldtype(RECIPIENTEXT),0);
SAFECOPY(rcpt_addr,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENTNETADDR),rcpt_name,value));
if((i=putsmsg(&scfg,usernum,telegram_buf))==0)
lprintf(LOG_INFO,"%04d SMTP Created telegram (%ld/%u bytes) from %s to %s <%s>"
,socket, length, strlen(telegram_buf), sender_addr, rcpt_name, rcpt_addr);
else
lprintf(LOG_ERR,"%04d !SMTP ERROR %d creating telegram from %s to %s <%s>"
,socket, i, sender_addr, rcpt_name, rcpt_addr);
}
iniFreeStringList(sec_list);
free(telegram_buf);
sockprintf(socket,ok_rsp);
telegram=FALSE;
continue;
}
fclose(msgtxt), msgtxt=NULL;
fclose(rcptlst), rcptlst=NULL;
/* External Mail Processing here */
msg_handled=FALSE;
if(mailproc_count) {
SAFEPRINTF2(proc_err_fname,"%sSBBS_SMTP.%s.err", scfg.temp_dir, session_id);
remove(proc_err_fname);
for(i=0;i<mailproc_count;i++) {
if(!chk_ar(&scfg,mailproc_list[i].ar,&relay_user))
continue;
if(mailproc_list[i].to!=NULL && !mailproc_to_match[i])
continue;
if(mailproc_list[i].from!=NULL
&& !findstr_in_list(sender_addr, mailproc_list[i].from))
continue;
if(mailproc_list[i].host!=NULL
&& !findstr_in_list(host_name, mailproc_list[i].host))
continue;
if(mailproc_list[i].ip!=NULL
&& !findstr_in_list(host_ip, mailproc_list[i].ip))
continue;
if(!mailproc_list[i].passthru)
msg_handled=TRUE;
mailcmdstr(mailproc_list[i].cmdline
,msgtxt_fname, newtxt_fname, logtxt_fname
,rcptlst_fname, proc_err_fname
,host_name, host_ip, relay_user.number
,sender, sender_addr, reverse_path, str);
lprintf(LOG_INFO,"%04d SMTP Executing external mail processor: %s"
,socket, mailproc_list[i].name);
if(mailproc_list[i].native) {
lprintf(LOG_DEBUG,"%04d SMTP Executing external command: %s"
,socket, str);
if((j=system(str))!=0) {
lprintf(LOG_NOTICE,"%04d !SMTP system(%s) returned %d (errno: %d)"
,socket, str, j, errno);
if(mailproc_list[i].ignore_on_error) {
lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL due to mail processor (%s) error: %d"
,socket, mailproc_list[i].name, j);
msg_handled=TRUE;
}
}
} else { /* JavaScript */
if(!js_mailproc(socket, &client, &relay_user
,&mailproc_list[i]
,str /* cmdline */
,msgtxt_fname, newtxt_fname, logtxt_fname
,rcpt_addr
,rcptlst_fname, proc_err_fname
,sender, sender_addr, reverse_path, hello_name, &js_result
#if 0 /* calling exit() in a script causes js_mailproc to return FALSE */
lprintf(LOG_NOTICE,"%04d !SMTP JavaScript mailproc command (%s) failed (returned: %d)"
if(mailproc_list[i].ignore_on_error) {
lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL due to mail processor (%s) failure"
,socket, mailproc_list[i].name);
msg_handled=TRUE;
}
#endif
}
}
if(flength(proc_err_fname)>0)
break;
if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname))
break;
}
if(flength(proc_err_fname)>0
&& (proc_err=fopen(proc_err_fname,"r"))!=NULL) {
while(!feof(proc_err)) {
int n;
if(!fgets(str,sizeof(str),proc_err))
break;
truncsp(str);
lprintf(LOG_WARNING,"%04d !SMTP External mail processor (%s) error: %s"
,socket, mailproc_list[i].name, str);
n=atoi(str);
if(n>=100 && n<1000)
sockprintf(socket,"%s", str);
else
sockprintf(socket,"554%c%s"
,ftell(proc_err)<filelength(fileno(proc_err)) ? '-' : ' '
,str);
}
fclose(proc_err);
msg_handled=TRUE;
}
else if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname)) {
lprintf(LOG_NOTICE,"%04d SMTP External mail processor (%s) removed %s file"
,socket
,mailproc_list[i].name
,fexist(msgtxt_fname)==FALSE ? "message text" : "recipient list");
sockprintf(socket,ok_rsp);
msg_handled=TRUE;
}
else if(msg_handled)
sockprintf(socket,ok_rsp);
remove(proc_err_fname); /* Remove error file here */
}
/* Re-open files */
/* We must do this before continuing for handled msgs */
/* to prevent freopen(NULL) and orphaned temp files */
if((rcptlst=fopen(rcptlst_fname,fexist(rcptlst_fname) ? "r":"w+"))==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d re-opening recipient list: %s"
,socket, errno, rcptlst_fname);
if(!msg_handled)
sockprintf(socket,sys_error);
continue;
}
if(msg_handled) {
lprintf(LOG_NOTICE,"%04d SMTP Message handled by external mail processor (%s)"
,socket, mailproc_list[i].name);
continue;
}
/* If mailproc has written new message text to .new file, use that instead of .msg */
if(flength(newtxt_fname) > 0) {
remove(msgtxt_fname);
SAFECOPY(msgtxt_fname, newtxt_fname);
} else
remove(newtxt_fname);
if((msgtxt=fopen(msgtxt_fname,"rb"))==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d re-opening message file: %s"
,socket, errno, msgtxt_fname);
sockprintf(socket,sys_error);
continue;
}
/* Initialize message header */
smb_freemsgmem(&msg);
memset(&msg,0,sizeof(smbmsg_t));
/* Parse message header here */
hfield_type=UNKNOWN;
smb_error=SMB_SUCCESS; /* no SMB error */
errmsg=insuf_stor;
while(!feof(msgtxt)) {
if(!fgets(buf,sizeof(buf),msgtxt))
break;
truncsp(buf);
if(buf[0]==0) /* blank line marks end of header */
break;
if(!strnicmp(buf, "SUBJECT:",8)) {
p=buf+8;
SKIP_WHITESPACE(p);
if(relay_user.number==0 && dnsbl_result.s_addr && startup->dnsbl_tag[0]
&& !(startup->options&MAIL_OPT_DNSBL_IGNORE)) {
safe_snprintf(str,sizeof(str),"%.*s: %.*s"
,(int)sizeof(str)/2, startup->dnsbl_tag
,(int)sizeof(str)/2, p);
p=str;
lprintf(LOG_NOTICE,"%04d !SMTP TAGGED MAIL SUBJECT from blacklisted server with: %s"
,socket, startup->dnsbl_tag);
}
smb_hfield_str(&msg, SUBJECT, p);
continue;
}
if(!strnicmp(buf, "FROM:", 5)
&& !chk_email_addr(socket,buf+5,host_name,host_ip,rcpt_addr,reverse_path,"FROM")) {
errmsg="554 Sender not allowed.";
smb_error=SMB_FAILURE;
break;
}
if(!strnicmp(buf, "TO:", 3)
&& !chk_email_addr(socket,buf+3,host_name,host_ip,rcpt_addr,reverse_path,"TO")) {
errmsg="550 Unknown user.";
smb_error=SMB_FAILURE;
break;
}
if((smb_error=parse_header_field(buf,&msg,&hfield_type))!=SMB_SUCCESS) {
if(smb_error==SMB_ERR_HDR_LEN)
lprintf(LOG_WARNING,"%04d !SMTP MESSAGE HEADER EXCEEDS %u BYTES"
,socket, SMB_MAX_HDR_LEN);
else
lprintf(LOG_ERR,"%04d !SMTP ERROR %d adding header field: %s"
,socket, smb_error, buf);
break;
}
}
if(smb_error!=SMB_SUCCESS) { /* SMB Error */
sockprintf(socket, errmsg);
}
if((p=smb_get_hfield(&msg, RFC822TO, NULL))!=NULL) {
if(*p=='<') p++;
SAFECOPY(rcpt_name,p);
truncstr(rcpt_name,">");
}
if((p=smb_get_hfield(&msg, RFC822FROM, NULL))!=NULL) {
parse_mail_address(p
,sender ,sizeof(sender)-1
,sender_addr,sizeof(sender_addr)-1);
}
/* SPAM Filtering/Logging */
if(relay_user.number==0 && msg.subj!=NULL && trashcan(&scfg,msg.subj,"subject")) {
lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED SUBJECT (%s) from: %s"
,socket, msg.subj, reverse_path);
SAFEPRINTF2(tmp,"Blocked subject (%s) from: %s"
,msg.subj, reverse_path);
spamlog(&scfg, "SMTP", "REFUSED"
,tmp, host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket, "554 Subject not allowed.");
continue;
}
dnsbl_recvhdr=FALSE;
if(startup->options&MAIL_OPT_DNSBL_CHKRECVHDRS) {
for(i=0;!dnsbl_result.s_addr && i<msg.total_hfields;i++) {
if(msg.hfield[i].type == SMTPRECEIVED) {
if(chk_received_hdr(socket,msg.hfield_dat[i],&dnsbl_result,dnsbl,dnsbl_ip)) {
dnsbl_recvhdr=TRUE;
break;
}
}
}
if(relay_user.number==0 && dnsbl_result.s_addr) {
if(startup->options&MAIL_OPT_DNSBL_IGNORE) {
lprintf(LOG_NOTICE,"%04d !SMTP IGNORED MAIL from blacklisted server"
SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
spamlog(&scfg, "SMTP", "IGNORED"
,str, host_name, dnsbl_ip, rcpt_addr, reverse_path);
/* pretend we received it */
sockprintf(socket,ok_rsp);
/* tag message as spam */
if(startup->dnsbl_hdr[0]) {
safe_snprintf(str,sizeof(str),"%s: %s is listed on %s as %s"
,startup->dnsbl_hdr, dnsbl_ip
,dnsbl, inet_ntoa(dnsbl_result));
smb_hfield_str(&msg, RFC822HEADER, str);
lprintf(LOG_NOTICE,"%04d !SMTP TAGGED MAIL HEADER from blacklisted server with: %s"
,socket, startup->dnsbl_hdr);
}
if(startup->dnsbl_hdr[0] || startup->dnsbl_tag[0]) {
SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
spamlog(&scfg, "SMTP", "TAGGED", str, host_name, dnsbl_ip, rcpt_addr, reverse_path);
if(dnsbl_recvhdr) /* DNSBL-listed IP found in Received header? */
dnsbl_result.s_addr=0; /* Reset DNSBL look-up result between messages */

rswindell
committed
if(sender[0]==0) {
lprintf(LOG_WARNING,"%04d !SMTP MISSING mail header 'FROM' field", socket);

rswindell
committed
sockprintf(socket, "554 Mail header missing 'FROM' field");
subnum=INVALID_SUB;

rswindell
committed
continue;
}
nettype=NET_INTERNET;
smb_hfield_str(&msg, SMTPREVERSEPATH, reverse_path);
smb_hfield_str(&msg, SENDER, sender);

rswindell
committed
smb_hfield(&msg, SENDERNETTYPE, sizeof(nettype), &nettype);
smb_hfield_str(&msg, SENDERNETADDR, sender_addr);
if(msg.subj==NULL)
smb_hfield(&msg, SUBJECT, 0, NULL);

rswindell
committed
length=filelength(fileno(msgtxt))-ftell(msgtxt);
if(startup->max_msg_size && length>startup->max_msg_size) {
lprintf(LOG_WARNING,"%04d !SMTP message size (%lu) exceeds maximum: %lu bytes"
,socket,length,startup->max_msg_size);
sockprintf(socket, "552 Message size (%lu) exceeds maximum: %lu bytes"
,length,startup->max_msg_size);
subnum=INVALID_SUB;
continue;
}
if((msgbuf=(char*)malloc(length+1))==NULL) {
lprintf(LOG_CRIT,"%04d !SMTP ERROR allocating %d bytes of memory"
,socket,length+1);
sockprintf(socket, insuf_stor);
subnum=INVALID_SUB;
continue;
}
fread(msgbuf,length,1,msgtxt);
msgbuf[length]=0; /* ASCIIZ */
/* Do external JavaScript processing here? */
if(subnum!=INVALID_SUB) { /* Message Base */
uint reason;
if(relay_user.number==0)
memset(&relay_user,0,sizeof(relay_user));
if(!can_user_post(&scfg,subnum,&relay_user,&reason)) {
lprintf(LOG_WARNING,"%04d !SMTP %s (user #%u) cannot post on %s (reason: %u)"
,socket, sender_addr, relay_user.number
,scfg.sub[subnum]->sname, reason);
sockprintf(socket,"550 Insufficient access");
subnum=INVALID_SUB;
continue;
}
if(rcpt_name[0]==0)
strcpy(rcpt_name,"All");
smb_hfield_str(&msg, RECIPIENT, rcpt_name);
smb.subnum=subnum;
if((i=savemsg(&scfg, &smb, &msg, &client, msgbuf))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d !SMTP ERROR %d (%s) saving message"
,socket,i,smb.last_error);
sockprintf(socket, "452 ERROR %d (%s) saving message"
,i,smb.last_error);
} else {
lprintf(LOG_INFO,"%04d SMTP %s posted a message on %s"
,socket, sender_addr, scfg.sub[subnum]->sname);
sockprintf(socket,ok_rsp);
signal_smtp_sem();
}
free(msgbuf);
smb_close(&smb);
subnum=INVALID_SUB;
continue;
}
/* E-mail */
smb.subnum=INVALID_SUB;
/* creates message data, but no header or index records (since msg.to==NULL) */
i=savemsg(&scfg, &smb, &msg, &client, msgbuf);
free(msgbuf);
if(i!=SMB_SUCCESS) {
smb_close(&smb);
lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%s) saving message"
,socket,i,smb.last_error);
sockprintf(socket, "452 ERROR %d (%s) saving message"
,i,smb.last_error);
continue;
}
sec_list=iniReadSectionList(rcptlst,NULL); /* Each section is a recipient */
for(rcpt_count=0; sec_list!=NULL
&& sec_list[rcpt_count]!=NULL
&& rcpt_count<startup->max_recipients; rcpt_count++) {
section=sec_list[rcpt_count];
SAFECOPY(rcpt_name,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENT),"unknown",value));
usernum=iniReadInteger(rcptlst,section ,smb_hfieldtype(RECIPIENTEXT),0);
agent=iniReadShortInt(rcptlst,section ,smb_hfieldtype(RECIPIENTAGENT),AGENT_PERSON);
nettype=iniReadShortInt(rcptlst,section ,smb_hfieldtype(RECIPIENTNETTYPE),NET_NONE);
sprintf(str,"#%u",usernum);
SAFECOPY(rcpt_addr,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENTNETADDR),str,value));

rswindell
committed
if(nettype==NET_NONE /* Local destination */ && usernum==0) {
lprintf(LOG_ERR,"%04d !SMTP can't deliver mail to user #0"
,socket);
break;
}
if((i=smb_copymsgmem(&smb,&newmsg,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%s) copying message"
,socket, i, smb.last_error);
break;
}

rswindell
committed
snprintf(hdrfield,sizeof(hdrfield),
"from %s (%s [%s])\r\n"
" by %s [%s] (%s %s-%s) with %s\r\n"
" for %s; %s\r\n"
" (envelope-from %s)"

rswindell
committed
,host_name,hello_name,host_ip
,startup->host_name,inet_ntoa(server_addr.sin_addr)
,server_name
,revision,PLATFORM_DESC

rswindell
committed
,esmtp ? "ESMTP" : "SMTP"
,rcpt_name,msgdate(msg.hdr.when_imported,date)
,reverse_path);
smb_hfield_add_str(&newmsg, SMTPRECEIVED, hdrfield, /* insert: */TRUE);

rswindell
committed
smb_hfield_str(&newmsg, RECIPIENT, rcpt_name);

rswindell
committed
if(usernum && nettype!=NET_INTERNET) { /* Local destination or QWKnet routed */
/* This is required for fixsmb to be able to rebuild the index */
sprintf(str,"%u",usernum);
smb_hfield_str(&newmsg, RECIPIENTEXT, str);
}
if(nettype!=NET_NONE) {
smb_hfield(&newmsg, RECIPIENTNETTYPE, sizeof(nettype), &nettype);
smb_hfield_str(&newmsg, RECIPIENTNETADDR, rcpt_addr);

rswindell
committed
}
if(agent!=newmsg.to_agent)
smb_hfield(&newmsg, RECIPIENTAGENT, sizeof(agent), &agent);

rswindell
committed
i=smb_addmsghdr(&smb,&newmsg,SMB_SELFPACK);
smb_freemsgmem(&newmsg);
if(i!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%s) adding message header"
,socket, i, smb.last_error);

rswindell
committed
break;
lprintf(LOG_INFO,"%04d SMTP Created message #%ld from %s to %s <%s>"

rswindell
committed
,socket, newmsg.hdr.number, sender, rcpt_name, rcpt_addr);
if(!(startup->options&MAIL_OPT_NO_NOTIFY) && usernum
&& !dnsbl_recvhdr && !dnsbl_result.s_addr) {
safe_snprintf(str,sizeof(str)
,"\7\1n\1hOn %.24s\r\n\1m%s \1n\1msent you e-mail from: "

rswindell
committed
"\1h%s\1n\r\n"
,timestr(&scfg,newmsg.hdr.when_imported.time,tmp)

rswindell
committed
,sender,sender_addr);
if(!newmsg.idx.to) { /* Forwarding */
strcat(str,"\1mand it was automatically forwarded to: \1h");
strcat(str,rcpt_addr);

rswindell
committed
strcat(str,"\1n\r\n");
}
putsmsg(&scfg, usernum, str);
iniFreeStringList(sec_list);
if(rcpt_count<1) {
smb_freemsg_dfields(&smb,&msg,SMB_ALL_REFS);
sockprintf(socket, insuf_stor);

rswindell
committed
}
else {
if(rcpt_count>1)
smb_incmsg_dfields(&smb,&msg,(ushort)(rcpt_count-1));
sockprintf(socket,ok_rsp);
signal_smtp_sem();
#if 0 /* This shouldn't be necessary here */
smb_close_da(&smb);
#endif
smb_close(&smb);
continue;
}
if(buf[0]==0 && state==SMTP_STATE_DATA_HEADER) {
state=SMTP_STATE_DATA_BODY; /* Null line separates header and body */
if(msgtxt!=NULL) {
fprintf(msgtxt, "\r\n");
hdr_len=ftell(msgtxt);
}
continue;
}
if(state==SMTP_STATE_DATA_BODY) {
p=buf;
if(*p=='.') p++; /* Transparency (RFC821 4.5.2) */
if(msgtxt!=NULL)
fprintf(msgtxt, "%s\r\n", p);
/* release time-slices every x lines */
if(startup->lines_per_yield &&
!(lines%startup->lines_per_yield))
YIELD();
continue;
}
/* RFC822 Header parsing */
if(startup->options&MAIL_OPT_DEBUG_RX_HEADER)
lprintf(LOG_DEBUG,"%04d SMTP %s",socket, buf);
if(!strnicmp(buf, "FROM:", 5))
parse_mail_address(buf+5
,sender, sizeof(sender)-1
,sender_addr, sizeof(sender_addr)-1);
if(msgtxt!=NULL)
fprintf(msgtxt, "%s\r\n", buf);
hdr_lines++;
strip_ctrl(buf, buf);
lprintf(LOG_DEBUG,"%04d SMTP RX: %s", socket, buf);
if(!strnicmp(buf,"HELO",4)) {
p=buf+4;
SKIP_WHITESPACE(p);
sockprintf(socket,"250 %s",startup->host_name);
esmtp=FALSE;
state=SMTP_STATE_HELO;

rswindell
committed
telegram=FALSE;
subnum=INVALID_SUB;
continue;
}
if(!strnicmp(buf,"EHLO",4)) {
p=buf+4;
SKIP_WHITESPACE(p);
sockprintf(socket,"250-%s",startup->host_name);
sockprintf(socket,"250 AUTH PLAIN LOGIN CRAM-MD5");
esmtp=TRUE;
state=SMTP_STATE_HELO;

rswindell
committed
telegram=FALSE;
subnum=INVALID_SUB;
if((auth_login=(stricmp(buf,"AUTH LOGIN")==0))==TRUE
|| strnicmp(buf,"AUTH PLAIN",10)==0) {
if(auth_login) {
sockprintf(socket,"334 VXNlcm5hbWU6"); /* Base64-encoded "Username:" */
if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
sockprintf(socket,badarg_rsp);
continue;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
if(b64_decode(user_name,sizeof(user_name),buf,rd)<1) {
sockprintf(socket,badarg_rsp);
continue;
}
sockprintf(socket,"334 UGFzc3dvcmQ6"); /* Base64-encoded "Password:" */
if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
sockprintf(socket,badarg_rsp);
continue;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
if(b64_decode(user_pass,sizeof(user_pass),buf,rd)<1) {
sockprintf(socket,badarg_rsp);
continue;
}
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
} else { /* AUTH PLAIN b64(<username>\0<user-id>\0<password>) */
p=buf+10;
SKIP_WHITESPACE(p);
if(*p==0) {
sockprintf(socket,badarg_rsp);
continue;
}
ZERO_VAR(tmp);
if(b64_decode(tmp,sizeof(tmp),p,strlen(p))<1) {
sockprintf(socket,badarg_rsp);
continue;
}
p=tmp;
while(*p) p++; /* skip username */
p++; /* skip NULL */
if(*p==0) {
sockprintf(socket,badarg_rsp);
continue;
}
SAFECOPY(user_name,p);
while(*p) p++; /* skip user-id */
p++; /* skip NULL */
if(*p==0) {
sockprintf(socket,badarg_rsp);
continue;
}
SAFECOPY(user_pass,p);
}
if((relay_user.number=matchuser(&scfg,user_name,FALSE))==0) {
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: %s (password: %s)"
,socket, user_name, user_pass);
else