--- sbbs/src/sbbs3/mailsrvr.c 2018/04/24 16:41:23 1.1.1.1 +++ sbbs/src/sbbs3/mailsrvr.c 2018/04/24 16:43:09 1.1.1.2 @@ -2,13 +2,13 @@ /* Synchronet Mail (SMTP/POP3) server and sendmail threads */ -/* $Id: mailsrvr.c,v 1.1.1.1 2018/04/24 16:41:23 root Exp $ */ +/* $Id: mailsrvr.c,v 1.1.1.2 2018/04/24 16:43:09 root Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright 2011 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 * @@ -36,6 +36,7 @@ ****************************************************************************/ /* ANSI C Library headers */ +#include /* UINT_MAX */ #include #include /* ltoa in GNU C lib */ #include /* va_list */ @@ -54,8 +55,12 @@ #include "base64.h" #include "ini_file.h" #include "netwrap.h" /* getNameServerList() */ +#include "xpendian.h" +#include "js_rtpool.h" +#include "js_request.h" /* Constants */ +static const char* server_name="Synchronet Mail Server"; #define FORWARD "forward:" #define NO_FORWARD "local:" @@ -67,6 +72,7 @@ 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"; @@ -74,34 +80,60 @@ static char* badrsp_err = "%s replied wi "instead of the expected reply:\r\n\"%s ...\""; #define TIMEOUT_THREAD_WAIT 60 /* Seconds */ -#define DNSBL_THROTTLE_VALUE 5000 /* Milliseconds */ +#define DNSBL_THROTTLE_VALUE 1000 /* Milliseconds */ +#define SPAM_HASH_SUBJECT_MIN_LEN 10 /* characters */ #define STATUS_WFC "Listening" static mail_startup_t* startup=NULL; static scfg_t scfg; static SOCKET server_socket=INVALID_SOCKET; +static SOCKET submission_socket=INVALID_SOCKET; static SOCKET pop3_socket=INVALID_SOCKET; -static DWORD active_clients=0; -static int active_sendmail=0; -static DWORD thread_count=0; -static BOOL sendmail_running=FALSE; -static DWORD sockets=0; -static DWORD served=0; -static BOOL terminate_server=FALSE; -static BOOL terminate_sendmail=FALSE; +static protected_int32_t active_clients; +static protected_int32_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 time_t uptime; +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 name[INI_MAX_VALUE_LEN]; char cmdline[INI_MAX_VALUE_LEN]; + char eval[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 { @@ -109,18 +141,33 @@ typedef struct { SOCKADDR_IN client_addr; } smtp_t,pop3_t; -static int lprintf(int level, char *fmt, ...) +static int lprintf(int level, const char *fmt, ...) { va_list argptr; char sbuf[1024]; - if(startup==NULL || startup->lputs==NULL) - return(0); - va_start(argptr,fmt); vsnprintf(sbuf,sizeof(sbuf),fmt,argptr); sbuf[sizeof(sbuf)-1]=0; va_end(argptr); + + if(level <= LOG_ERR) { + errorlog(&scfg,startup==NULL ? NULL:startup->host_name,sbuf), stats.errors++; + if(startup!=NULL && startup->errormsg!=NULL) + startup->errormsg(startup->cbdata,level,sbuf); + } + + 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)); } @@ -135,7 +182,7 @@ static BOOL winsock_startup(void) int status; /* Status Code */ if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) { - lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus); + lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus); WSAInitialized=TRUE; return (TRUE); } @@ -154,7 +201,7 @@ static BOOL winsock_startup(void) static void update_clients(void) { if(startup!=NULL && startup->clients!=NULL) - startup->clients(startup->cbdata,active_clients+active_sendmail); + startup->clients(startup->cbdata,active_clients.value+active_sendmail); } static void client_on(SOCKET sock, client_t* client, BOOL update) @@ -169,19 +216,20 @@ static void client_off(SOCKET sock) startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE); } -static void thread_up(BOOL setuid) +static int32_t thread_up(BOOL setuid) { - thread_count++; + int32_t count = protected_int32_adjust(&thread_count,1); if(startup!=NULL && startup->thread_up!=NULL) startup->thread_up(startup->cbdata,TRUE,setuid); + return count; } -static void thread_down(void) +static int32_t thread_down(void) { - if(thread_count>0) - thread_count--; + int32_t count = protected_int32_adjust(&thread_count,-1); if(startup!=NULL && startup->thread_up!=NULL) startup->thread_up(startup->cbdata,FALSE,FALSE); + return count; } SOCKET mail_open_socket(int type, const char* protocol) @@ -198,9 +246,9 @@ SOCKET mail_open_socket(int type, const if(set_socket_options(&scfg, sock, section, error, sizeof(error))) lprintf(LOG_ERR,"%04d !ERROR %s",sock,error); - sockets++; + stats.sockets++; #if 0 /*def _DEBUG */ - lprintf(LOG_DEBUG,"%04d Socket opened (%d sockets in use)",sock,sockets); + lprintf(LOG_DEBUG,"%04d Socket opened (%d sockets in use)",sock,stats.sockets); #endif } return(sock); @@ -217,14 +265,14 @@ int mail_close_socket(SOCKET sock) result=closesocket(sock); if(startup!=NULL && startup->socket_open!=NULL) startup->socket_open(startup->cbdata,FALSE); - sockets--; + stats.sockets--; if(result!=0) { if(ERROR_VALUE!=ENOTSOCK) - lprintf(LOG_ERR,"%04d !ERROR %d closing socket",sock, ERROR_VALUE); + lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",sock, ERROR_VALUE); } #if 0 /*def _DEBUG */ else - lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",sock,sockets); + lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",sock,stats.sockets); #endif return(result); @@ -298,34 +346,24 @@ int sockprintf(SOCKET sock, char *fmt, . return(len); } -static time_t checktime(void) -{ - struct tm tm; - - memset(&tm,0,sizeof(tm)); - tm.tm_year=94; - tm.tm_mday=1; - return(mktime(&tm)-0x2D24BD00L); -} - -static void recverror(SOCKET socket, int rd, int line) +static void sockerror(SOCKET socket, int rd, const char* action) { if(rd==0) - lprintf(LOG_NOTICE,"%04d Socket closed by peer on receive (line %d)" - ,socket, line); + lprintf(LOG_NOTICE,"%04d Socket closed by peer on %s" + ,socket, action); else if(rd==SOCKET_ERROR) { if(ERROR_VALUE==ECONNRESET) - lprintf(LOG_NOTICE,"%04d Connection reset by peer on receive (line %d)" - ,socket, line); + lprintf(LOG_NOTICE,"%04d Connection reset by peer on %s" + ,socket, action); else if(ERROR_VALUE==ECONNABORTED) - lprintf(LOG_NOTICE,"%04d Connection aborted by peer on receive (line %d)" - ,socket, line); + lprintf(LOG_NOTICE,"%04d Connection aborted by peer on %s" + ,socket, action); else - lprintf(LOG_NOTICE,"%04d !ERROR %d receiving on socket (line %d)" - ,socket, ERROR_VALUE, line); + lprintf(LOG_NOTICE,"%04d !SOCKET ERROR %d on %s" + ,socket, ERROR_VALUE, action); } else - lprintf(LOG_WARNING,"%04d !ERROR: recv on socket returned unexpected value: %d (line %d)" - ,socket, rd, line); + lprintf(LOG_WARNING,"%04d !SOCKET ERROR: unexpected return value %d from %s" + ,socket, rd, action); } @@ -369,12 +407,12 @@ static int sockreadline(SOCKET socket, c } continue; } - recverror(socket,i,__LINE__); + sockerror(socket,i,"select"); return(-1); } i=recv(socket, &ch, 1, 0); if(i<1) { - recverror(socket,i,__LINE__); + sockerror(socket,i,"receive"); return(-1); } if(ch=='\n' /* && rd>=1 */ ) { /* Mar-9-2003: terminate on sole LF */ @@ -413,7 +451,13 @@ static BOOL sockgetrsp(SOCKET socket, ch return(TRUE); } -#define MAX_LINE_LEN 1000 +/* RFC822: The maximum total length of a text line including the + 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, smbmsg_t* msg, char* msgtxt, ulong maxlines ,str_list_t file_list, char* mime_boundary) @@ -421,15 +465,27 @@ static ulong sockmimetext(SOCKET socket, char toaddr[256]=""; char fromaddr[256]=""; char fromhost[256]; + char msgid[256]; char date[64]; - char* p; - char* tp; + uchar* p; + uchar* np; char* content_type=NULL; int i; int s; ulong lines; + int len,tlen; /* HEADERS (in recommended order per RFC822 4.1) */ + + if(msg->reverse_path!=NULL) + if(!sockprintf(socket,"Return-Path: %s", msg->reverse_path)) + return(0); + + for(i=0;itotal_hfields;i++) + if(msg->hfield[i].type == SMTPRECEIVED && msg->hfield_dat[i]!=NULL) + if(!sockprintf(socket,"Received: %s", msg->hfield_dat[i])) + return(0); + if(!sockprintf(socket,"Date: %s",msgdate(msg->hdr.when_written,date))) return(0); @@ -479,22 +535,41 @@ static ulong sockmimetext(SOCKET socket, } if(!s) return(0); + if((p=smb_get_hfield(msg,SMB_CARBONCOPY,NULL))!=NULL) + if(!sockprintf(socket,"CC: %s",p)) + return(0); + np=NULL; if((p=smb_get_hfield(msg,RFC822REPLYTO,NULL))==NULL) { + np=msg->replyto; if(msg->replyto_net.type==NET_INTERNET) p=msg->replyto_net.addr; - else if(msg->replyto!=NULL) - p=msg->replyto; } - if(p!=NULL) - s=sockprintf(socket,"Reply-To: %s",p); /* use original RFC822 header field */ + if(p!=NULL) { + if(np!=NULL) + s=sockprintf(socket,"Reply-To: \"%s\" <%s>",np,p); + else + s=sockprintf(socket,"Reply-To: %s",p); + } if(!s) return(0); - if(!sockprintf(socket,"Message-ID: %s",get_msgid(&scfg,INVALID_SUB,msg))) + if(!sockprintf(socket,"Message-ID: %s",get_msgid(&scfg,INVALID_SUB,msg,msgid,sizeof(msgid)))) return(0); if(msg->reply_id!=NULL) if(!sockprintf(socket,"In-Reply-To: %s",msg->reply_id)) return(0); + /* non-standard, but documented (mostly) in draft-newman-msgheader-originfo-05 */ + sockprintf(socket,"Originator-Info: account=%s; login-id=%s; server=%s; client=%s; addr=%s; prot=%s; port=%s; time=%s" + ,msg->from_ext + ,smb_get_hfield(msg,SENDERUSERID,NULL) + ,smb_get_hfield(msg,SENDERSERVER,NULL) + ,smb_get_hfield(msg,SENDERHOSTNAME,NULL) + ,smb_get_hfield(msg,SENDERIPADDR,NULL) + ,smb_get_hfield(msg,SENDERPROTOCOL,NULL) + ,smb_get_hfield(msg,SENDERPORT,NULL) + ,smb_get_hfield(msg,SENDERTIME,NULL) + ); + for(i=0;itotal_hfields;i++) { if(msg->hfield[i].type==RFC822HEADER) { if(strnicmp((char*)msg->hfield_dat[i],"Content-Type:",13)==0) @@ -522,34 +597,24 @@ static ulong sockmimetext(SOCKET socket, /* MESSAGE BODY */ lines=0; -#if 0 /* This is now handled in smb_getmsgtxt() */ - for(i=0;itotal_hfields;i++) { /* delivery failure notification? */ - if(msg->hfield[i].type==SMTPSYSMSG || msg->hfield[i].type==SMB_COMMENT) { - if(!sockprintf(socket,"%s",(char*)msg->hfield_dat[i])) - return(0); - lines++; - } - } -#endif p=msgtxt; while(*p && linesMAX_LINE_LEN) - tp=p+MAX_LINE_LEN; - *tp=0; - } - truncsp(p); /* Takes care of '\r' or spaces */ - if(*p=='.') - i=sockprintf(socket,".%.*s",MAX_LINE_LEN,p); - else - i=sockprintf(socket,"%.*s",MAX_LINE_LEN,p); - if(!i) - break; - if(tp==NULL) + len=0; + while(lenlines_per_yield && !(lines%startup->lines_per_yield)) @@ -643,13 +708,40 @@ static u_long resolve_ip(char *inaddr) if(!(*p)) return(inet_addr(addr)); - if((host=gethostbyname(inaddr))==NULL) { - lprintf(LOG_WARNING,"0000 !ERROR resolving hostname: %s",inaddr); + 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, const char* prot, const char* resp, char* user, char* passwd, char* host, SOCKADDR_IN* addr) +{ + char reason[128]; + ulong count; + + 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" + ,host, inet_ntoa(addr->sin_addr), user, /* fname: */NULL); + } + + mswait(startup->login_attempt_delay); + sockprintf(sock,(char*)resp); +} static void pop3_thread(void* arg) { @@ -658,20 +750,22 @@ static void pop3_thread(void* arg) char buf[512]; char host_name[128]; char host_ip[64]; - char username[LEN_ALIAS+1]; - char password[LEN_PASS+1]; + char username[128]; + char password[128]; char challenge[256]; char digest[MD5_DIGEST_SIZE]; char* response=""; char* msgtxt; int i; int rd; - BOOL activity=FALSE; + BOOL activity=TRUE; BOOL apop=FALSE; long l; ulong lines; ulong lines_sent; - long msgs,msgnum; + ulong login_attempts; + int32_t msgs; + long msgnum; ulong bytes; SOCKET socket; HOSTENT* host; @@ -682,7 +776,7 @@ static void pop3_thread(void* arg) mail_t* mail; pop3_t pop3=*(pop3_t*)arg; - SetThreadName("POP3 Thread"); + SetThreadName("POP3"); thread_up(TRUE /* setuid */); free(arg); @@ -714,15 +808,11 @@ static void pop3_thread(void* arg) else strcpy(host_name,""); - if(startup->options&MAIL_OPT_DEBUG_POP3 - && !(startup->options&MAIL_OPT_NO_HOST_LOOKUP)) { + if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP) && (startup->options&MAIL_OPT_DEBUG_POP3)) lprintf(LOG_INFO,"%04d POP3 Hostname: %s", socket, host_name); - for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++) - lprintf(LOG_INFO,"%04d POP3 HostAlias: %s", socket, host->h_aliases[i]); - } if(trashcan(&scfg,host_ip,"ip")) { - lprintf(LOG_NOTICE,"%04d !POP3 BLOCKED CLIENT IP ADDRESS: %s" + lprintf(LOG_NOTICE,"%04d !POP3 CLIENT IP ADDRESS BLOCKED: %s" ,socket, host_ip); sockprintf(socket,"-ERR Access denied."); mail_close_socket(socket); @@ -731,7 +821,7 @@ static void pop3_thread(void* arg) } if(trashcan(&scfg,host_name,"host")) { - lprintf(LOG_NOTICE,"%04d !POP3 BLOCKED CLIENT HOSTNAME: %s" + lprintf(LOG_NOTICE,"%04d !POP3 CLIENT HOSTNAME BLOCKED: %s" ,socket, host_name); sockprintf(socket,"-ERR Access denied."); mail_close_socket(socket); @@ -739,7 +829,8 @@ static void pop3_thread(void* arg) return; } - active_clients++, update_clients(); + protected_int32_adjust(&active_clients, 1); + update_clients(); /* Initialize client display */ client.size=sizeof(client); @@ -754,6 +845,13 @@ static void pop3_thread(void* arg) SAFEPRINTF(str,"POP3: %s", host_ip); status(str); + if(startup->login_attempt_throttle + && (login_attempts=loginAttempts(startup->login_attempt_list, &pop3.client_addr)) > 1) { + lprintf(LOG_DEBUG,"%04d POP3 Throttling suspicious connection from: %s (%u login attempts)" + ,socket, inet_ntoa(pop3.client_addr.sin_addr), login_attempts); + mswait(login_attempts*startup->login_attempt_throttle); + } + mail=NULL; do { @@ -762,7 +860,7 @@ static void pop3_thread(void* arg) memset(&user,0,sizeof(user)); password[0]=0; - srand(time(NULL) ^ (DWORD)GetCurrentThreadId()); /* seed random number generator */ + srand((unsigned int)(time(NULL) ^ (time_t)GetCurrentThreadId())); /* seed random number generator */ rand(); /* throw-away first result */ safe_snprintf(challenge,sizeof(challenge),"<%x%x%lx%lx@%.128s>" ,rand(),socket,(ulong)time(NULL),clock(),startup->host_name); @@ -807,24 +905,24 @@ static void pop3_thread(void* arg) user.number=matchuser(&scfg,username,FALSE /*sysop_alias*/); if(!user.number) { if(scfg.sys_misc&SM_ECHO_PW) - lprintf(LOG_WARNING,"%04d !POP3 UNKNOWN USER: %s (password: %s)" + lprintf(LOG_NOTICE,"%04d !POP3 UNKNOWN USER: %s (password: %s)" ,socket, username, password); else - lprintf(LOG_WARNING,"%04d !POP3 UNKNOWN USER: %s" + lprintf(LOG_NOTICE,"%04d !POP3 UNKNOWN USER: %s" ,socket, username); - sockprintf(socket,pop_err); + badlogin(socket, client.protocol, pop_err, username, password, host_name, &pop3.client_addr); break; } if((i=getuserdat(&scfg, &user))!=0) { lprintf(LOG_ERR,"%04d !POP3 ERROR %d getting data on user (%s)" ,socket, i, username); - sockprintf(socket, pop_err); + badlogin(socket, client.protocol, pop_err, NULL, NULL, NULL, NULL); break; } if(user.misc&(DELETED|INACTIVE)) { - lprintf(LOG_WARNING,"%04d !POP3 DELETED or INACTIVE user #%u (%s)" + lprintf(LOG_NOTICE,"%04d !POP3 DELETED or INACTIVE user #%u (%s)" ,socket, user.number, username); - sockprintf(socket, pop_err); + badlogin(socket, client.protocol, pop_err, NULL, NULL, NULL, NULL); break; } if(apop) { @@ -833,39 +931,44 @@ static void pop3_thread(void* arg) MD5_calc(digest,challenge,strlen(challenge)); MD5_hex(str,digest); if(strcmp(str,response)) { - lprintf(LOG_WARNING,"%04d !POP3 %s FAILED APOP authentication" + lprintf(LOG_NOTICE,"%04d !POP3 %s FAILED APOP authentication" ,socket,username); #if 0 lprintf(LOG_DEBUG,"%04d !POP3 digest data: %s",socket,challenge); lprintf(LOG_DEBUG,"%04d !POP3 calc digest: %s",socket,str); lprintf(LOG_DEBUG,"%04d !POP3 resp digest: %s",socket,response); #endif - sockprintf(socket,pop_err); + badlogin(socket, client.protocol, pop_err, username, response, host_name, &pop3.client_addr); break; } } else if(stricmp(password,user.pass)) { if(scfg.sys_misc&SM_ECHO_PW) - lprintf(LOG_WARNING,"%04d !POP3 FAILED Password attempt for user %s: '%s' expected '%s'" + lprintf(LOG_NOTICE,"%04d !POP3 FAILED Password attempt for user %s: '%s' expected '%s'" ,socket, username, password, user.pass); else - lprintf(LOG_WARNING,"%04d !POP3 FAILED Password attempt for user %s" + lprintf(LOG_NOTICE,"%04d !POP3 FAILED Password attempt for user %s" ,socket, username); - sockprintf(socket, pop_err); + badlogin(socket, client.protocol, pop_err, username, password, host_name, &pop3.client_addr); break; } + + if(user.pass[0]) + loginSuccess(startup->login_attempt_list, &pop3.client_addr); + putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name); putuserrec(&scfg,user.number,U_NOTE,LEN_NOTE,host_ip); /* Update client display */ client.user=user.alias; client_on(socket,&client,TRUE /* update */); + activity=FALSE; if(startup->options&MAIL_OPT_DEBUG_POP3) lprintf(LOG_INFO,"%04d POP3 %s logged in %s", socket, user.alias, apop ? "via APOP":""); SAFEPRINTF(str,"POP3: %s",user.alias); status(str); - sprintf(smb.file,"%smail",scfg.data_dir); + SAFEPRINTF(smb.file,"%smail",scfg.data_dir); if(smb_islocked(&smb)) { lprintf(LOG_WARNING,"%04d !POP3 MAIL BASE LOCKED: %s",socket,smb.last_error); sockprintf(socket,"-ERR database locked, try again later"); @@ -977,7 +1080,7 @@ static void pop3_thread(void* arg) if(isdigit(*p)) { msgnum=atol(p); if(msgnum<1 || msgnum>msgs) { - lprintf(LOG_WARNING,"%04d !POP3 INVALID message #%ld" + lprintf(LOG_NOTICE,"%04d !POP3 INVALID message #%ld" ,socket, msgnum); sockprintf(socket,"-ERR no such message"); continue; @@ -990,7 +1093,7 @@ static void pop3_thread(void* arg) break; } if(msg.idx.attr&MSG_DELETE) { - lprintf(LOG_WARNING,"%04d !POP3 ATTEMPT to list DELETED message" + lprintf(LOG_NOTICE,"%04d !POP3 ATTEMPT to list DELETED message" ,socket); sockprintf(socket,"-ERR message deleted"); continue; @@ -1068,15 +1171,15 @@ static void pop3_thread(void* arg) lines=atol(p); } if(msgnum<1 || msgnum>msgs) { - lprintf(LOG_WARNING,"%04d !POP3 %s ATTEMPTED to retrieve an INVALID message #%ld" + lprintf(LOG_NOTICE,"%04d !POP3 %s ATTEMPTED to retrieve an INVALID message #%ld" ,socket, user.alias, msgnum); sockprintf(socket,"-ERR no such message"); continue; } msg.hdr.number=mail[msgnum-1].number; - lprintf(LOG_INFO,"%04d POP3 %s retrieving message #%ld" - ,socket, user.alias, msg.hdr.number); + lprintf(LOG_INFO,"%04d POP3 %s retrieving message #%ld with command: %s" + ,socket, user.alias, msg.hdr.number, buf); if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) { lprintf(LOG_ERR,"%04d !POP3 ERROR %d (%s) getting message index" @@ -1085,7 +1188,7 @@ static void pop3_thread(void* arg) continue; } if(msg.idx.attr&MSG_DELETE) { - lprintf(LOG_WARNING,"%04d !POP3 ATTEMPT to retrieve DELETED message" + lprintf(LOG_NOTICE,"%04d !POP3 ATTEMPT to retrieve DELETED message" ,socket); sockprintf(socket,"-ERR message deleted"); continue; @@ -1113,6 +1216,12 @@ static void pop3_thread(void* arg) continue; } + remove_ctrl_a(msgtxt, msgtxt); + + if(lines > 0 /* Works around BlackBerry mail server */ + && lines >= strlen(msgtxt)) /* which requests the number of bytes (instead of lines) using TOP */ + lines=-1; + sockprintf(socket,"+OK message follows"); lprintf(LOG_DEBUG,"%04d POP3 sending message text (%u bytes)" ,socket,strlen(msgtxt)); @@ -1157,7 +1266,7 @@ static void pop3_thread(void* arg) msgnum=atol(p); if(msgnum<1 || msgnum>msgs) { - lprintf(LOG_WARNING,"%04d !POP3 %s ATTEMPTED to delete an INVALID message #%ld" + lprintf(LOG_NOTICE,"%04d !POP3 %s ATTEMPTED to delete an INVALID message #%ld" ,socket, user.alias, msgnum); sockprintf(socket,"-ERR no such message"); continue; @@ -1213,18 +1322,25 @@ static void pop3_thread(void* arg) lprintf(LOG_INFO,"%04d POP3 message deleted", socket); continue; } - lprintf(LOG_WARNING,"%04d !POP3 UNSUPPORTED COMMAND from %s: '%s'" + lprintf(LOG_NOTICE,"%04d !POP3 UNSUPPORTED COMMAND from %s: '%s'" ,socket, user.alias, buf); sockprintf(socket,"-ERR UNSUPPORTED COMMAND: %s",buf); } - if(user.number) - logoutuserdat(&scfg,&user,time(NULL),client.time); + if(user.number) { + if(!logoutuserdat(&scfg,&user,time(NULL),client.time)) + lprintf(LOG_ERR,"%04d !ERROR in logoutuserdat", socket); + } } while(0); - if(activity) - lprintf(LOG_INFO,"%04d POP3 %s logged out from port %u on %s [%s]" - ,socket, user.alias, ntohs(pop3.client_addr.sin_port), host_name, host_ip); + if(activity) { + if(user.number) + lprintf(LOG_INFO,"%04d POP3 %s logged out from port %u on %s [%s]" + ,socket, user.alias, ntohs(pop3.client_addr.sin_port), host_name, host_ip); + else + lprintf(LOG_INFO,"%04d POP3 client disconnected from port %u on %s [%s]" + ,socket, ntohs(pop3.client_addr.sin_port), host_name, host_ip); + } status(STATUS_WFC); @@ -1235,14 +1351,16 @@ static void pop3_thread(void* arg) smb_freemsgmem(&msg); smb_close(&smb); - if(active_clients) - active_clients--, update_clients(); + protected_int32_adjust(&active_clients, -1); + update_clients(); client_off(socket); - thread_down(); - if(startup->options&MAIL_OPT_DEBUG_POP3) - lprintf(LOG_DEBUG,"%04d POP3 session thread terminated (%u threads remain, %lu clients served)" - ,socket, thread_count, served); + { + int32_t remain = thread_down(); + if(startup->options&MAIL_OPT_DEBUG_POP3) + lprintf(LOG_DEBUG,"%04d POP3 session thread terminated (%u threads remain, %lu clients served)" + ,socket, remain, ++stats.pop3_served); + } /* Must be last */ mail_close_socket(socket); @@ -1264,8 +1382,7 @@ static ulong rblchk(SOCKET sock, DWORD m ,rbl_addr ); - if(startup->options&MAIL_OPT_DNSBL_DEBUG) - lprintf(LOG_DEBUG,"%04d SMTP DNSBL Query: %s",sock,name); + lprintf(LOG_DEBUG,"%04d SMTP DNSBL Query: %s",sock,name); if((host=gethostbyname(name))==NULL) return(0); @@ -1286,13 +1403,13 @@ static ulong dns_blacklisted(SOCKET sock FILE* fp; ulong found=0; - sprintf(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir); + SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir); if(findstr(inet_ntoa(addr),fname)) return(FALSE); if(findstr(host_name,fname)) return(FALSE); - sprintf(fname,"%sdns_blacklist.cfg", scfg.ctrl_dir); + SAFEPRINTF(fname,"%sdns_blacklist.cfg", scfg.ctrl_dir); if((fp=fopen(fname,"r"))==NULL) return(FALSE); @@ -1345,6 +1462,51 @@ static BOOL chk_email_addr(SOCKET socket return(FALSE); } +static BOOL email_addr_is_exempt(const char* addr) +{ + char fname[MAX_PATH+1]; + char netmail[128]; + char* p; + + if(*addr==0 || strcmp(addr,"<>")==0) + return FALSE; + SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir); + if(findstr((char*)addr,fname)) + return TRUE; + SAFECOPY(netmail, addr); + if(*(p=netmail)=='<') + p++; + truncstr(p,">"); + return userdatdupe(&scfg, 0, U_NETMAIL, LEN_NETMAIL, p, /* del */FALSE, /* next */FALSE); +} + +static void exempt_email_addr(const char* comment + ,const char* fromname, const char* fromext, const char* fromaddr + ,const char* toaddr) +{ + char fname[MAX_PATH+1]; + char to[128]; + char tmp[128]; + FILE* fp; + + SAFEPRINTF(to,"<%s>",toaddr); + if(!email_addr_is_exempt(to)) { + SAFEPRINTF(fname,"%sdnsbl_exempt.cfg",scfg.ctrl_dir); + if((fp=fopen(fname,"a"))==NULL) + lprintf(LOG_ERR,"0000 !Error opening file: %s", fname); + else { + lprintf(LOG_INFO,"0000 %s: %s", comment, to); + fprintf(fp,"\n;%s from \"%s\" " + ,comment, fromname); + if(fromext!=NULL) + fprintf(fp,"#%s ",fromext); + fprintf(fp,"%s on %s\n%s\n" + ,fromaddr, timestr(&scfg,time(NULL),tmp), to); + fclose(fp); + } + } +} + static void signal_smtp_sem(void) { int file; @@ -1352,15 +1514,17 @@ static void signal_smtp_sem(void) if(scfg.smtpmail_sem[0]==0) return; /* do nothing */ - if((file=open(scfg.smtpmail_sem,O_WRONLY|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE))!=-1) + if((file=open(scfg.smtpmail_sem,O_WRONLY|O_CREAT|O_TRUNC,DEFFILEMODE))!=-1) close(file); } /*****************************************************************************/ /* Returns command line generated from instr with %c replacments */ /*****************************************************************************/ -static char* mailcmdstr(char* instr, char* msgpath, char* lstpath, char* errpath +static char* mailcmdstr(char* instr, char* msgpath, char* newpath, char* logpath + ,char* lstpath, char* errpath ,char* host, char* ip, uint usernum + ,char* rcpt_addr ,char* sender, char* sender_addr, char* reverse_path, char* cmd) { char str[1024]; @@ -1372,6 +1536,9 @@ static char* mailcmdstr(char* instr, cha i++; cmd[j]=0; switch(toupper(instr[i])) { + case 'D': + strcat(cmd,logpath); + break; case 'E': strcat(cmd,errpath); break; @@ -1397,6 +1564,9 @@ static char* mailcmdstr(char* instr, cha case 'M': strcat(cmd,msgpath); break; + case 'N': + strcat(cmd,newpath); + break; case 'O': /* SysOp */ strcat(cmd,scfg.sys_op); break; @@ -1409,11 +1579,14 @@ static char* mailcmdstr(char* instr, cha case 'S': /* sender name */ strcat(cmd,sender); break; + case 'T': /* recipient */ + strcat(cmd,rcpt_addr); + break; case 'A': /* sender address */ strcat(cmd,sender_addr); break; case 'V': /* Synchronet Version */ - sprintf(str,"%s%c",VERSION,REVISION); + SAFEPRINTF2(str,"%s%c",VERSION,REVISION); strcat(cmd,str); break; case 'Z': @@ -1440,7 +1613,7 @@ static char* mailcmdstr(char* instr, cha strcat(cmd,str); break; case 'U': /* User number */ - sprintf(str,"%u",usernum); + SAFEPRINTF(str,"%u",usernum); strcat(cmd,str); break; default: /* unknown specification */ @@ -1457,19 +1630,28 @@ static char* mailcmdstr(char* instr, cha } #ifdef JAVASCRIPT +typedef struct { + SOCKET sock; + const char* log_prefix; + const char* proc_name; +} private_t; + static void js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) { char line[64]; char file[MAX_PATH+1]; char* warning; - SOCKET* sock; + private_t* p; + jsrefcount rc; + int log_level; - if((sock=(SOCKET*)JS_GetContextPrivate(cx))==NULL) + if((p=(private_t*)JS_GetContextPrivate(cx))==NULL) return; if(report==NULL) { - lprintf(LOG_ERR,"!JavaScript: %s", message); + lprintf(LOG_ERR,"%04d %s %s !JavaScript: %s" + , p->sock, p->log_prefix, p->proc_name, message); return; } @@ -1488,11 +1670,17 @@ js_ErrorReporter(JSContext *cx, const ch warning="strict warning"; else warning="warning"; - } else + log_level=LOG_WARNING; + } else { + log_level=LOG_ERR; warning=""; + } - lprintf(LOG_ERR,"%04d !JavaScript %s%s%s: %s" - ,*sock, warning ,file, line, message); + rc=JS_SUSPENDREQUEST(cx); + lprintf(log_level,"%04d %s %s !JavaScript %s%s%s: %s" + ,p->sock, p->log_prefix, p->proc_name + ,warning ,file, line, message); + JS_RESUMEREQUEST(cx, rc); } static JSBool @@ -1501,9 +1689,10 @@ js_log(JSContext *cx, JSObject *obj, uin uintN i=0; int32 level=LOG_INFO; JSString* str=NULL; - SOCKET* sock; + private_t* p; + jsrefcount rc; - if((sock=(SOCKET*)JS_GetContextPrivate(cx))==NULL) + if((p=(private_t*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); if(JSVAL_IS_NUMBER(argv[i])) @@ -1512,7 +1701,10 @@ js_log(JSContext *cx, JSObject *obj, uin for(; isock,p->log_prefix,p->proc_name,JS_GetStringBytes(str)); + JS_RESUMEREQUEST(cx, rc); } if(str==NULL) @@ -1532,25 +1724,32 @@ static JSFunctionSpec js_global_function }; static BOOL -js_mailproc(SOCKET sock, client_t* client, user_t* user +js_mailproc(SOCKET sock, client_t* client, user_t* user, struct mailproc* mailproc ,char* cmdline - ,char* msgtxt_fname, char* rcptlst_fname, char* proc_err_fname - ,char* sender, char* sender_addr, char* reverse_path) + ,char* msgtxt_fname, char* newtxt_fname, char* logtxt_fname + ,char* rcpt_addr + ,char* rcptlst_fname, char* proc_err_fname + ,char* sender, char* sender_addr, char* reverse_path, char* hello_name + ,int32* result + ,JSRuntime** js_runtime + ,JSContext** js_cx + ,JSObject** js_glob + ,const char* log_prefix +) { char* p; char fname[MAX_PATH+1]; char path[MAX_PATH+1]; char arg[MAX_PATH+1]; BOOL success=FALSE; - JSRuntime* js_runtime=NULL; - JSContext* js_cx=NULL; - JSObject* js_glob; + JSObject* js_scope=NULL; JSObject* argv; jsuint argc; JSScript* js_script; js_branch_t js_branch; jsval val; jsval rval=JSVAL_VOID; + private_t priv; ZERO_VAR(js_branch); @@ -1560,69 +1759,105 @@ js_mailproc(SOCKET sock, client_t* clien strcat(fname,".js"); SAFECOPY(path,fname); - if(getfname(path)==path) /* No path specified, assume exec_dir */ - sprintf(path,"%s%s",scfg.exec_dir,fname); + if(getfname(path)==path) { /* No path specified, assume mods or exec dir */ + SAFEPRINTF2(path,"%s%s",scfg.mods_dir,fname); + if(scfg.mods_dir[0]==0 || !fexist(path)) + SAFEPRINTF2(path,"%s%s",scfg.exec_dir,fname); + } + *result = 0; do { + if(*js_runtime==NULL) { + lprintf(LOG_DEBUG,"%04d %s JavaScript: Creating runtime: %lu bytes\n" + ,sock, log_prefix, startup->js.max_bytes); - lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes\n" - ,sock, startup->js.max_bytes); + if((*js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL) + break; + } - if((js_runtime = JS_NewRuntime(startup->js.max_bytes))==NULL) - break; + if(*js_cx==NULL) { + lprintf(LOG_DEBUG,"%04d %s JavaScript: Initializing context (stack: %lu bytes)\n" + ,sock, log_prefix, startup->js.cx_stack); - lprintf(LOG_DEBUG,"%04d JavaScript: Initializing context (stack: %lu bytes)\n" - ,sock, startup->js.cx_stack); + if((*js_cx = JS_NewContext(*js_runtime, startup->js.cx_stack))==NULL) + break; + } + JS_BEGINREQUEST(*js_cx); - if((js_cx = JS_NewContext(js_runtime, startup->js.cx_stack))==NULL) - break; + JS_SetErrorReporter(*js_cx, js_ErrorReporter); - JS_SetErrorReporter(js_cx, js_ErrorReporter); + priv.sock=sock; + priv.log_prefix=log_prefix; + priv.proc_name=mailproc->name; + JS_SetContextPrivate(*js_cx, &priv); + + if(*js_glob==NULL) { + /* Global Objects (including system, js, client, Socket, MsgBase, File, User, etc. */ + if((*js_glob=js_CreateCommonObjects(*js_cx, &scfg, &scfg, NULL + ,uptime, startup->host_name, SOCKLIB_DESC /* system */ + ,&js_branch /* js */ + ,&startup->js + ,client, sock /* client */ + ,&js_server_props /* server */ + ))==NULL) + break; - JS_SetContextPrivate(js_cx, &sock); + if(!JS_DefineFunctions(*js_cx, *js_glob, js_global_functions)) + break; - /* Global Object */ - if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL))==NULL) - break; + /* Area and "user" Objects */ + if(!js_CreateUserObjects(*js_cx, *js_glob, &scfg, user, client, NULL, NULL)) + break; - if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions)) - break; + /* Mailproc "API" filenames */ + JS_DefineProperty(*js_cx, *js_glob, "message_text_filename" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,msgtxt_fname)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - /* Internal JS Object */ - if(js_CreateInternalJsObject(js_cx, js_glob, &js_branch)==NULL) - break; + JS_DefineProperty(*js_cx, *js_glob, "new_message_text_filename" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,newtxt_fname)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - /* Client Object */ - if(js_CreateClientObject(js_cx, js_glob, "client", client, sock)==NULL) - break; + JS_DefineProperty(*js_cx, *js_glob, "log_text_filename" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,logtxt_fname)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - /* System Object */ - if(js_CreateSystemObject(js_cx, js_glob, &scfg, uptime, startup->host_name, SOCKLIB_DESC)==NULL) - break; + JS_DefineProperty(*js_cx, *js_glob, "recipient_address" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,rcpt_addr)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - /* Socket Class */ - if(js_CreateSocketClass(js_cx, js_glob)==NULL) - break; + JS_DefineProperty(*js_cx, *js_glob, "recipient_list_filename" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,rcptlst_fname)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - /* MsgBase Class */ - if(js_CreateMsgBaseClass(js_cx, js_glob, &scfg)==NULL) - break; + JS_DefineProperty(*js_cx, *js_glob, "processing_error_filename" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,proc_err_fname)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - /* File Class */ - if(js_CreateFileClass(js_cx, js_glob)==NULL) - break; + JS_DefineProperty(*js_cx, *js_glob, "sender_name" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,sender)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - /* User class */ - if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL) - break; + JS_DefineProperty(*js_cx, *js_glob, "sender_address" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,sender_addr)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); + + JS_DefineProperty(*js_cx, *js_glob, "reverse_path" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,reverse_path)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); + + JS_DefineProperty(*js_cx, *js_glob, "hello_name" + ,STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,hello_name)) + ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - /* Area and "user" Objects */ - if(!js_CreateUserObjects(js_cx, js_glob, &scfg, user, NULL, NULL)) + } + + if((js_scope=JS_NewObject(*js_cx, NULL, NULL, *js_glob))==NULL) break; /* Convert command-line to argv/argc */ - argv=JS_NewArrayObject(js_cx, 0, NULL); - JS_DefineProperty(js_cx, js_glob, "argv", OBJECT_TO_JSVAL(argv) + argv=JS_NewArrayObject(*js_cx, 0, NULL); + JS_DefineProperty(*js_cx, js_scope, "argv", OBJECT_TO_JSVAL(argv) ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE); p=cmdline; @@ -1631,62 +1866,90 @@ js_mailproc(SOCKET sock, client_t* clien for(argc=0;*p;argc++) { SAFECOPY(arg,p); truncstr(arg," \t"); - val=STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,arg)); - if(!JS_SetElement(js_cx, argv, argc, &val)) + val=STRING_TO_JSVAL(JS_NewStringCopyZ(*js_cx,arg)); + if(!JS_SetElement(*js_cx, argv, argc, &val)) break; FIND_WHITESPACE(p); SKIP_WHITESPACE(p); } - JS_DefineProperty(js_cx, js_glob, "argc", INT_TO_JSVAL(argc) + JS_DefineProperty(*js_cx, js_scope, "argc", INT_TO_JSVAL(argc) ,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE); - /* Mailproc "API" filenames */ - JS_DefineProperty(js_cx, js_glob, "message_text_filename" - ,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,msgtxt_fname)) - ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - - JS_DefineProperty(js_cx, js_glob, "recipient_list_filename" - ,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,rcptlst_fname)) - ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - - JS_DefineProperty(js_cx, js_glob, "processing_error_filename" - ,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,proc_err_fname)) - ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - - JS_DefineProperty(js_cx, js_glob, "sender_name" - ,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,sender)) - ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - - JS_DefineProperty(js_cx, js_glob, "sender_address" - ,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,sender_addr)) - ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - - JS_DefineProperty(js_cx, js_glob, "reverse_path" - ,STRING_TO_JSVAL(JS_NewStringCopyZ(js_cx,reverse_path)) - ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY); - - if((js_script=JS_CompileFile(js_cx, js_glob, path))==NULL) + if(mailproc->eval!=NULL && *mailproc->eval!=0) { + lprintf(LOG_DEBUG,"%04d %s Evaluating: %s" + ,sock, log_prefix, mailproc->eval); + js_script=JS_CompileScript(*js_cx, js_scope, mailproc->eval, strlen(mailproc->eval), NULL, 1); + } else { + lprintf(LOG_DEBUG,"%04d %s Executing: %s" + ,sock, log_prefix, cmdline); + if((js_script=JS_CompileFile(*js_cx, js_scope, path)) != NULL) + js_PrepareToExecute(*js_cx, js_scope, path, /* startup_dir: */NULL); + } + if(js_script==NULL) break; - success=JS_ExecuteScript(js_cx, js_glob, js_script, &rval); + success=JS_ExecuteScript(*js_cx, js_scope, js_script, &rval); + + JS_ReportPendingException(*js_cx); + + js_EvalOnExit(*js_cx, js_scope, &js_branch); + + JS_GetProperty(*js_cx, *js_glob, "exit_code", &rval); + + JS_DestroyScript(*js_cx, js_script); - js_EvalOnExit(js_cx, js_glob, &js_branch); + JS_ClearScope(*js_cx, js_scope); + + JS_GC(*js_cx); } while(0); + if(*js_cx!=NULL) { + + if(rval!=JSVAL_VOID && JSVAL_IS_NUMBER(rval)) + JS_ValueToInt32(*js_cx,rval,result); + + JS_ENDREQUEST(*js_cx); + } + + return(success); +} + +void js_cleanup(JSRuntime* js_runtime, JSContext* js_cx) +{ if(js_cx!=NULL) JS_DestroyContext(js_cx); if(js_runtime!=NULL) - JS_DestroyRuntime(js_runtime); - - return(success); + jsrt_Release(js_runtime); } #endif +static uchar* get_header_field(uchar* buf, char* name, size_t maxlen) +{ + uchar* p; + size_t len; + + if(buf[0]<=' ') /* folded header */ + return NULL; + + if((p=strchr(buf,':'))==NULL) + return NULL; + + len = p-buf; + if(len >= maxlen) + len = maxlen-1; + sprintf(name,"%.*s",len,buf); + truncsp(name); + + p++; /* skip colon */ + SKIP_WHITESPACE(p); + return p; +} static int parse_header_field(uchar* buf, smbmsg_t* msg, ushort* type) { char* p; + char* tp; char field[128]; int len; ushort nettype; @@ -1694,7 +1957,12 @@ static int parse_header_field(uchar* buf if(buf[0]<=' ' && *type!=UNKNOWN) { /* folded header, append to previous */ p=buf; truncsp(p); - smb_hfield_append_str(msg,*type,"\r\n"); + if(*type==RFC822HEADER || *type==SMTPRECEIVED) + smb_hfield_append_str(msg,*type,"\r\n"); + else { /* Unfold other common header field types (e.g. Subject, From, To) */ + smb_hfield_append_str(msg,*type," "); + SKIP_WHITESPACE(p); + } return smb_hfield_append_str(msg, *type, p); } @@ -1716,9 +1984,10 @@ static int parse_header_field(uchar* buf if(!stricmp(field, "REPLY-TO")) { smb_hfield_str(msg, *type=RFC822REPLYTO, p); - if(*p=='<') { - p++; - truncstr(p,">"); + if((tp=strrchr(p,'<'))!=NULL) { + tp++; + truncstr(tp,">"); + p=tp; } nettype=NET_INTERNET; smb_hfield(msg, REPLYTONETTYPE, sizeof(nettype), &nettype); @@ -1733,7 +2002,7 @@ static int parse_header_field(uchar* buf if(!stricmp(field, "DATE")) { msg->hdr.when_written=rfc822date(p); *type=UNKNOWN; - return(0); + return SMB_SUCCESS; } if(!stricmp(field, "MESSAGE-ID")) return smb_hfield_str(msg, *type=RFC822MSGID, p); @@ -1741,6 +2010,17 @@ static int parse_header_field(uchar* buf if(!stricmp(field, "IN-REPLY-TO")) return smb_hfield_str(msg, *type=RFC822REPLYID, p); + if(!stricmp(field, "CC")) + return smb_hfield_str(msg, *type=SMB_CARBONCOPY, p); + + if(!stricmp(field, "RECEIVED")) + return smb_hfield_str(msg, *type=SMTPRECEIVED, p); + + if(!stricmp(field, "RETURN-PATH")) { + *type=UNKNOWN; + return SMB_SUCCESS; /* Ignore existing "Return-Path" header fields */ + } + /* Fall-through */ return smb_hfield_str(msg, *type=RFC822HEADER, buf); } @@ -1769,7 +2049,7 @@ static int chk_received_hdr(SOCKET socke if(*p==0) break; p2=host_name; - for(;*p && !isspace(*p) && p2options&MAIL_OPT_DNSBL_DEBUG) - lprintf(LOG_DEBUG,"%04d DEBUG checking %s (%s)",socket,host_name,ip); + lprintf(LOG_DEBUG,"%04d SMTP DNSBL checking received header address %s [%s]",socket,host_name,ip); if((dnsbl_result->s_addr=dns_blacklisted(socket,check_addr,host_name,dnsbl,dnsbl_ip))!=0) - lprintf(LOG_WARNING,"%04d !SMTP BLACKLISTED SERVER on %s: %s [%s] = %s" + lprintf(LOG_NOTICE,"%04d SMTP BLACKLISTED SERVER on %s: %s [%s] = %s" ,socket, dnsbl, host_name, ip, inet_ntoa(*dnsbl_result)); } while(0); free(fromstr); @@ -1802,7 +2081,7 @@ static void parse_mail_address(char* p SKIP_WHITESPACE(p); /* Get the address */ - if((tp=strchr(p,'<'))!=NULL) + if((tp=strrchr(p,'<'))!=NULL) tp++; else tp=p; @@ -1829,6 +2108,98 @@ static void parse_mail_address(char* p truncsp(name); } +/* Decode quoted-printable content-transfer-encoded text */ +/* Ignores (strips) unsupported ctrl chars and non-ASCII chars */ +/* Does not enforce 76 char line length limit */ +static char* qp_decode(uchar* buf) +{ + uchar* p=buf; + uchar* dest=buf; + + for(;;p++) { + if(*p==0) { + *dest++='\r'; + *dest++='\n'; + break; + } + if(*p==' ' || (*p>='!' && *p<='~' && *p!='=') || *p=='\t') + *dest++=*p; + else if(*p=='=') { + p++; + if(*p==0) /* soft link break */ + break; + if(isxdigit(*p) && isxdigit(*(p+1))) { + char hex[3]; + hex[0]=*p; + hex[1]=*(p+1); + hex[2]=0; + /* ToDo: what about encoded NULs and the like? */ + *dest++=(uchar)strtoul(hex,NULL,16); + p++; + } else { /* bad encoding */ + *dest++='='; + *dest++=*p; + } + } + } + *dest=0; + return buf; +} + +static BOOL checktag(scfg_t *scfg, char *tag, uint usernum) +{ + char fname[MAX_PATH+1]; + + if(tag==NULL) + return(FALSE); + SAFEPRINTF2(fname,"%suser/%04d.smtpblock",scfg->data_dir,usernum); + return(findstr(tag, fname)); +} + +static BOOL smtp_splittag(char *in, char **name, char **tag) +{ + char *last; + + if(in==NULL) + return(FALSE); + + *name=strtok_r(in, "#", &last); + 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, /* del */FALSE, /* next */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; @@ -1846,17 +2217,21 @@ static void smtp_thread(void* arg) char reverse_path[128]; char date[64]; char qwkid[32]; + char rcpt_to[128]; char rcpt_name[128]; char rcpt_addr[128]; char sender[128]; + char sender_ext[128]; char sender_addr[128]; char hello_name[128]; - char user_name[LEN_ALIAS+1]; - char user_pass[LEN_PASS+1]; + char user_name[128]; + char user_pass[128]; char relay_list[MAX_PATH+1]; char domain_list[MAX_PATH+1]; char spam_bait[MAX_PATH+1]; + BOOL spam_bait_result=FALSE; char spam_block[MAX_PATH+1]; + char spam_block_exempt[MAX_PATH+1]; char host_name[128]; char host_ip[64]; char dnsbl[256]; @@ -1881,6 +2256,8 @@ static void smtp_thread(void* arg) ulong hdr_len=0; ulong length; ulong badcmds=0; + ulong login_attempts; + ulong waiting; BOOL esmtp=FALSE; BOOL telegram=FALSE; BOOL forward=FALSE; @@ -1892,6 +2269,8 @@ static void smtp_thread(void* arg) uint subnum=INVALID_SUB; FILE* msgtxt=NULL; char msgtxt_fname[MAX_PATH+1]; + char newtxt_fname[MAX_PATH+1]; + char logtxt_fname[MAX_PATH+1]; FILE* rcptlst; char rcptlst_fname[MAX_PATH+1]; ushort rcpt_count=0; @@ -1903,6 +2282,7 @@ static void smtp_thread(void* arg) HOSTENT* host; int smb_error; smb_t smb; + smb_t spam; smbmsg_t msg; smbmsg_t newmsg; user_t user; @@ -1912,7 +2292,14 @@ static void smtp_thread(void* arg) smtp_t smtp=*(smtp_t*)arg; SOCKADDR_IN server_addr; IN_ADDR dnsbl_result; - BOOL* mailproc_match; + BOOL* mailproc_to_match; + int mailproc_match; + JSRuntime* js_runtime=NULL; + JSContext* js_cx=NULL; + JSObject* js_glob=NULL; + int32 js_result; + struct mailproc* mailproc; + enum { SMTP_STATE_INITIAL ,SMTP_STATE_HELO @@ -1932,14 +2319,20 @@ static void smtp_thread(void* arg) } cmd = SMTP_CMD_NONE; - SetThreadName("SMTP Thread"); + enum { + ENCODING_NONE + ,ENCODING_BASE64 + ,ENCODING_QUOTED_PRINTABLE + } content_encoding = ENCODING_NONE; + + SetThreadName("SMTP"); thread_up(TRUE /* setuid */); free(arg); socket=smtp.socket; - lprintf(LOG_DEBUG,"%04d SMTP RX Session thread started", socket); + lprintf(LOG_DEBUG,"%04d SMTP Session thread started", socket); #ifdef _WIN32 if(startup->inbound_sound[0] && !(startup->options&MAIL_OPT_MUTE)) @@ -1948,7 +2341,7 @@ static void smtp_thread(void* arg) 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" + lprintf(LOG_CRIT,"%04d !SMTP ERROR %d (%d) getting address/port" ,socket, i, ERROR_VALUE); sockprintf(socket,sys_error); mail_close_socket(socket); @@ -1956,24 +2349,25 @@ static void smtp_thread(void* arg) return; } - if((mailproc_match=alloca(sizeof(BOOL)*mailproc_count))==NULL) { - lprintf(LOG_ERR,"%04d !SMTP ERROR allocating memory for mailproc_match", socket); + if((mailproc_to_match=alloca(sizeof(BOOL)*mailproc_count))==NULL) { + lprintf(LOG_CRIT,"%04d !SMTP ERROR allocating memory for mailproc_to_match", socket); sockprintf(socket,sys_error); mail_close_socket(socket); thread_down(); return; } - memset(mailproc_match,FALSE,sizeof(BOOL)*mailproc_count); + memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count); memset(&smb,0,sizeof(smb)); memset(&msg,0,sizeof(msg)); + memset(&spam,0,sizeof(spam)); 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 from: %s port %u" - , socket, host_ip, ntohs(smtp.client_addr.sin_port)); + 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; @@ -1986,104 +2380,107 @@ static void smtp_thread(void* arg) else strcpy(host_name,""); - if(!(startup->options&MAIL_OPT_NO_HOST_LOOKUP)) { + 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 - } - - SAFECOPY(hello_name,host_name); - sprintf(spam_bait,"%sspambait.cfg",scfg.ctrl_dir); - sprintf(spam_block,"%sspamblock.cfg",scfg.ctrl_dir); - - if(trashcan(&scfg,host_ip,"ip") || findstr(host_ip,spam_block)) { - lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED SERVER IP ADDRESS: %s" - ,socket, host_ip); - sockprintf(socket,"550 Access denied."); - mail_close_socket(socket); - thread_down(); - return; - } + protected_int32_adjust(&active_clients, 1); + update_clients(); - if(trashcan(&scfg,host_name,"host") || findstr(host_name,spam_block)) { - lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED SERVER HOSTNAME: %s" - ,socket, host_name); - sockprintf(socket,"550 Access denied."); - mail_close_socket(socket); - thread_down(); - return; - } + SAFECOPY(hello_name,host_name); - active_clients++, update_clients(); + SAFEPRINTF(spam_bait,"%sspambait.cfg",scfg.ctrl_dir); + SAFEPRINTF(spam_block,"%sspamblock.cfg",scfg.ctrl_dir); + SAFEPRINTF(spam_block_exempt,"%sspamblock_exempt.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) && !findstr(host_ip,spam_block_exempt))) { + lprintf(LOG_NOTICE,"%04d !SMTP CLIENT IP ADDRESS BLOCKED: %s (%u total)" + ,socket, host_ip, ++stats.sessions_refused); + sockprintf(socket,"550 CLIENT IP ADDRESS BLOCKED: %s", host_ip); + mail_close_socket(socket); + thread_down(); + protected_int32_adjust(&active_clients, -1); + update_clients(); + return; + } - /* 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_WARNING,"%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); + if(trashcan(&scfg,host_name,"host") + || (findstr(host_name,spam_block) && !findstr(host_name,spam_block_exempt))) { + lprintf(LOG_NOTICE,"%04d !SMTP CLIENT HOSTNAME BLOCKED: %s (%u total)" + ,socket, host_name, ++stats.sessions_refused); + sockprintf(socket,"550 CLIENT HOSTNAME BLOCKED: %s", host_name); mail_close_socket(socket); - lprintf(LOG_WARNING,"%04d !SMTP REFUSED SESSION from blacklisted server" - ,socket); thread_down(); - if(active_clients) - active_clients--, update_clients(); + protected_int32_adjust(&active_clients, -1); + update_clients(); return; } + + /* 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 (%u total)" + ,socket, ++stats.sessions_refused); + thread_down(); + protected_int32_adjust(&active_clients, -1); + update_clients(); + return; + } + } } - sprintf(smb.file,"%smail",scfg.data_dir); + SAFEPRINTF(smb.file,"%smail",scfg.data_dir); if(smb_islocked(&smb)) { - lprintf(LOG_WARNING,"%04d !SMTP MAIL BASE LOCKED: %s" + lprintf(LOG_CRIT,"%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(); + protected_int32_adjust(&active_clients, -1); + update_clients(); return; } + SAFEPRINTF(spam.file,"%sspam",scfg.data_dir); + spam.retry_time=scfg.smb_retry_time; + spam.subnum=INVALID_SUB; - srand(time(NULL) ^ (DWORD)GetCurrentThreadId()); /* seed random number generator */ + srand((unsigned int)(time(NULL) ^ (time_t)GetCurrentThreadId())); /* seed random number generator */ rand(); /* throw-away first result */ - SAFEPRINTF3(session_id,"%x%x%lx",socket,rand(),clock()); - + SAFEPRINTF4(session_id,"%x%x%x%lx",getpid(),socket,rand(),clock()); + lprintf(LOG_DEBUG,"%04d SMTP Session ID=%s", socket, session_id); + 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" + lprintf(LOG_CRIT,"%04d !SMTP ERROR %d creating recipient list: %s" ,socket, errno, rcptlst_fname); sockprintf(socket,sys_error); mail_close_socket(socket); thread_down(); - if(active_clients) - active_clients--, update_clients(); + protected_int32_adjust(&active_clients, -1); + update_clients(); return; } if(trashcan(&scfg,host_name,"smtpspy") || trashcan(&scfg,host_ip,"smtpspy")) { - sprintf(str,"%ssmtpspy.txt", scfg.logs_dir); + SAFEPRINTF(str,"%ssmtpspy.txt", scfg.logs_dir); spy=fopen(str,"a"); } @@ -2100,6 +2497,15 @@ static void smtp_thread(void* arg) SAFEPRINTF(str,"SMTP: %s",host_ip); status(str); + if(startup->login_attempt_throttle + && (login_attempts=loginAttempts(startup->login_attempt_list, &smtp.client_addr)) > 1) { + lprintf(LOG_DEBUG,"%04d SMTP Throttling suspicious connection from: %s (%u login attempts)" + ,socket, inet_ntoa(smtp.client_addr.sin_addr), login_attempts); + mswait(login_attempts*startup->login_attempt_throttle); + } + + /* SMTP session active: */ + sockprintf(socket,"220 %s Synchronet SMTP Server %s-%s Ready" ,startup->host_name,revision,PLATFORM_DESC); while(1) { @@ -2133,11 +2539,18 @@ static void smtp_thread(void* arg) 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); + if(!socket_check(socket, NULL, NULL, 0)) { + lprintf(LOG_WARNING,"%04d !SMTP sender disconnected (premature evacuation)", socket); + continue; + } + + stats.msgs_received++; + /* Twit-listing (sender's name and e-mail addresses) here */ - sprintf(path,"%stwitlist.cfg",scfg.ctrl_dir); + SAFEPRINTF(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); + lprintf(LOG_NOTICE,"%04d !SMTP FILTERING TWIT-LISTED SENDER: %s <%s> (%u total)" + ,socket, sender, sender_addr, ++stats.msgs_refused); 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."); @@ -2161,14 +2574,14 @@ static void smtp_thread(void* arg) 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, "452 Insufficient system storage"); + 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, "452 Insufficient system storage"); + sockprintf(socket, insuf_stor); free(telegram_buf); continue; } @@ -2178,20 +2591,20 @@ static void smtp_thread(void* arg) sec_list=iniReadSectionList(rcptlst,NULL); /* Each section is a recipient */ for(rcpt_count=0; sec_list!=NULL && sec_list[rcpt_count]!=NULL - && rcpt_countmax_recipients; rcpt_count++) { + && (startup->max_recipients==0 || rcpt_countmax_recipients); rcpt_count++) { section=sec_list[rcpt_count]; - SAFECOPY(rcpt_name,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENT),"unknown",value)); + 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_name,value)); + SAFECOPY(rcpt_addr,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENTNETADDR),rcpt_to,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); + ,socket, length, strlen(telegram_buf), sender_addr, rcpt_to, 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); + ,socket, i, sender_addr, rcpt_to, rcpt_addr); } iniFreeStringList(sec_list); free(telegram_buf); @@ -2210,29 +2623,72 @@ static void smtp_thread(void* arg) remove(proc_err_fname); for(i=0;idisabled) + continue; + + if(!mailproc->process_dnsbl && dnsbl_result.s_addr) + continue; - /* This processor is for specific recipients only and did not match */ - if(strListCount(mailproc_list[i].to) && !mailproc_match[i]) + if(!mailproc->process_spam && spam_bait_result) continue; - if(!mailproc_list[i].passthru) + if(!chk_ar(&scfg,mailproc->ar,&relay_user,&client)) + continue; + + if(mailproc->to!=NULL && !mailproc_to_match[i]) + continue; + + if(mailproc->from!=NULL + && !findstr_in_list(sender_addr, mailproc->from)) + continue; + + if(!mailproc->passthru) msg_handled=TRUE; - mailcmdstr(mailproc_list[i].cmdline - ,msgtxt_fname, rcptlst_fname, proc_err_fname + mailcmdstr(mailproc->cmdline + ,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); - lprintf(LOG_DEBUG,"%04d SMTP Executing external process: %s" - ,socket, str); + lprintf(LOG_INFO,"%04d SMTP Executing external mail processor: %s" + ,socket, mailproc->name); - if(mailproc_list[i].native) { - if((j=system(str))!=0) - lprintf(LOG_WARNING,"%04d !SMTP system(%s) returned %d (errno: %d)" + if(mailproc->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); - } else /* JavaScript */ - js_mailproc(socket, &client, &relay_user, str /* cmdline */ - ,msgtxt_fname, rcptlst_fname, proc_err_fname - ,sender, sender_addr, reverse_path); + if(mailproc->ignore_on_error) { + lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL due to mail processor (%s) error: %d" + ,socket, mailproc->name, j); + msg_handled=TRUE; + } + } + } else { /* JavaScript */ + if(!js_mailproc(socket, &client, &relay_user + ,mailproc + ,str /* cmdline */ + ,msgtxt_fname, newtxt_fname, logtxt_fname + ,rcpt_addr + ,rcptlst_fname, proc_err_fname + ,sender, sender_addr, reverse_path, hello_name, &js_result + ,&js_runtime, &js_cx, &js_glob + ,"SMTP") || 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)" + ,socket, str, js_result); + 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 + } + } if(flength(proc_err_fname)>0) break; if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname)) @@ -2241,12 +2697,14 @@ static void smtp_thread(void* arg) 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 process error: %s", socket, str); - i=atoi(str); - if(i>=100 && i<1000) + lprintf(LOG_WARNING,"%04d !SMTP External mail processor (%s) error: %s" + ,socket, mailproc->name, str); + n=atoi(str); + if(n>=100 && n<1000) sockprintf(socket,"%s", str); else sockprintf(socket,"554%c%s" @@ -2257,8 +2715,10 @@ static void smtp_thread(void* arg) msg_handled=TRUE; } else if(!fexist(msgtxt_fname) || !fexist(rcptlst_fname)) { - lprintf(LOG_WARNING,"%04d SMTP External process removed %s file" - ,socket, fexist(msgtxt_fname)==FALSE ? "message text" : "recipient list"); + lprintf(LOG_NOTICE,"%04d SMTP External mail processor (%s) removed %s file" + ,socket + ,mailproc->name + ,fexist(msgtxt_fname)==FALSE ? "message text" : "recipient list"); sockprintf(socket,ok_rsp); msg_handled=TRUE; } @@ -2279,11 +2739,18 @@ static void smtp_thread(void* arg) } if(msg_handled) { - lprintf(LOG_NOTICE,"%04d SMTP Message handled by external mail processor" - ,socket); + lprintf(LOG_NOTICE,"%04d SMTP Message handled by external mail processor (%s, %u total)" + ,socket, 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 !SMTP ERROR %d re-opening message file: %s" ,socket, errno, msgtxt_fname); @@ -2298,40 +2765,55 @@ static void smtp_thread(void* arg) /* Parse message header here */ hfield_type=UNKNOWN; smb_error=SMB_SUCCESS; /* no SMB error */ - errmsg="452 Insufficient system storage"; + 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(!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_WARNING,"%04d !SMTP TAGGED MAIL SUBJECT from blacklisted server with: %s" - ,socket, startup->dnsbl_tag); + if((p=get_header_field(buf, field, sizeof(field)))!=NULL) { + if(stricmp(field, "SUBJECT")==0) { + /* SPAM Filtering/Logging */ + if(relay_user.number==0) { + if(trashcan(&scfg,p,"subject")) { + lprintf(LOG_NOTICE,"%04d !SMTP BLOCKED SUBJECT (%s) from: %s (%u total)" + ,socket, p, reverse_path, ++stats.msgs_refused); + SAFEPRINTF2(tmp,"Blocked subject (%s) from: %s" + ,p, reverse_path); + 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; } - 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) @@ -2345,35 +2827,23 @@ static void smtp_thread(void* arg) } if(smb_error!=SMB_SUCCESS) { /* SMB Error */ sockprintf(socket, errmsg); + stats.msgs_refused++; continue; } if((p=smb_get_hfield(&msg, RFC822TO, NULL))!=NULL) { - if(*p=='<') p++; - SAFECOPY(rcpt_name,p); - truncstr(rcpt_name,">"); + 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); } - - /* SPAM Filtering/Logging */ - if(relay_user.number==0 && msg.subj!=NULL && trashcan(&scfg,msg.subj,"subject")) { - lprintf(LOG_WARNING,"%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 && ioptions&MAIL_OPT_DNSBL_IGNORE) { - lprintf(LOG_WARNING,"%04d !SMTP IGNORED MAIL from blacklisted server" - ,socket); - 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); - continue; - } + 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_WARNING,"%04d !SMTP TAGGED MAIL HEADER from blacklisted server with: %s" + 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]) { @@ -2410,34 +2870,45 @@ static void smtp_thread(void* arg) dnsbl_result.s_addr=0; /* Reset DNSBL look-up result between messages */ if(sender[0]==0) { - lprintf(LOG_WARNING,"%04d !SMTP MISSING mail header 'FROM' field", socket); + lprintf(LOG_WARNING,"%04d !SMTP MISSING mail header 'FROM' field (%u total)" + ,socket, ++stats.msgs_refused); sockprintf(socket, "554 Mail header missing 'FROM' field"); subnum=INVALID_SUB; continue; } - nettype=NET_INTERNET; + 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); - smb_hfield_str(&msg, SENDER, sender); - smb_hfield(&msg, SENDERNETTYPE, sizeof(nettype), &nettype); - smb_hfield_str(&msg, SENDERNETADDR, sender_addr); if(msg.subj==NULL) smb_hfield(&msg, SUBJECT, 0, NULL); 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" + 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++; continue; } if((msgbuf=(char*)malloc(length+1))==NULL) { lprintf(LOG_CRIT,"%04d !SMTP ERROR allocating %d bytes of memory" ,socket,length+1); - sockprintf(socket, "452 Insufficient system storage"); + sockprintf(socket, insuf_stor); subnum=INVALID_SUB; continue; } @@ -2447,16 +2918,17 @@ static void smtp_thread(void* arg) /* 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(!chk_ar(&scfg,scfg.grp[scfg.sub[subnum]->grp]->ar, &relay_user) - || !chk_ar(&scfg,scfg.sub[subnum]->ar, &relay_user) - || !chk_ar(&scfg,scfg.sub[subnum]->post_ar, &relay_user)) { - lprintf(LOG_WARNING,"%04d !SMTP %s has insufficient access to post on %s" - ,socket, sender_addr, scfg.sub[subnum]->sname); + 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; } @@ -2465,7 +2937,7 @@ static void smtp_thread(void* arg) smb_hfield_str(&msg, RECIPIENT, rcpt_name); smb.subnum=subnum; - if((i=savemsg(&scfg, &smb, &msg, &client, msgbuf))!=SMB_SUCCESS) { + 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" @@ -2474,6 +2946,8 @@ static void smtp_thread(void* arg) 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); @@ -2482,31 +2956,114 @@ static void smtp_thread(void* arg) 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; + + if(msg.subj==NULL || strlen(msg.subj) < SPAM_HASH_SUBJECT_MIN_LEN) + sources&=~(1<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) + ); + total++; + } + if(total) { + 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))) { + free(msgbuf); + if(is_spam) + lprintf(LOG_NOTICE,"%04d !SMTP IGNORED SPAM MESSAGE (%u total)" + ,socket, ++stats.msgs_ignored); + else { + 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; - i=savemsg(&scfg, &smb, &msg, &client, msgbuf); + /* 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_ERR,"%04d !SMTP ERROR %d (%s) saving message" + 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 - && rcpt_countmax_recipients; rcpt_count++) { + && (startup->max_recipients==0 || rcpt_countmax_recipients); rcpt_count++) { section=sec_list[rcpt_count]; - SAFECOPY(rcpt_name,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENT),"unknown",value)); + 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); - sprintf(str,"#%u",usernum); + SAFEPRINTF(str,"#%u",usernum); SAFECOPY(rcpt_addr,iniReadString(rcptlst,section ,smb_hfieldtype(RECIPIENTNETADDR),str,value)); if(nettype==NET_NONE /* Local destination */ && usernum==0) { @@ -2522,21 +3079,24 @@ static void smtp_thread(void* arg) } snprintf(hdrfield,sizeof(hdrfield), - "Received: from %s (%s [%s])\r\n" - " by %s [%s] (Synchronet Mail Server %s-%s) with %s\r\n" - " for %s; %s" + "from %s (%s [%s])\r\n" + " by %s [%s] (%s %s-%s) with %s\r\n" + " for %s; %s\r\n" + " (envelope-from %s)" ,host_name,hello_name,host_ip ,startup->host_name,inet_ntoa(server_addr.sin_addr) + ,server_name ,revision,PLATFORM_DESC ,esmtp ? "ESMTP" : "SMTP" - ,rcpt_name,msgdate(msg.hdr.when_imported,date)); - smb_hfield_str(&newmsg, RFC822HEADER, hdrfield); + ,rcpt_to,msgdate(msg.hdr.when_imported,date) + ,reverse_path); + smb_hfield_add_str(&newmsg, SMTPRECEIVED, hdrfield, /* insert: */TRUE); smb_hfield_str(&newmsg, RECIPIENT, rcpt_name); - if(usernum) { /* Local destination or QWKnet routed */ + 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); + SAFEPRINTF(str,"%u",usernum); smb_hfield_str(&newmsg, RECIPIENTEXT, str); } if(nettype!=NET_NONE) { @@ -2553,27 +3113,40 @@ static void smtp_thread(void* arg) ,socket, i, smb.last_error); break; } - lprintf(LOG_INFO,"%04d SMTP Created message #%ld from %s to %s <%s>" - ,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: " - "\1h%s\1n\r\n" - ,timestr(&scfg,(time_t*)&newmsg.hdr.when_imported.time,tmp) - ,sender,sender_addr); - if(!newmsg.idx.to) { /* Forwarding */ - strcat(str,"\1mand it was automatically forwarded to: \1h"); - strcat(str,user.netmail); - strcat(str,"\1n\r\n"); + 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(!(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) { + safe_snprintf(str,sizeof(str) + ,"\7\1n\1hOn %.24s\r\n\1m%s \1n\1msent you e-mail from: " + "\1h%s\1n\r\n" + ,timestr(&scfg,newmsg.hdr.when_imported.time,tmp) + ,sender,sender_addr); + 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); } - putsmsg(&scfg, usernum, str); } } iniFreeStringList(sec_list); if(rcpt_count<1) { smb_freemsg_dfields(&smb,&msg,SMB_ALL_REFS); - sockprintf(socket, "452 Insufficient system storage"); + sockprintf(socket, insuf_stor); } else { if(rcpt_count>1) @@ -2599,8 +3172,26 @@ static void smtp_thread(void* arg) 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); + 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; + } + } lines++; /* release time-slices every x lines */ if(startup->lines_per_yield && @@ -2612,17 +3203,38 @@ static void smtp_thread(void* arg) 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); + { + 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++; continue; } - strip_ctrl(buf); + strip_ctrl(buf, buf); lprintf(LOG_DEBUG,"%04d SMTP RX: %s", socket, buf); if(!strnicmp(buf,"HELO",4)) { p=buf+4; @@ -2641,7 +3253,12 @@ static void smtp_thread(void* arg) SKIP_WHITESPACE(p); SAFECOPY(hello_name,p); sockprintf(socket,"250-%s",startup->host_name); - sockprintf(socket,"250 AUTH PLAIN LOGIN CRAM-MD5"); + sockprintf(socket,"250-AUTH PLAIN LOGIN CRAM-MD5"); + sockprintf(socket,"250-SEND"); + sockprintf(socket,"250-SOML"); + sockprintf(socket,"250-SAML"); + sockprintf(socket,"250-8BITMIME"); + sockprintf(socket,"250 SIZE %lu", startup->max_msg_size); esmtp=TRUE; state=SMTP_STATE_HELO; cmd=SMTP_CMD_NONE; @@ -2650,42 +3267,59 @@ static void smtp_thread(void* arg) continue; } if((auth_login=(stricmp(buf,"AUTH LOGIN")==0))==TRUE - || stricmp(buf,"AUTH PLAIN")==0) { - if(auth_login) - sockprintf(socket,"334 VXNlcm5hbWU6"); /* Base64-encoded "Username:" */ - else - sockprintf(socket,"334 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); + || 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; } - } else - SAFECOPY(user_name,buf); - - if(auth_login) sockprintf(socket,"334 UGFzc3dvcmQ6"); /* Base64-encoded "Password:" */ - else - sockprintf(socket,"334 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(auth_login) { + 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; } - } else - SAFECOPY(user_pass,buf); + } else { /* AUTH PLAIN b64(\0\0) */ + 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)" @@ -2693,21 +3327,19 @@ static void smtp_thread(void* arg) else lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: %s" ,socket, user_name); - sockprintf(socket,badauth_rsp); - continue; + 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); - sockprintf(socket,badauth_rsp); - relay_user.number=0; - continue; + 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); - sockprintf(socket,badauth_rsp); - relay_user.number=0; + badlogin(socket, client.protocol, badauth_rsp, NULL, NULL, NULL, NULL); break; } if(stricmp(user_pass,relay_user.pass)) { @@ -2717,10 +3349,13 @@ static void smtp_thread(void* arg) else lprintf(LOG_WARNING,"%04d !SMTP FAILED Password attempt for user %s" ,socket, user_name); - sockprintf(socket,badauth_rsp); - relay_user.number=0; + 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 */); @@ -2762,22 +3397,20 @@ static void smtp_thread(void* arg) if((relay_user.number=matchuser(&scfg,user_name,FALSE))==0) { lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: %s" ,socket, user_name); - sockprintf(socket,badauth_rsp); - continue; + 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); - sockprintf(socket,badauth_rsp); - relay_user.number=0; - continue; + 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); - sockprintf(socket,badauth_rsp); - relay_user.number=0; - continue; + badlogin(socket, client.protocol, badauth_rsp, NULL, NULL, NULL, NULL); + break; } /* Calculate correct response */ memset(secret,0,sizeof(secret)); @@ -2801,10 +3434,13 @@ static void smtp_thread(void* arg) lprintf(LOG_DEBUG,"%04d !SMTP resp digest: %s" ,socket,p); #endif - sockprintf(socket,badauth_rsp); - relay_user.number=0; - continue; + 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 */); @@ -2846,6 +3482,7 @@ static void smtp_thread(void* arg) cmd=SMTP_CMD_NONE; telegram=FALSE; subnum=INVALID_SUB; + spam_bait_result=FALSE; /* reset recipient list */ if((rcptlst=freopen(rcptlst_fname,"w+",rcptlst))==NULL) { @@ -2855,6 +3492,9 @@ static void smtp_thread(void* arg) break; } rcpt_count=0; + content_encoding=ENCODING_NONE; + + memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count); sockprintf(socket,ok_rsp); badcmds=0; @@ -2867,12 +3507,23 @@ static void smtp_thread(void* arg) || !strnicmp(buf,"SAML FROM:",10) /* Send AND Mail a Message to a local user */ ) { p=buf+10; - if(!chk_email_addr(socket,p,host_name,host_ip,NULL,NULL,"REVERSE PATH")) { + if(relay_user.number==0 + && !chk_email_addr(socket,p,host_name,host_ip,NULL,NULL,"REVERSE PATH")) { sockprintf(socket, "554 Sender not allowed."); + stats.msgs_refused++; break; } SKIP_WHITESPACE(p); SAFECOPY(reverse_path,p); + if((p=strchr(reverse_path,' '))!=NULL) /* Truncate " KEYWORD=VALUE" to just "" 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) { @@ -2899,6 +3550,8 @@ static void smtp_thread(void* arg) break; } rcpt_count=0; + content_encoding=ENCODING_NONE; + memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count); sockprintf(socket,ok_rsp); badcmds=0; continue; @@ -2923,13 +3576,9 @@ static void smtp_thread(void* arg) continue; } - if(spy==NULL && trashcan(&scfg,reverse_path,"smtpspy")) { - sprintf(str,"%ssmtpspy.txt", scfg.logs_dir); - spy=fopen(str,"a"); - } - p=buf+8; SKIP_WHITESPACE(p); + SAFECOPY(rcpt_to,p); SAFECOPY(str,p); p=strrchr(str,'<'); if(p==NULL) @@ -2952,7 +3601,7 @@ static void smtp_thread(void* arg) } if(*p==0) { - lprintf(LOG_WARNING,"%04d !SMTP NO RECIPIENT SPECIFIED" + lprintf(LOG_NOTICE,"%04d !SMTP NO RECIPIENT SPECIFIED" ,socket); sockprintf(socket, "500 No recipient specified"); continue; @@ -2962,53 +3611,95 @@ static void smtp_thread(void* arg) SAFECOPY(rcpt_addr,p); /* Check recipient counter */ - if(rcpt_count>=startup->max_recipients) { - lprintf(LOG_WARNING,"%04d !SMTP MAXIMUM RECIPIENTS (%d) REACHED" - ,socket, startup->max_recipients); - sprintf(tmp,"Maximum recipient count (%d)",startup->max_recipients); + 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, "552 Too many recipients"); + sockprintf(socket, "452 Too many emails today"); + stats.msgs_refused++; continue; } - + /* Check for SPAM bait recipient */ - if(findstr(rcpt_addr,spam_bait)) { - SAFEPRINTF(str,"SPAM BAIT (%s) taken", rcpt_addr); - lprintf(LOG_WARNING,"%04d !SMTP %s by: %s" - ,socket, str, reverse_path); - strcpy(tmp,"REFUSED"); - if(dnsbl_result.s_addr==0) { /* Don't double-filter */ - lprintf(LOG_WARNING,"%04d !BLOCKING IP ADDRESS: %s in %s", socket, host_ip, spam_block); - filter_ip(&scfg, "SMTP", str, host_name, host_ip, reverse_path, spam_block); - strcat(tmp," and BLOCKED"); + 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); + if(relay_user.number==0) { + strcpy(tmp,"IGNORED"); + if(dnsbl_result.s_addr==0 /* Don't double-filter */ + && !findstr(host_name,spam_block_exempt) + && !findstr(host_ip,spam_block_exempt)) { + 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; } - spamlog(&scfg, "SMTP", tmp, "Attempted recipient in SPAM BAIT list" - ,host_name, host_ip, rcpt_addr, reverse_path); - break; + sockprintf(socket,ok_rsp); + state=SMTP_STATE_RCPT_TO; + continue; } /* Check for blocked recipients */ - if(!chk_email_addr(socket,rcpt_addr,host_name,host_ip,rcpt_addr,reverse_path,"RECIPIENT")) { - sockprintf(socket, "550 Unknown User:%s", buf+8); + 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_WARNING,"%04d !SMTP REFUSED MAIL from blacklisted server" - ,socket); + 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); - continue; + 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_INFO,"%04d SMTP ADDRESS ALIAS: %s",socket,p); + lprintf(LOG_DEBUG,"%04d SMTP ADDRESS ALIAS: %s (for %s)" + ,socket,p,rcpt_addr); tp=strrchr(p,'@'); if(cmd==SMTP_CMD_MAIL && tp!=NULL) { @@ -3021,17 +3712,17 @@ static void smtp_thread(void* arg) *cp=0; dest_port=atoi(cp+1); } - sprintf(domain_list,"%sdomains.cfg",scfg.ctrl_dir); + SAFEPRINTF(domain_list,"%sdomains.cfg",scfg.ctrl_dir); if((stricmp(dest_host,scfg.sys_inetaddr)!=0 && stricmp(dest_host,startup->host_name)!=0 && resolve_ip(dest_host)!=server_addr.sin_addr.s_addr && findstr(dest_host,domain_list)==FALSE) || dest_port!=server_addr.sin_port) { - sprintf(relay_list,"%srelay.cfg",scfg.ctrl_dir); + 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_NOTE, LEN_NOTE, host_ip, FALSE); + relay_user.number=userdatdupe(&scfg, 0, U_NOTE, LEN_NOTE, host_ip, /* del */FALSE, /* next */FALSE); if(relay_user.number) { getuserdat(&scfg,&relay_user); if(relay_user.laston < time(NULL)-(60*60)) /* logon in past hour? */ @@ -3053,8 +3744,10 @@ static void smtp_thread(void* arg) sockprintf(socket, "553 Relaying through this server " "requires authentication. " "Please authenticate before sending."); - else + else { sockprintf(socket, "550 Relay not allowed."); + stats.msgs_refused++; + } break; } @@ -3091,15 +3784,36 @@ static void smtp_thread(void* arg) p=alias(&scfg,p,name_alias_buf); if(p==name_alias_buf) - lprintf(LOG_INFO,"%04d SMTP NAME ALIAS: %s",socket,p); + lprintf(LOG_DEBUG,"%04d SMTP NAME ALIAS: %s (for %s)" + ,socket,p,rcpt_addr); + /* Check if message is to be processed by an external mail processor */ + for(i=0;icode)) break; if(i>=scfg.total_subs) { - lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN SUB-BOARD: %s", socket, p); + lprintf(LOG_NOTICE,"%04d !SMTP UNKNOWN SUB-BOARD: %s", socket, p); sockprintf(socket, "550 Unknown sub-board: %s", p); continue; } @@ -3110,29 +3824,15 @@ static void smtp_thread(void* arg) continue; } - memset(mailproc_match,FALSE,sizeof(BOOL)*mailproc_count); - for(i=0;ioptions&MAIL_OPT_ALLOW_RX_BY_NUMBER) + if((p==alias_buf || p==name_alias_buf || startup->options&MAIL_OPT_ALLOW_RX_BY_NUMBER) && isdigit(*p)) { usernum=atoi(p); /* RX by user number */ /* verify usernum */ @@ -3174,7 +3874,7 @@ static void smtp_thread(void* arg) p=str; } else { /* RX by "user alias", "user.alias" or "user_alias" */ - usernum=matchuser(&scfg,p,startup->options&MAIL_OPT_ALLOW_SYSOP_ALIASES); + usernum=smtp_matchuser(&scfg,p,startup->options&MAIL_OPT_ALLOW_SYSOP_ALIASES,FALSE); if(!usernum) { /* RX by "real name", "real.name", or "sysop.alias" */ @@ -3187,10 +3887,10 @@ static void smtp_thread(void* arg) usernum=1; /* RX by "sysop.alias" */ if(!usernum && scfg.msg_misc&MM_REALNAME) /* RX by "real name" */ - usernum=userdatdupe(&scfg, 0, U_NAME, LEN_NAME, p, FALSE); + usernum=smtp_matchuser(&scfg, p, FALSE, TRUE); if(!usernum && scfg.msg_misc&MM_REALNAME) /* RX by "real.name" */ - usernum=userdatdupe(&scfg, 0, U_NAME, LEN_NAME, rcpt_name, FALSE); + usernum=smtp_matchuser(&scfg, rcpt_name, FALSE, TRUE); } } if(!usernum && startup->default_user[0]) { @@ -3203,25 +3903,47 @@ static void smtp_thread(void* arg) ,socket,startup->default_user); } + if(usernum==UINT_MAX) { + lprintf(LOG_INFO,"%04d SMTP Blocked tag: %s", socket, rcpt_to); + sockprintf(socket, "550 Unknown User: %s", rcpt_to); + continue; + } if(!usernum) { - lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER:%s", socket, buf+8); - sockprintf(socket, "550 Unknown User:%s", buf+8); + lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: %s", socket, rcpt_to); + sockprintf(socket, "550 Unknown User: %s", rcpt_to); continue; } user.number=usernum; if((i=getuserdat(&scfg, &user))!=0) { lprintf(LOG_ERR,"%04d !SMTP ERROR %d getting data on user #%u (%s)" ,socket, i, usernum, p); - sockprintf(socket, "550 Unknown User:%s", buf+8); + sockprintf(socket, "550 Unknown User: %s", rcpt_to); continue; } if(user.misc&(DELETED|INACTIVE)) { lprintf(LOG_WARNING,"%04d !SMTP DELETED or INACTIVE user #%u (%s)" ,socket, usernum, p); - sockprintf(socket, "550 Unknown User:%s", buf+8); + sockprintf(socket, "550 Unknown User: %s", rcpt_to); continue; } - if(cmd==SMTP_CMD_SEND) { /* Check if user online */ + if(cmd==SMTP_CMD_MAIL) { + if((user.rest&FLAG('M')) && relay_user.number==0) { + lprintf(LOG_NOTICE,"%04d !SMTP M-restricted user #u (%s) cannot receive unauthenticated SMTP mail" + ,socket, user.number, user.alias); + sockprintf(socket, "550 Closed mailbox: %s", rcpt_to); + stats.msgs_refused++; + continue; + } + if(startup->max_msgs_waiting && !(user.exempt&FLAG('W')) + && (waiting=getmail(&scfg, user.number, /* sent: */FALSE)) > startup->max_msgs_waiting) { + lprintf(LOG_NOTICE,"%04d !SMTP User #%u (%s) mailbox (%u msgs) exceeds the maximum (%u) msgs waiting" + ,socket, user.number, user.alias, waiting, startup->max_msgs_waiting); + sockprintf(socket, "450 Mailbox full: %s", rcpt_to); + stats.msgs_refused++; + continue; + } + } + else if(cmd==SMTP_CMD_SEND) { /* Check if user online */ for(i=0;i=cfg.level_emailperday[useron.level] - && !(useron.rest&FLAG('Q'))) { - bputs(text[TooManyEmailsToday]); - continue; - } -#endif - } else + if(cmd!=SMTP_CMD_MAIL) telegram=TRUE; fprintf(rcptlst,"[%u]\n",rcpt_count++); fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr); fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTEXT),user.number); - /* Forward to Internet */ tp=strrchr(user.netmail,'@'); if(!telegram @@ -3284,19 +3997,17 @@ static void smtp_thread(void* arg) } if(msgtxt!=NULL) { fclose(msgtxt), msgtxt=NULL; - if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY)) - unlink(msgtxt_fname); } - SAFEPRINTF2(msgtxt_fname,"%sSBBS_SMTP.%s.msg", scfg.temp_dir, session_id); + remove(msgtxt_fname); if((msgtxt=fopen(msgtxt_fname,"w+b"))==NULL) { lprintf(LOG_ERR,"%04d !SMTP ERROR %d opening %s" ,socket, errno, msgtxt_fname); - sockprintf(socket, "452 Insufficient system storage"); + sockprintf(socket, insuf_stor); continue; } /* These vars are potentially over-written by parsing an RFC822 header */ /* get sender_addr */ - p=strchr(reverse_path,'<'); + p=strrchr(reverse_path,'<'); if(p==NULL) p=reverse_path; else @@ -3313,6 +4024,8 @@ static void smtp_thread(void* arg) state=SMTP_STATE_DATA_BODY; /* No RFC headers in Telegrams */ else state=SMTP_STATE_DATA_HEADER; + lprintf(LOG_INFO,"%04d SMTP Receiving %s message from: %s to %s" + ,socket, telegram ? "telegram":"mail", reverse_path, rcpt_addr); hdr_lines=0; continue; } @@ -3327,33 +4040,34 @@ static void smtp_thread(void* arg) /* Free up resources here */ smb_freemsgmem(&msg); - if(msgtxt!=NULL) { + if(msgtxt!=NULL) fclose(msgtxt); - if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY)) - unlink(msgtxt_fname); - } - if(rcptlst!=NULL) { + if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY)) + remove(msgtxt_fname); + if(rcptlst!=NULL) fclose(rcptlst); - unlink(rcptlst_fname); - } + remove(rcptlst_fname); if(spy!=NULL) fclose(spy); + js_cleanup(js_runtime, js_cx); status(STATUS_WFC); - if(active_clients) - active_clients--, update_clients(); + protected_int32_adjust(&active_clients, -1); + update_clients(); client_off(socket); - thread_down(); - lprintf(LOG_DEBUG,"%04d SMTP RX Session thread terminated (%u threads remain, %lu clients served)" - ,socket, thread_count, served); + { + int32_t remain = thread_down(); + lprintf(LOG_INFO,"%04d SMTP Session thread terminated (%u threads remain, %lu clients served)" + ,socket, remain, ++stats.smtp_served); + } /* Must be last */ mail_close_socket(socket); } -BOOL bounce(smb_t* smb, smbmsg_t* msg, char* err, BOOL immediate) +BOOL bounce(SOCKET sock, smb_t* smb, smbmsg_t* msg, char* err, BOOL immediate) { char str[128]; char attempts[64]; @@ -3362,7 +4076,8 @@ BOOL bounce(smb_t* smb, smbmsg_t* msg, c smbmsg_t newmsg; msg->hdr.delivery_attempts++; - lprintf(LOG_WARNING,"0000 !Delivery attempt #%u FAILED (%s) for message #%lu from %s to %s" + lprintf(LOG_WARNING,"%04d !SEND Delivery attempt #%u FAILED (%s) for message #%lu from %s to %s" + ,sock ,msg->hdr.delivery_attempts ,err ,msg->hdr.number @@ -3370,8 +4085,8 @@ BOOL bounce(smb_t* smb, smbmsg_t* msg, c ,msg->to_net.addr); if((i=smb_updatemsg(smb,msg))!=SMB_SUCCESS) { - lprintf(LOG_ERR,"0000 !BOUNCE ERROR %d (%s) incrementing delivery attempt counter" - ,i, smb->last_error); + lprintf(LOG_ERR,"%04d !SEND BOUNCE ERROR %d (%s) incrementing delivery attempt counter" + ,sock, i, smb->last_error); return(FALSE); } @@ -3386,19 +4101,20 @@ BOOL bounce(smb_t* smb, smbmsg_t* msg, c if(msg->hdr.auxattr&MSG_FILEATTACH) delfattach(&scfg,msg); if(i!=SMB_SUCCESS) { - lprintf(LOG_ERR,"0000 !BOUNCE ERROR %d (%s) deleting message" - ,i, smb->last_error); + lprintf(LOG_ERR,"%04d !SEND BOUNCE ERROR %d (%s) deleting message" + ,sock, i, smb->last_error); return(FALSE); } - if(msg->from_agent!=AGENT_PERSON /* don't bounce 'bounce messages' */ + if(msg->from_agent==AGENT_SMTPSYSMSG /* don't bounce 'bounce messages' */ + || (msg->hdr.attr&MSG_NOREPLY) || (msg->idx.from==0 && msg->from_net.type==NET_NONE) || (msg->reverse_path!=NULL && *msg->reverse_path==0)) { - lprintf(LOG_WARNING,"0000 !Deleted undeliverable message from %s", msg->from); + lprintf(LOG_WARNING,"%04d !SEND Deleted undeliverable message from %s", sock, msg->from); return(TRUE); } - lprintf(LOG_WARNING,"0000 !Bouncing message back to %s", msg->from); + lprintf(LOG_WARNING,"%04d !SEND Bouncing message back to %s", sock, msg->from); newmsg.hfield=NULL; newmsg.hfield_dat=NULL; @@ -3408,23 +4124,29 @@ BOOL bounce(smb_t* smb, smbmsg_t* msg, c SAFEPRINTF(str,"Delivery failure: %s",newmsg.subj); smb_hfield_str(&newmsg, SUBJECT, str); smb_hfield_str(&newmsg, RECIPIENT, newmsg.from); - if(newmsg.from_ext!=NULL) { /* Back to sender */ - smb_hfield_str(&newmsg, RECIPIENTEXT, newmsg.from_ext); - newmsg.from_ext=NULL; /* Clear the sender extension */ - } + if(msg->from_agent==AGENT_PERSON) { + + if(newmsg.from_ext!=NULL) { /* Back to sender */ + smb_hfield_str(&newmsg, RECIPIENTEXT, newmsg.from_ext); + newmsg.from_ext=NULL; /* Clear the sender extension */ + } - if((newmsg.from_net.type==NET_QWK || newmsg.from_net.type==NET_INTERNET) - && newmsg.reverse_path!=NULL) { - smb_hfield(&newmsg, RECIPIENTNETTYPE, sizeof(newmsg.from_net.type), &newmsg.from_net.type); - smb_hfield_str(&newmsg, RECIPIENTNETADDR, newmsg.reverse_path); + if((newmsg.from_net.type==NET_QWK || newmsg.from_net.type==NET_INTERNET) + && newmsg.reverse_path!=NULL) { + smb_hfield(&newmsg, RECIPIENTNETTYPE, sizeof(newmsg.from_net.type), &newmsg.from_net.type); + smb_hfield_str(&newmsg, RECIPIENTNETADDR, newmsg.reverse_path); + } + } else { + smb_hfield(&newmsg, RECIPIENTAGENT, sizeof(msg->from_agent), &msg->from_agent); } + newmsg.hdr.attr|=MSG_NOREPLY; strcpy(str,"Mail Delivery Subsystem"); smb_hfield_str(&newmsg, SENDER, str); smb_hfield(&newmsg, SENDERAGENT, sizeof(agent), &agent); /* Put error message in subject for now */ if(msg->hdr.delivery_attempts>1) - sprintf(attempts,"after %u attempts", msg->hdr.delivery_attempts); + SAFEPRINTF(attempts,"after %u attempts", msg->hdr.delivery_attempts); else attempts[0]=0; SAFEPRINTF2(str,"%s reporting delivery failure of message %s" @@ -3440,14 +4162,14 @@ BOOL bounce(smb_t* smb, smbmsg_t* msg, c smb_hfield_str(&newmsg, SMB_COMMENT, "\r\nOriginal message text follows:\r\n"); if((i=smb_addmsghdr(smb,&newmsg,SMB_SELFPACK))!=SMB_SUCCESS) - lprintf(LOG_ERR,"0000 !BOUNCE ERROR %d (%s) adding message header" - ,i,smb->last_error); + lprintf(LOG_ERR,"%04d !BOUNCE ERROR %d (%s) adding message header" + ,sock,i,smb->last_error); else { - lprintf(LOG_WARNING,"0000 !Delivery failure notification (message #%ld) created for %s" - ,newmsg.hdr.number, newmsg.from); + lprintf(LOG_WARNING,"%04d !SEND Delivery failure notification (message #%ld) created for %s" + ,sock, newmsg.hdr.number, newmsg.from); if((i=smb_incmsg_dfields(smb,&newmsg,1))!=SMB_SUCCESS) - lprintf(LOG_ERR,"0000 !BOUNCE ERROR %d (%s) incrementing data allocation units" - ,i,smb->last_error); + lprintf(LOG_ERR,"%04d !SEND BOUNCE ERROR %d (%s) incrementing data allocation units" + ,sock, i,smb->last_error); } newmsg.dfield=NULL; /* Don't double-free the data fields */ @@ -3509,6 +4231,7 @@ static void sendmail_thread(void* arg) char str[128]; char resp[512]; char toaddr[256]; + char fromext[128]; char fromaddr[256]; char challenge[256]; char secret[64]; @@ -3535,21 +4258,25 @@ static void sendmail_thread(void* arg) smb_t smb; smbmsg_t msg; mail_t* mail; - long msgs; + int32_t msgs; long l; + size_t len; BOOL sending_locally=FALSE; + link_list_t failed_server_list; - SetThreadName("SendMail Thread"); + SetThreadName("SendMail"); thread_up(TRUE /* setuid */); sendmail_running=TRUE; terminate_sendmail=FALSE; - lprintf(LOG_DEBUG,"0000 SendMail thread started"); + lprintf(LOG_INFO,"0000 SendMail thread started"); memset(&msg,0,sizeof(msg)); memset(&smb,0,sizeof(smb)); + listInit(&failed_server_list, /* flags: */0); + while(server_socket!=INVALID_SOCKET && !terminate_sendmail) { if(startup->options&MAIL_OPT_NO_SENDMAIL) { @@ -3560,6 +4287,8 @@ static void sendmail_thread(void* arg) if(active_sendmail!=0) active_sendmail=0, update_clients(); + listFreeNodes(&failed_server_list); + smb_close(&smb); if(sock!=INVALID_SOCKET) { @@ -3580,7 +4309,7 @@ static void sendmail_thread(void* arg) else sem_trywait_block(&sendmail_wakeup_sem,startup->sem_chk_freq*1000); - sprintf(smb.file,"%smail",scfg.data_dir); + SAFEPRINTF(smb.file,"%smail",scfg.data_dir); smb.retry_time=scfg.smb_retry_time; smb.subnum=INVALID_SUB; if((i=smb_open(&smb))!=SMB_SUCCESS) @@ -3593,6 +4322,8 @@ static void sendmail_thread(void* arg) continue; if(smb.status.last_msg==last_msg && time(NULL)-last_scanrescan_frequency) continue; + lprintf(LOG_DEBUG, "0000 SEND last_msg=%u, smb.status.last_msg=%u, elapsed=%u" + ,last_msg, smb.status.last_msg, time(NULL)-last_scan); last_msg=smb.status.last_msg; last_scan=time(NULL); mail=loadmail(&smb,&msgs,/* to network */0,MAIL_YOUR,0); @@ -3639,7 +4370,7 @@ static void sendmail_thread(void* arg) if(!(startup->options&MAIL_OPT_SEND_INTRANSIT) && msg.hdr.netattr&MSG_INTRANSIT) { smb_unlockmsghdr(&smb,&msg); - lprintf(LOG_ERR,"0000 SEND Message #%lu from %s to %s - in transit" + lprintf(LOG_NOTICE,"0000 SEND Message #%lu from %s to %s - in transit" ,msg.hdr.number, msg.from, msg.to_net.addr); continue; } @@ -3649,9 +4380,20 @@ static void sendmail_thread(void* arg) active_sendmail=1, update_clients(); - lprintf(LOG_INFO,"0000 SEND Message #%lu from %s to %s" - ,msg.hdr.number, msg.from, msg.to_net.addr); - status("Sending"); + fromext[0]=0; + if(msg.from_ext) + SAFEPRINTF(fromext," #%s", msg.from_ext); + if(msg.from_net.type==NET_INTERNET && msg.reverse_path!=NULL) + SAFECOPY(fromaddr,msg.reverse_path); + else + usermailaddr(&scfg,fromaddr,msg.from); + truncstr(fromaddr," "); + + lprintf(LOG_INFO,"0000 SEND Message #%lu (%u of %u) from %s%s %s to %s [%s]" + ,msg.hdr.number, l+1, msgs, msg.from, fromext, fromaddr + ,msg.to, msg.to_net.addr); + SAFEPRINTF2(str,"Sending (%u of %u)", l+1, msgs); + status(str); #ifdef _WIN32 if(startup->outbound_sound[0] && !(startup->options&MAIL_OPT_MUTE)) PlaySound(startup->outbound_sound, NULL, SND_ASYNC|SND_FILENAME); @@ -3664,8 +4406,12 @@ static void sendmail_thread(void* arg) continue; } + remove_ctrl_a(msgtxt, msgtxt); + port=0; + mx2[0]=0; + sending_locally=FALSE; /* Check if this is a local email ToDo */ SAFECOPY(to,(char*)msg.to_net.addr); truncstr(to,"> "); @@ -3675,11 +4421,11 @@ static void sendmail_thread(void* arg) remove_msg_intransit(&smb,&msg); lprintf(LOG_WARNING,"0000 !SEND INVALID destination address: %s", to); SAFEPRINTF(err,"Invalid destination address: %s", to); - bounce(&smb,&msg,err,TRUE); + bounce(0, &smb,&msg,err, /* immediate: */TRUE); continue; } p++; - sprintf(domain_list,"%sdomains.cfg",scfg.ctrl_dir); + SAFEPRINTF(domain_list,"%sdomains.cfg",scfg.ctrl_dir); if(stricmp(p,scfg.sys_inetaddr)==0 || stricmp(p,startup->host_name)==0 || findstr(p,domain_list)) { @@ -3688,7 +4434,7 @@ static void sendmail_thread(void* arg) if(startup->interface_addr==0) server="127.0.0.1"; else { - sprintf(numeric_ip, "%u.%u.%u.%u" + SAFEPRINTF4(numeric_ip, "%u.%u.%u.%u" , startup->interface_addr >> 24 , (startup->interface_addr >> 16) & 0xff , (startup->interface_addr >> 8) & 0xff @@ -3717,14 +4463,14 @@ static void sendmail_thread(void* arg) continue; } lprintf(LOG_DEBUG,"0000 SEND getting MX records for %s from %s",p,dns_server); - if((i=dns_getmx(p, mx, mx2, startup->interface_addr, dns + if((i=dns_getmx(p, mx, mx2, INADDR_ANY, dns ,startup->options&MAIL_OPT_USE_TCP_DNS ? TRUE : FALSE ,TIMEOUT_THREAD_WAIT/2))!=0) { remove_msg_intransit(&smb,&msg); lprintf(LOG_WARNING,"0000 !SEND ERROR %d obtaining MX records for %s from %s" - ,i,p,startup->dns_server); + ,i,p,dns_server); SAFEPRINTF2(err,"Error %d obtaining MX record for %s",i,p); - bounce(&smb,&msg,err,FALSE); + bounce(0, &smb,&msg,err, /* immediate: */FALSE); continue; } server=mx; @@ -3740,15 +4486,27 @@ static void sendmail_thread(void* arg) continue; } + if(startup->connect_timeout) { /* Use non-blocking socket */ + long nbio=1; + if((i=ioctlsocket(sock, FIONBIO, &nbio))!=0) { + remove_msg_intransit(&smb,&msg); + lprintf(LOG_ERR,"%04d !SEND ERROR %d (%d) disabling blocking on socket" + ,sock, i, ERROR_VALUE); + continue; + } + } + memset(&addr,0,sizeof(addr)); addr.sin_addr.s_addr = htonl(startup->interface_addr); addr.sin_family = AF_INET; + /* Not needed. Port is zero if(startup->seteuid!=NULL) - startup->seteuid(FALSE); + startup->seteuid(FALSE); */ i=bind(sock,(struct sockaddr *)&addr, sizeof(addr)); + /* Not needed. Port is zero if(startup->seteuid!=NULL) - startup->seteuid(TRUE); + startup->seteuid(TRUE); */ if(i!=0) { remove_msg_intransit(&smb,&msg); lprintf(LOG_ERR,"%04d !SEND ERROR %d (%d) binding socket", sock, i, ERROR_VALUE); @@ -3758,9 +4516,12 @@ static void sendmail_thread(void* arg) strcpy(err,"UNKNOWN ERROR"); success=FALSE; for(j=0;j<2 && !success;j++) { + list_node_t* node; + if(j) { if(startup->options&MAIL_OPT_RELAY_TX || !mx2[0]) break; + lprintf(LOG_DEBUG,"%04d SEND reverting to second MX: %s", sock, mx2); server=mx2; /* Give second mx record a try */ } @@ -3768,6 +4529,7 @@ static void sendmail_thread(void* arg) ip_addr=resolve_ip(server); if(ip_addr==INADDR_NONE) { SAFEPRINTF(err,"Failed to resolve SMTP hostname: %s",server); + lprintf(LOG_WARNING,"%04d !SEND failure resolving hostname: %s", sock, server); continue; } @@ -3776,6 +4538,17 @@ static void sendmail_thread(void* arg) server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); + if((node=listFindNode(&failed_server_list,&server_addr,sizeof(server_addr))) != NULL) { + lprintf(LOG_INFO,"%04d SEND skipping failed SMTP server: Error %d connecting to port %u on %s [%s]" + ,sock + ,node->tag + ,ntohs(server_addr.sin_port) + ,server,inet_ntoa(server_addr.sin_addr)); + SAFEPRINTF2(err,"Error %d connecting to SMTP server: %s" + ,node->tag, server); + continue; + } + if((server==mx || server==mx2) && ((ip_addr&0xff)==127 || ip_addr==0)) { SAFEPRINTF2(err,"Bad IP address (%s) for MX server: %s" @@ -3787,20 +4560,20 @@ static void sendmail_thread(void* arg) ,sock ,ntohs(server_addr.sin_port) ,server,inet_ntoa(server_addr.sin_addr)); - if((i=connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)))!=0) { - i=ERROR_VALUE; + if((i=nonblocking_connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr), startup->connect_timeout))!=0) { lprintf(LOG_WARNING,"%04d !SEND ERROR %d connecting to SMTP server: %s" ,sock ,i, server); SAFEPRINTF2(err,"Error %d connecting to SMTP server: %s" ,i, server); + listAddNodeData(&failed_server_list,&server_addr,sizeof(server_addr),i,NULL); continue; } success=TRUE; } if(!success) { /* Failed to send, so bounce */ remove_msg_intransit(&smb,&msg); - bounce(&smb,&msg,err,FALSE); + bounce(sock, &smb,&msg,err,/* immediate: */FALSE); continue; } @@ -3810,7 +4583,7 @@ static void sendmail_thread(void* arg) if(!sockgetrsp(sock,"220",buf,sizeof(buf))) { remove_msg_intransit(&smb,&msg); SAFEPRINTF3(err,badrsp_err,server,buf,"220"); - bounce(&smb,&msg,err,buf[0]=='5'); + bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5'); continue; } if(startup->options&MAIL_OPT_RELAY_TX @@ -3821,98 +4594,97 @@ static void sendmail_thread(void* arg) if(!sockgetrsp(sock,"250", buf, sizeof(buf))) { remove_msg_intransit(&smb,&msg); SAFEPRINTF3(err,badrsp_err,server,buf,"250"); - bounce(&smb,&msg,err,buf[0]=='5'); + bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5'); continue; } /* AUTH */ if(startup->options&MAIL_OPT_RELAY_TX && (startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=0 && !sending_locally) { - switch(startup->options&MAIL_OPT_RELAY_AUTH_MASK) { - case MAIL_OPT_RELAY_AUTH_PLAIN: - p="PLAIN"; - break; - case MAIL_OPT_RELAY_AUTH_LOGIN: - p="LOGIN"; - break; - case MAIL_OPT_RELAY_AUTH_CRAM_MD5: - p="CRAM-MD5"; - break; - default: - p=""; - break; - } - sockprintf(sock,"AUTH %s",p); - if(!sockgetrsp(sock,"334",buf,sizeof(buf))) { - SAFEPRINTF3(err,badrsp_err,server,buf,"334 Username/Challenge"); - bounce(&smb,&msg,err,buf[0]=='5'); - continue; - } - switch(startup->options&MAIL_OPT_RELAY_AUTH_MASK) { - case MAIL_OPT_RELAY_AUTH_PLAIN: - p=startup->relay_user; - break; - case MAIL_OPT_RELAY_AUTH_LOGIN: - b64_encode(p=resp,sizeof(resp),startup->relay_user,0); - break; - case MAIL_OPT_RELAY_AUTH_CRAM_MD5: - p=buf; - FIND_WHITESPACE(p); - SKIP_WHITESPACE(p); - b64_decode(challenge,sizeof(challenge),p,0); - - /* Calculate response */ - memset(secret,0,sizeof(secret)); - SAFECOPY(secret,startup->relay_pass); - for(i=0;irelay_user,MD5_hex(str,digest)); - b64_encode(p=resp,sizeof(resp),buf,0); - break; - default: - p=""; - break; - } - sockprintf(sock,"%s",p); - if((startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=MAIL_OPT_RELAY_AUTH_CRAM_MD5) { + + if((startup->options&MAIL_OPT_RELAY_AUTH_MASK)==MAIL_OPT_RELAY_AUTH_PLAIN) { + /* Build the buffer: \0\0relay_user + ,0 + ,startup->relay_user + ,0 + ,startup->relay_pass); + b64_encode(resp,sizeof(resp),buf,len); + sockprintf(sock,"AUTH PLAIN %s",resp); + } else { + switch(startup->options&MAIL_OPT_RELAY_AUTH_MASK) { + case MAIL_OPT_RELAY_AUTH_LOGIN: + p="LOGIN"; + break; + case MAIL_OPT_RELAY_AUTH_CRAM_MD5: + p="CRAM-MD5"; + break; + default: + p=""; + break; + } + sockprintf(sock,"AUTH %s",p); if(!sockgetrsp(sock,"334",buf,sizeof(buf))) { - SAFEPRINTF3(err,badrsp_err,server,buf,"334 Password"); - bounce(&smb,&msg,err,buf[0]=='5'); + SAFEPRINTF3(err,badrsp_err,server,buf,"334 Username/Challenge"); + bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5'); continue; } switch(startup->options&MAIL_OPT_RELAY_AUTH_MASK) { - case MAIL_OPT_RELAY_AUTH_PLAIN: - p=startup->relay_pass; - break; case MAIL_OPT_RELAY_AUTH_LOGIN: - b64_encode(p=buf,sizeof(buf),startup->relay_pass,0); + b64_encode(p=resp,sizeof(resp),startup->relay_user,0); + break; + case MAIL_OPT_RELAY_AUTH_CRAM_MD5: + p=buf; + FIND_WHITESPACE(p); + SKIP_WHITESPACE(p); + b64_decode(challenge,sizeof(challenge),p,0); + + /* Calculate response */ + memset(secret,0,sizeof(secret)); + SAFECOPY(secret,startup->relay_pass); + for(i=0;irelay_user,MD5_hex(str,digest)); + b64_encode(p=resp,sizeof(resp),buf,0); break; default: p=""; break; } sockprintf(sock,"%s",p); + if((startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=MAIL_OPT_RELAY_AUTH_CRAM_MD5) { + if(!sockgetrsp(sock,"334",buf,sizeof(buf))) { + SAFEPRINTF3(err,badrsp_err,server,buf,"334 Password"); + bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5'); + continue; + } + switch(startup->options&MAIL_OPT_RELAY_AUTH_MASK) { + case MAIL_OPT_RELAY_AUTH_LOGIN: + b64_encode(p=buf,sizeof(buf),startup->relay_pass,0); + break; + default: + p=""; + break; + } + sockprintf(sock,"%s",p); + } } if(!sockgetrsp(sock,"235",buf,sizeof(buf))) { SAFEPRINTF3(err,badrsp_err,server,buf,"235"); - bounce(&smb,&msg,err,buf[0]=='5'); + bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5'); continue; } } /* MAIL */ - if(msg.from_net.type==NET_INTERNET && msg.reverse_path!=NULL) - SAFECOPY(fromaddr,msg.reverse_path); - else - usermailaddr(&scfg,fromaddr,msg.from); - truncstr(fromaddr," "); if(fromaddr[0]=='<') sockprintf(sock,"MAIL FROM: %s",fromaddr); else @@ -3920,7 +4692,7 @@ static void sendmail_thread(void* arg) if(!sockgetrsp(sock,"250", buf, sizeof(buf))) { remove_msg_intransit(&smb,&msg); SAFEPRINTF3(err,badrsp_err,server,buf,"250"); - bounce(&smb,&msg,err,buf[0]=='5'); + bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5'); continue; } /* RCPT */ @@ -3941,7 +4713,7 @@ static void sendmail_thread(void* arg) if(!sockgetrsp(sock,"25", buf, sizeof(buf))) { remove_msg_intransit(&smb,&msg); SAFEPRINTF3(err,badrsp_err,server,buf,"25*"); - bounce(&smb,&msg,err,buf[0]=='5'); + bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5'); continue; } /* DATA */ @@ -3949,7 +4721,7 @@ static void sendmail_thread(void* arg) if(!sockgetrsp(sock,"354", buf, sizeof(buf))) { remove_msg_intransit(&smb,&msg); SAFEPRINTF3(err,badrsp_err,server,buf,"354"); - bounce(&smb,&msg,err,buf[0]=='5'); + bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5'); continue; } lprintf(LOG_DEBUG,"%04d SEND sending message text (%u bytes)" @@ -3958,10 +4730,10 @@ static void sendmail_thread(void* arg) if(!sockgetrsp(sock,"250", buf, sizeof(buf))) { remove_msg_intransit(&smb,&msg); SAFEPRINTF3(err,badrsp_err,server,buf,"250"); - bounce(&smb,&msg,err,buf[0]=='5'); + bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5'); continue; } - lprintf(LOG_DEBUG,"%04d SEND message transfer complete (%lu lines)", sock, lines); + lprintf(LOG_INFO,"%04d SEND message transfer complete (%lu lines)", sock, lines); /* Now lets mark this message for deletion without corrupting the index */ msg.hdr.attr|=MSG_DELETE; @@ -3972,6 +4744,9 @@ static void sendmail_thread(void* arg) if(msg.hdr.auxattr&MSG_FILEATTACH) delfattach(&scfg,&msg); + if(msg.from_agent==AGENT_PERSON && !(startup->options&MAIL_OPT_NO_AUTO_EXEMPT)) + exempt_email_addr("SEND Auto-exempting",msg.from,fromext,fromaddr,toaddr); + /* QUIT */ sockprintf(sock,"QUIT"); sockgetrsp(sock,"221", buf, sizeof(buf)); @@ -3986,6 +4761,8 @@ static void sendmail_thread(void* arg) if(sock!=INVALID_SOCKET) mail_close_socket(sock); + listFree(&failed_server_list); + smb_freemsgtxt(msgtxt); smb_freemsgmem(&msg); smb_close(&smb); @@ -3993,21 +4770,23 @@ static void sendmail_thread(void* arg) if(active_sendmail!=0) active_sendmail=0, update_clients(); - thread_down(); - lprintf(LOG_DEBUG,"0000 SendMail thread terminated (%u threads remain)", thread_count); + { + int32_t remain = thread_down(); + lprintf(LOG_DEBUG,"0000 SendMail thread terminated (%u threads remain)", remain); + } sendmail_running=FALSE; } void DLLCALL mail_terminate(void) { - lprintf(LOG_DEBUG,"%04d Mail Server terminate",server_socket); + lprintf(LOG_INFO,"%04d Mail Server terminate",server_socket); terminate_server=TRUE; } static void cleanup(int code) { - int i; + int i; free_cfg(&scfg); @@ -4015,8 +4794,12 @@ static void cleanup(int code) semfile_list_free(&shutdown_semfiles); if(mailproc_list!=NULL) { - for(i=0;iterminated!=NULL) startup->terminated(startup->cbdata,code); } @@ -4053,10 +4870,11 @@ const char* DLLCALL mail_ver(void) DESCRIBE_COMPILER(compiler); - sscanf("$Revision: 1.1.1.1 $", "%*s %s", revision); + sscanf("$Revision: 1.1.1.2 $", "%*s %s", revision); - sprintf(ver,"Synchronet Mail Server %s%s SMBLIB %s " + sprintf(ver,"%s %s%s SMBLIB %s " "Compiled %s %s with %s" + ,server_name ,revision #ifdef _DEBUG ," Debug" @@ -4074,6 +4892,7 @@ void DLLCALL mail_server(void* arg) { char* p; char path[MAX_PATH+1]; + char mailproc_ini[MAX_PATH+1]; char str[256]; char error[256]; char compiler[32]; @@ -4120,12 +4939,12 @@ void DLLCALL mail_server(void* arg) /* Setup intelligent defaults */ if(startup->relay_port==0) startup->relay_port=IPPORT_SMTP; + if(startup->submission_port==0) startup->submission_port=IPPORT_SUBMISSION; if(startup->smtp_port==0) startup->smtp_port=IPPORT_SMTP; if(startup->pop3_port==0) startup->pop3_port=IPPORT_POP3; if(startup->rescan_frequency==0) startup->rescan_frequency=3600; /* 60 minutes */ if(startup->max_delivery_attempts==0) startup->max_delivery_attempts=50; if(startup->max_inactivity==0) startup->max_inactivity=120; /* seconds */ - if(startup->max_recipients==0) startup->max_recipients=100; if(startup->sem_chk_freq==0) startup->sem_chk_freq=2; #ifdef JAVASCRIPT @@ -4133,8 +4952,15 @@ void DLLCALL mail_server(void* arg) if(startup->js.cx_stack==0) startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK; #endif + ZERO_VAR(js_server_props); + SAFEPRINTF2(js_server_props.version,"%s %s",server_name,revision); + js_server_props.version_detail=mail_ver(); + js_server_props.clients=&active_clients.value; + js_server_props.options=&startup->options; + js_server_props.interface_addr=&startup->interface_addr; + uptime=0; - served=0; + memset(&stats,0,sizeof(stats)); startup->recycle_now=FALSE; startup->shutdown_now=FALSE; terminate_server=FALSE; @@ -4143,13 +4969,16 @@ void DLLCALL mail_server(void* arg) do { + protected_int32_init(&thread_count, 0); + thread_up(FALSE /* setuid */); status("Initializing"); memset(&scfg, 0, sizeof(scfg)); - lprintf(LOG_INFO,"Synchronet Mail Server Revision %s%s" + lprintf(LOG_INFO,"%s Revision %s%s" + ,server_name ,revision #ifdef _DEBUG ," Debug" @@ -4162,7 +4991,7 @@ void DLLCALL mail_server(void* arg) lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler); - lprintf(LOG_INFO,"SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff); + lprintf(LOG_DEBUG,"SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff); sbbs_srand(); @@ -4173,7 +5002,10 @@ void DLLCALL mail_server(void* arg) t=time(NULL); lprintf(LOG_INFO,"Initializing on %.24s with options: %lx" - ,CTIME_R(&t,str),startup->options); + ,ctime_r(&t,str),startup->options); + + if(chdir(startup->ctrl_dir)!=0) + lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", errno, startup->ctrl_dir); /* Initial configuration and load from CNF files */ SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir); @@ -4181,8 +5013,8 @@ void DLLCALL mail_server(void* arg) scfg.size=sizeof(scfg); SAFECOPY(error,UNKNOWN_LOAD_ERROR); if(!load_cfg(&scfg, NULL, TRUE, error)) { - lprintf(LOG_ERR,"!ERROR %s",error); - lprintf(LOG_ERR,"!Failed to load configuration files"); + lprintf(LOG_CRIT,"!ERROR %s",error); + lprintf(LOG_CRIT,"!Failed to load configuration files"); cleanup(1); return; } @@ -4195,7 +5027,7 @@ void DLLCALL mail_server(void* arg) MKDIR(scfg.temp_dir); lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir); if(!isdir(scfg.temp_dir)) { - lprintf(LOG_ERR,"!Invalid temp directory: %s", scfg.temp_dir); + lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir); cleanup(1); return; } @@ -4203,21 +5035,38 @@ void DLLCALL mail_server(void* arg) /* Parse the mailproc[.host].ini */ mailproc_list=NULL; mailproc_count=0; - iniFileName(path,sizeof(path),scfg.ctrl_dir,"mailproc.ini"); - if((fp=iniOpenFile(path, /* create? */FALSE))!=NULL) { - lprintf(LOG_DEBUG,"Reading %s",path); + iniFileName(mailproc_ini,sizeof(mailproc_ini),scfg.ctrl_dir,"mailproc.ini"); + if((fp=iniOpenFile(mailproc_ini, /* create? */FALSE))!=NULL) { + lprintf(LOG_DEBUG,"Reading %s",mailproc_ini); sec_list = iniReadSectionList(fp,/* prefix */NULL); if((mailproc_count=strListCount(sec_list))!=0 && (mailproc_list=malloc(mailproc_count*sizeof(struct mailproc)))!=NULL) { + char buf[INI_MAX_VALUE_LEN+1]; for(i=0;ihost_name[0]==0) SAFECOPY(startup->host_name,scfg.sys_inetaddr); - if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&MAIL_OPT_LOCAL_TIMEZONE)) { - if(putenv("TZ=UTC0")) - lprintf(LOG_ERR,"!putenv() FAILED"); - tzset(); - - if((t=checktime())!=0) { /* Check binary time */ - lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t); - cleanup(1); - return; - } + if((t=checktime())!=0) { /* Check binary time */ + lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t); } if(uptime==0) @@ -4252,14 +5093,15 @@ void DLLCALL mail_server(void* arg) lprintf(LOG_DEBUG,"Maximum inactivity: %u seconds",startup->max_inactivity); - active_clients=0,update_clients(); + protected_int32_init(&active_clients, 0); + update_clients(); /* open a socket and wait for a client */ server_socket = mail_open_socket(SOCK_STREAM,"smtp"); if(server_socket == INVALID_SOCKET) { - lprintf(LOG_ERR,"!ERROR %d opening socket", ERROR_VALUE); + lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE); cleanup(1); return; } @@ -4275,29 +5117,81 @@ void DLLCALL mail_server(void* arg) server_addr.sin_family = AF_INET; server_addr.sin_port = htons(startup->smtp_port); - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); + if(startup->smtp_port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(FALSE); + } result = retry_bind(server_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) ,startup->bind_retry_count,startup->bind_retry_delay,"SMTP Server",lprintf); - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); + if(startup->smtp_port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(TRUE); + } if(result != 0) { - lprintf(LOG_ERR,"%04d %s",server_socket, BIND_FAILURE_HELP); + lprintf(LOG_CRIT,"%04d %s",server_socket, BIND_FAILURE_HELP); cleanup(1); return; } - - lprintf(LOG_DEBUG,"%04d SMTP socket bound to port %u" - ,server_socket, startup->smtp_port); - - result = listen (server_socket, 1); + result = listen(server_socket, 1); if(result != 0) { - lprintf(LOG_ERR,"%04d !ERROR %d (%d) listening on socket" + lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on SMTP socket" ,server_socket, result, ERROR_VALUE); cleanup(1); return; } + lprintf(LOG_INFO,"%04d SMTP Server listening on port %u" + ,server_socket, startup->smtp_port); + + if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT) { + + submission_socket = mail_open_socket(SOCK_STREAM,"submission"); + + if(submission_socket == INVALID_SOCKET) { + lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE); + cleanup(1); + return; + } + + lprintf(LOG_DEBUG,"%04d SUBMISSION socket opened",submission_socket); + + /*****************************/ + /* Listen for incoming calls */ + /*****************************/ + memset(&server_addr, 0, sizeof(server_addr)); + + server_addr.sin_addr.s_addr = htonl(startup->interface_addr); + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(startup->submission_port); + + if(startup->submission_port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(FALSE); + } + result = retry_bind(submission_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) + ,startup->bind_retry_count,startup->bind_retry_delay,"SMTP Submission Agent",lprintf); + if(startup->submission_port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(TRUE); + } + if(result != 0) { + lprintf(LOG_CRIT,"%04d %s",submission_socket, BIND_FAILURE_HELP); + cleanup(1); + return; + } + + result = listen(submission_socket, 1); + + if(result != 0) { + lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on SUBMISSION socket" + ,submission_socket, result, ERROR_VALUE); + cleanup(1); + return; + } + + lprintf(LOG_INFO,"%04d SUBMISSION Server listening on port %u" + ,submission_socket, startup->submission_port); + } if(startup->options&MAIL_OPT_ALLOW_POP3) { @@ -4306,7 +5200,7 @@ void DLLCALL mail_server(void* arg) pop3_socket = mail_open_socket(SOCK_STREAM,"pop3"); if(pop3_socket == INVALID_SOCKET) { - lprintf(LOG_ERR,"!ERROR %d opening POP3 socket", ERROR_VALUE); + lprintf(LOG_CRIT,"!ERROR %d opening POP3 socket", ERROR_VALUE); cleanup(1); return; } @@ -4322,29 +5216,33 @@ void DLLCALL mail_server(void* arg) server_addr.sin_family = AF_INET; server_addr.sin_port = htons(startup->pop3_port); - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); + if(startup->pop3_port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(FALSE); + } result = retry_bind(pop3_socket,(struct sockaddr *)&server_addr,sizeof(server_addr) ,startup->bind_retry_count,startup->bind_retry_delay,"POP3 Server",lprintf); - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); + if(startup->pop3_port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(FALSE); + } if(result != 0) { - lprintf(LOG_ERR,"%04d %s",pop3_socket,BIND_FAILURE_HELP); + lprintf(LOG_CRIT,"%04d %s",pop3_socket,BIND_FAILURE_HELP); cleanup(1); return; } - lprintf(LOG_DEBUG,"%04d POP3 socket bound to port %u" - ,pop3_socket, startup->pop3_port); - - result = listen (pop3_socket, 1); + result = listen(pop3_socket, 1); if(result != 0) { - lprintf(LOG_ERR,"%04d !ERROR %d (%d) listening on POP3 socket" + lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on POP3 socket" ,pop3_socket, result, ERROR_VALUE); cleanup(1); return; } + + lprintf(LOG_INFO,"%04d POP3 Server listening on port %u" + ,pop3_socket, startup->pop3_port); } sem_init(&sendmail_wakeup_sem,0,0); @@ -4352,7 +5250,6 @@ void DLLCALL mail_server(void* arg) if(!(startup->options&MAIL_OPT_NO_SENDMAIL)) _beginthread(sendmail_thread, 0, NULL); - lprintf(LOG_NOTICE,"%04d Mail Server thread started",server_socket); status(STATUS_WFC); /* Setup recycle/shutdown semaphore file lists */ @@ -4360,8 +5257,8 @@ void DLLCALL mail_server(void* arg) recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","mail"); SAFEPRINTF(path,"%smailsrvr.rec",scfg.ctrl_dir); /* legacy */ semfile_list_add(&recycle_semfiles,path); + semfile_list_add(&recycle_semfiles,mailproc_ini); if(!initialized) { - initialized=time(NULL); semfile_list_check(&initialized,recycle_semfiles); semfile_list_check(&initialized,shutdown_semfiles); } @@ -4370,19 +5267,17 @@ void DLLCALL mail_server(void* arg) if(startup->started!=NULL) startup->started(startup->cbdata); + lprintf(LOG_INFO,"%04d Mail Server thread started",server_socket); + while(server_socket!=INVALID_SOCKET && !terminate_server) { - if(active_clients==0) { + if(active_clients.value==0) { if(!(startup->options&MAIL_OPT_NO_RECYCLE)) { if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) { lprintf(LOG_INFO,"%04d Recycle semaphore file (%s) detected" ,server_socket,p); break; } -#if 0 /* unused */ - if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0) - startup->recycle_now=TRUE; -#endif if(startup->recycle_now==TRUE) { lprintf(LOG_NOTICE,"%04d Recycle semaphore signaled", server_socket); startup->recycle_now=FALSE; @@ -4411,6 +5306,12 @@ void DLLCALL mail_server(void* arg) if(pop3_socket+1>high_socket_set) high_socket_set=pop3_socket+1; } + if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT + && submission_socket!=INVALID_SOCKET) { + FD_SET(submission_socket,&socket_set); + if(submission_socket+1>high_socket_set) + high_socket_set=submission_socket+1; + } tv.tv_sec=startup->sem_chk_freq; tv.tv_usec=0; @@ -4428,10 +5329,14 @@ void DLLCALL mail_server(void* arg) } if(server_socket!=INVALID_SOCKET && !terminate_server - && FD_ISSET(server_socket,&socket_set)) { + && (FD_ISSET(server_socket,&socket_set) + || (startup->options&MAIL_OPT_USE_SUBMISSION_PORT + && FD_ISSET(submission_socket,&socket_set)))) { client_addr_len = sizeof(client_addr); - client_socket = accept(server_socket, (struct sockaddr *)&client_addr + client_socket = accept( + FD_ISSET(server_socket,&socket_set) ? server_socket:submission_socket + ,(struct sockaddr *)&client_addr ,&client_addr_len); if(client_socket == INVALID_SOCKET) @@ -4444,7 +5349,8 @@ void DLLCALL mail_server(void* arg) } #endif lprintf(LOG_WARNING,"%04d SMTP !ERROR %d accepting connection" - ,server_socket, ERROR_VALUE); + ,FD_ISSET(server_socket,&socket_set) ? server_socket:submission_socket + ,ERROR_VALUE); #ifdef _WIN32 if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */ break; @@ -4453,16 +5359,17 @@ void DLLCALL mail_server(void* arg) } if(startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - sockets++; + stats.sockets++; if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) { mail_close_socket(client_socket); + stats.connections_ignored++; continue; } - if(active_clients>=startup->max_clients) { - lprintf(LOG_WARNING,"%04d SMTP !MAXIMUM CLIENTS (%u) reached, access denied" - ,client_socket, startup->max_clients); + if(active_clients.value>=startup->max_clients) { + lprintf(LOG_WARNING,"%04d SMTP !MAXIMUM CLIENTS (%u) reached, access denied (%u total)" + ,client_socket, startup->max_clients, ++stats.connections_refused); sockprintf(client_socket,"421 Maximum active clients reached, please try again later."); mswait(3000); mail_close_socket(client_socket); @@ -4472,7 +5379,7 @@ void DLLCALL mail_server(void* arg) l=1; if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) { - lprintf(LOG_ERR,"%04d SMTP !ERROR %d (%d) disabling blocking on socket" + lprintf(LOG_CRIT,"%04d SMTP !ERROR %d (%d) disabling blocking on socket" ,client_socket, i, ERROR_VALUE); mail_close_socket(client_socket); continue; @@ -4487,8 +5394,8 @@ void DLLCALL mail_server(void* arg) smtp->socket=client_socket; smtp->client_addr=client_addr; - _beginthread (smtp_thread, 0, smtp); - served++; + _beginthread(smtp_thread, 0, smtp); + stats.connections_served++; } if(pop3_socket!=INVALID_SOCKET @@ -4516,27 +5423,27 @@ void DLLCALL mail_server(void* arg) } if(startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - sockets++; + stats.sockets++; if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) { mail_close_socket(client_socket); + stats.connections_ignored++; continue; } - if(active_clients>=startup->max_clients) { - lprintf(LOG_WARNING,"%04d POP3 !MAXIMUM CLIENTS (%u) reached, access denied" - ,client_socket, startup->max_clients); + if(active_clients.value>=startup->max_clients) { + lprintf(LOG_WARNING,"%04d POP3 !MAXIMUM CLIENTS (%u) reached, access denied (%u total)" + ,client_socket, startup->max_clients, ++stats.connections_refused); sockprintf(client_socket,"-ERR Maximum active clients reached, please try again later."); mswait(3000); mail_close_socket(client_socket); continue; } - l=1; if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) { - lprintf(LOG_ERR,"%04d POP3 !ERROR %d (%d) disabling blocking on socket" + lprintf(LOG_CRIT,"%04d POP3 !ERROR %d (%d) disabling blocking on socket" ,client_socket, i, ERROR_VALUE); sockprintf(client_socket,"-ERR System error, please try again later."); mswait(3000); @@ -4556,19 +5463,19 @@ void DLLCALL mail_server(void* arg) pop3->socket=client_socket; pop3->client_addr=client_addr; - _beginthread (pop3_thread, 0, pop3); - served++; + _beginthread(pop3_thread, 0, pop3); + stats.connections_served++; } } - if(active_clients) { + if(active_clients.value) { lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..." - ,server_socket, active_clients); + ,server_socket, active_clients.value); start=time(NULL); - while(active_clients) { + while(active_clients.value) { if(time(NULL)-start>startup->max_inactivity) { lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d active clients" - ,server_socket, active_clients); + ,server_socket, active_clients.value); break; } mswait(100);