Newer
Older
/* Synchronet Mail (SMTP/POP3) server and sendmail threads */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include <stdlib.h> /* ltoa in GNU C lib */
#include <stdarg.h> /* va_list */
#include <string.h> /* strrchr */
#include <fcntl.h> /* Open flags */
#include <errno.h> /* errno */
#undef SBBS /* this shouldn't be defined unless building sbbs.dll/libsbbs.so */
#include "sbbs.h"
#include "utf8.h"
#include "mime.h"
#include "md5.h"
#include "base64.h"
#include "ini_file.h"
#include "netwrap.h" /* getNameServerList() */
#include "xpendian.h"
#include "js_rtpool.h"
#include "js_request.h"
#include "ssl.h"
#include "cryptlib.h"
#include "git_branch.h"
#include "git_hash.h"
static const char* server_name="Synchronet Mail Server";
static const char* server_abbrev = "mail";
#define FORWARD "forward:"
#define NO_FORWARD "local:"
#define NO_SPAM "#nospam"

rswindell
committed
int dns_getmx(char* name, char* mx, char* mx2
,DWORD intf, DWORD ip_addr, BOOL use_tcp, int timeout);
#define pop_error "-ERR System Error: %s, try again later"
#define pop_auth_error "-ERR Authentication failure"
#define ok_rsp "250 OK"
#define auth_ok "235 User Authenticated"
#define smtp_error "421 System Error: %s, try again later"
#define insuf_stor "452 Insufficient system storage"
#define badarg_rsp "501 Bad argument"
#define badseq_rsp "503 Bad sequence of commands"
#define badauth_rsp "535 Authentication failure"
#define badrsp_err "%s replied with:\r\n\"%s\"\r\ninstead of the expected reply:\r\n\"%s ...\""
#define TIMEOUT_THREAD_WAIT 60 /* Seconds */
#define DNSBL_THROTTLE_VALUE 1000 /* Milliseconds */
#define SMTP_MAX_BAD_CMDS 9
static mail_startup_t* startup=NULL;
static scfg_t scfg;
static char* text[TOTAL_TEXT];
static struct xpms_set *mail_set=NULL;
static BOOL terminated=FALSE;
static protected_uint32_t active_clients;
static protected_uint32_t thread_count;
static volatile int active_sendmail=0;
static volatile BOOL sendmail_running=FALSE;
static volatile BOOL terminate_server=FALSE;
static volatile BOOL terminate_sendmail=FALSE;
static sem_t sendmail_wakeup_sem;
static 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;
static link_list_t current_logins;
static link_list_t current_connections;
static bool savemsg_mutex_created = false;
static pthread_mutex_t savemsg_mutex;
static const char* servprot_smtp = "SMTP";
static const char* servprot_submission = "SMTP";
static const char* servprot_submissions = "SMTPS";
static const char* servprot_pop3 = "POP3";
static const char* servprot_pop3s = "POP3S";
struct {
volatile ulong sockets;
volatile ulong errors;
volatile ulong crit_errors;
volatile ulong connections_exceeded;
volatile ulong connections_ignored;
volatile ulong connections_refused;
volatile ulong connections_served;
volatile ulong pop3_served;
volatile ulong smtp_served;
/* SMTP: */
volatile ulong sessions_refused;
volatile ulong msgs_ignored;
volatile ulong msgs_refused;
volatile ulong msgs_received;
} stats;
struct mailproc {
char cmdline[INI_MAX_VALUE_LEN];
str_list_t to;
str_list_t from;
BOOL passthru;
BOOL native;
BOOL ignore_on_error; /* Ignore mail message if cmdline fails */
BOOL disabled;
BOOL process_spam;
BOOL process_dnsbl;
uint8_t* ar;
ulong handled; /* counter (for stats display) */
} *mailproc_list;
typedef struct {
SOCKET socket;
BOOL tls_port;
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
if(GCES_level < startup->tls_error_level) \
GCES_level = startup->tls_error_level; \

Rob Swindell
committed
lprintf(GCES_level, "%04d %s %s", sock, server, GCES_estr); \
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
if(GCES_level < startup->tls_error_level) \
GCES_level = startup->tls_error_level; \

Rob Swindell
committed
lprintf(GCES_level, "%04d %s [%s] %s", sock, server, host, GCES_estr); \
free_crypt_attrstr(GCES_estr); \
} \
} while(0)
#define GCESHL(status, server, sock, host, log_level, sess, action) do { \
char *GCES_estr; \
int GCES_level; \
get_crypt_error_string(status, sess, &GCES_estr, action, &GCES_level); \
if (GCES_estr) { \
lprintf((log_level < startup->tls_error_level) ? startup->tls_error_level : log_level \
, "%04d %s [%s] %s", sock, server, host, GCES_estr); \
free_crypt_attrstr(GCES_estr); \
} \
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
static int lputs(int level, const char* str)
{
mqtt_lputs(&mqtt, TOPIC_SERVER, level, str);
if(level <= LOG_ERR) {
char errmsg[1024];
errorlog(&scfg, &mqtt, level, startup == NULL ? NULL : startup->host_name, str);
stats.errors++;
SAFEPRINTF2(errmsg, "%s %s", server_abbrev, str);
if(startup != NULL && startup->errormsg != NULL)
startup->errormsg(startup->cbdata, level, errmsg);
}
if(level <= LOG_CRIT)
stats.crit_errors++;
if(startup == NULL || startup->lputs == NULL || str == NULL || level > startup->log_level)
return 0;
#if defined(_WIN32)
if(IsBadCodePtr((FARPROC)startup->lputs))
return 0;
#endif
return startup->lputs(startup->cbdata,level,str);
}
#if defined(__GNUC__) // Catch printf-format errors with lprintf
static int lprintf(int level, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#endif
static int lprintf(int level, const char *fmt, ...)
{
va_list argptr;
char sbuf[1024];
va_start(argptr,fmt);
vsnprintf(sbuf,sizeof(sbuf),fmt,argptr);
sbuf[sizeof(sbuf)-1]=0;
return lputs(level, condense_whitespace(sbuf));
}
#ifdef _WINSOCKAPI_
static WSADATA WSAData;
#define SOCKLIB_DESC WSAData.szDescription
static BOOL WSAInitialized=FALSE;
static BOOL winsock_startup(void)
{
int status; /* Status Code */
if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) {
lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus);
WSAInitialized=TRUE;
lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status);
return (FALSE);
}
#else /* No WINSOCK */
#define SOCKLIB_DESC NULL
static char* server_host_name(void)
{
return startup->host_name[0] ? startup->host_name : scfg.sys_inetaddr;
}
static void set_state(enum server_state state)
{
if(startup != NULL) {
if(startup->set_state != NULL)
startup->set_state(startup->cbdata, state);
static void update_clients(void)
{
if(startup != NULL) {
if(startup->clients != NULL)
startup->clients(startup->cbdata,protected_uint32_value(active_clients)+active_sendmail);
}
static void client_on(SOCKET sock, client_t* client, BOOL update)
if(!update)
listAddNodeData(¤t_connections, client->addr, strlen(client->addr)+1, sock, LAST_NODE);
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,TRUE,sock,client,update);
mqtt_client_on(&mqtt, TRUE, sock, client, update);
}
static void client_off(SOCKET sock)
{
listRemoveTaggedNode(¤t_connections, sock, /* free_data */TRUE);
if(startup!=NULL && startup->client_on!=NULL)
startup->client_on(startup->cbdata,FALSE,sock,NULL,FALSE);
mqtt_client_on(&mqtt, FALSE, sock, NULL, FALSE);
static void thread_up(BOOL setuid)
if(startup != NULL) {
if(startup->thread_up != NULL)
startup->thread_up(startup->cbdata,TRUE,setuid);
}
static int32_t thread_down(void)
int32_t count = protected_uint32_adjust_fetch(&thread_count,-1);
if(startup != NULL) {
if(startup->thread_up != NULL)
startup->thread_up(startup->cbdata,FALSE,FALSE);
}
return count;
char error[256];
char section[128];
startup->socket_open(startup->cbdata,TRUE);
SAFEPRINTF(section,"mail|%s",protocol);
if(set_socket_options(&scfg, sock, section, error, sizeof(error)))
lprintf(LOG_ERR,"%04d %s !ERROR %s", sock, protocol, error);
stats.sockets++;
}
void mail_close_socket_cb(SOCKET sock, void* cb_protocol)
{
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);
stats.sockets--;
int mail_close_socket(SOCKET *sock, int *sess)
if (*sess != -1) {
cryptDestroySession(*sess);
*sess = -1;
}
if(*sock==INVALID_SOCKET)
return(-1);
shutdown(*sock,SHUT_RDWR); /* required on Unix */
result=closesocket(*sock);
if(startup!=NULL && startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,FALSE);
stats.sockets--;
if(result!=0) {
if(ERROR_VALUE!=ENOTSOCK)
lprintf(LOG_WARNING,"%04d !ERROR %d closing socket",*sock, ERROR_VALUE);
lprintf(LOG_DEBUG,"%04d Socket closed (%d sockets in use)",*sock,stats.sockets);
*sock = -1;
int sockprintf(SOCKET sock, const char* prot, CRYPT_SESSION sess, char *fmt, ...)
int maxlen;
int result;
va_list argptr;
char sbuf[1024];
va_start(argptr,fmt);
len=vsnprintf(sbuf,maxlen=sizeof(sbuf)-2,fmt,argptr);
va_end(argptr);
if(len<0 || len > maxlen) /* format error or output truncated */
len=maxlen;
if(startup->options&MAIL_OPT_DEBUG_TX)
lprintf(LOG_DEBUG,"%04d %s TX: %.*s", sock, prot, len, sbuf);
memcpy(sbuf+len,"\r\n",2);
if(sock==INVALID_SOCKET) {
lprintf(LOG_WARNING,"%s !INVALID SOCKET in call to sockprintf", prot);
return(0);
}
/* Check socket for writability */
if (!socket_writable(sock, 300000)) {
lprintf(LOG_NOTICE,"%04d %s !NOTICE socket did not become writable"
,sock, prot);
return(0);
}
if (sess != -1) {
int tls_sent;
int sent = 0;
while (sent < len) {
result = cryptPushData(sess, sbuf+sent, len-sent, &tls_sent);
if (result == CRYPT_OK)
sent += tls_sent;
GCES(result, prot, sock, sess, "pushing data");
if ((result = cryptFlushData(sess)) != CRYPT_OK) {
GCES(result, prot, sock, sess, "flushing data");
}
else {
// It looks like this could stutter on partial sends -- Deuce
while((result=sendsocket(sock,sbuf,len))!=len) {
if(result==SOCKET_ERROR) {
if(ERROR_VALUE==EWOULDBLOCK) {
YIELD();
continue;
}
if(ERROR_VALUE==ECONNRESET)
lprintf(LOG_NOTICE,"%04d %s Connection reset by peer on send",sock,prot);
lprintf(LOG_NOTICE,"%04d %s Connection aborted by peer on send",sock, prot);
lprintf(LOG_NOTICE,"%04d %s !ERROR %d sending on socket",sock,prot,ERROR_VALUE);
lprintf(LOG_WARNING,"%04d %s !ERROR: short send on socket: %d instead of %d",sock,prot,result,len);
}
}
return(len);
}
static void sockerror(SOCKET socket, const char* prot, int rd, const char* action)
lprintf(LOG_NOTICE,"%04d %s Socket closed by peer on %s"
,socket, prot, action);
lprintf(LOG_NOTICE,"%04d %s Connection reset by peer on %s"
,socket, prot, action);
lprintf(LOG_NOTICE,"%04d %s Connection aborted by peer on %s"
,socket, prot, action);
lprintf(LOG_NOTICE,"%04d %s !SOCKET ERROR %d on %s"
,socket, prot, ERROR_VALUE, action);
lprintf(LOG_WARNING,"%04d %s !SOCKET ERROR: unexpected return value %d from %s"
,socket, prot, rd, action);
static int sock_recvbyte(SOCKET sock, const char* prot, CRYPT_SESSION sess, char *buf, time_t start)
{
int len=0;
int ret;
int i;
if (sess > -1) {
while (1) {
ret = cryptPopData(sess, buf, 1, &len);
GCES(ret, prot, sock, sess, "popping data");
switch(ret) {
case CRYPT_OK:
break;
case CRYPT_ERROR_TIMEOUT:
return -1;
case CRYPT_ERROR_COMPLETE:
return 0;
default:
if (ret < -1)
return ret;
return -2;
}
if (len)
return len;
if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d %s !TIMEOUT in sock_recvbyte (%u seconds): INACTIVE SOCKET"
,sock, prot, startup->max_inactivity);
return(-1);
}
}
}
else {
if (!socket_readable(sock, startup->max_inactivity * 1000)) {
if(startup->max_inactivity && (time(NULL)-start)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d %s !TIMEOUT in sock_recvbyte (%u seconds): INACTIVE SOCKET"
,sock, prot, startup->max_inactivity);
return(-1);
}
return 0;
}
i=recv(sock, buf, 1, 0);
if(i<1)
sockerror(sock,prot,i,"receive");
static int sockreadline(SOCKET socket, const char* prot, CRYPT_SESSION sess, char* buf, int len)
{
char ch;
int i,rd=0;
time_t start;
if(socket==INVALID_SOCKET) {
lprintf(LOG_WARNING,"%s !INVALID SOCKET in call to sockreadline", prot);
return(-1);

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

rswindell
committed
return(-1);
if(ch=='\n' /* && rd>=1 */ ) { /* Mar-9-2003: terminate on sole LF */
if(rd>0 && buf[rd-1]=='\r')
rd--;
buf[rd]=0;
static BOOL sockgetrsp(SOCKET socket, const char* prot, CRYPT_SESSION sess, char* rsp, char *buf, int len)
{
int rd;
while(1) {
rd = sockreadline(socket, prot, sess, buf, len);
if(rd<1) {
if(rd==0 && rsp != NULL)
lprintf(LOG_WARNING,"%04d %s !RECEIVED BLANK RESPONSE, Expected '%s'", socket, prot, rsp);
if(buf[3]=='-') { /* Multi-line response */
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
if(rsp!=NULL && strnicmp(buf,rsp,strlen(rsp))) {
lprintf(LOG_WARNING,"%04d %s !INVALID RESPONSE: '%s' Expected: '%s'", socket, prot, buf, rsp);
return(FALSE);
}
break;
}
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
static int sockgetrsp_opt(SOCKET socket, const char* prot, CRYPT_SESSION sess, char* rsp, char *opt, char *buf, int len)
{
int rd;
int ret = 0;
size_t moptlen;
char *mopt;
moptlen = strlen(rsp)+strlen(opt) + 1;
mopt = malloc(moptlen+1);
if (mopt == NULL)
return -1;
sprintf(mopt, "%s-%s", rsp, opt);
while(1) {
rd = sockreadline(socket, prot, sess, buf, len);
lprintf(LOG_WARNING,"%04d %s !RECEIVED BLANK RESPONSE, Expected '%s'", socket, prot, rsp);
free(mopt);
return(-1);
}
if(buf[3]=='-') { /* Multi-line response */
if (strncmp(buf, mopt, moptlen) == 0)
ret = 1;
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
if(strnicmp(buf,rsp,strlen(rsp))) {
lprintf(LOG_WARNING,"%04d %s !INVALID RESPONSE: '%s' Expected: '%s'", socket, prot, buf, rsp);
free(mopt);
return(-1);
}
break;
}
mopt[strlen(rsp)] = ' ';
if (strncmp(buf, mopt, moptlen) == 0)
free(mopt);
if(startup->options&MAIL_OPT_DEBUG_RX_RSP)
lprintf(LOG_DEBUG,"%04d %s RX: %s",socket, prot, buf);
/* non-standard, but documented (mostly) in draft-newman-msgheader-originfo-05 */
void originator_info(SOCKET socket, const char* protocol, CRYPT_SESSION sess, smbmsg_t* msg)
{
char* user = msg->from_ext;
char* login = smb_get_hfield(msg,SENDERUSERID,NULL);
char* server = smb_get_hfield(msg,SENDERSERVER,NULL);
char* client = smb_get_hfield(msg,SENDERHOSTNAME,NULL);
char* addr = smb_get_hfield(msg,SENDERIPADDR,NULL);
char* prot = smb_get_hfield(msg,SENDERPROTOCOL,NULL);
char* port = smb_get_hfield(msg,SENDERPORT,NULL);
char* time = smb_get_hfield(msg,SENDERTIME,NULL);
if(user || login || server || client || addr || prot || port || time)
sockprintf(socket, protocol, sess
,"X-Originator-Info: account=%s; login-id=%s; server=%s; client=%s; addr=%s; prot=%s; port=%s; time=%s"
,user
,login
,server
,client
,addr
,prot
,port
,time
);
}
char* angle_bracket(char* buf, size_t max, const char* addr)
{
if(*addr == '<')
safe_snprintf(buf, max, "%s", addr);
else
safe_snprintf(buf, max, "<%s>", addr);
return buf;
}
int compare_addrs(const char* addr1, const char* addr2)
{
char tmp1[256];
char tmp2[256];
return strcmp(angle_bracket(tmp1, sizeof(tmp1), addr1), angle_bracket(tmp2, sizeof(tmp2), addr2));
}
/* RFC822: The maximum total length of a text line including the
<CRLF> is 1000 characters (but not counting the leading
dot duplicated for transparency).
POP3 (RFC1939) actually calls for a 512 byte line length limit!
*/
#define MAX_LINE_LEN 998
static ulong sockmimetext(SOCKET socket, const char* prot, CRYPT_SESSION sess, smbmsg_t* msg, char* msgtxt, ulong maxlines
,str_list_t file_list, char* mime_boundary)
char toaddr[256]="";
char fromaddr[256]="";
char fromhost[256];
char tmp[256];
int len,tlen;
/* HEADERS (in recommended order per RFC822 4.1) */
if(msg->reverse_path!=NULL)
if(!sockprintf(socket,prot,sess,"Return-Path: %s", msg->reverse_path))
return(0);
for(i=0;i<msg->total_hfields;i++)
if(msg->hfield[i].type == SMTPRECEIVED && msg->hfield_dat[i]!=NULL)
if(!sockprintf(socket,prot,sess,"Received: %s", (char*)msg->hfield_dat[i]))
return(0);
if(!sockprintf(socket,prot,sess,"Date: %s",msgdate(msg->hdr.when_written,date)))
if((p=smb_get_hfield(msg,RFC822FROM,NULL))!=NULL)
s=sockprintf(socket,prot,sess,"From: %s",p); /* use original RFC822 header field */
else {
if(msg->from_net.type==NET_QWK && msg->from_net.addr!=NULL)
SAFEPRINTF2(fromaddr,"%s!%s"
,(char*)msg->from_net.addr
,usermailaddr(&scfg,fromhost,msg->from));
else if(msg->from_net.type==NET_FIDO && msg->from_net.addr!=NULL) {
faddr_t* faddr = (faddr_t *)msg->from_net.addr;
char faddrstr[128];
SAFEPRINTF2(fromname,"\"%s\" (%s)", msg->from, smb_faddrtoa(faddr, NULL));
if(faddr->point)
SAFEPRINTF4(faddrstr,"p%hu.f%hu.n%hu.z%hu"FIDO_TLD
,faddr->point, faddr->node, faddr->net, faddr->zone);
else
SAFEPRINTF3(faddrstr,"f%hu.n%hu.z%hu"FIDO_TLD
,faddr->node, faddr->net, faddr->zone);
SAFEPRINTF2(fromaddr,"%s@%s", usermailaddr(NULL,fromhost,msg->from), faddrstr);
} else if(msg->from_net.type!=NET_NONE && msg->from_net.addr!=NULL)
SAFECOPY(fromaddr,(char*)msg->from_net.addr);
else
usermailaddr(&scfg,fromaddr,msg->from);
s = sockprintf(socket,prot,sess,"From: %s %s", fromname, angle_bracket(tmp, sizeof(tmp), fromaddr));
}
if(!s)
return(0);
if((p = smb_get_hfield(msg, RFC822ORG, NULL)) != NULL) {
if(!sockprintf(socket,prot,sess,"Organization: %s", p))
return(0);
} else {
if(msg->from_org!=NULL || msg->from_net.type==NET_NONE)
if(!sockprintf(socket,prot,sess,"Organization: %s"
,msg->from_org==NULL ? scfg.sys_name : msg->from_org))
return(0);
}
p = smb_get_hfield(msg, RFC822SUBJECT, NULL);
if(!sockprintf(socket,prot,sess,"Subject: %s", p == NULL ? msg->subj : p))
if((p = smb_get_hfield(msg, RFC822TO, NULL)) != NULL)
s=sockprintf(socket,prot,sess,"To: %s", p); /* use original RFC822 header field (MIME-Encoded) */
else if((p = msg->to_list) != NULL)
s=sockprintf(socket,prot,sess,"To: %s", p); /* use original RFC822 header field */
else {
if(strchr(msg->to,'@')!=NULL || msg->to_net.addr==NULL)
s=sockprintf(socket,prot,sess,"To: %s",msg->to); /* Avoid double-@ */
else if(msg->to_net.type==NET_INTERNET || msg->to_net.type==NET_QWK) {
if(strchr((char*)msg->to_net.addr,'<')!=NULL)
s=sockprintf(socket,prot,sess,"To: %s",(char*)msg->to_net.addr);
else
s=sockprintf(socket,prot,sess,"To: \"%s\" <%s>",msg->to,(char*)msg->to_net.addr);
s=sockprintf(socket,prot,sess,"To: \"%s\" (%s)",msg->to, smb_faddrtoa((fidoaddr_t*)msg->to_net.addr, NULL));
} else {
usermailaddr(&scfg,toaddr,msg->to);
s=sockprintf(socket,prot,sess,"To: \"%s\" <%s>",msg->to,toaddr);
}
if(!s)
return(0);
if((p = smb_get_hfield(msg, RFC822CC, NULL)) != NULL || msg->cc_list != NULL)
if(!sockprintf(socket,prot,sess,"Cc: %s", p == NULL ? msg->cc_list : p))
return(0);
p = smb_get_hfield(msg, RFC822REPLYTO, NULL);
if(p == NULL && (p = msg->replyto_list) == NULL) {
if(msg->replyto_net.type==NET_INTERNET)
if(p!=NULL && strchr((char*)p, '@') != NULL) {
s=sockprintf(socket,prot,sess,"Reply-To: \"%s\" <%s>",np,p);
s=sockprintf(socket,prot,sess,"Reply-To: %s",p);
if(!s)
return(0);
if(!sockprintf(socket,prot,sess,"Message-ID: %s",get_msgid(&scfg,INVALID_SUB,msg,msgid,sizeof(msgid))))
if(msg->reply_id!=NULL)
if(!sockprintf(socket,prot,sess,"In-Reply-To: %s",msg->reply_id))
if(msg->hdr.priority != SMB_PRIORITY_UNSPECIFIED)
if(!sockprintf(socket,prot,sess,"X-Priority: %u", (uint)msg->hdr.priority))
return(0);
originator_info(socket, prot, sess, msg);
/* Include all possible FidoNet header fields here */
for(i=0;i<msg->total_hfields;i++) {
switch(msg->hfield[i].type) {
case FIDOCTRL:
case FIDOAREA:
case FIDOSEENBY:
case FIDOPATH:
case FIDOMSGID:
case FIDOREPLYID:
case FIDOPID:
case FIDOFLAGS:
case FIDOTID:
if(!sockprintf(socket, prot, sess, "%s: %s", smb_hfieldtype(msg->hfield[i].type), (char*)msg->hfield_dat[i]))
return(0);
break;
}
}
for(i=0;i<msg->total_hfields;i++) {
if(msg->hfield[i].type==RFC822HEADER) {
if(!sockprintf(socket,prot, sess,"%s",(char*)msg->hfield_dat[i]))
const char* charset = msg->text_charset;
if(charset == NULL) {
if(smb_msg_is_utf8(msg) || (msg->hdr.auxattr & MSG_HFIELDS_UTF8))
charset = "UTF-8";
else if(str_is_ascii(msgtxt))
charset = "US-ASCII";
else
charset = "IBM437";
}
if(strListCount(file_list)) { /* File attachments */
mimeheaders(socket,prot,sess,mime_boundary);
sockprintf(socket,prot,sess,"");
mimeblurb(socket,prot,sess,mime_boundary);
sockprintf(socket,prot,sess,"");
mimetextpartheader(socket,prot,sess,mime_boundary, msg->text_subtype, charset);
} else {
/* Default MIME Content-Type for non-Internet messages */
if(msg->from_net.type!=NET_INTERNET && msg->content_type==NULL) {
sockprintf(socket,prot,sess, "Content-Type: text/plain; charset=%s", charset);
sockprintf(socket,prot,sess, "Content-Transfer-Encoding: 8bit");
}
if(!sockprintf(socket,prot,sess,"")) /* Header Terminator */
np=msgtxt;
while(*np && lines<maxlines) {
len=0;
while(len<MAX_LINE_LEN && *(np+len)!=0 && *(np+len)!='\n')
len++;
tlen=len;
while(tlen && *(np+(tlen-1))<=' ') /* Takes care of '\r' or spaces */
tlen--;
if(!sockprintf(socket,prot,sess, "%s%.*s", *np=='.' ? ".":"", tlen, np))
if(*(np+len)=='\r')
len++;
if(*(np+len)=='\n')
len++;
/* release time-slices every x lines */
if(startup->lines_per_yield
&& !(lines%startup->lines_per_yield))
YIELD();
if((lines%100) == 0)
lprintf(LOG_DEBUG,"%04d %s sent %lu lines (%ld bytes) of body text"
,socket, prot, lines, (long)(np-msgtxt));
lprintf(LOG_DEBUG,"%04d %s sent %lu lines (%ld bytes) of body text"
,socket, prot, lines, (long)(np-msgtxt));
if(file_list!=NULL) {
for(i=0;file_list[i];i++) {
sockprintf(socket,prot,sess,"");
lprintf(LOG_INFO,"%04u %s MIME Encoding and sending %s", socket, prot, file_list[i]);
if(!mimeattach(socket,prot,sess,mime_boundary,file_list[i]))
lprintf(LOG_ERR,"%04u %s !ERROR opening/encoding/sending %s", socket, prot, file_list[i]);
else {
if(msg->hdr.auxattr&MSG_KILLFILE)
if(remove(file_list[i])!=0)
lprintf(LOG_WARNING,"%04u %s !ERROR %d (%s) removing %s", socket, prot, errno, strerror(errno), file_list[i]);
endmime(socket,prot,sess,mime_boundary);
sockprintf(socket,prot,sess,"."); /* End of text */
static ulong sockmsgtxt(SOCKET socket, const char* prot, CRYPT_SESSION sess, smbmsg_t* msg, char* msgtxt, ulong maxlines)
char dirname[MAX_PATH + 1];
char filepath[MAX_PATH + 1];
ulong retval;
char* boundary=NULL;
str_list_t file_list=NULL;
if(msg->hdr.auxattr&MSG_FILEATTACH) {
if(msg->idx.to != 0)
SAFEPRINTF2(dirname, "%sfile/%04u.in", scfg.data_dir, msg->idx.to);
else
SAFEPRINTF2(dirname, "%sfile/%04u.out", scfg.data_dir, msg->idx.from);
boundary = mimegetboundary();
file_list = strListInit();
/* filename(s) in subject */
char* p = msg->subj;
SKIP_WHITESPACE(p);
while(*p != '\0') {
char delim = ' ';
if(*p == '"') {
delim = '"';
p++;
char* tp = strchr(p, delim);
if(tp == NULL) {
if(delim != ' ')
break;
} else
*tp = '\0';
char* fname = getfname(truncsp(p));
if(strcspn(fname, ILLEGAL_FILENAME_CHARS) == strlen(fname)) {
SAFEPRINTF2(filepath, "%s/%s", dirname, fname);
strListPush(&file_list, filepath);
}
if(tp == NULL)
break;
p = tp + 1;
SKIP_WHITESPACE(p);
}
}
retval = sockmimetext(socket,prot,sess,msg,msgtxt,maxlines,file_list,boundary);
strListFree(&file_list);
if(boundary!=NULL)
free(boundary);
return(retval);
}
static u_long resolve_ip(const char *inaddr)
char* p;
char* addr;
char buf[128];
HOSTENT* host;
SAFECOPY(buf,inaddr);
addr=buf;
if(*addr=='[' && *(p=lastchar(addr))==']') { /* Support [ip_address] notation */
addr++;
*p=0;
}
if(*addr==0)
return((u_long)INADDR_NONE);
for(p=addr;*p;p++)
if(*p!='.' && !IS_DIGIT(*p))
break;
if(!(*p))
if((host=gethostbyname(inaddr))==NULL)
return((u_long)INADDR_NONE);
return(*((ulong*)host->h_addr_list[0]));
}
/****************************************************************************/
/* Consecutive failed login (possible password hack) attempt tracking */
/****************************************************************************/
/* Counter is global so it is tracked between multiple connections. */
/* Failed consecutive login attempts > 10 will generate a hacklog entry and */
/* immediately disconnect (after the usual failed-login delay). */
/* A failed login from a different host resets the counter. */
/* A successful login from the same host resets the counter. */
/****************************************************************************/
static void badlogin(SOCKET sock, CRYPT_SESSION sess, const char* prot, const char* resp, char* user, char* passwd, char* host, union xp_sockaddr* addr)
{
char reason[128];
ulong count;
if(user == NULL)
user = "<unspecified>";
if(addr!=NULL) {
SAFEPRINTF(reason,"%s LOGIN", prot);
count=loginFailure(startup->login_attempt_list, addr, prot, user, passwd);
if(startup->login_attempt.hack_threshold && count>=startup->login_attempt.hack_threshold) {
hacklog(&scfg, &mqtt, reason, user, passwd, host, addr);
#ifdef _WIN32
if(startup->sound.hack[0] && !sound_muted(&scfg))
PlaySound(startup->sound.hack, NULL, SND_ASYNC|SND_FILENAME);
#endif
}
if(startup->login_attempt.filter_threshold && count>=startup->login_attempt.filter_threshold) {
SAFEPRINTF(reason, "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS (%lu)", count);
filter_ip(&scfg, (char*)prot, reason ,host, ip, user, /* fname: */NULL);
}
}
sockprintf(sock,prot,sess, "%s", resp);
}
static void pop3_thread(void* arg)