Newer
Older
," Debug"
#else
,""
#endif
,GIT_BRANCH, GIT_HASH
,__DATE__, __TIME__, compiler
);
return(ver);
}
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;
char* servprot = "N/A";
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;
}
set_state(SERVER_INIT);
ZERO_VAR(js_server_props);
SAFEPRINTF3(js_server_props.version,"%s %s%c",server_name, VERSION, REVISION);
js_server_props.version_detail=mail_ver();
js_server_props.clients=&active_clients;
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);
listInit(¤t_logins, LINK_LIST_MUTEX);
listInit(¤t_connections, LINK_LIST_MUTEX);
protected_uint32_init(&active_clients, 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;
(void)protected_uint32_adjust(&thread_count,1);
thread_up(FALSE /* setuid */);
memset(&scfg, 0, sizeof(scfg));
lprintf(LOG_INFO,"%s Version %s%c%s"
,server_name
,VERSION, REVISION
," Debug"
lprintf(LOG_INFO,"Compiled %s/%s %s %s with %s", GIT_BRANCH, GIT_HASH, __DATE__, __TIME__, compiler);
sbbs_srand();
if(!winsock_startup()) {

rswindell
committed
cleanup(1);
return;
}
t=time(NULL);
lprintf(LOG_INFO,"Initializing on %.24s with options: %x"
,ctime_r(&t,str),startup->options);
if(chdir(startup->ctrl_dir)!=0)
lprintf(LOG_ERR,"!ERROR %d (%s) changing directory to: %s", errno, strerror(errno), startup->ctrl_dir);
if((startup->options & MAIL_OPT_RELAY_TX) && startup->relay_server[0] == '\0') {
lprintf(LOG_ERR, "SMTP-Transmit-Relay enabled, but no relay server (hostname or IP address) configured. Disabling.");
startup->options &= ~MAIL_OPT_RELAY_TX;
}
/* 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, text, /* prep: */TRUE, /* node: */FALSE, error, sizeof(error))) {
lprintf(LOG_CRIT,"!ERROR %s",error);
lprintf(LOG_CRIT,"!Failed to load configuration files");
cleanup(1);
return;
}
mqtt_startup(&mqtt, &scfg, (struct startup*)startup, mail_ver(), lputs);
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));
if((i = md(scfg.temp_dir)) != 0) {
lprintf(LOG_CRIT, "!ERROR %d (%s) creating directory: %s", i, strerror(i), scfg.temp_dir);
lprintf(LOG_DEBUG,"Temporary file 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, /* for_modify: */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 =

rswindell
committed
arstr(NULL,iniReadString(fp,sec_list[i],"AccessRequirements","",buf),&scfg,NULL);
}
}
iniFreeStringList(sec_list);
iniCloseFile(fp);
}
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);
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");
cleanup(1);
return;
}
if(!xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces
, startup->smtp_port, "SMTP Transfer Agent", mail_open_socket, startup->seteuid, (void*)servprot_smtp))
if(startup->options&MAIL_OPT_USE_SUBMISSION_PORT) {
xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces
, startup->submission_port, "SMTP Submission Agent", mail_open_socket, startup->seteuid, (void*)servprot_submission);
}
if(startup->options&MAIL_OPT_TLS_SUBMISSION) {
xpms_add_list(mail_set, PF_UNSPEC, SOCK_STREAM, 0, startup->interfaces, startup->submissions_port
, "SMTPS Submission Agent", mail_open_socket, startup->seteuid, (void*)servprot_submissions);
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, (void*)servprot_pop3))
lprintf(LOG_INFO,"POP3 No extra interfaces listening");
if(startup->options&MAIL_OPT_TLS_POP3) {
/* 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, "POP3S Server", mail_open_socket, startup->seteuid, (void*)servprot_pop3s))
lprintf(LOG_INFO,"POP3S No extra interfaces listening");
}
sem_init(&sendmail_wakeup_sem,0,0);
if(!(startup->options&MAIL_OPT_NO_SENDMAIL)) {
sendmail_running=TRUE;
(void)protected_uint32_adjust(&thread_count,1);
_beginthread(sendmail_thread, 0, NULL);
}
/* Setup recycle/shutdown semaphore file lists */
shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown", server_abbrev);
recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle", server_abbrev);
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);
pthread_mutex_init(&savemsg_mutex, NULL);
savemsg_mutex_created = true;
/* signal caller that we've started up successfully */
set_state(SERVER_READY);
mqtt_client_max(&mqtt, startup->max_clients);
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_socket = xpms_accept(mail_set,&client_addr, &client_addr_len, startup->sem_chk_freq*1000, XPMS_FLAGS_NONE, (void**)&servprot);
bool is_smtp = (servprot != servprot_pop3 && servprot != servprot_pop3s);
if(startup->socket_open!=NULL)
startup->socket_open(startup->cbdata,TRUE);
stats.sockets++;
if(startup->max_concurrent_connections > 0) {
int ip_len = strlen(host_ip)+1;
int connections = listCountMatches(¤t_connections, host_ip, ip_len);
int logins = listCountMatches(¤t_logins, host_ip, ip_len);
if(connections - logins >= (int)startup->max_concurrent_connections
&& !is_host_exempt(&scfg, host_ip, /* host_name */NULL)) {
lprintf(LOG_NOTICE, "%04d %s [%s] !Maximum concurrent connections without login (%u) exceeded (%lu total)"
,client_socket, servprot, host_ip, startup->max_concurrent_connections, ++stats.connections_exceeded);
sockprintf(client_socket, servprot, session, is_smtp ? smtp_error : pop_error, "Maximum connections exceeded");
mail_close_socket(&client_socket, &session);
continue;
}
}
mail_close_socket(&client_socket, &session);
stats.connections_ignored++;
continue;
}
if(protected_uint32_value(active_clients)>=startup->max_clients) {
lprintf(LOG_WARNING,"%04d %s !MAXIMUM CLIENTS (%u) reached, access denied (%lu total)"
,client_socket, servprot, startup->max_clients, ++stats.connections_refused);
sockprintf(client_socket, servprot, session, is_smtp ? smtp_error : pop_error, "Maximum active clients reached");
mswait(3000);
mail_close_socket(&client_socket, &session);
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, servprot, i, ERROR_VALUE);
sockprintf(client_socket, servprot, session, is_smtp ? smtp_error : pop_error, "ioctlsocket error");
mswait(3000);
mail_close_socket(&client_socket, &session);
continue;
}
lprintf(LOG_CRIT,"%04d SMTP !ERROR allocating %lu bytes of memory for smtp_t"
sockprintf(client_socket, servprot, session, smtp_error, "malloc failure");
mail_close_socket(&client_socket, &session);
continue;
}
smtp->socket=client_socket;
memcpy(&smtp->client_addr, &client_addr, client_addr_len);
smtp->client_addr_len=client_addr_len;
smtp->tls_port = (servprot == servprot_submissions);
(void)protected_uint32_adjust(&thread_count,1);
_beginthread(smtp_thread, 0, smtp);
stats.connections_served++;
lprintf(LOG_CRIT,"%04d POP3 !ERROR allocating %lu bytes of memory for pop3_t"
sockprintf(client_socket, "POP3", session,"-ERR System error, please try again later.");
mail_close_socket(&client_socket, &session);
pop3->socket=client_socket;
memcpy(&pop3->client_addr, &client_addr, client_addr_len);
pop3->client_addr_len=client_addr_len;
pop3->tls_port = (servprot == servprot_pop3s);
(void)protected_uint32_adjust(&thread_count,1);
_beginthread(pop3_thread, 0, pop3);
stats.connections_served++;
}
set_state(terminate_server ? SERVER_STOPPING : SERVER_RELOADING);
if(protected_uint32_value(active_clients)) {
lprintf(LOG_INFO,"Waiting for %d active clients to disconnect..."
start=time(NULL);
while(protected_uint32_value(active_clients)) {
if(startup->max_inactivity && time(NULL)-start>startup->max_inactivity) {
lprintf(LOG_WARNING,"!TIMEOUT (%u seconds) waiting for %d active clients"
, startup->max_inactivity, protected_uint32_value(active_clients));
break;
}
mswait(100);
lprintf(LOG_INFO, "Done waiting for active clients to disconnect");
if(sendmail_running) {
terminate_sendmail=TRUE;
sem_post(&sendmail_wakeup_sem);
mswait(100);
}
if(sendmail_running) {
lprintf(LOG_INFO, "Waiting for SendMail thread to terminate...");
start=time(NULL);
while(sendmail_running) {
if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
lprintf(LOG_WARNING, "!TIMEOUT waiting for SendMail thread to terminate");
break;
}
mswait(500);
lprintf(LOG_INFO, "Done waiting for SendMail thread to terminate");
if(!sendmail_running) {
while(sem_destroy(&sendmail_wakeup_sem)==-1 && errno==EBUSY) {
mswait(1);
cleanup(0);
if(!terminate_server) {
lprintf(LOG_INFO,"Recycling server...");
if(startup->recycle!=NULL)
startup->recycle(startup->cbdata);
} while(!terminate_server);
protected_uint32_destroy(thread_count);