Newer
Older
spamlog(&scfg, "SMTP", "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 SMTP TAGGED MAIL SUBJECT from blacklisted server with: %s"
,socket, startup->dnsbl_tag);
}
}
smb_hfield_str(&msg, hfield_type=SUBJECT, p);
continue;
}
if(relay_user.number==0 && stricmp(field, "FROM")==0
&& !chk_email_addr(socket,p,host_name,host_ip,rcpt_addr,reverse_path,"FROM")) {
errmsg="554 Sender not allowed.";
smb_error=SMB_FAILURE;
break;
}
if(relay_user.number==0 && stricmp(field, "TO")==0 && !spam_bait_result
&& !chk_email_addr(socket,p,host_name,host_ip,rcpt_addr,reverse_path,"TO")) {
errmsg="550 Unknown user.";
smb_error=SMB_FAILURE;
break;
}
}
if((smb_error=parse_header_field((char*)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);
stats.msgs_refused++;
}
if((p=smb_get_hfield(&msg, RFC822TO, NULL))!=NULL) {
parse_mail_address(p
,rcpt_name ,sizeof(rcpt_name)-1
,rcpt_addr ,sizeof(rcpt_addr)-1);
}
if((p=smb_get_hfield(&msg, RFC822FROM, NULL))!=NULL) {
parse_mail_address(p
,sender ,sizeof(sender)-1
,sender_addr,sizeof(sender_addr)-1);
}
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 && !(startup->options&MAIL_OPT_DNSBL_IGNORE)) {
/* 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 (%u total)"
,socket, ++stats.msgs_refused);

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

rswindell
committed
continue;
}
if(relay_user.number) {
SAFEPRINTF(str,"%u",relay_user.number);
smb_hfield_str(&msg, SENDEREXT, str);
}
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(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;
stats.msgs_refused++;
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,&client,&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;
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, startup->host_name, 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);
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 SMTP Calculating message hashes (sources=%lx, msglen=%u)"
,socket, sources, strlen(msgbuf));
if((hashes=smb_msghashes(&msg, (uchar*)msgbuf, sources)) != NULL) {
for(i=0;hashes[i];i++)
lprintf(LOG_DEBUG,"%04d SMTP Message %s crc32=%lx flags=%lx length=%u"
,socket, smb_hashsourcetype(hashes[i]->source)
,hashes[i]->crc32, hashes[i]->flags, hashes[i]->length);
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 SMTP Message %s", socket, str);
if(!is_spam) {
spamlog(&scfg, "SMTP", "IGNORED"
,str, host_name, host_ip, rcpt_addr, reverse_path);
is_spam=TRUE;
}
} else if(i!=SMB_ERR_NOT_FOUND)
lprintf(LOG_ERR,"%04d !SMTP ERROR %d (%s) opening SPAM database"
,socket, 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 SMTP Adding message %s (%s) to SPAM database"
,socket
,smb_hashsourcetype(hashes[n]->source)
,smb_hashsource(&msg,hashes[n]->source)
);
}
lprintf(LOG_DEBUG,"%04d SMTP Adding %u message hashes to SPAM database", socket, 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 SMTP smb_msghashes returned NULL", socket);
if(is_spam || ((startup->options&MAIL_OPT_DNSBL_IGNORE) && (dnsbl_recvhdr || dnsbl_result.s_addr))) {
lprintf(LOG_NOTICE,"%04d !SMTP IGNORED SPAM MESSAGE (%u total)"
,socket, ++stats.msgs_ignored);
SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
lprintf(LOG_NOTICE,"%04d !SMTP IGNORED MAIL from server: %s (%u total)"
,socket, str, ++stats.msgs_ignored);
spamlog(&scfg, "SMTP", "IGNORED"
,str, host_name, dnsbl_ip, rcpt_addr, reverse_path);
/* pretend we received it */
sockprintf(socket,ok_rsp);
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, startup->host_name, msgbuf);
free(msgbuf);
if(i!=SMB_SUCCESS) {
smb_close(&smb);
lprintf(LOG_CRIT,"%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;
}
lprintf(LOG_DEBUG,"%04d SMTP Recipient name: '%s'", socket, rcpt_name);
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));

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%s])\r\n"
" by %s [%s%s] (%s %s-%s) with %s\r\n"
" for %s; %s\r\n"
" (envelope-from %s)"
,host_name,hello_name
,smtp.client_addr.addr.sa_family==AF_INET6?"IPv6: ":""
,host_ip
,startup->host_name
,server_addr.addr.sa_family==AF_INET6?"IPv6: ":""
,server_ip
,server_name
,revision,PLATFORM_DESC

rswindell
committed
,esmtp ? "ESMTP" : "SMTP"
,rcpt_to,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 */
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);

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;
sender_ext[0]=0;
if(msg.from_ext!=NULL)
SAFEPRINTF(sender_ext," #%s",msg.from_ext);
lprintf(LOG_INFO,"%04d SMTP Created message #%ld from %s%s [%s] to %s [%s]"
,socket, newmsg.hdr.number, sender, sender_ext, smb_netaddrstr(&msg.from_net,tmp), rcpt_name, rcpt_addr);
if(relay_user.number!=0)
user_sent_email(&scfg, &relay_user, 1, usernum==1);
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) {
if(newmsg.idx.to)
for(i=1;i<=scfg.sys_nodes;i++) {
getnodedat(&scfg, i, &node, 0);
if(node.useron==usernum
&& (node.status==NODE_INUSE || node.status==NODE_QUIET))
break;
}
if(!newmsg.idx.to || i<=scfg.sys_nodes) {
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)
,"\7\1n\1hOn %.24s\r\n\1m%s \1n\1msent you \1h\1we-mail\1n\1m from: "
"\1h%s\1n\r\n"
,timestr(&scfg,newmsg.hdr.when_imported.time,tmp)
,sender, p);
if(!newmsg.idx.to) { /* Forwarding */
strcat(str,"\1mand it was automatically forwarded to: \1h");
strcat(str,rcpt_addr);
strcat(str,"\1n\r\n");
}
putsmsg(&scfg, usernum, str);

rswindell
committed
}
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) {
switch(content_encoding) {
case ENCODING_BASE64:
{
char decode_buf[sizeof(buf)];
if(b64_decode(decode_buf, sizeof(decode_buf), p, strlen(p))<0)
fprintf(msgtxt,"\r\n!Base64 decode error: %s\r\n", p);
else
fputs(decode_buf, msgtxt);
}
break;
case ENCODING_QUOTED_PRINTABLE:
fputs(qp_decode(p), msgtxt);
break;
default:
fprintf(msgtxt, "%s\r\n", p);
break;
}
}
/* release time-slices every x lines */
if(startup->lines_per_yield &&
!(lines%startup->lines_per_yield))
YIELD();
continue;
}
/* RFC822 Header parsing */
strip_ctrl(buf, buf); /* There should be no control characters in header fields */
if(startup->options&MAIL_OPT_DEBUG_RX_HEADER)
lprintf(LOG_DEBUG,"%04d SMTP %s",socket, buf);
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
{
char field[32];
if((p=get_header_field(buf, field, sizeof(field)))!=NULL) {
if(stricmp(field, "FROM")==0) {
parse_mail_address(p
,sender, sizeof(sender)-1
,sender_addr, sizeof(sender_addr)-1);
}
else if(stricmp(field,"CONTENT-TRANSFER-ENCODING")==0) {
lprintf(LOG_INFO,"%04d SMTP %s = %s", socket, field, p);
if(stricmp(p,"base64")==0)
content_encoding=ENCODING_BASE64;
else if(stricmp(p,"quoted-printable")==0)
content_encoding=ENCODING_QUOTED_PRINTABLE;
else { /* Other (e.g. 7bit, 8bit, binary) */
content_encoding=ENCODING_NONE;
if(msgtxt!=NULL)
fprintf(msgtxt, "%s\r\n", buf);
}
hdr_lines++;
continue;
}
}
}
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");
sockprintf(socket,"250-SEND");
sockprintf(socket,"250-SOML");
sockprintf(socket,"250-SAML");
sockprintf(socket,"250-8BITMIME");
if(startup->max_msg_size)
sockprintf(socket,"250 SIZE %lu", startup->max_msg_size);
esmtp=TRUE;
state=SMTP_STATE_HELO;

rswindell
committed
telegram=FALSE;
subnum=INVALID_SUB;
ZERO_VAR(user_pass);
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) {
lprintf(LOG_WARNING,"%04d !SMTP missing AUTH LOGIN username argument", socket);
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) {
lprintf(LOG_WARNING,"%04d !SMTP bad AUTH LOGIN username argument", socket);
sockprintf(socket,badarg_rsp);
continue;
}
sockprintf(socket,"334 UGFzc3dvcmQ6"); /* Base64-encoded "Password:" */
if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
lprintf(LOG_WARNING,"%04d !SMTP missing AUTH LOGIN password argument", socket);
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) {
lprintf(LOG_WARNING,"%04d !SMTP bad AUTH LOGIN password argument", socket);
sockprintf(socket,badarg_rsp);
continue;
}
} else { /* AUTH PLAIN b64(<username>\0<user-id>\0<password>) */
p=buf+10;
SKIP_WHITESPACE(p);
if(*p==0) {
lprintf(LOG_WARNING,"%04d !SMTP missing AUTH PLAIN argument", socket);
sockprintf(socket,badarg_rsp);
continue;
}
ZERO_VAR(tmp);
if(b64_decode(tmp,sizeof(tmp),p,strlen(p))<1) {
lprintf(LOG_WARNING,"%04d !SMTP bad AUTH PLAIN argument", socket);
sockprintf(socket,badarg_rsp);
continue;
}
p=tmp;
while(*p) p++; /* skip username */
p++; /* skip NULL */
if(*p==0) {
lprintf(LOG_WARNING,"%04d !SMTP missing AUTH PLAIN user-id argument", socket);
sockprintf(socket,badarg_rsp);
continue;
}
SAFECOPY(user_name,p);
while(*p) p++; /* skip user-id */
p++; /* skip NULL */
if(*p==0) {
lprintf(LOG_WARNING,"%04d !SMTP missing AUTH PLAIN password argument", socket);
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)

rswindell
committed
lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: '%s' (password: %s)"
,socket, user_name, user_pass);
else

rswindell
committed
lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: '%s'"
,socket, user_name);
badlogin(socket, client.protocol, badauth_rsp, user_name, user_pass, host_name, &smtp.client_addr);
break;
}
if((i=getuserdat(&scfg, &relay_user))!=0) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d getting data on user (%s)"
,socket, i, user_name);
badlogin(socket, client.protocol, badauth_rsp, NULL, NULL, NULL, NULL);
break;
}
if(relay_user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_WARNING,"%04d !SMTP DELETED or INACTIVE user #%u (%s)"
,socket, relay_user.number, user_name);
badlogin(socket, client.protocol, badauth_rsp, NULL, NULL, NULL, NULL);
break;
}
if(stricmp(user_pass,relay_user.pass)) {
if(scfg.sys_misc&SM_ECHO_PW)
lprintf(LOG_WARNING,"%04d !SMTP FAILED Password attempt for user %s: '%s' expected '%s'"
,socket, user_name, user_pass, relay_user.pass);
else
lprintf(LOG_WARNING,"%04d !SMTP FAILED Password attempt for user %s"
,socket, user_name);
badlogin(socket, client.protocol, badauth_rsp, user_name, user_pass, host_name, &smtp.client_addr);
break;
}
if(relay_user.pass[0])
loginSuccess(startup->login_attempt_list, &smtp.client_addr);
/* Update client display */
client.user=relay_user.alias;
client_on(socket,&client,TRUE /* update */);
lprintf(LOG_INFO,"%04d SMTP %s authenticated using %s authentication"
,socket,relay_user.alias,auth_login ? "LOGIN" : "PLAIN");
sockprintf(socket,auth_ok);
continue;
}
if(!stricmp(buf,"AUTH CRAM-MD5")) {
safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%s>"
,rand(),socket,(ulong)time(NULL),clock(),startup->host_name);
lprintf(LOG_DEBUG,"%04d SMTP CRAM-MD5 challenge: %s"
,socket,challenge);
b64_encode(str,sizeof(str),challenge,0);
sockprintf(socket,"334 %s",str);
if((rd=sockreadline(socket, buf, sizeof(buf)))<1) {
lprintf(LOG_WARNING,"%04d !SMTP missing AUTH CRAM-MD5 response", socket);
sockprintf(socket,badarg_rsp);
continue;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX: %s",socket,buf);
if(b64_decode(response,sizeof(response),buf,rd)<1) {
lprintf(LOG_WARNING,"%04d !SMTP bad AUTH CRAM-MD5 response", socket);
sockprintf(socket,badarg_rsp);
continue;
}
#if 0
lprintf(LOG_DEBUG,"%04d SMTP CRAM-MD5 response: %s"
,socket,response);
if((p=strrchr(response,' '))!=NULL)
*(p++)=0;
else
p=response;
SAFECOPY(user_name,response);
if((relay_user.number=matchuser(&scfg,user_name,FALSE))==0) {

rswindell
committed
lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: '%s'"
,socket, user_name);
badlogin(socket, client.protocol, badauth_rsp, user_name, NULL, host_name, &smtp.client_addr);
break;
}
if((i=getuserdat(&scfg, &relay_user))!=0) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d getting data on user (%s)"
,socket, i, user_name);
badlogin(socket, client.protocol, badauth_rsp, NULL, NULL, NULL, NULL);
break;
}
if(relay_user.misc&(DELETED|INACTIVE)) {
lprintf(LOG_WARNING,"%04d !SMTP DELETED or INACTIVE user #%u (%s)"
,socket, relay_user.number, user_name);
badlogin(socket, client.protocol, badauth_rsp, NULL, NULL, NULL, NULL);
break;
}
/* Calculate correct response */
memset(secret,0,sizeof(secret));
SAFECOPY(secret,relay_user.pass);
strlwr(secret); /* this is case sensitive, so convert to lowercase first */
for(i=0;i<sizeof(secret);i++)
md5_data[i]=secret[i]^0x36; /* ipad */
strcpy(md5_data+i,challenge);
MD5_calc(digest,md5_data,sizeof(secret)+strlen(challenge));
for(i=0;i<sizeof(secret);i++)
md5_data[i]=secret[i]^0x5c; /* opad */
memcpy(md5_data+i,digest,sizeof(digest));
MD5_calc(digest,md5_data,sizeof(secret)+sizeof(digest));
MD5_hex((BYTE*)str,digest);
if(strcmp(p,str)) {
lprintf(LOG_WARNING,"%04d !SMTP %s FAILED CRAM-MD5 authentication"
,socket,relay_user.alias);
lprintf(LOG_DEBUG,"%04d !SMTP calc digest: %s"
,socket,str);
lprintf(LOG_DEBUG,"%04d !SMTP resp digest: %s"
,socket,p);
badlogin(socket, client.protocol, badauth_rsp, user_name, p, host_name, &smtp.client_addr);
break;
}
if(relay_user.pass[0])
loginSuccess(startup->login_attempt_list, &smtp.client_addr);
/* Update client display */
client.user=relay_user.alias;
client_on(socket,&client,TRUE /* update */);
lprintf(LOG_INFO,"%04d SMTP %s authenticated using CRAM-MD5 authentication"
,socket,relay_user.alias);
sockprintf(socket,auth_ok);
continue;
}
if(!strnicmp(buf,"AUTH",4)) {
sockprintf(socket,"504 Unrecognized authentication type.");
continue;
}
sockprintf(socket,"221 %s Service closing transmission channel",startup->host_name);
break;
}
if(!stricmp(buf,"NOOP")) {
sockprintf(socket, ok_rsp);
badcmds=0;
continue;
}
if(state<SMTP_STATE_HELO) {
/* RFC 821 4.1.1 "The first command in a session must be the HELO command." */
lprintf(LOG_WARNING,"%04d !SMTP MISSING 'HELO' command",socket);
sockprintf(socket, badseq_rsp);
continue;
}
if(!stricmp(buf,"TURN")) {
sockprintf(socket,"502 command not supported");
badcmds=0;
continue;
}
if(!stricmp(buf,"RSET")) {
smb_freemsgmem(&msg);
memset(&msg,0,sizeof(smbmsg_t)); /* Initialize message header */
reverse_path[0]=0;
state=SMTP_STATE_HELO;

rswindell
committed
telegram=FALSE;
subnum=INVALID_SUB;
/* reset recipient list */
if((rcptlst=freopen(rcptlst_fname,"w+",rcptlst))==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d re-opening %s"
,socket, errno, rcptlst_fname);
sockprintf(socket,sys_error);
break;
}
rcpt_count=0;
content_encoding=ENCODING_NONE;
memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count);
sockprintf(socket,ok_rsp);
badcmds=0;
lprintf(LOG_INFO,"%04d SMTP Session reset",socket);
if(!strnicmp(buf,"MAIL FROM:",10)
|| !strnicmp(buf,"SEND FROM:",10) /* Send a Message (Telegram) to a local ONLINE user */
|| !strnicmp(buf,"SOML FROM:",10) /* Send OR Mail a Message to a local user */
|| !strnicmp(buf,"SAML FROM:",10) /* Send AND Mail a Message to a local user */
) {
if(relay_user.number==0
&& !chk_email_addr(socket,p,host_name,host_ip,NULL,NULL,"REVERSE PATH")) {
sockprintf(socket, "554 Sender not allowed.");
}
SKIP_WHITESPACE(p);
if((p=strchr(reverse_path,' '))!=NULL) /* Truncate "<user@domain> KEYWORD=VALUE" to just "<user@domain>" per RFC 1869 */
*p=0;
/* If MAIL FROM address is in dnsbl_exempt.cfg, clear DNSBL results */
if(dnsbl_result.s_addr && email_addr_is_exempt(reverse_path)) {
lprintf(LOG_INFO,"%04d SMTP Ignoring DNSBL results for exempt sender: %s"
,socket,reverse_path);
dnsbl_result.s_addr=0;
}
/* Update client display */
if(relay_user.number==0) {
client.user=reverse_path;
client_on(socket,&client,TRUE /* update */);
}
/* Setup state */
if(!strnicmp(buf,"MAIL FROM:",10))
cmd=SMTP_CMD_MAIL;
else if(!strnicmp(buf,"SEND FROM:",10))
cmd=SMTP_CMD_SEND;
else if(!strnicmp(buf,"SOML FROM:",10))
cmd=SMTP_CMD_SOML;
else if(!strnicmp(buf,"SAML FROM:",10))
cmd=SMTP_CMD_SAML;
/* reset recipient list */
if((rcptlst=freopen(rcptlst_fname,"w+",rcptlst))==NULL) {
lprintf(LOG_ERR,"%04d !SMTP ERROR %d re-opening %s"
,socket, errno, rcptlst_fname);
sockprintf(socket,sys_error);
break;
}
rcpt_count=0;
content_encoding=ENCODING_NONE;
memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count);
sockprintf(socket,ok_rsp);
badcmds=0;

rswindell
committed
continue;
}
#if 0 /* No one uses this command */
if(!strnicmp(buf,"VRFY",4)) {
p=buf+4;
SKIP_WHITESPACE(p);
if(*p==0) {
sockprintf(socket,"550 No user specified.");
continue;
}
#endif
/* Add to Recipient list */
if(!strnicmp(buf,"RCPT TO:",8)) {
lprintf(LOG_WARNING,"%04d !SMTP MISSING 'MAIL' command",socket);
sockprintf(socket, badseq_rsp);
SKIP_WHITESPACE(p);
p=strrchr(str,'<');
if(p==NULL)
p=str;
else
p++;
truncstr(str,">"); /* was truncating at space too */
routed=FALSE;
forward=FALSE;
no_forward=FALSE;
if(!strnicmp(p,FORWARD,strlen(FORWARD))) {
forward=TRUE; /* force forward to user's netmail address */
p+=strlen(FORWARD);
}
if(!strnicmp(p,NO_FORWARD,strlen(NO_FORWARD))) {
no_forward=TRUE; /* do not forward to user's netmail address */
p+=strlen(NO_FORWARD);
}
if(*p==0) {
lprintf(LOG_NOTICE,"%04d !SMTP NO RECIPIENT SPECIFIED"
,socket);
sockprintf(socket, "500 No recipient specified");
continue;
}
rcpt_name[0]=0;
/* Check recipient counter */
if(startup->max_recipients) {
if(rcpt_count>=startup->max_recipients) {
lprintf(LOG_NOTICE,"%04d !SMTP MAXIMUM RECIPIENTS (%d) REACHED"
,socket, startup->max_recipients);
SAFEPRINTF(tmp,"Maximum recipient count (%d)",startup->max_recipients);
spamlog(&scfg, "SMTP", "REFUSED", tmp
,host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket, "452 Too many recipients");
stats.msgs_refused++;
continue;
}
if(relay_user.number!=0 && !(relay_user.exempt&FLAG('M'))
&& rcpt_count+(waiting=getmail(&scfg,relay_user.number,/* sent: */TRUE)) > startup->max_recipients) {
lprintf(LOG_NOTICE,"%04d !SMTP MAXIMUM PENDING SENT EMAILS (%u) REACHED for User #%u (%s)"
,socket, waiting, relay_user.number, relay_user.alias);
sockprintf(socket, "452 Too many pending emails sent");
stats.msgs_refused++;
continue;
}
if(relay_user.number && (relay_user.etoday+rcpt_count) >= scfg.level_emailperday[relay_user.level]
&& !(relay_user.exempt&FLAG('M'))) {
lprintf(LOG_NOTICE,"%04d !SMTP EMAILS PER DAY LIMIT (%u) REACHED FOR USER #%u (%s)"
,socket, scfg.level_emailperday[relay_user.level], relay_user.number, relay_user.alias);
SAFEPRINTF2(tmp,"Maximum emails per day (%u) for %s"
,scfg.level_emailperday[relay_user.level], relay_user.alias);
spamlog(&scfg, "SMTP", "REFUSED", tmp
,host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket, "452 Too many emails today");
stats.msgs_refused++;
continue;
}
/* Check for SPAM bait recipient */
if((spam_bait_result=findstr(rcpt_addr,spam_bait))==TRUE) {
char reason[256];
SAFEPRINTF(reason,"SPAM BAIT (%s) taken", rcpt_addr);
lprintf(LOG_NOTICE,"%04d SMTP %s by: %s"
,socket, reason, reverse_path);
strcpy(tmp,"IGNORED");
if(dnsbl_result.s_addr==0 /* Don't double-filter */
lprintf(LOG_NOTICE,"%04d !BLOCKING IP ADDRESS: %s in %s", socket, host_ip, spam_block);
filter_ip(&scfg, "SMTP", reason, host_name, host_ip, reverse_path, spam_block);
strcat(tmp," and BLOCKED");
}
spamlog(&scfg, "SMTP", tmp, "Attempted recipient in SPAM BAIT list"
,host_name, host_ip, rcpt_addr, reverse_path);
dnsbl_result.s_addr=0;
}
sockprintf(socket,ok_rsp);
state=SMTP_STATE_RCPT_TO;
continue;
}
/* Check for blocked recipients */
if(relay_user.number==0
&& !chk_email_addr(socket,rcpt_addr,host_name,host_ip,rcpt_addr,reverse_path,"RECIPIENT")) {
sockprintf(socket, "550 Unknown User: %s", rcpt_to);
stats.msgs_refused++;
continue;
}
if(relay_user.number==0 && dnsbl_result.s_addr && startup->options&MAIL_OPT_DNSBL_BADUSER) {
lprintf(LOG_NOTICE,"%04d !SMTP REFUSED MAIL from blacklisted server (%u total)"
,socket, ++stats.sessions_refused);
SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
spamlog(&scfg, "SMTP", "REFUSED", str, host_name, host_ip, rcpt_addr, reverse_path);
sockprintf(socket
,"550 Mail from %s refused due to listing at %s"
,host_ip, dnsbl);
break;
}
if(spy==NULL
&& (trashcan(&scfg,reverse_path,"smtpspy")
|| trashcan(&scfg,rcpt_addr,"smtpspy"))) {
SAFEPRINTF(path,"%ssmtpspy.txt", scfg.logs_dir);
spy=fopen(path,"a");
}
/* Check for full address aliases */
p=alias(&scfg,p,alias_buf);
if(p==alias_buf)
lprintf(LOG_DEBUG,"%04d SMTP ADDRESS ALIAS: %s (for %s)"
,socket,p,rcpt_addr);
tp=strrchr(p,'@');
if(cmd==SMTP_CMD_MAIL && tp!=NULL) {
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
if(relay_user.number && scfg.total_faddrs) {
char* ftn_tld = strstr(dest_host, FIDO_TLD);
if(ftn_tld != NULL && ftn_tld[strlen(FIDO_TLD)] == 0) {
fidoaddr_t faddr = scfg.faddr[0];
faddr.point = 0;
if((sscanf(dest_host,"p%hu.f%hu.n%hu.z%hu.fidonet"
,&faddr.point
,&faddr.node
,&faddr.net
,&faddr.zone)==4
||
sscanf(dest_host,"f%hu.n%hu.z%hu.fidonet"
,&faddr.node
,&faddr.net
,&faddr.zone)==3
) && faddr.zone) {
lprintf(LOG_INFO,"%04d SMTP %s relaying to FidoNet address: %s (%s)"
,socket, relay_user.alias, tp+1, smb_faddrtoa(&faddr, NULL));
fprintf(rcptlst,"[%u]\n",rcpt_count++);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_FIDO);
fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),smb_faddrtoa(&faddr,NULL));
sockprintf(socket,ok_rsp);
state=SMTP_STATE_RCPT_TO;
continue;
}
}
}
cp=strrchr(dest_host,':');
if(cp!=NULL) {
*cp=0;
dest_port=atoi(cp+1);
}
SAFEPRINTF(domain_list,"%sdomains.cfg",scfg.ctrl_dir);
if((stricmp(dest_host,scfg.sys_inetaddr)!=0
&& stricmp(dest_host,startup->host_name)!=0
&& findstr(dest_host,domain_list)==FALSE)
SAFEPRINTF(relay_list,"%srelay.cfg",scfg.ctrl_dir);
if(relay_user.number==0 /* not authenticated, search for IP */
&& startup->options&MAIL_OPT_SMTP_AUTH_VIA_IP) {
relay_user.number=userdatdupe(&scfg, 0, U_IPADDR, LEN_IPADDR, host_ip, /* del */FALSE, /* next */FALSE);
if(relay_user.number) {
getuserdat(&scfg,&relay_user);