Newer
Older
for(u=0; u<msgs; u++) {
if(active_sendmail!=0)
active_sendmail=0, update_clients();
if (session != -1) {
cryptDestroySession(session);
session = -1;
}

rswindell
committed
mail_close_socket(sock);
sock=INVALID_SOCKET;
}
if(msgtxt!=NULL) {
smb_freemsgtxt(msgtxt);
msgtxt=NULL;
}
smb_freemsgmem(&msg);
msg.hdr.number=mail[u].number;
if((i=smb_getmsgidx(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"0000 !SEND ERROR %d (%s) getting message index #%lu"
,i, smb.last_error, mail[u].number);
if((i=smb_lockmsghdr(&smb,&msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"0000 !SEND ERROR %d (%s) locking message header #%lu"
,i, smb.last_error, msg.idx.number);
if((i=smb_getmsghdr(&smb,&msg))!=SMB_SUCCESS) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_ERR,"0000 !SEND ERROR %d (%s) line %u, msg #%lu"
,i, smb.last_error, __LINE__, msg.idx.number);
if(msg.hdr.attr&MSG_DELETE || msg.to_net.type!=NET_INTERNET || msg.to_net.addr==NULL) {
smb_unlockmsghdr(&smb,&msg);
if(!(startup->options&MAIL_OPT_SEND_INTRANSIT) && msg.hdr.netattr&MSG_INTRANSIT) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_NOTICE,"0000 SEND Message #%lu from %s to %s - in transit"
,msg.hdr.number, msg.from, msg.to_net.addr);
continue;
}
msg.hdr.netattr|=MSG_INTRANSIT; /* Prevent another sendmail thread from sending this msg */
smb_putmsghdr(&smb,&msg);
smb_unlockmsghdr(&smb,&msg);
active_sendmail=1, update_clients();
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, u+1, msgs, msg.from, fromext, fromaddr
,msg.to, msg.to_net.addr);
SAFEPRINTF2(str,"Sending (%u of %u)", u+1, msgs);
if(startup->outbound_sound[0] && !(startup->options&MAIL_OPT_MUTE))
PlaySound(startup->outbound_sound, NULL, SND_ASYNC|SND_FILENAME);
lprintf(LOG_DEBUG,"0000 SEND getting message text");
if((msgtxt=smb_getmsgtxt(&smb,&msg,GETMSGTXT_ALL))==NULL) {
remove_msg_intransit(&smb,&msg);
lprintf(LOG_ERR,"0000 !SEND ERROR (%s) retrieving message text",smb.last_error);
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,"> ");
p=strrchr(to,'@');
if(p==NULL) {
remove_msg_intransit(&smb,&msg);
lprintf(LOG_WARNING,"0000 !SEND INVALID destination address: %s", to);
SAFEPRINTF(err,"Invalid destination address: %s", to);
bounce(0, &smb,&msg,err, /* immediate: */TRUE);
continue;
}
p++;
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)) {
/* This is a local message... no need to send to remote */
port = startup->smtp_port;
server="127.0.0.1";
else {
SAFEPRINTF4(numeric_ip, "%u.%u.%u.%u"
, startup->outgoing4.s_addr >> 24
, (startup->outgoing4.s_addr >> 16) & 0xff
, (startup->outgoing4.s_addr >> 8) & 0xff
, startup->outgoing4.s_addr & 0xff);
server = numeric_ip;
sending_locally=TRUE;
}
else {
if(startup->options&MAIL_OPT_RELAY_TX) {
server=startup->relay_server;
port=startup->relay_port;
} else {
server=p;
tp=strrchr(p,':'); /* non-standard SMTP port */
if(tp!=NULL) {
*tp=0;
port=atoi(tp+1);
if(port==0) { /* No port specified, use MX look-up */
get_dns_server(dns_server,sizeof(dns_server));
if((dns=resolve_ip(dns_server))==INADDR_NONE) {
remove_msg_intransit(&smb,&msg);
lprintf(LOG_WARNING,"0000 !SEND INVALID DNS server address: %s"
,dns_server);
continue;
}
lprintf(LOG_DEBUG,"0000 SEND getting MX records for %s from %s",p,dns_server);
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,dns_server);
SAFEPRINTF2(err,"Error %d obtaining MX record for %s",i,p);
bounce(0, &smb,&msg,err, /* immediate: */FALSE);
continue;
}
server=mx;
if(!port)
port=IPPORT_SMTP;
if((sock=socket(AF_INET, SOCK_STREAM, IPPROTO_IP))==INVALID_SOCKET) {
remove_msg_intransit(&smb,&msg);
lprintf(LOG_ERR,"0000 !SEND ERROR %d opening socket", ERROR_VALUE);
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_family = AF_INET;
i=bind(sock,(struct sockaddr *)&addr, sizeof(addr));
if(i!=0) {
remove_msg_intransit(&smb,&msg);
lprintf(LOG_ERR,"%04d !SEND ERROR %d (%d) binding socket", sock, i, ERROR_VALUE);
continue;
}
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 */
}
lprintf(LOG_DEBUG,"%04d SEND resolving SMTP hostname: %s", sock, server);
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;
}
memset(&server_addr,0,sizeof(server_addr));
server_addr.in.sin_addr.s_addr = ip_addr;
server_addr.in.sin_family = AF_INET;
server_addr.in.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
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"
continue;
}
lprintf(LOG_INFO,"%04d SEND connecting to port %u on %s [%s]"
if((i=nonblocking_connect(sock, (struct sockaddr *)&server_addr, xp_sockaddr_len(&server_addr), startup->connect_timeout))!=0) {
lprintf(LOG_WARNING,"%04d !SEND ERROR %d connecting to SMTP server: %s"
SAFEPRINTF2(err,"Error %d connecting to SMTP server: %s"
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(sock, &smb,&msg,err,/* immediate: */FALSE);
lprintf(LOG_DEBUG,"%04d SEND connected to %s",sock,server);
if(!sockgetrsp(sock,session,"220",buf,sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"220");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
5262
5263
5264
5265
5266
5267
5268
5269
5270
5271
5272
5273
5274
5275
5276
5277
5278
5279
5280
5281
5282
5283
5284
5285
5286
5287
5288
5289
5290
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
sockprintf(sock,session,"EHLO %s",startup->host_name);
switch (sockgetrsp_opt(sock,session,"250", "STARTTLS", buf, sizeof(buf))) {
case -1:
if(startup->options&MAIL_OPT_RELAY_TX
&& (startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=0) { /* Requires ESMTP */
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
sockprintf(sock,session,"HELO %s",startup->host_name);
if(!sockgetrsp(sock,session,"250",buf,sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
break;
case 1:
if ((!tls_failed) && get_ssl_cert(&scfg, NULL) != -1) {
sockprintf(sock, session, "STARTTLS");
if (sockgetrsp(sock, session, "220", buf, sizeof(buf))) {
if (cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_SSL) != CRYPT_OK) {
remove_msg_intransit(&smb,&msg);
bounce(sock, &smb,&msg,"Unable to create TLS session",/* immediate: */buf[0]=='5');
tls_failed = TRUE;
j--;
continue;
}
if (cryptSetAttribute(session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY) != CRYPT_OK) {
cryptDestroySession(session);
session = -1;
remove_msg_intransit(&smb,&msg);
bounce(sock, &smb,&msg,"Unable to disable certverify",/* immediate: */buf[0]=='5');
tls_failed = TRUE;
j--;
continue;
}
if (cryptSetAttribute(session, CRYPT_OPTION_CERT_COMPLIANCELEVEL, CRYPT_COMPLIANCELEVEL_OBLIVIOUS) != CRYPT_OK) {
cryptDestroySession(session);
session = -1;
remove_msg_intransit(&smb,&msg);
bounce(sock, &smb,&msg,"Unable to disable certverify",/* immediate: */buf[0]=='5');
tls_failed = TRUE;
j--;
continue;
}
if (cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate) != CRYPT_OK) {
cryptDestroySession(session);
session = -1;
remove_msg_intransit(&smb,&msg);
bounce(sock, &smb,&msg,"Unable to set private key",/* immediate: */buf[0]=='5');
tls_failed = TRUE;
j--;
continue;
}
nodelay = TRUE;
setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
nb=0;
ioctlsocket(sock,FIONBIO,&nb);
if (cryptSetAttribute(session, CRYPT_SESSINFO_NETWORKSOCKET, sock) != CRYPT_OK) {
cryptDestroySession(session);
session = -1;
remove_msg_intransit(&smb,&msg);
bounce(sock, &smb,&msg,"Unable to set TLS network socket",/* immediate: */buf[0]=='5');
tls_failed = TRUE;
j--;
continue;
}
if (cryptSetAttribute(session, CRYPT_SESSINFO_VERSION, 1) != CRYPT_OK) {
cryptDestroySession(session);
session = -1;
remove_msg_intransit(&smb,&msg);
bounce(sock, &smb,&msg,"Unable to set TLS version to 1.0",/* immediate: */buf[0]=='5');
tls_failed = TRUE;
j--;
continue;
}
if (cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1) != CRYPT_OK) {
cryptDestroySession(session);
session = -1;
remove_msg_intransit(&smb,&msg);
bounce(sock, &smb,&msg,"Unable to set session active",/* immediate: */buf[0]=='5');
tls_failed = TRUE;
j--;
continue;
}
if (startup->max_inactivity) {
if (cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity) != CRYPT_OK) {
cryptDestroySession(session);
session = -1;
remove_msg_intransit(&smb,&msg);
bounce(sock, &smb,&msg,"Unable to set max inactivity",/* immediate: */buf[0]=='5');
tls_failed = TRUE;
j--;
continue;
}
}
sockprintf(sock,session,"EHLO %s",startup->host_name);
if(!sockgetrsp(sock,session,"250",buf,sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
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) {
if((startup->options&MAIL_OPT_RELAY_AUTH_MASK)==MAIL_OPT_RELAY_AUTH_PLAIN) {
/* Build the buffer: <username>\0<user-id>\0<password */
len=safe_snprintf(buf,sizeof(buf),"%s%c%s%c%s"
,startup->relay_user
,0
,startup->relay_user
,0
,startup->relay_pass);
b64_encode(resp,sizeof(resp),buf,len);
sockprintf(sock,session,"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="<unknown>";
break;
}
sockprintf(sock,session,"AUTH %s",p);
if(!sockgetrsp(sock,session,"334",buf,sizeof(buf))) {
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_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;i<sizeof(secret);i++)
md5_data[i]=secret[i]^0x36; /* ipad */
strcpy(md5_data+i,challenge);
MD5_calc(digest,md5_data,sizeof(secret)+strlen(challenge));
for(i=0;i<sizeof(secret);i++)
md5_data[i]=secret[i]^0x5c; /* opad */
memcpy(md5_data+i,digest,sizeof(digest));
MD5_calc(digest,md5_data,sizeof(secret)+sizeof(digest));
safe_snprintf(buf,sizeof(buf),"%s %s",startup->relay_user,MD5_hex((BYTE*)str,digest));
b64_encode(p=resp,sizeof(resp),buf,0);
break;
default:
p="<unknown>";
break;
}
if((startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=MAIL_OPT_RELAY_AUTH_CRAM_MD5) {
if(!sockgetrsp(sock,session,"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="<unknown>";
break;
}
}
if(!sockgetrsp(sock,session,"235",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"235");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
}
/* MAIL */
if(fromaddr[0]=='<')
sockprintf(sock,session,"MAIL FROM: %s",fromaddr);
sockprintf(sock,session,"MAIL FROM: <%s>",fromaddr);
if(!sockgetrsp(sock,session,"250", buf, sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
/* RCPT */
if(msg.forward_path!=NULL) {
SAFECOPY(toaddr,msg.forward_path);
} else {
if((p=strrchr((char*)msg.to_net.addr,'<'))!=NULL)
p++;
else
p=(char*)msg.to_net.addr;
SAFECOPY(toaddr,p);
truncstr(toaddr,"> ");
if((p=strrchr(toaddr,'@'))!=NULL && (tp=strrchr(toaddr,':'))!=NULL
&& tp > p)
*tp=0; /* Remove ":port" designation from envelope */
}
sockprintf(sock,session,"RCPT TO: <%s>", toaddr);
if(!sockgetrsp(sock,session,"25", buf, sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"25*");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
/* DATA */
sockprintf(sock,session,"DATA");
if(!sockgetrsp(sock,session,"354", buf, sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"354");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
bytes=strlen(msgtxt);
lprintf(LOG_DEBUG,"%04d SEND sending message text (%u bytes) begin"
,sock, bytes);
lines=sockmsgtxt(sock,session,&msg,msgtxt,session);
lprintf(LOG_DEBUG,"%04d SEND send of message text (%u bytes, %u lines) complete, waiting for acknowledgment (250)"
,sock, bytes, lines);
if(!sockgetrsp(sock,session,"250", buf, sizeof(buf))) {
/* Wait doublely-long for the acknowledgment */
if(buf[0] || !sockgetrsp(sock,session,"250", buf, sizeof(buf))) {
remove_msg_intransit(&smb,&msg);
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
lprintf(LOG_INFO,"%04d SEND message transfer complete (%u bytes, %lu lines)", sock, bytes, lines);
/* Now lets mark this message for deletion without corrupting the index */
msg.hdr.attr|=MSG_DELETE;
msg.hdr.netattr&=~MSG_INTRANSIT;
if((i=smb_updatemsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d !SEND ERROR %d (%s) deleting message #%lu"
,sock, i, smb.last_error, msg.hdr.number);
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);
sockprintf(sock,session,"QUIT");
sockgetrsp(sock,session,"221", buf, sizeof(buf));
if (session != -1) {
cryptDestroySession(session);
session = -1;
}

rswindell
committed
mail_close_socket(sock);
sock=INVALID_SOCKET;
}
status(STATUS_WFC);
/* Free up resources here */
if(mail!=NULL)
freemail(mail);
if (session != -1)
cryptDestroySession(session);

rswindell
committed
mail_close_socket(sock);
listFree(&failed_server_list);
smb_freemsgtxt(msgtxt);
smb_freemsgmem(&msg);
smb_close(&smb);
if(active_sendmail!=0)
active_sendmail=0, update_clients();
{
int32_t remain = thread_down();
lprintf(LOG_DEBUG,"0000 SendMail thread terminated (%u threads remain)", remain);
}
sendmail_running=FALSE;
}
void DLLCALL mail_terminate(void)
terminate_server=TRUE;
}
static void cleanup(int code)
{
int i;
if(protected_uint32_value(thread_count) > 1) {
lprintf(LOG_DEBUG,"#### Waiting for %d child threads to terminate", protected_uint32_value(thread_count)-1);
while(protected_uint32_value(thread_count) > 1) {
mswait(100);
}
}
semfile_list_free(&recycle_semfiles);
semfile_list_free(&shutdown_semfiles);
if(mailproc_list!=NULL) {
for(i=0;i<mailproc_count;i++) {
if(mailproc_list[i].ar!=NULL && mailproc_list[i].ar!=nular)
free(mailproc_list[i].ar);
strListFree(&mailproc_list[i].to);
strListFree(&mailproc_list[i].from);
}
FREE_AND_NULL(mailproc_list);
}
/* Check a if(mail_set!=NULL) check be performed here? */
xpms_destroy(mail_set, mail_close_socket_cb, NULL);
mail_set=NULL;
terminated=TRUE;
update_clients(); /* active_clients is destroyed below */
if(protected_uint32_value(active_clients))
lprintf(LOG_WARNING,"#### !Mail Server terminating with %ld active clients", protected_uint32_value(active_clients));
else
protected_uint32_destroy(active_clients);
#ifdef _WINSOCKAPI_
if(WSAInitialized && WSACleanup()!=0)
lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
thread_down();
if(terminate_server || code) {
char str[1024];
sprintf(str,"%lu connections served", stats.connections_served);
if(stats.connections_refused)
sprintf(str+strlen(str),", %lu refused", stats.connections_refused);
if(stats.connections_ignored)
sprintf(str+strlen(str),", %lu ignored", stats.connections_refused);
if(stats.sessions_refused)
sprintf(str+strlen(str),", %lu sessions refused", stats.sessions_refused);
sprintf(str+strlen(str),", %lu messages received", stats.msgs_received);
if(stats.msgs_refused)
sprintf(str+strlen(str),", %lu refused", stats.msgs_refused);
if(stats.msgs_ignored)
sprintf(str+strlen(str),", %lu ignored", stats.msgs_ignored);
if(stats.errors)
sprintf(str+strlen(str),", %lu errors", stats.errors);
if(stats.crit_errors)
sprintf(str+strlen(str),", %lu critcal", stats.crit_errors);
lprintf(LOG_INFO,"#### Mail Server thread terminated (%s)",str);
}
if(startup!=NULL && startup->terminated!=NULL)
startup->terminated(startup->cbdata,code);
const char* DLLCALL mail_ver(void)
{
static char ver[256];
char compiler[32];
sscanf("$Revision$", "%*s %s", revision);
sprintf(ver,"%s %s%s SMBLIB %s "
,server_name
#ifdef _DEBUG
," Debug"
#else
,""
#endif
,smb_lib_ver()
,__DATE__, __TIME__, compiler
);
return(ver);
}
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];
socklen_t client_addr_len;
SOCKET client_socket;
int i;
ulong l;
time_t t;
time_t start;
time_t initialized=0;
pop3_t* pop3;
smtp_t* smtp;
FILE* fp;
str_list_t sec_list;
startup=(mail_startup_t*)arg;
#ifdef _THREAD_SUID_BROKEN
if(thread_suid_broken)
startup->seteuid(TRUE);
#endif
sbbs_beep(100,500);
fprintf(stderr, "No startup structure passed!\n");
return;
}
if(startup->size!=sizeof(mail_startup_t)) { /* verify size */
sbbs_beep(100,500);
sbbs_beep(300,500);
sbbs_beep(100,500);
fprintf(stderr, "Invalid startup structure!\n");
return;
}
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.interfaces=&startup->interfaces;
uptime=0;
memset(&stats,0,sizeof(stats));
startup->recycle_now=FALSE;
startup->shutdown_now=FALSE;
terminate_server=FALSE;
SetThreadName("sbbs/mailServer");
protected_uint32_init(&thread_count, 0);
/* 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->submissions_port==0) startup->submissions_port=IPPORT_SUBMISSIONS;
if(startup->smtp_port==0) startup->smtp_port=IPPORT_SMTP;
if(startup->pop3_port==0) startup->pop3_port=IPPORT_POP3;
if(startup->pop3s_port==0) startup->pop3s_port=IPPORT_POP3S;
if(startup->rescan_frequency==0) startup->rescan_frequency=MAIL_DEFAULT_RESCAN_FREQUENCY;
if(startup->max_delivery_attempts==0) startup->max_delivery_attempts=MAIL_DEFAULT_MAX_DELIVERY_ATTEMPTS;
if(startup->max_inactivity==0) startup->max_inactivity=MAIL_DEFAULT_MAX_INACTIVITY; /* seconds */
if(startup->sem_chk_freq==0) startup->sem_chk_freq=DEFAULT_SEM_CHK_FREQ;
if(startup->js.max_bytes==0) startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
if(startup->js.cx_stack==0) startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
protected_uint32_adjust(&thread_count,1);
thread_up(FALSE /* setuid */);
status("Initializing");
memset(&scfg, 0, sizeof(scfg));
lprintf(LOG_INFO,"%s Revision %s%s"
,server_name
," Debug"
lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
lprintf(LOG_DEBUG,"SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff);
sbbs_srand();
if(!winsock_startup()) {

rswindell
committed
cleanup(1);
return;
}
t=time(NULL);
lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
,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);
lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
scfg.size=sizeof(scfg);
SAFECOPY(error,UNKNOWN_LOAD_ERROR);
if(!load_cfg(&scfg, NULL, TRUE, error)) {
lprintf(LOG_CRIT,"!ERROR %s",error);
lprintf(LOG_CRIT,"!Failed to load configuration files");
cleanup(1);
return;
}
if(startup->temp_dir[0])
SAFECOPY(scfg.temp_dir,startup->temp_dir);
else
SAFECOPY(scfg.temp_dir,"../temp");
prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
MKDIR(scfg.temp_dir);
lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
if(!isdir(scfg.temp_dir)) {
lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir);
/* Parse the mailproc[.host].ini */
mailproc_list=NULL;
mailproc_count=0;
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;i<mailproc_count;i++) {
memset(&mailproc_list[i],0,sizeof(struct mailproc));
SAFECOPY(mailproc_list[i].name,sec_list[i]);
SAFECOPY(mailproc_list[i].cmdline,
iniReadString(fp,sec_list[i],"Command",sec_list[i],buf));
SAFECOPY(mailproc_list[i].eval,
iniReadString(fp,sec_list[i],"Eval","",buf));
mailproc_list[i].to =
mailproc_list[i].from =
mailproc_list[i].passthru =
mailproc_list[i].native =
mailproc_list[i].disabled =
mailproc_list[i].ignore_on_error =
iniReadBool(fp,sec_list[i],"IgnoreOnError",FALSE);
mailproc_list[i].process_spam =
iniReadBool(fp,sec_list[i],"ProcessSPAM",TRUE);
mailproc_list[i].process_dnsbl =
iniReadBool(fp,sec_list[i],"ProcessDNSBL",TRUE);
mailproc_list[i].ar =
arstr(NULL,iniReadString(fp,sec_list[i],"AccessRequirements","",buf),&scfg);
}
}
iniFreeStringList(sec_list);
iniCloseFile(fp);
}
if(startup->host_name[0]==0)
SAFECOPY(startup->host_name,scfg.sys_inetaddr);
if((t=checktime())!=0) { /* Check binary time */
lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
}
if(uptime==0)
uptime=time(NULL); /* this must be done *after* setting the timezone */
if(startup->max_clients==0) {
startup->max_clients=scfg.sys_nodes;
if(startup->max_clients<10)
startup->max_clients=10;
}
lprintf(LOG_DEBUG,"Maximum clients: %u",startup->max_clients);
lprintf(LOG_DEBUG,"Maximum inactivity: %u seconds",startup->max_inactivity);
protected_uint32_init(&active_clients, 0);
update_clients();
/* open a socket and wait for a client */
mail_set = xpms_create(startup->bind_retry_count, startup->bind_retry_delay, lprintf);
if(mail_set == NULL) {
lprintf(LOG_CRIT,"!ERROR creating mail server socket set", ERROR_VALUE);
cleanup(1);
return;
}
if(!xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->smtp_port, "SMTP Server", mail_open_socket, startup->seteuid, "smtp"))
lprintf(LOG_INFO,"SMTP No extra interfaces listening");
lprintf(LOG_INFO,"SMTP Server listening");
if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT) {
if(xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->submission_port, "SMTP Submission Agent", mail_open_socket, startup->seteuid, "submission"))
lprintf(LOG_INFO,"SUBMISSION Server listening");
}
if(startup->options&MAIL_OPT_USE_SUBMISSIONS_PORT) {
if(xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->submissions_port, "TLS/SMTP Submission Agent", mail_open_socket, startup->seteuid, "submissions"))
lprintf(LOG_INFO,"SUBMISSIONS Server listening");
}
if(startup->options&MAIL_OPT_ALLOW_POP3) {
/* open a socket and wait for a client */
if(!xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->pop3_interfaces, startup->pop3_port, "POP3 Server", mail_open_socket, startup->seteuid, "pop3"))
lprintf(LOG_INFO,"POP3 No extra interfaces listening");
if(startup->options&MAIL_OPT_USE_POP3S_PORT) {
/* open a socket and wait for a client */
if(!xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->pop3_interfaces, startup->pop3s_port, "TLS/POP3 Server", mail_open_socket, startup->seteuid, "pop3s"))
lprintf(LOG_INFO,"POP3S No extra interfaces listening");
lprintf(LOG_INFO,"POP3S Server listening");
}
sem_init(&sendmail_wakeup_sem,0,0);
if(!(startup->options&MAIL_OPT_NO_SENDMAIL)) {
sendmail_running=TRUE;
protected_uint32_adjust(&thread_count,1);
_beginthread(sendmail_thread, 0, NULL);
}
status(STATUS_WFC);
/* Setup recycle/shutdown semaphore file lists */
shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","mail");
recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","mail");
semfile_list_add(&recycle_semfiles,startup->ini_fname);
SAFEPRINTF(path,"%smailsrvr.rec",scfg.ctrl_dir); /* legacy */
semfile_list_add(&recycle_semfiles,path);
semfile_list_add(&recycle_semfiles,mailproc_ini);
if(!initialized) {
semfile_list_check(&initialized,recycle_semfiles);
semfile_list_check(&initialized,shutdown_semfiles);
/* signal caller that we've started up successfully */
if(startup->started!=NULL)
startup->started(startup->cbdata);
YIELD();
if(protected_uint32_value(thread_count) <= (unsigned int)(1+(sendmail_running?1:0))) {
if(!(startup->options&MAIL_OPT_NO_RECYCLE)) {
if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) {
break;
}
if(startup->recycle_now==TRUE) {
startup->recycle_now=FALSE;
break;
}
if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
&& lprintf(LOG_INFO,"Shutdown semaphore file (%s) detected",p))
|| (startup->shutdown_now==TRUE
startup->shutdown_now=FALSE;
terminate_server=TRUE;
break;
}
/* now wait for connection */
client_addr_len = sizeof(client_addr);
client_socket = xpms_accept(mail_set,&client_addr, &client_addr_len, startup->sem_chk_freq*1000, &cbdata);
if(client_socket != INVALID_SOCKET) {
if(startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);
stats.sockets++;
inet_addrtop(&client_addr, host_ip, sizeof(host_ip));
if(trashcan(&scfg,host_ip,"ip-silent")) {
mail_close_socket(client_socket);
stats.connections_ignored++;
continue;
}
if(protected_uint32_value(active_clients)>=startup->max_clients) {
lprintf(LOG_WARNING,"%04d %s !MAXIMUM CLIENTS (%u) reached, access denied (%u total)"
,client_socket, (char *)cbdata, startup->max_clients, ++stats.connections_refused);
sockprintf(client_socket,session,"-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_CRIT,"%04d %s !ERROR %d (%d) disabling blocking on socket"
,client_socket, (char *)cbdata, i, ERROR_VALUE);
sockprintf(client_socket,session,"-ERR System error, please try again later.");
mswait(3000);
mail_close_socket(client_socket);
continue;
}
if(strcmp((char *)cbdata, "pop3") && strcmp((char *)cbdata, "pop3s")) { /* Not POP3 */