Newer
Older
/* Synchronet Mail (SMTP/POP3) server and sendmail threads */
/* $Id$ */
// vi: tabstop=4
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include <stdlib.h> /* ltoa in GNU C lib */
#include <stdarg.h> /* va_list */
#include <string.h> /* strrchr */
#include <ctype.h> /* isdigit */
#include <fcntl.h> /* Open flags */
#include <errno.h> /* errno */
#undef SBBS /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
#include "sbbs.h"
#include "mime.h"
#include "md5.h"
#include "base64.h"
#include "ini_file.h"
#include "netwrap.h" /* getNameServerList() */
#include "xpendian.h"
#include "js_rtpool.h"
#include "js_request.h"
#include "ssl.h"
#include "cryptlib.h"
static const char* server_name="Synchronet Mail Server";
#define FORWARD "forward:"
#define NO_FORWARD "local:"
#define NO_SPAM "#nospam"

rswindell
committed
int dns_getmx(char* name, char* mx, char* mx2
,DWORD intf, DWORD ip_addr, BOOL use_tcp, int timeout);
static char* ok_rsp = "250 OK";
static char* auth_ok = "235 User Authenticated";
static char* sys_error = "421 System error";
static char* sys_unavail= "421 System unavailable, try again later";
static char* insuf_stor = "452 Insufficient system storage";
static char* badarg_rsp = "501 Bad argument";
static char* badseq_rsp = "503 Bad sequence of commands";
static char* badauth_rsp= "535 Authentication failure";
static char* badrsp_err = "%s replied with:\r\n\"%s\"\r\n"
"instead of the expected reply:\r\n\"%s ...\"";
#define TIMEOUT_THREAD_WAIT 60 /* Seconds */
#define DNSBL_THROTTLE_VALUE 1000 /* Milliseconds */
#define STATUS_WFC "Listening"
static mail_startup_t* startup=NULL;
static scfg_t scfg;
static struct xpms_set *mail_set=NULL;
static BOOL terminated=FALSE;
static protected_uint32_t active_clients;
static protected_uint32_t thread_count;
static volatile int active_sendmail=0;
static volatile BOOL sendmail_running=FALSE;
static volatile BOOL terminate_server=FALSE;
static volatile BOOL terminate_sendmail=FALSE;
static sem_t sendmail_wakeup_sem;
static char revision[16];
static volatile time_t uptime;
static str_list_t recycle_semfiles;
static str_list_t shutdown_semfiles;
static int mailproc_count;
static js_server_props_t js_server_props;
struct {
volatile ulong sockets;
volatile ulong errors;
volatile ulong crit_errors;
volatile ulong connections_ignored;
volatile ulong connections_refused;
volatile ulong connections_served;
volatile ulong pop3_served;
volatile ulong smtp_served;
/* SMTP: */
volatile ulong sessions_refused;
volatile ulong msgs_ignored;
volatile ulong msgs_refused;
volatile ulong msgs_received;
} stats;
struct mailproc {
char cmdline[INI_MAX_VALUE_LEN];
str_list_t to;
str_list_t from;
BOOL passthru;
BOOL native;
BOOL ignore_on_error; /* Ignore mail message if cmdline fails */
BOOL disabled;
BOOL process_spam;
BOOL process_dnsbl;
uint8_t* ar;
ulong handled; /* counter (for stats display) */
} *mailproc_list;
typedef struct {
SOCKET socket;
BOOL tls_port;
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
lprintf(GCES_level, "%04d %s/TLS %s", sock, server, GCES_estr); \
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
lprintf(GCES_level, "%04d %s/TLS [%s] %s", sock, server, host, GCES_estr); \
static int lprintf(int level, const char *fmt, ...)
{
va_list argptr;
char sbuf[1024];
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0;
if(level <= LOG_ERR) {
char errmsg[sizeof(sbuf)+16];
SAFEPRINTF(errmsg, "mail %s", sbuf);
errorlog(&scfg,startup==NULL ? NULL:startup->host_name,errmsg), stats.errors++;
if(startup!=NULL && startup->errormsg!=NULL)
startup->errormsg(startup->cbdata,level,errmsg);
}
if(level <= LOG_CRIT)
stats.crit_errors++;
if(startup==NULL || startup->lputs==NULL || level > startup->log_level)
return(0);
#if defined(_WIN32)
if(IsBadCodePtr((FARPROC)startup->lputs))
return(0);
#endif
return(startup->lputs(startup->cbdata,level,sbuf));
}
#ifdef _WINSOCKAPI_
static WSADATA WSAData;
#define SOCKLIB_DESC WSAData.szDescription
static BOOL WSAInitialized=FALSE;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
WSAInitialized=TRUE;
lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
return (FALSE);
}
#else /* No WINSOCK */
#define SOCKLIB_DESC NULL
#endif
static void update_clients(void)
{
if(startup!=NULL && startup->clients!=NULL)
startup->clients(startup->cbdata,protected_uint32_value(active_clients)+active_sendmail);
static void client_on(SOCKET sock, client_t* client, BOOL update)
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,TRUE,sock,client,update);
}
static void client_off(SOCKET sock)
{
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
static void thread_up(BOOL setuid)
{
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,TRUE,setuid);
static int32_t thread_down(void)
int32_t count = protected_uint32_adjust(&thread_count,-1);
if(startup!=NULL && startup->thread_up!=NULL)
startup->thread_up(startup->cbdata,FALSE,FALSE);
return count;
char error[256];
char section[128];
startup->socket_open(startup->cbdata,TRUE);
SAFEPRINTF(section,"mail|%s",protocol);
if(set_socket_options(&scfg, sock, section, error, sizeof(error)))
lprintf(LOG_ERR,"%04d !ERROR %s",sock,error);
stats.sockets++;
}
void mail_close_socket_cb(SOCKET sock, void* cb_protocol)
{
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);
stats.sockets--;

rswindell
committed
int mail_close_socket(SOCKET sock)
if(sock==INVALID_SOCKET)
return(-1);
shutdown(sock,SHUT_RDWR); /* required on Unix */
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);
stats.sockets--;
if(result!=0) {
if(ERROR_VALUE!=ENOTSOCK)
lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",sock, ERROR_VALUE);
lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",sock,stats.sockets);
#endif
return(result);
}
static void status(char* str)
{
if(startup!=NULL && startup->status!=NULL)
startup->status(startup->cbdata,str);
int sockprintf(SOCKET sock, CRYPT_SESSION sess, char *fmt, ...)
int maxlen;
int result;
va_list argptr;
char sbuf[1024];
fd_set socket_set;
struct timeval tv;
len=vsnprintf(sbuf,maxlen=sizeof(sbuf)-2,fmt,argptr);
va_end(argptr);
if(len<0 || len > maxlen) /* format error or output truncated */
len=maxlen;
if(startup->options&MAIL_OPT_DEBUG_TX)
lprintf(LOG_DEBUG,"%04d TX%s: %.*s", sock, sess != -1 ? "S" : "", len, sbuf);
memcpy(sbuf+len,"\r\n",2);
if(sock==INVALID_SOCKET) {
lprintf(LOG_WARNING,"!INVALID SOCKET in call to sockprintf");
return(0);
}
/* Check socket for writability (using select) */
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
if((result=select(sock+1,NULL,&socket_set,NULL,&tv))<1) {
lprintf(LOG_NOTICE,"%04d !TIMEOUT selecting socket for send"
lprintf(LOG_NOTICE,"%04d !ERROR %d selecting socket for send"
,sock, ERROR_VALUE);
return(0);
}
if (sess != -1) {
int tls_sent;
int sent = 0;
while (sent < len) {
result = cryptPushData(sess, sbuf+sent, len-sent, &tls_sent);
if (result == CRYPT_OK)
sent += tls_sent;
else {
GCES(result, "", sock, sess, "pushing data");
if ((result = cryptFlushData(sess)) != CRYPT_OK) {
GCES(result, "", sock, sess, "flushing data");
}
else {
// It looks like this could stutter on partial sends -- Deuce
while((result=sendsocket(sock,sbuf,len))!=len) {
if(result==SOCKET_ERROR) {
if(ERROR_VALUE==EWOULDBLOCK) {
YIELD();
continue;
}
if(ERROR_VALUE==ECONNRESET)
lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",sock);
else if(ERROR_VALUE==ECONNABORTED)
lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",sock);
else
lprintf(LOG_NOTICE,"%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
return(0);
}
lprintf(LOG_WARNING,"%04d !ERROR: short send on socket: %d instead of %d",sock,result,len);
}
}
return(len);
}
static void sockerror(SOCKET socket, int rd, const char* action)
lprintf(LOG_NOTICE,"%04d Socket closed by peer on %s"
,socket, action);
lprintf(LOG_NOTICE,"%04d Connection reset by peer on %s"
,socket, action);
lprintf(LOG_NOTICE,"%04d Connection aborted by peer on %s"
,socket, action);
lprintf(LOG_NOTICE,"%04d !SOCKET ERROR %d on %s"
,socket, ERROR_VALUE, action);
lprintf(LOG_WARNING,"%04d !SOCKET ERROR: unexpected return value %d from %s"
,socket, rd, action);
static int sock_recvbyte(SOCKET sock, CRYPT_SESSION sess, char *buf, time_t start)
{
int len=0;
fd_set socket_set;
struct timeval tv;
int ret;
int i;
if (sess > -1) {
while (1) {
ret = cryptPopData(sess, buf, 1, &len);
GCES(ret, "", sock, sess, "popping data");
switch(ret) {
case CRYPT_OK:
break;
case CRYPT_ERROR_TIMEOUT:
return -1;
case CRYPT_ERROR_COMPLETE:
return 0;
default:
if (ret < -1)
return ret;
return -2;
}
if (len)
return len;
if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d !TIMEOUT in sock_recvbyte (%u seconds): INACTIVE SOCKET"
,sock, startup->max_inactivity);
return(-1);
}
}
}
else {
tv.tv_sec=startup->max_inactivity;
tv.tv_usec=0;
FD_ZERO(&socket_set);
FD_SET(sock,&socket_set);
i=select(sock+1,&socket_set,NULL,NULL,&tv);

rswindell
committed
if(i<1) {
if(i==0) {
if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d !TIMEOUT in sock_recvbyte (%u seconds): INACTIVE SOCKET"
,sock, startup->max_inactivity);
return(-1);
}
return 0;
}
sockerror(sock,i,"select");
return 0;
}
i=recv(sock, buf, 1, 0);
if(i<1)
sockerror(sock,i,"receive");
return i;
}
}
static int sockreadline(SOCKET socket, CRYPT_SESSION sess, char* buf, int len)
{
char ch;
int i,rd=0;
time_t start;
if(socket==INVALID_SOCKET) {
lprintf(LOG_WARNING,"!INVALID SOCKET in call to sockreadline");
return(-1);

rswindell
committed
lprintf(LOG_WARNING,"%04d !ABORTING sockreadline",socket);
i = sock_recvbyte(socket, sess, &ch, start);

rswindell
committed
return(-1);
if(ch=='\n' /* && rd>=1 */ ) { /* Mar-9-2003: terminate on sole LF */
if(rd>0 && buf[rd-1]=='\r')
rd--;
buf[rd]=0;
static BOOL sockgetrsp(SOCKET socket, CRYPT_SESSION sess, char* rsp, char *buf, int len)
{
int rd;
while(1) {
rd = sockreadline(socket, sess, buf, len);
if(rd<1) {
if(rd==0 && rsp != NULL)
lprintf(LOG_WARNING,"%04d !RECEIVED BLANK RESPONSE, Expected '%s'", socket, rsp);
if(buf[3]=='-') { /* Multi-line response */
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX%s: %s",socket, sess != -1 ? "S" : "", buf);
if(rsp!=NULL && strnicmp(buf,rsp,strlen(rsp))) {
lprintf(LOG_WARNING,"%04d !INVALID RESPONSE: '%s' Expected: '%s'", socket, buf, rsp);
return(FALSE);
}
break;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX%s: %s",socket,sess != -1 ? "S" : "", buf);
static int sockgetrsp_opt(SOCKET socket, CRYPT_SESSION sess, char* rsp, char *opt, char *buf, int len)
{
int rd;
int ret = 0;
size_t moptlen;
char *mopt;
moptlen = strlen(rsp)+strlen(opt) + 1;
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
mopt = malloc(moptlen+1);
if (mopt == NULL)
return -1;
sprintf(mopt, "%s-%s", rsp, opt);
while(1) {
rd = sockreadline(socket, sess, buf, len);
if(rd<1) {
if(rd==0)
lprintf(LOG_WARNING,"%04d !RECEIVED BLANK RESPONSE, Expected '%s'", socket, rsp);
free(mopt);
return(-1);
}
if(buf[3]=='-') { /* Multi-line response */
if (strncmp(buf, mopt, moptlen) == 0)
ret = 1;
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX%s: %s",socket, sess != -1 ? "S" : "", buf);
continue;
}
if(rsp!=NULL && strnicmp(buf,rsp,strlen(rsp))) {
lprintf(LOG_WARNING,"%04d !INVALID RESPONSE: '%s' Expected: '%s'", socket, buf, rsp);
free(mopt);
return(-1);
}
break;
}
mopt[strlen(rsp)] = ' ';
if (strncmp(buf, mopt, moptlen) == 0)
free(mopt);
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d RX%s: %s",socket,sess != -1 ? "S" : "", buf);
return(ret);
}
/* non-standard, but documented (mostly) in draft-newman-msgheader-originfo-05 */
void originator_info(SOCKET socket, CRYPT_SESSION sess, smbmsg_t* msg)
{
char* user = msg->from_ext;
char* login = smb_get_hfield(msg,SENDERUSERID,NULL);
char* server = smb_get_hfield(msg,SENDERSERVER,NULL);
char* client = smb_get_hfield(msg,SENDERHOSTNAME,NULL);
char* addr = smb_get_hfield(msg,SENDERIPADDR,NULL);
char* prot = smb_get_hfield(msg,SENDERPROTOCOL,NULL);
char* port = smb_get_hfield(msg,SENDERPORT,NULL);
char* time = smb_get_hfield(msg,SENDERTIME,NULL);
if(user || login || server || client || addr || prot || port || time)
,"X-Originator-Info: account=%s; login-id=%s; server=%s; client=%s; addr=%s; prot=%s; port=%s; time=%s"
,user
,login
,server
,client
,addr
,prot
,port
,time
);
}
/* RFC822: The maximum total length of a text line including the
<CRLF> is 1000 characters (but not counting the leading
dot duplicated for transparency).
POP3 (RFC1939) actually calls for a 512 byte line length limit!
*/
#define MAX_LINE_LEN 998
static ulong sockmimetext(SOCKET socket, CRYPT_SESSION sess, smbmsg_t* msg, char* msgtxt, ulong maxlines
,str_list_t file_list, char* mime_boundary)
char toaddr[256]="";
char fromaddr[256]="";
char fromhost[256];
uchar* p;
char* content_type=NULL;
int len,tlen;
/* HEADERS (in recommended order per RFC822 4.1) */
if(msg->reverse_path!=NULL)
if(!sockprintf(socket,sess,"Return-Path: %s", msg->reverse_path))
return(0);
for(i=0;i<msg->total_hfields;i++)
if(msg->hfield[i].type == SMTPRECEIVED && msg->hfield_dat[i]!=NULL)
if(!sockprintf(socket,sess,"Received: %s", msg->hfield_dat[i]))
return(0);
if(!sockprintf(socket,sess,"Date: %s",msgdate(msg->hdr.when_written,date)))
if((p=smb_get_hfield(msg,RFC822FROM,NULL))!=NULL)
s=sockprintf(socket,sess,"From: %s",p); /* use original RFC822 header field */
else {
if(msg->from_net.type==NET_QWK && msg->from_net.addr!=NULL)
SAFEPRINTF2(fromaddr,"%s!%s"
,(char*)msg->from_net.addr
,usermailaddr(&scfg,fromhost,msg->from));
else if(msg->from_net.type==NET_FIDO && msg->from_net.addr!=NULL) {
faddr_t* faddr = (faddr_t *)msg->from_net.addr;
char faddrstr[128];
SAFEPRINTF2(fromname,"\"%s\" (%s)", msg->from, smb_faddrtoa(faddr, NULL));
if(faddr->point)
SAFEPRINTF4(faddrstr,"p%hu.f%hu.n%hu.z%hu"FIDO_TLD
,faddr->point, faddr->node, faddr->net, faddr->zone);
else
SAFEPRINTF3(faddrstr,"f%hu.n%hu.z%hu"FIDO_TLD
,faddr->node, faddr->net, faddr->zone);
SAFEPRINTF2(fromaddr,"%s@%s", usermailaddr(NULL,fromhost,msg->from), faddrstr);
} else if(msg->from_net.type!=NET_NONE && msg->from_net.addr!=NULL)
SAFECOPY(fromaddr,(char*)msg->from_net.addr);
else
usermailaddr(&scfg,fromaddr,msg->from);
if(fromaddr[0]=='<')
s=sockprintf(socket,sess,"From: %s %s",fromname,fromaddr);
else
s=sockprintf(socket,sess,"From: %s <%s>",fromname,fromaddr);
}
if(!s)
return(0);
if(msg->from_org!=NULL || msg->from_net.type==NET_NONE)
if(!sockprintf(socket,sess,"Organization: %s"
,msg->from_org==NULL ? scfg.sys_name : msg->from_org))
return(0);
if(!sockprintf(socket,sess,"Subject: %s",msg->subj))
if((p=smb_get_hfield(msg,RFC822TO,NULL))!=NULL)
s=sockprintf(socket,sess,"To: %s",p); /* use original RFC822 header field */
else {
if(strchr(msg->to,'@')!=NULL || msg->to_net.addr==NULL)
s=sockprintf(socket,sess,"To: %s",msg->to); /* Avoid double-@ */
else if(msg->to_net.type==NET_INTERNET || msg->to_net.type==NET_QWK) {
if(strchr((char*)msg->to_net.addr,'<')!=NULL)
s=sockprintf(socket,sess,"To: %s",(char*)msg->to_net.addr);
else
s=sockprintf(socket,sess,"To: \"%s\" <%s>",msg->to,(char*)msg->to_net.addr);
s=sockprintf(socket,sess,"To: \"%s\" (%s)",msg->to, smb_faddrtoa((fidoaddr_t*)msg->to_net.addr, NULL));
} else {
usermailaddr(&scfg,toaddr,msg->to);
s=sockprintf(socket,sess,"To: \"%s\" <%s>",msg->to,toaddr);
}
if(!s)
return(0);
if((p=smb_get_hfield(msg,SMB_CARBONCOPY,NULL))!=NULL)
if(!sockprintf(socket,sess,"CC: %s",p))
return(0);
if((p=smb_get_hfield(msg,RFC822REPLYTO,NULL))==NULL) {
if(msg->replyto_net.type==NET_INTERNET)
if(p!=NULL) {
if(np!=NULL)
s=sockprintf(socket,sess,"Reply-To: \"%s\" <%s>",np,p);
s=sockprintf(socket,sess,"Reply-To: %s",p);
if(!s)
return(0);
if(!sockprintf(socket,sess,"Message-ID: %s",get_msgid(&scfg,INVALID_SUB,msg,msgid,sizeof(msgid))))
if(msg->reply_id!=NULL)
if(!sockprintf(socket,sess,"In-Reply-To: %s",msg->reply_id))
/* Include all possible FidoNet header fields here */
for(i=0;i<msg->total_hfields;i++) {
switch(msg->hfield[i].type) {
case FIDOCTRL:
case FIDOAREA:
case FIDOSEENBY:
case FIDOPATH:
case FIDOMSGID:
case FIDOREPLYID:
case FIDOPID:
case FIDOFLAGS:
case FIDOTID:
if(!sockprintf(socket,sess, "%s: %s", smb_hfieldtype(msg->hfield[i].type), (char*)msg->hfield_dat[i]))
return(0);
break;
}
}
for(i=0;i<msg->total_hfields;i++) {
if(msg->hfield[i].type==RFC822HEADER) {
if(strnicmp((char*)msg->hfield_dat[i],"Content-Type:",13)==0)
content_type=msg->hfield_dat[i];
if(!sockprintf(socket,sess,"%s",(char*)msg->hfield_dat[i]))
/* Default MIME Content-Type for non-Internet messages */
if(msg->from_net.type!=NET_INTERNET && content_type==NULL && startup->default_charset[0]) {
/* No content-type specified, so assume IBM code-page 437 (full ex-ASCII) */
sockprintf(socket,sess,"Content-Type: text/plain; charset=%s", startup->default_charset);
sockprintf(socket,sess,"Content-Transfer-Encoding: 8bit");
}
if(strListCount(file_list)) { /* File attachments */
mimeheaders(socket,sess,mime_boundary);
sockprintf(socket,sess,"");
mimeblurb(socket,sess,mime_boundary);
sockprintf(socket,sess,"");
mimetextpartheader(socket, sess,mime_boundary, startup->default_charset);
if(!sockprintf(socket,sess,"")) /* Header Terminator */
np=msgtxt;
while(*np && lines<maxlines) {
len=0;
while(len<MAX_LINE_LEN && *(np+len)!=0 && *(np+len)!='\n')
len++;
tlen=len;
while(tlen && *(np+(tlen-1))<=' ') /* Takes care of '\r' or spaces */
tlen--;
if(!sockprintf(socket,sess, "%s%.*s", *np=='.' ? ".":"", tlen, np))
if(*(np+len)=='\r')
len++;
if(*(np+len)=='\n')
len++;
/* release time-slices every x lines */
if(startup->lines_per_yield
&& !(lines%startup->lines_per_yield))
YIELD();
if(file_list!=NULL) {
for(i=0;file_list[i];i++) {
lprintf(LOG_INFO,"%04u MIME Encoding and sending %s",socket,file_list[i]);
if(!mimeattach(socket,sess,mime_boundary,file_list[i]))
lprintf(LOG_ERR,"%04u !ERROR opening/encoding/sending %s",socket,file_list[i]);
else {
if(msg->hdr.auxattr&MSG_KILLFILE)
if(remove(file_list[i])!=0)
lprintf(LOG_WARNING,"%04u !ERROR %d removing %s",socket,errno,file_list[i]);
}
sockprintf(socket,sess,"."); /* End of text */
static ulong sockmsgtxt(SOCKET socket, CRYPT_SESSION sess, smbmsg_t* msg, char* msgtxt, ulong maxlines)
{
char filepath[MAX_PATH+1];
ulong retval;
char* boundary=NULL;
unsigned i;
str_list_t file_list=NULL;
str_list_t split;
if(msg->hdr.auxattr&MSG_FILEATTACH) {
boundary = mimegetboundary();
file_list = strListInit();
/* Parse header fields */
for(i=0;i<msg->total_hfields;i++)
if(msg->hfield[i].type==FILEATTACH)
strListPush(&file_list,(char*)msg->hfield_dat[i]);
/* Parse subject (if necessary) */
if(!strListCount(file_list)) { /* filename(s) stored in subject */
split=strListSplitCopy(NULL,msg->subj," ");
if(split!=NULL) {
for(i=0;split[i];i++) {
if(msg->idx.to!=0)
SAFEPRINTF3(filepath,"%sfile/%04u.in/%s"
,scfg.data_dir,msg->idx.to,getfname(truncsp(split[i])));
else
SAFEPRINTF3(filepath,"%sfile/%04u.out/%s"
,scfg.data_dir,msg->idx.from,getfname(truncsp(split[i])));
strListPush(&file_list,filepath);
}
strListFree(&split);
}
}
}
retval = sockmimetext(socket,sess,msg,msgtxt,maxlines,file_list,boundary);
strListFree(&file_list);
if(boundary!=NULL)
free(boundary);
return(retval);
}
static u_long resolve_ip(const char *inaddr)
char* p;
char* addr;
char buf[128];
HOSTENT* host;
SAFECOPY(buf,inaddr);
addr=buf;
if(*addr=='[' && *(p=lastchar(addr))==']') { /* Support [ip_address] notation */
addr++;
*p=0;
}
if(*addr==0)
return((u_long)INADDR_NONE);
for(p=addr;*p;p++)
if(*p!='.' && !isdigit((uchar)*p))
break;
if(!(*p))
if((host=gethostbyname(inaddr))==NULL)
return((u_long)INADDR_NONE);
return(*((ulong*)host->h_addr_list[0]));
}
/****************************************************************************/
/* Consecutive failed login (possible password hack) attempt tracking */
/****************************************************************************/
/* Counter is global so it is tracked between multiple connections. */
/* Failed consecutive login attempts > 10 will generate a hacklog entry and */
/* immediately disconnect (after the usual failed-login delay). */
/* A failed login from a different host resets the counter. */
/* A successful login from the same host resets the counter. */
/****************************************************************************/
static void badlogin(SOCKET sock, CRYPT_SESSION sess, const char* prot, const char* resp, char* user, char* passwd, char* host, union xp_sockaddr* addr)
{
char reason[128];
ulong count;
if(user == NULL)
user = "<unspecified>";
if(addr!=NULL) {
SAFEPRINTF(reason,"%s LOGIN", prot);
count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd);
if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold)
hacklog(&scfg, reason, user, passwd, host, addr);
if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold)
filter_ip(&scfg, (char*)prot, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS"
}
}
static void pop3_thread(void* arg)
{
char* p;
char str[128];
char buf[512];
char host_name[128];
char username[128];
char password[128];
uchar digest[MD5_DIGEST_SIZE];
char* response="";
char* msgtxt;
int i;
int rd;
long lm_mode = 0;
uint32_t msgnum;
ulong bytes;
SOCKET socket;
smb_t smb;
smbmsg_t msg;
user_t user;
client_t client;
mail_t* mail;
pop3_t pop3=*(pop3_t*)arg;
CRYPT_SESSION session = -1;
BOOL nodelay=TRUE;
ulong nb = 0;
char *estr;
int level;
int stat;
SetThreadName("sbbs/pop3");
thread_up(TRUE /* setuid */);
free(arg);
socket=pop3.socket;
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_DEBUG,"%04d POP3 session thread started", socket);
if(startup->pop3_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->pop3_sound, NULL, SND_ASYNC|SND_FILENAME);
if(startup->options&MAIL_OPT_DEBUG_POP3)
lprintf(LOG_INFO,"%04d POP3 connection accepted from: %s port %u"
if(getnameinfo(&pop3.client_addr.addr, pop3.client_addr_len, host_name, sizeof(host_name), NULL, 0, (startup->options&MAIL_OPT_NO_HOST_LOOKUP)?NI_NUMERICHOST:0)!=0)
SAFECOPY(host_name, "<no name>");
if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP) && (startup->options&MAIL_OPT_DEBUG_POP3))
lprintf(LOG_INFO,"%04d POP3 Hostname: %s", socket, host_name);
if (pop3.tls_port) {
if (get_ssl_cert(&scfg, &estr, &level) == -1) {
if (estr) {
lprintf(level, "%04d !POP3 [%s] %s", socket, host_ip);
free_crypt_attrstr(estr);
mail_close_socket(socket);
thread_down();
return;