Newer
Older
SAFEPRINTF(spam_block,"%sspamblock.cfg",scfg.ctrl_dir);
SAFEPRINTF(spam_block_exemptions,"%sspamblock_exempt.cfg",scfg.ctrl_dir);
/* local connection */
dnsbl_result.s_addr=0;
} else {
ulong banned = loginBanned(&scfg, startup->login_attempt_list, socket, host_name, startup->login_attempt, &attempted);
lprintf(LOG_NOTICE, "%04d %s [%s] !TEMPORARY BAN (%lu login attempts, last: %s) - remaining: %s"
,socket, client.protocol, host_ip, attempted.count-attempted.dupes, attempted.user
,duration_estimate_to_vstr(banned, ban_duration, sizeof ban_duration, 1, 1));
spam_block_exempt = find2strs(host_ip, host_name, spam_block_exemptions, NULL);
if(!spam_block_exempt && findstr(host_ip,spam_block)) {
lprintf(LOG_NOTICE,"%04d %s [%s] !CLIENT BLOCKED in %s (%lu total)"
,socket, client.protocol, host_ip, spam_block, ++stats.sessions_refused);
sockprintf(socket,client.protocol,session,"550 CLIENT IP ADDRESS BLOCKED: %s", host_ip);
}
struct trash trash;
if(trashcan2(&scfg, host_ip, NULL, "ip", &trash)) {
char details[128];
lprintf(LOG_NOTICE,"%04d %s [%s] !CLIENT BLOCKED in ip.can %s (%lu total)"
,socket, client.protocol, host_ip, trash_details(&trash, details, sizeof details), ++stats.sessions_refused);
sockprintf(socket,client.protocol,session,"550 CLIENT IP ADDRESS BLOCKED: %s", host_ip);
return false;
}
if(trashcan2(&scfg, host_name, NULL, "host", &trash)) {
char details[128];
lprintf(LOG_NOTICE,"%04d %s [%s] !CLIENT BLOCKED in host.can: %s %s (%lu total)"
,socket, client.protocol, host_ip, host_name, trash_details(&trash, details, sizeof details), ++stats.sessions_refused);
sockprintf(socket,client.protocol,session,"550 CLIENT HOSTNAME BLOCKED: %s", host_name);
/* SPAM Filters (mail-abuse.org) */
dnsbl_result.s_addr = dns_blacklisted(socket,client.protocol,&smtp->client_addr,host_name,dnsbl,dnsbl_ip);
if(dnsbl_result.s_addr) {
lprintf(LOG_NOTICE,"%04d %s [%s] BLACKLISTED SERVER on %s: %s = %s"
,socket, client.protocol, dnsbl_ip, dnsbl, host_name, 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, &mqtt, (char*)client.protocol, "SESSION REFUSED", str, host_name, dnsbl_ip, NULL, NULL);
sockprintf(socket,client.protocol,session
,"550 Mail from %s refused due to listing at %s"
,dnsbl_ip, dnsbl);
lprintf(LOG_NOTICE,"%04d %s !REFUSED SESSION from blacklisted server (%lu total)"
,socket, client.protocol, ++stats.sessions_refused);
}
}
SAFEPRINTF(smb.file,"%smail",scfg.data_dir);
if(smb_islocked(&smb)) {
lprintf(LOG_WARNING,"%04d %s [%s] !MAIL BASE LOCKED: %s"
,socket, client.protocol, host_ip, smb.last_error);
sockprintf(socket,client.protocol,session, smtp_error, "mail base locked");
}
SAFEPRINTF(spam.file,"%sspam",scfg.data_dir);
spam.retry_time=scfg.smb_retry_time;
spam.subnum=INVALID_SUB;
srand((unsigned int)(time(NULL) ^ (time_t)GetCurrentThreadId())); /* seed random number generator */
rand(); /* throw-away first result */
SAFEPRINTF4(session_id,"%x%x%x%lx",getpid(),socket,rand(),(long)clock());
lprintf(LOG_DEBUG,"%04d %s [%s] Session ID=%s", socket, client.protocol, host_ip, session_id);
SAFEPRINTF3(msgtxt_fname,"%sSBBS_%s.%s.msg", scfg.temp_dir, client.protocol, session_id);
SAFEPRINTF3(newtxt_fname,"%sSBBS_%s.%s.new", scfg.temp_dir, client.protocol, session_id);
SAFEPRINTF3(logtxt_fname,"%sSBBS_%s.%s.log", scfg.temp_dir, client.protocol, session_id);
SAFEPRINTF3(rcptlst_fname,"%sSBBS_%s.%s.lst", scfg.temp_dir, client.protocol, session_id);
rcptlst=fopen(rcptlst_fname,"w+");
if(rcptlst==NULL) {
lprintf(LOG_CRIT,"%04d %s [%s] !ERROR %d creating recipient list: %s"
,socket, client.protocol, host_ip, errno, rcptlst_fname);
sockprintf(socket,client.protocol,session,smtp_error, "fopen error");
}
/* Initialize client display */
client.size=sizeof(client);
client.time=time32(NULL);
SAFECOPY(client.addr,host_ip);
SAFECOPY(client.host,host_name);
client.port=inet_addrport(&smtp->client_addr);
SAFECOPY(client.user, STR_UNKNOWN_USER);
client.usernum = 0;
client_on(socket,&client,FALSE /* update */);
&& (login_attempts=loginAttempts(startup->login_attempt_list, &smtp->client_addr)) > 1) {
lprintf(LOG_DEBUG,"%04d %s Throttling suspicious connection from: %s (%lu login attempts)"
,socket, client.protocol, host_ip, login_attempts);
mswait(login_attempts*startup->login_attempt.throttle);
}
BOOL* mailproc_to_match = calloc(sizeof(*mailproc_to_match), mailproc_count);
if(mailproc_to_match == NULL) {
fclose(rcptlst);
lprintf(LOG_CRIT,"%04d %s !ERROR allocating memory for mailproc_to_match", socket, client.protocol);
sockprintf(socket,client.protocol,session,smtp_error, "malloc failure");
return false;
}
if(trashcan2(&scfg, host_ip, host_name, "smtpspy", NULL)) {
SAFECOPY(str, client.protocol);
strlwr(str);
SAFEPRINTF2(path,"%s%sspy.txt", scfg.logs_dir, str);
spy=fopen(path,"a");
}
/* SMTP session active: */
sockprintf(socket,client.protocol,session,"220 %s Synchronet %s Server %s%c-%s Ready"
,server_host_name(), client.protocol, VERSION, REVISION, PLATFORM_DESC);
rd = sockreadline(socket, client.protocol, session, 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 %s %s !NO MESSAGE TEXT FILE POINTER?", socket, client.protocol, client_id);
sockprintf(socket,client.protocol,session,"554 No message text");
continue;
}
if(ftell(msgtxt)<1) {
lprintf(LOG_ERR,"%04d %s %s !INVALID MESSAGE LENGTH: %ld (%lu lines)"
, socket, client.protocol, client_id, ftell(msgtxt), lines);
sockprintf(socket,client.protocol,session,"554 No message text");
lprintf(LOG_INFO,"%04d %s %s End of message (body: %lu lines, %lu bytes, header: %lu lines, %lu bytes)"
, socket, client.protocol, client_id, lines, ftell(msgtxt)-hdr_len, hdr_lines, hdr_len);
if(!socket_check(socket, NULL, NULL, 0)) {
lprintf(LOG_NOTICE,"%04d %s %s !Sender disconnected (premature evacuation)", socket, client.protocol, client_id);
continue;
}
stats.msgs_received++;
/* Twit-listing (sender's name and e-mail addresses) here */
twitlist_fname(&scfg, path, sizeof path);
if(fexist(path) && find2strs(sender, sender_addr, path, NULL)) {
lprintf(LOG_NOTICE,"%04d %s %s !FILTERING TWIT-LISTED SENDER: '%s' <%s> (%lu total)"
,socket, client.protocol, client_id, sender, sender_addr, ++stats.msgs_refused);
SAFEPRINTF2(tmp,"Twit-listed sender: '%s' <%s>", sender, sender_addr);
spamlog(&scfg, &mqtt, (char*)client.protocol, "REFUSED", tmp, host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket,client.protocol,session, "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";
struct addrinfo ai;
struct addrinfo *res,*cur;
BOOL matched=FALSE;
rewind(msgtxt);
length=filelength(fileno(msgtxt));
p=strchr(sender_addr,'@');
ai.ai_family = smtp->client_addr.addr.sa_family;
if(getaddrinfo(p+1, NULL, &ai, &res) != 0)
p=NULL;
else {
for(cur=res; cur; cur=cur->ai_next) {
char cur_ip[INET6_ADDRSTRLEN];
if(inet_addrtop((void *)cur->ai_addr, cur_ip, sizeof(cur_ip))) {
if(strcmp(host_ip, cur_ip)==0)
matched=TRUE;
}
}
freeaddrinfo(res);
if(!matched)
p=NULL;
}
if(p==NULL)
/* 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((size_t)(length+strlen(str)+1)))==NULL) {
lprintf(LOG_CRIT,"%04d %s %s !ERROR allocating %lu bytes of memory for telegram from %s"
,socket, client.protocol, client_id, length+strlen(str)+1,sender_addr);
sockprintf(socket,client.protocol,session, insuf_stor);
continue;
}
strcpy(telegram_buf,str); /* can't use SAFECOPY here */
if(fread(telegram_buf+strlen(str),1,(size_t)length,msgtxt)!=length) {
lprintf(LOG_ERR,"%04d %s %s !ERROR reading %lu bytes from telegram file"
,socket, client.protocol, client_id, length);
sockprintf(socket,client.protocol,session, 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
&& (startup->max_recipients==0 || rcpt_count<startup->max_recipients); rcpt_count++) {
section=sec_list[rcpt_count];
SAFECOPY(rcpt_to,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_to,value));
if((i=putsmsg(&scfg,usernum,telegram_buf))==0)
lprintf(LOG_INFO,"%04d %s %s Created telegram (%ld/%lu bytes) from '%s' to '%s' <%s>"
,socket, client.protocol, client_id, length, (ulong)strlen(telegram_buf), sender_addr, rcpt_to, rcpt_addr);
else
lprintf(LOG_ERR,"%04d %s %s !ERROR %d creating telegram from '%s' to '%s' <%s>"
,socket, client.protocol, client_id, i, sender_addr, rcpt_to, rcpt_addr);
}
iniFreeStringList(sec_list);
free(telegram_buf);
sockprintf(socket,client.protocol,session,ok_rsp);
telegram=FALSE;
continue;
}
fclose(msgtxt), msgtxt=NULL;
fclose(rcptlst), rcptlst=NULL;
/* External Mail Processing here */
mailproc=NULL;
msg_handled=FALSE;
if(mailproc_count) {
SAFEPRINTF3(proc_err_fname,"%sSBBS_%s.%s.err", scfg.temp_dir, client.protocol, session_id);
remove(proc_err_fname);
for(i=0;i<mailproc_count && !msg_handled;i++) {
struct mailproc* mp=&mailproc_list[i];
if(mp->disabled)
if(!mp->process_dnsbl && dnsbl_result.s_addr)
continue;
if(!mp->process_spam && spam_bait_result)
continue;
if(!chk_ar(&scfg,mp->ar,&relay_user,&client))
if(mp->to!=NULL && !mailproc_to_match[i])
continue;
if(mp->from!=NULL
&& !findstr_in_list(sender_addr, mp->from, NULL))
mailcmdstr(mp->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 %s %s Executing external mail processor: %s"
,socket, client.protocol, client_id, mp->name);
lprintf(LOG_DEBUG,"%04d %s %s Executing external command: %s"
,socket, client.protocol, client_id, str);
if((j=system(str))!=0) {
lprintf(LOG_NOTICE,"%04d %s %s system(%s) returned %d (errno: %d)"
,socket, client.protocol, client_id, str, j, errno);
if(mp->ignore_on_error) {
lprintf(LOG_WARNING,"%04d %s %s !IGNORED MAIL due to mail processor (%s) error: %d"
,socket, client.protocol, client_id, mp->name, j);
msg_handled=TRUE;
}
}
} else { /* JavaScript */
,msgtxt_fname, newtxt_fname, logtxt_fname
,rcpt_addr
,rcptlst_fname, proc_err_fname
,sender, sender_addr, reverse_path, hello_name, &js_result
,client.protocol) || js_result!=0) {
#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->ignore_on_error) {
lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL due to mail processor (%s) failure"
,socket, mailproc->name);
msg_handled=TRUE;
}
#endif
}
}
/* Log debug output (file) from mailproc: */
if(flength(logtxt_fname) > 0 && (proc_out=fopen(logtxt_fname,"r"))!=NULL) {
while(!feof(proc_out)) {
if(!fgets(str,sizeof(str),proc_out))
break;
truncsp(str);
lprintf(LOG_DEBUG,"%04d %s %s External mail processor (%s) debug: %s"
,socket, client.protocol, client_id, mp->name, str);
}
fclose(proc_out);
}
remove(logtxt_fname);
if(!mp->passthru || flength(proc_err_fname)>0 || !fexist(msgtxt_fname) || !fexist(rcptlst_fname)) {
mailproc=mp;
msg_handled=TRUE;
break;
}
if(flength(proc_err_fname)>0
&& (proc_out=fopen(proc_err_fname,"r"))!=NULL) {
lprintf(LOG_NOTICE,"%04d %s %s !External mail processor (%s) created: %s"
,socket, client.protocol, client_id, mailproc->name, proc_err_fname);
while(!feof(proc_out)) {
int n;
if(!fgets(str,sizeof(str),proc_out))
break;
truncsp(str);
lprintf(LOG_WARNING,"%04d %s %s !External mail processor (%s) error: %s"
,socket, client.protocol, client_id, mailproc->name, str);
n=atoi(str);
if(n>=100 && n<1000)
sockprintf(socket,client.protocol,session,"%s", str);
else
sockprintf(socket,client.protocol,session,"554%c%s"
,ftell(proc_out)<filelength(fileno(proc_out)) ? '-' : ' '
,str);
}
fclose(proc_out);
msg_handled=TRUE;
}
else if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname)) {
lprintf(LOG_NOTICE,"%04d %s %s External mail processor (%s) removed %s file"
,socket, client.protocol, client_id
,mailproc->name
,fexist(msgtxt_fname)==FALSE ? "message text" : "recipient list");
sockprintf(socket,client.protocol,session,ok_rsp);
msg_handled=TRUE;
}
else if(msg_handled)
sockprintf(socket,client.protocol,session,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 %s %s !ERROR %d re-opening recipient list: %s"
,socket, client.protocol, client_id, errno, rcptlst_fname);
if(!msg_handled)
sockprintf(socket,client.protocol,session,smtp_error, "fopen error");
continue;
}
if(!msg_handled && subnum==INVALID_SUB && iniReadSectionCount(rcptlst,NULL) < 1) {
lprintf(LOG_DEBUG,"%04d %s %s No recipients in recipient list file (message handled by external mail processor?)"
,socket, client.protocol, client_id);
sockprintf(socket,client.protocol,session,ok_rsp);
msg_handled=TRUE;
}
if(msg_handled) {
if(mailproc!=NULL)
lprintf(LOG_NOTICE,"%04d %s %s Message handled by external mail processor (%s, %lu total)"
,socket, client.protocol, client_id, mailproc->name, ++mailproc->handled);
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 %s %s !ERROR %d re-opening message file: %s"
,socket, client.protocol, client_id, errno, msgtxt_fname);
sockprintf(socket,client.protocol,session,smtp_error, "fopen 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)) {
char field[32];
if(!fgets(buf,sizeof(buf),msgtxt))
break;
truncsp(buf);
if(buf[0]==0) /* blank line marks end of header */
break;
if((p=get_header_field(buf, field, sizeof(field)))!=NULL) {
//normalize_hfield_value(p);
if(stricmp(field, "SUBJECT")==0) {
/* SPAM Filtering/Logging */
if(relay_user.number==0) {
if(trashcan(&scfg,p,"subject")) {
lprintf(LOG_NOTICE,"%04d %s %s !BLOCKED SUBJECT (%s) from: %s (%lu total)"
,socket, client.protocol, client_id, p, reverse_path, ++stats.msgs_refused);
SAFEPRINTF2(tmp,"Blocked subject (%s) from: %s"
,p, reverse_path);
spamlog(&scfg, &mqtt, (char*)client.protocol, "REFUSED"
,tmp, host_name, host_ip, rcpt_addr, reverse_path);
errmsg="554 Subject not allowed.";
smb_error=SMB_FAILURE;
break;
}
if(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 %s %s TAGGED MAIL SUBJECT from blacklisted server with: %s"
,socket, client.protocol, client_id, startup->dnsbl_tag);
msg.hdr.attr |= MSG_SPAM;
}
}
smb_hfield_str(&msg, hfield_type=RFC822SUBJECT, p);
continue;
}
if(stricmp(field, "FROM")==0) {
if(relay_user.number==0
&& !chk_email_addr(socket, client.protocol,p,host_name,host_ip,rcpt_addr,reverse_path,"FROM")) {
errmsg="554 Sender not allowed.";
smb_error=SMB_FAILURE;
break;
}
char from_addr[128];
parse_mail_address(p, /* name: */NULL, 0, from_addr, sizeof(from_addr)-1);
if(dnsbl_result.s_addr && email_addr_is_exempt(from_addr)) {
lprintf(LOG_INFO,"%04d %s %s Ignoring DNSBL results for exempt sender (from): %s"
,socket, client.protocol, client_id, from_addr);
dnsbl_result.s_addr=0;
}
}
if(relay_user.number==0 && stricmp(field, "TO")==0 && !spam_bait_result
&& !chk_email_addr(socket, client.protocol,p,host_name,host_ip,rcpt_addr,reverse_path,"TO")) {
errmsg="550 Unknown user.";
smb_error=SMB_FAILURE;
break;
}
if(stricmp(field, "X-Spam-Flag") == 0 && stricmp(p, "Yes") == 0)
msg.hdr.attr |= MSG_SPAM; /* e.g. flagged by SpamAssasin */
}
if((smb_error=parse_header_field((char*)buf, &msg, &hfield_type))!=SMB_SUCCESS) {
if(smb_error==SMB_ERR_HDR_LEN)
lprintf(LOG_WARNING,"%04d %s %s !MESSAGE HEADER EXCEEDS %u BYTES"
,socket, client.protocol, client_id, SMB_MAX_HDR_LEN);
lprintf(LOG_ERR,"%04d %s %s !ERROR %d adding header field: %s"
,socket, client.protocol, client_id, smb_error, buf);
break;
}
}
if(smb_error!=SMB_SUCCESS) { /* SMB Error */
sockprintf(socket,client.protocol,session, "%s", errmsg);
stats.msgs_refused++;
}
hfield_t* hfield;
if((p=smb_get_hfield(&msg, RFC822TO, &hfield))!=NULL) {
char* np = strdup(p);
if(np != NULL) {
if(mimehdr_value_decode(np, &msg))
smb_hfield_str(&msg, RECIPIENTLIST, np);
else
hfield->type = RECIPIENTLIST;
parse_mail_address(np
,rcpt_name ,sizeof(rcpt_name)-1
,rcpt_addr ,sizeof(rcpt_addr)-1);
free(np);
}
}
if((p=smb_get_hfield(&msg, RFC822FROM, NULL))!=NULL) {
parse_mail_address(p
,sender ,sizeof(sender)-1
,sender_addr,sizeof(sender_addr)-1);
// We only support MIME-encoded name portion of 'name <user@addr>'
mimehdr_value_decode(sender, &msg);
}
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, client.protocol,msg.hfield_dat[i],&dnsbl_result,dnsbl,dnsbl_ip)) {
dnsbl_recvhdr=TRUE;
break;
}
}
}
if(relay_user.number==0 && dnsbl_result.s_addr && !(startup->options&MAIL_OPT_DNSBL_IGNORE)) {
msg.hdr.attr |= MSG_SPAM;
/* 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 %s %s TAGGED MAIL HEADER from blacklisted server with: %s"
,socket, client.protocol, client_id, 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, &mqtt, (char*)client.protocol, "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 */
if((scfg.sys_misc&SM_DELREADM)
|| ((startup->options&MAIL_OPT_KILL_READ_SPAM) && (msg.hdr.attr&MSG_SPAM)))
msg.hdr.attr |= MSG_KILLREAD;

rswindell
committed
if(sender[0]==0) {
lprintf(LOG_NOTICE,"%04d %s %s !MISSING mail header 'FROM' field (%lu total)"
,socket, client.protocol, client_id, ++stats.msgs_refused);
sockprintf(socket,client.protocol,session, "554 Mail header missing 'FROM' field");
subnum=INVALID_SUB;

rswindell
committed
continue;
}
if(relay_user.number == 0
&& smb_netaddr_type(sender) == NET_INTERNET
&& compare_addrs(sender, sender_addr) != 0) {
lprintf(LOG_NOTICE,"%04d %s %s !FORGED mail header 'FROM' field ('%s' vs '%s', %lu total)"
,socket, client.protocol, client_id, sender, sender_addr, ++stats.msgs_refused);
sockprintf(socket,client.protocol,session, "554 Mail header contains mismatched 'FROM' field");
subnum=INVALID_SUB;
continue;
}
char sender_info[512];
if(relay_user.number) {
SAFEPRINTF(str,"%u",relay_user.number);
smb_hfield_str(&msg, SENDEREXT, str);
SAFEPRINTF2(sender_info, "'%s' #%u", sender, relay_user.number);
} else if(compare_addrs(sender, sender_addr) == 0) {
angle_bracket(sender_info, sizeof(sender_info), sender_addr);
safe_snprintf(sender_info, sizeof(sender_info), "'%s' %s", sender, angle_bracket(tmp, sizeof(tmp), sender_addr));
}
if(relay_user.number && subnum!=INVALID_SUB) {
nettype=NET_NONE;
smb_hfield_str(&msg, SENDER, relay_user.alias);
} else {
nettype=NET_INTERNET;
smb_hfield_str(&msg, SENDER, sender);
smb_hfield(&msg, SENDERNETTYPE, sizeof(nettype), &nettype);
smb_hfield_str(&msg, SENDERNETADDR, sender_addr);
}
smb_hfield_str(&msg, SMTPREVERSEPATH, reverse_path);
if((p = smb_get_hfield(&msg, RFC822ORG, &hfield)) != NULL) {
char* np = strdup(p);
if(np != NULL) {
if(mimehdr_value_decode(np, &msg))
smb_hfield_str(&msg, SENDERORG, np);
else
hfield->type = SENDERORG;
free(np);
}
}
if((p = smb_get_hfield(&msg, RFC822CC, &hfield)) != NULL) {
char* np = strdup(p);
if(np != NULL) {
if(mimehdr_value_decode(np, &msg))
smb_hfield_str(&msg, SMB_CARBONCOPY, np);
else
hfield->type = SMB_CARBONCOPY;
free(np);
}
}
if((p = smb_get_hfield(&msg, RFC822REPLYTO, &hfield)) != NULL) {
char* np = strdup(p);
if(np != NULL) {
if(mimehdr_value_decode(np, &msg))
smb_hfield_str(&msg, REPLYTOLIST, np);
else
hfield->type = REPLYTOLIST;
free(np);
}
}
if((p = smb_get_hfield(&msg, RFC822SUBJECT, &hfield)) != NULL) {
char* np = strdup(p);
if(np != NULL) {
if(mimehdr_value_decode(np, &msg))
smb_hfield_str(&msg, SUBJECT, np);
else
hfield->type = SUBJECT;
free(np);
}
}
else
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_NOTICE,"%04d %s %s !Message size (%lu) from %s to <%s> exceeds maximum: %u bytes"
,socket, client.protocol, client_id, length, sender_info, rcpt_addr, startup->max_msg_size);
sockprintf(socket,client.protocol,session, "552 Message size (%lu) exceeds maximum: %u bytes"
,length,startup->max_msg_size);
subnum=INVALID_SUB;
stats.msgs_refused++;
if((msgbuf=(char*)malloc((size_t)(length+1)))==NULL) {
lprintf(LOG_CRIT,"%04d %s %s !ERROR allocating %lu bytes of memory"
,socket, client.protocol, client_id, length+1);
sockprintf(socket,client.protocol,session, insuf_stor);
subnum=INVALID_SUB;
continue;
}
if(fread(msgbuf,(size_t)length,1,msgtxt) != 1)
*msgbuf = '\0';
else
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(dnsbl_recvhdr || dnsbl_result.s_addr) {
lprintf(LOG_NOTICE,"%04d %s %s !refusing to post message (on %s) from DNS-Blacklisted client: %s"
,socket, client.protocol, client_id, scfg.sub[subnum]->sname, sender_addr);
sockprintf(socket,client.protocol,session,"550 Insufficient access");
subnum = INVALID_SUB;
stats.msgs_refused++;
continue;
}
}
if(!can_user_post(&scfg,subnum,&relay_user,&client,&reason)) {
lprintf(LOG_NOTICE,"%04d %s %s !%s (user #%u) cannot post on %s (reason: %u)"
,socket, client.protocol, client_id, sender_addr, relay_user.number
,scfg.sub[subnum]->sname, reason + 1);
sockprintf(socket,client.protocol,session,"550 Insufficient access");
subnum=INVALID_SUB;
stats.msgs_refused++;
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, server_host_name(), msgbuf, /* remsg: */NULL))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"%04d %s %s !ERROR %d (%s) %s posting message to %s (%s)"
,socket, client.protocol, client_id, i, smb.last_error, sender_info, scfg.sub[subnum]->sname, smb.file);
sockprintf(socket,client.protocol,session, "452 ERROR %d (%s) posting message"
,i,smb.last_error);
} else {
lprintf(LOG_INFO,"%04d %s %s %s posted a message on %s (%s)"
,socket, client.protocol, client_id, sender_info, scfg.sub[subnum]->sname, smb.file);
sockprintf(socket,client.protocol,session,ok_rsp);
if(relay_user.number != 0)
user_posted_msg(&scfg, &relay_user, 1);
signal_smtp_sem();
}
free(msgbuf);
smb_close(&smb);
subnum=INVALID_SUB;
continue;
}
/* Create/check hashes of known SPAM */
{
hash_t** hashes;
BOOL is_spam=spam_bait_result;
long sources=SMB_HASH_SOURCE_SPAM;
if((dnsbl_recvhdr || dnsbl_result.s_addr) && startup->options&MAIL_OPT_DNSBL_SPAMHASH)
is_spam=TRUE;
lprintf(LOG_DEBUG,"%04d %s %s Calculating message hashes (sources=%lx, msglen=%lu)"
,socket, client.protocol, client_id, sources, (ulong)strlen(msgbuf));
if((hashes=smb_msghashes(&msg, (uchar*)msgbuf, sources)) != NULL) {
for(i=0;hashes[i];i++)
lprintf(LOG_DEBUG,"%04d %s %s Message %s crc32=%x flags=%x length=%u"
,socket, client.protocol, client_id, smb_hashsourcetype(hashes[i]->source)
,hashes[i]->data.crc32, hashes[i]->flags, hashes[i]->length);
lprintf(LOG_DEBUG, "%04d %s %s Searching SPAM database for a match", socket, client.protocol, client_id);
if((i=smb_findhash(&spam, hashes, &found, sources, /* Mark: */TRUE))==SMB_SUCCESS) {
SAFEPRINTF3(str,"%s (%s) found in SPAM database (added on %s)"
,smb_hashsourcetype(found.source)
,smb_hashsource(&msg,found.source)
,timestr(&scfg,found.time,tmp)
lprintf(LOG_NOTICE,"%04d %s %s Message from %s %s", socket, client.protocol, client_id, sender_info, str);
if(!is_spam) {
spamlog(&scfg, &mqtt, (char*)client.protocol, "IGNORED"
,str, host_name, host_ip, rcpt_addr, reverse_path);
is_spam=TRUE;
}
} else {
lprintf(LOG_DEBUG, "%04d %s %s Done searching SPAM database", socket, client.protocol, client_id);
if(i!=SMB_ERR_NOT_FOUND)
lprintf(LOG_ERR,"%04d %s %s !ERROR %d (%s) opening SPAM database"
,socket, client.protocol, client_id, i, spam.last_error);
if(is_spam) {
size_t n,total=0;
for(n=0;hashes[n]!=NULL;n++)
if(!(hashes[n]->flags&SMB_HASH_MARKED)) {
lprintf(LOG_INFO,"%04d %s %s Adding message %s (%s) from %s to SPAM database"
,socket, client.protocol, client_id
,smb_hashsourcetype(hashes[n]->source)
,smb_hashsource(&msg,hashes[n]->source)
);
}
lprintf(LOG_DEBUG,"%04d %s %s Adding %lu message hashes to SPAM database", socket, client.protocol, client_id, (ulong)total);
smb_addhashes(&spam, hashes, /* skip_marked: */TRUE);
}
if(i!=SMB_SUCCESS && !spam_bait_result && (dnsbl_recvhdr || dnsbl_result.s_addr))
is_spam=FALSE;
}
smb_close_hash(&spam);
smb_freehashes(hashes);
} else
lprintf(LOG_ERR,"%04d %s %s !smb_msghashes returned NULL", socket, client.protocol, client_id);
if(is_spam || ((startup->options&MAIL_OPT_DNSBL_IGNORE) && (dnsbl_recvhdr || dnsbl_result.s_addr))) {
lprintf(LOG_NOTICE,"%04d %s %s !IGNORED SPAM MESSAGE from %s to <%s> (%lu total)"
,socket, client.protocol, client_id, sender_info, rcpt_addr, ++stats.msgs_ignored);
SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
lprintf(LOG_NOTICE,"%04d %s %s !IGNORED MAIL from %s to <%s> from server: %s (%lu total)"
,socket, client.protocol, client_id, sender_info, rcpt_addr, str, ++stats.msgs_ignored);
spamlog(&scfg, &mqtt, (char*)client.protocol, "IGNORED"
,str, host_name, dnsbl_ip, rcpt_addr, reverse_path);
sockprintf(socket,client.protocol,session,ok_rsp);
subnum=INVALID_SUB;
continue;
}
}
char rcpt_info[512];
if(compare_addrs(rcpt_name, rcpt_addr) == 0)
angle_bracket(rcpt_info, sizeof(rcpt_info), rcpt_addr);
safe_snprintf(rcpt_info, sizeof(rcpt_info), "'%s' %s", rcpt_name, angle_bracket(tmp, sizeof(tmp), rcpt_addr));
lprintf(LOG_DEBUG,"%04d %s %s Saving message data from %s to %s"
,socket, client.protocol, client_id, sender_info, rcpt_info);
pthread_mutex_lock(&savemsg_mutex);
/* 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, server_host_name(), msgbuf, /* remsg: */NULL);
if(smb_countattachments(&smb, &msg, msgbuf) > 0)
msg.hdr.auxattr |= MSG_MIMEATTACH;
msg.hdr.netattr |= MSG_KILLSENT;
free(msgbuf);
if(i!=SMB_SUCCESS) {
smb_close(&smb);
pthread_mutex_unlock(&savemsg_mutex);
lprintf(LOG_CRIT,"%04d %s %s !ERROR %d (%s) saving message from %s to %s"
,socket, client.protocol, client_id, i, smb.last_error, sender_info, rcpt_info);
sockprintf(socket,client.protocol,session, "452 ERROR %d (%s) saving message"
,i,smb.last_error);
continue;
}
lprintf(LOG_DEBUG,"%04d %s %s Saved message data from %s to %s"
,socket, client.protocol, client_id, sender_info, rcpt_info);
sec_list=iniReadSectionList(rcptlst,NULL); /* Each section is a recipient */
for(rcpt_count=0; sec_list!=NULL
&& sec_list[rcpt_count]!=NULL
&& (startup->max_recipients==0 || rcpt_count<startup->max_recipients); rcpt_count++) {
section=sec_list[rcpt_count];
SAFECOPY(rcpt_to,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);
SAFECOPY(rcpt_addr,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENTNETADDR),str,value));
SAFECOPY(forward_path, iniReadString(rcptlst, section, smb_hfieldtype(SMTPFORWARDPATH), "", value));
if(compare_addrs(rcpt_to, rcpt_addr) == 0)
angle_bracket(rcpt_info, sizeof(rcpt_info), rcpt_addr);
else
safe_snprintf(rcpt_info, sizeof(rcpt_info), "'%s' %s", rcpt_to, angle_bracket(tmp, sizeof(tmp), rcpt_addr));

rswindell
committed
if(nettype==NET_NONE /* Local destination */ && usernum==0) {
lprintf(LOG_ERR,"%04d %s %s !Can't deliver mail from %s to user #0"
,socket, client.protocol, client_id, sender_info);
break;
}
if((i=smb_copymsgmem(&smb,&newmsg,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s %s !ERROR %d (%s) copying message from %s"
,socket, client.protocol, client_id, i, smb.last_error, sender_info);
break;
}
with_val = 0;
if (esmtp)
with_val |= WITH_ESMTP;
if (auth_login)
with_val |= WITH_AUTH;
if (session != -1)
with_val |= WITH_TLS;

rswindell
committed
snprintf(hdrfield,sizeof(hdrfield),
" by %s [%s%s] (%s %s%c-%s) with %s\r\n"
" for %s; %s\r\n"
" (envelope-from %s)"
,smtp->client_addr.addr.sa_family==AF_INET6?"IPv6: ":""
,server_host_name()
,server_addr.addr.sa_family==AF_INET6?"IPv6: ":""
,server_ip
,server_name
,VERSION, REVISION, PLATFORM_DESC
,with_clauses[with_val]
,forward_path,msgdate(msg.hdr.when_imported,date)
,reverse_path);
smb_hfield_add_str(&newmsg, SMTPRECEIVED, hdrfield, /* insert: */TRUE);

rswindell
committed
if(nettype == NET_FIDO) {
newmsg.hdr.netattr |= MSG_LOCAL | MSG_KILLSENT;
char* tp = strchr(rcpt_name, '@');
if(tp != NULL)
*tp = 0;
// Remove "(ftn_addr)" portion of to name
SAFEPRINTF(str,"(%s)", rcpt_addr);
if((tp = strstr(rcpt_name, str)) != NULL && tp != rcpt_name) {
*tp = 0;
truncsp(rcpt_name);
}
if(!matchusername(&scfg, rcpt_name, rcpt_to)) {
SAFECOPY(rcpt_name, rcpt_to);
truncstr(rcpt_name, "@");
}
}
smb_hfield_str(&newmsg, RECIPIENT, rcpt_name);
if(forward_path[0] != 0)
smb_hfield_str(&newmsg, SMTPFORWARDPATH, forward_path);

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

rswindell
committed
}
if(agent!=newmsg.to_agent)
smb_hfield(&newmsg, RECIPIENTAGENT, sizeof(agent), &agent);
add_msg_ids(&scfg, &smb, &newmsg, /* remsg: */NULL);
lprintf(LOG_DEBUG,"%04d %s %s Adding message header from %s to %s"
,socket, client.protocol, client_id, sender_info, rcpt_info);
i=smb_addmsghdr(&smb,&newmsg,smb_storage_mode(&scfg, &smb));

rswindell
committed
smb_freemsgmem(&newmsg);
if(i!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d %s %s !ERROR %d (%s) adding message header from %s to %s"
,socket, client.protocol, client_id, i, smb.last_error, sender_info, rcpt_info);

rswindell
committed
break;
lprintf(LOG_INFO,"%04d %s %s Added message header #%u from %s to %s"
,socket, client.protocol, client_id, newmsg.hdr.number, sender_info, rcpt_info);
if(relay_user.number!=0) {
lprintf(LOG_DEBUG, "%04d %s %s #%u sent %u email messages today (of %u max), %u total"
,socket, client.protocol, client_id, relay_user.number
,relay_user.etoday, scfg.level_emailperday[relay_user.level], relay_user.emails);
}
if(nettype == NET_FIDO && scfg.netmail_sem[0])
ftouch(mailcmdstr(scfg.netmail_sem
,msgtxt_fname, newtxt_fname, logtxt_fname
,rcptlst_fname, proc_err_fname
,host_name, host_ip, relay_user.number
,rcpt_addr
,sender, sender_addr, reverse_path, str));
if(!(startup->options&MAIL_OPT_NO_NOTIFY) && usernum) {

Rob Swindell
committed
if(!newmsg.idx.to || startup->notify_offline_users || is_user_online(&scfg, usernum)) {
p=sender_addr;
if(stricmp(sender, sender_addr) == 0) {
if((p = strchr(sender_addr, '@')) == NULL)
p = sender_addr;
else
p++;
}
safe_snprintf(str,sizeof(str)
,text[InternetMailReceived]
,timestr(&scfg,newmsg.hdr.when_imported.time,tmp)
,sender, p);
if(newmsg.hdr.auxattr&MSG_HFIELDS_UTF8)
utf8_to_cp437_inplace(str);
if(!newmsg.idx.to) /* Forwarding */
sprintf(str+strlen(str), text[InternetMailForwarded], rcpt_addr);
putsmsg(&scfg, usernum, str);

rswindell
committed
}
iniFreeStringList(sec_list);
if(rcpt_count<1) {
smb_freemsg_dfields(&smb,&msg,SMB_ALL_REFS);
sockprintf(socket,client.protocol,session, insuf_stor);

rswindell
committed
}
else {
if(rcpt_count>1)
smb_incmsg_dfields(&smb,&msg,(ushort)(rcpt_count-1));
sockprintf(socket,client.protocol,session,ok_rsp);
signal_smtp_sem();
#if 0 /* This shouldn't be necessary here */
smb_close_da(&smb);
#endif
smb_close(&smb);
pthread_mutex_unlock(&savemsg_mutex);
if(buf[0]==0 && state==SMTP_STATE_DATA_HEADER) {
state=SMTP_STATE_DATA_BODY; /* Null line separates header and body */
if(msgtxt!=NULL) {
fputs("\r\n", msgtxt);
hdr_len=ftell(msgtxt);
}
continue;
}
if(state==SMTP_STATE_DATA_BODY) {
p=buf;
if(*p=='.') p++; /* Transparency (RFC821 4.5.2) */
if(strlen(p) > RFC822_MAX_LINE_LEN) {
lprintf(LOG_NOTICE, "%04d %s %s !%s sent an ILLEGALLY-LONG body line (%d chars > %d): '%s'"
,socket, client.protocol, client_id, reverse_path, (int)strlen(p), RFC822_MAX_LINE_LEN, p);
sockprintf(socket, client.protocol, session, "500 Line too long (body)");
break;
}
if(msgtxt!=NULL) {
fputs("\r\n", msgtxt);
}
if((lines%100) == 0 && (msgtxt != NULL))
lprintf(LOG_DEBUG,"%04d %s %s received %lu lines (%lu bytes) of body text"
,socket, client.protocol, client_id, lines, ftell(msgtxt)-hdr_len);
continue;
}
/* RFC822 Header parsing */