Newer
Older
sockprintf(socket, client.protocol, session, "454 TLS not available");
sockprintf(socket, client.protocol, -1, "220 Ready to start TLS");
if ((cstat=cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
unlock_ssl_cert();
GCES(cstat, "SMTPS", socket, session, "setting session active");
unlock_ssl_cert();
if ((cstat=cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity)) != CRYPT_OK) {
GCES(cstat, "SMTPS", socket, session, "setting read timeout");
client.protocol = "SMTPS";
sockprintf(socket,client.protocol,session,"500 Syntax error");
lprintf(LOG_WARNING,"%04d %s %s !UNSUPPORTED COMMAND: '%s'", socket, client.protocol, client_id, buf);
if(++badcmds > SMTP_MAX_BAD_CMDS) {
lprintf(LOG_WARNING,"%04d %s %s !TOO MANY INVALID COMMANDS (%lu)",socket, client.protocol, client_id, badcmds);
break;
}
}
/* Free up resources here */
smb_freemsgmem(&msg);
if(msgtxt!=NULL)
if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY))
remove(msgtxt_fname);
if(rcptlst!=NULL)
remove(rcptlst_fname);
if(spy!=NULL)
fclose(spy);
js_cleanup(js_runtime, js_cx, &js_glob);
status(STATUS_WFC);
listRemoveTaggedNode(¤t_logins, socket, /* free_data */TRUE);
protected_uint32_adjust(&active_clients, -1);
update_clients();
{
int32_t remain = thread_down();
lprintf(LOG_INFO,"%04d %s %s Session thread terminated (%u threads remain, %lu clients served)"
,socket, client.protocol, client_id, remain, ++stats.smtp_served);
/* Must be last */
mail_close_socket(&socket, &session);
BOOL bounce(SOCKET sock, smb_t* smb, smbmsg_t* msg, char* err, BOOL immediate)
char str[128];
char attempts[64];
ushort agent=AGENT_SMTPSYSMSG;
msg->hdr.delivery_attempts++;
lprintf(LOG_WARNING,"%04d SEND !Delivery attempt #%u FAILED (%s) for message #%u from %s to %s"
,sock
,msg->hdr.delivery_attempts
,err
,msg->hdr.number
,msg->from
,(char*)msg->to_net.addr);
if((i=smb_updatemsg(smb,msg))!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d SEND !BOUNCE ERROR %d (%s) incrementing delivery attempt counter of message #%u"
,sock, i, smb->last_error, msg->hdr.number);
if(!immediate && msg->hdr.delivery_attempts < startup->max_delivery_attempts)
return(TRUE);
newmsg=*msg;
/* Mark original message as deleted */
msg->hdr.attr|=MSG_DELETE;
i=smb_updatemsg(smb,msg);
if(msg->hdr.auxattr&MSG_FILEATTACH)
delfattach(&scfg,msg);
if(i!=SMB_SUCCESS) {
lprintf(LOG_ERR,"%04d SEND !BOUNCE ERROR %d (%s) deleting message"
,sock, i, smb->last_error);
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,"%04d SEND !Deleted undeliverable message from %s", sock, msg->from);
return(TRUE);
}
newmsg.hfield=NULL;
newmsg.hfield_dat=NULL;
newmsg.total_hfields=0;
newmsg.hdr.delivery_attempts=0;
msg->text_subtype=NULL;
msg->text_charset=NULL;
char* reverse_path = msg->reverse_path==NULL ? msg->from : msg->reverse_path;
lprintf(LOG_WARNING,"%04d SEND !Bouncing message back to %s", sock, reverse_path);
SAFEPRINTF(str,"Delivery failure: %s",newmsg.subj);
smb_hfield_str(&newmsg, SUBJECT, str);
smb_hfield_str(&newmsg, RECIPIENT, reverse_path);
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);
}
} else {
smb_hfield(&newmsg, RECIPIENTAGENT, sizeof(msg->from_agent), &msg->from_agent);
}
newmsg.hdr.attr|=MSG_NOREPLY;
newmsg.hdr.attr&=~MSG_READ;
if(scfg.sys_misc&SM_DELREADM)
newmsg.hdr.attr |= MSG_KILLREAD;
strcpy(str,"Mail Delivery Subsystem");
smb_hfield_str(&newmsg, SENDER, str);
smb_hfield(&newmsg, SENDERAGENT, sizeof(agent), &agent);
smb_hfield_str(&newmsg, RFC822MSGID, get_msgid(&scfg, INVALID_SUB, &newmsg, msgid, sizeof(msgid)));
/* Put error message in subject for now */
if(msg->hdr.delivery_attempts>1)
SAFEPRINTF(attempts,"after %u attempts", msg->hdr.delivery_attempts);
else
attempts[0]=0;
SAFEPRINTF2(str,"%s reporting delivery failure of message %s"
,server_host_name(), attempts);
smb_hfield_str(&newmsg, SMB_COMMENT, str);
SAFEPRINTF2(str,"from %s to %s\r\n"
,msg->from
,(char*)msg->to_net.addr);
smb_hfield_str(&newmsg, SMB_COMMENT, str);
strcpy(str,"Reason:");
smb_hfield_str(&newmsg, SMB_COMMENT, str);
smb_hfield_str(&newmsg, SMB_COMMENT, err);
smb_hfield_str(&newmsg, SMB_COMMENT, "\r\nOriginal message text follows:");
if((i=smb_addmsghdr(smb,&newmsg,smb_storage_mode(&scfg, smb)))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d SEND !BOUNCE ERROR %d (%s) adding message header"
,sock,i,smb->last_error);
else {
lprintf(LOG_WARNING,"%04d SEND !Delivery failure notification (message #%u) created for %s"
,sock, newmsg.hdr.number, reverse_path);
if((i=smb_incmsg_dfields(smb,&newmsg,1))!=SMB_SUCCESS)
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 */
newmsg.hdr.total_dfields=0;
smb_freemsgmem(&newmsg);
return(TRUE);
}
static int remove_msg_intransit(smb_t* smb, smbmsg_t* msg)
{
int i;
if((i=smb_lockmsghdr(smb,msg))!=SMB_SUCCESS) {
lprintf(LOG_WARNING,"0000 SEND !ERROR %d (%s) locking message header #%u"
,i, smb->last_error, msg->idx.number);
return(i);
}
msg->hdr.netattr&=~MSG_INTRANSIT;
i=smb_putmsghdr(smb,msg);
smb_unlockmsghdr(smb,msg);
if(i!=0)
lprintf(LOG_ERR,"0000 SEND !ERROR %d (%s) writing message header #%u"
,i, smb->last_error, msg->idx.number);
return(i);
}
void get_dns_server(char* dns_server, size_t len)
{
str_list_t list;
size_t count;
sprintf(dns_server,"%.*s",(int)len-1,startup->dns_server);
if(!isalnum(dns_server[0])) {
if((list=getNameServerList())!=NULL) {
if((count=strListCount(list))>0) {
lprintf(LOG_DEBUG,"0000 SEND using auto-detected DNS server address: %s"
,dns_server);
}
freeNameServerList(list);
}
}
}
static BOOL sendmail_open_socket(SOCKET *sock, CRYPT_SESSION *session)
{
int i;
SOCKADDR_IN addr;
if (*sock != INVALID_SOCKET)
mail_close_socket(sock, session);
if((*sock=socket(AF_INET, SOCK_STREAM, IPPROTO_IP))==INVALID_SOCKET) {
lprintf(LOG_ERR,"0000 SEND !ERROR %d opening socket", ERROR_VALUE);
return FALSE;
}
mail_open_socket(*sock,"smtp|sendmail");
if(startup->connect_timeout) { /* Use non-blocking socket */
long nbio=1;
if((i=ioctlsocket(*sock, FIONBIO, &nbio))!=0) {
lprintf(LOG_ERR,"%04d SEND !ERROR %d (%d) disabling blocking on socket"
,*sock, i, ERROR_VALUE);
return FALSE;
}
}
memset(&addr,0,sizeof(addr));
addr.sin_addr.s_addr = htonl(startup->outgoing4.s_addr);
addr.sin_family = AF_INET;
i=bind(*sock,(struct sockaddr *)&addr, sizeof(addr));
if(i!=0) {
lprintf(LOG_ERR,"%04d SEND !ERROR %d (%d) binding socket", *sock, i, ERROR_VALUE);
return FALSE;
}
return TRUE;
}
static SOCKET sendmail_negotiate(CRYPT_SESSION *session, smb_t *smb, smbmsg_t *msg, const char *mx, const char *mx2, const char *server, link_list_t *failed_server_list, ushort port)
{
int i;
int tls_retry;
SOCKET sock=INVALID_SOCKET;
list_node_t* node;
ulong ip_addr;
union xp_sockaddr server_addr;
char server_ip[INET6_ADDRSTRLEN];
BOOL success;
BOOL nodelay=TRUE;
ulong nb = 0;
int status;
char buf[512];
char err[1024] = "UNKNOWN ERROR";
for (tls_retry = 0; tls_retry < 2; tls_retry++) {
if (!sendmail_open_socket(&sock, session))
continue;
success=FALSE;
for(i=0;i<2 && !success;i++) {
if(i) {
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, "Error resolving 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);
inet_addrtop(&server_addr,server_ip,sizeof(server_ip));
if((node=listFindNode(failed_server_list,&server_addr,sizeof(server_addr))) != NULL) {
SAFEPRINTF4(err, "Error %ld connecting to port %u on %s [%s]", node->tag, inet_addrport(&server_addr), server, server_ip);
lprintf(LOG_INFO,"%04d SEND skipping failed SMTP server: %s", sock, err);
continue;
}
if((server==mx || server==mx2)
&& ((ip_addr&0xff)==127 || ip_addr==0)) {
SAFEPRINTF2(err,"Bad IP address (%s) for MX server: %s"
,server_ip,server);
lprintf(LOG_INFO, "%04d SEND %s", sock, err);
continue;
}
lprintf(LOG_INFO,"%04d SEND connecting to port %u on %s [%s]"
,sock
,inet_addrport(&server_addr)
,server,server_ip);
if((i=nonblocking_connect(sock, (struct sockaddr *)&server_addr, xp_sockaddr_len(&server_addr), startup->connect_timeout))!=0) {
SAFEPRINTF2(err,"ERROR %d connecting to SMTP server: %s"
,i, server);
lprintf(LOG_INFO,"%04d SEND !%s" ,sock, err);
listAddNodeData(failed_server_list,&server_addr,sizeof(server_addr),i,NULL);
continue;
}
lprintf(LOG_DEBUG,"%04d SEND connected to %s",sock,server);
/* HELO */
if(!sockgetrsp(sock, "SEND", *session,"220",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"220");
lprintf(LOG_INFO, "%04d SEND %s", sock, err);
continue;
}
success=TRUE;
}
if(!success) { /* Failed to connect to an MX, so bounce */
remove_msg_intransit(smb,msg);
bounce(sock /* Should be zero? */, smb,msg,err,/* immediate: */FALSE);
mail_close_socket(&sock, session);
return INVALID_SOCKET;
}
sockprintf(sock, "SEND", *session,"EHLO %s",server_host_name());
switch (sockgetrsp_opt(sock, "SEND", *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 */
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
remove_msg_intransit(smb,msg);
bounce(sock, smb,msg,err,/* immediate: */buf[0]=='5');
mail_close_socket(&sock, session);
return INVALID_SOCKET;
}
sockprintf(sock, "SEND", *session,"HELO %s",server_host_name());
if(!sockgetrsp(sock, "SEND", *session,"250",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"250");
remove_msg_intransit(smb,msg);
bounce(sock, smb,msg,err,/* immediate: */buf[0]=='5');
mail_close_socket(&sock, session);
return INVALID_SOCKET;
}
return sock;
case 0:
return sock;
case 1:
/* We NEVER bounce() because of TLS errors, so we don't need to set err */
if (!tls_retry) {
char* estr = NULL;
int level;
lprintf(LOG_DEBUG, "%04d SEND Starting TLS session", sock);
if(get_ssl_cert(&scfg, &estr, &level) == -1) {
if (estr) {
lprintf(level, "%04d SEND/TLS !%s", sock, estr);
free_crypt_attrstr(estr);
}
continue;
}
const char* prot = "SEND/TLS";
sockprintf(sock, prot, *session, "STARTTLS");
if (sockgetrsp(sock, prot, *session, "220", buf, sizeof(buf))) {
if ((status=cryptCreateSession(session, CRYPT_UNUSED, CRYPT_SESSION_SSL)) != CRYPT_OK) {
GCESH(status, prot, sock, server, CRYPT_UNUSED, "creating TLS session");
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY)) != CRYPT_OK) {
GCESH(status, prot, sock, server, *session, "disabling certificate verification");
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_OPTION_CERT_COMPLIANCELEVEL, CRYPT_COMPLIANCELEVEL_OBLIVIOUS)) != CRYPT_OK) {
GCESH(status, prot, sock, server, *session, "setting certificate compliance level");
continue;
}
lock_ssl_cert();
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(status, prot, sock, server, *session, "setting private key");
continue;
}
nodelay = TRUE;
setsockopt(sock,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
nb=0;
ioctlsocket(sock,FIONBIO,&nb);
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_NETWORKSOCKET, sock)) != CRYPT_OK) {
unlock_ssl_cert();
GCESH(status, prot, sock, server, *session, "setting network socket");
continue;
}
if ((status=cryptSetAttribute(*session, CRYPT_SESSINFO_ACTIVE, 1)) != CRYPT_OK) {
unlock_ssl_cert();

Rob Swindell
committed
GCESHL(status, prot, sock, server, LOG_WARNING, *session, "setting session active");
continue;
}
unlock_ssl_cert();
if (startup->max_inactivity) {
if ((status=cryptSetAttribute(*session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity)) != CRYPT_OK) {
GCESH(status, prot, sock, server, *session, "setting read timeout");
continue;
}
}
sockprintf(sock,prot,*session,"EHLO %s",server_host_name());
if(!sockgetrsp(sock, prot, *session,"250",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"220");
lprintf(LOG_INFO, "%04d SEND/TLS %s", sock, err);
continue;
}
lprintf(LOG_INFO, "%04d SEND TLS Session started successfully", sock);
}
}
return sock;
}
}
remove_msg_intransit(smb,msg);
bounce(sock, smb,msg,err,/* immediate: */FALSE);
mail_close_socket(&sock, session);
return INVALID_SOCKET;
}
#ifdef __BORLANDC__
#pragma argsused
#endif
static void sendmail_thread(void* arg)
{
int i;
char to[128];
char mx[128];
char mx2[128];
char str[128];
char resp[512];
char toaddr[256];
char challenge[256];
char secret[64];
char md5_data[384];
uchar digest[MD5_DIGEST_SIZE];
char numeric_ip[16];
char domain_list[MAX_PATH+1];
char dns_server[128];
char* server;
char* msgtxt=NULL;
char* p;
char* tp;
ushort port;
ulong last_msg=0;
ulong dns;
ulong bytes;
BOOL first_cycle=TRUE;
SOCKET sock=INVALID_SOCKET;
time_t last_scan=0;
smb_t smb;
smbmsg_t msg;
uint32_t msgs;
uint32_t u;
size_t len;
BOOL sending_locally=FALSE;
link_list_t failed_server_list;
SetThreadName("sbbs/sendMail");
thread_up(TRUE /* setuid */);
terminate_sendmail=FALSE;
lprintf(LOG_INFO,"SendMail thread started");
memset(&msg,0,sizeof(msg));
memset(&smb,0,sizeof(smb));
listInit(&failed_server_list, /* flags: */0);
YIELD();
if(startup->options&MAIL_OPT_NO_SENDMAIL) {
sem_trywait_block(&sendmail_wakeup_sem,1000);
continue;
}
if(active_sendmail!=0)
active_sendmail=0, update_clients();
listFreeNodes(&failed_server_list);
if(sock!=INVALID_SOCKET)
mail_close_socket(&sock, &session);
if(msgtxt!=NULL) {
smb_freemsgtxt(msgtxt);
msgtxt=NULL;
}
smb_freemsgmem(&msg);
/* Don't delay on first loop */
if(first_cycle)
first_cycle=FALSE;
else
sem_trywait_block(&sendmail_wakeup_sem,startup->sem_chk_freq*1000);
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)
if((i=smb_locksmbhdr(&smb))!=SMB_SUCCESS)
i=smb_getstatus(&smb);
if(i!=0)
continue;
if(smb.status.last_msg==last_msg && time(NULL)-last_scan<startup->rescan_frequency)
continue;
lprintf(LOG_DEBUG, "0000 SEND last_msg=%lu, smb.status.last_msg=%u, elapsed=%ld"
,last_msg, smb.status.last_msg, (long)(time(NULL)-last_scan));
last_msg=smb.status.last_msg;
last_scan=time(NULL);
mail=loadmail(&smb,&msgs,/* to network */0,MAIL_YOUR,0);
for(u=0; u<msgs; u++) {
if(active_sendmail!=0)
active_sendmail=0, update_clients();
if(sock!=INVALID_SOCKET)
mail_close_socket(&sock, &session);
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 #%u"
,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 #%u"
,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 #%u"
,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
|| (msg.hdr.netattr&MSG_SENT)) {
smb_unlockmsghdr(&smb,&msg);
if(msg.from_net.type==NET_INTERNET && msg.reverse_path!=NULL)
SAFECOPY(fromaddr, msg.reverse_path);
else
SAFEPRINTF(fromaddr, "<%s>", usermailaddr(&scfg, str, msg.from));
char sender_info[256];
if(msg.from_ext != NULL)
SAFEPRINTF2(sender_info, "'%s' #%s", msg.from, msg.from_ext);
else if(strstr(fromaddr, msg.from) == fromaddr + 1)
SAFECOPY(sender_info, fromaddr);
else
SAFEPRINTF2(sender_info, "'%s' %s", msg.from, fromaddr);
char rcpt_info[256];
if(strcmp(msg.to, (char*)msg.to_net.addr) == 0)
SAFEPRINTF(rcpt_info, "<%s>", msg.to);
else
SAFEPRINTF2(rcpt_info, "'%s' <%s>", msg.to, (char*)msg.to_net.addr);
if(!(startup->options&MAIL_OPT_SEND_INTRANSIT) && msg.hdr.netattr&MSG_INTRANSIT) {
smb_unlockmsghdr(&smb,&msg);
lprintf(LOG_NOTICE,"0000 SEND Message #%u from %s to %s - in transit"
,msg.hdr.number, sender_info, rcpt_info);
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();
lprintf(LOG_INFO,"0000 SEND Message #%u (%u of %u) from %s to %s"
,msg.hdr.number, u+1, msgs, sender_info, rcpt_info);
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 */
*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;
sock = sendmail_negotiate(&session, &smb, &msg, mx, mx2, server, &failed_server_list, port);
if (sock == INVALID_SOCKET)
const char* prot = session >= 0 ? "SEND/TLS" : "SEND";
/* 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,prot,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,prot,session,"AUTH %s",p);
if(!sockgetrsp(sock,prot,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;
}
sockprintf(sock,prot,session,"%s",p);
if((startup->options&MAIL_OPT_RELAY_AUTH_MASK)!=MAIL_OPT_RELAY_AUTH_CRAM_MD5) {
if(!sockgetrsp(sock,prot,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;
}
sockprintf(sock,prot,session,"%s",p);
}
if(!sockgetrsp(sock,prot,session,"235",buf,sizeof(buf))) {
SAFEPRINTF3(err,badrsp_err,server,buf,"235");
bounce(sock, &smb,&msg,err,/* immediate: */buf[0]=='5');
continue;
}
}
sockprintf(sock,prot,session,"MAIL FROM:%s",fromaddr);
if(!sockgetrsp(sock,prot,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 */
}
if(*toaddr == '<')
sockprintf(sock,prot,session,"RCPT TO:%s", toaddr);
else
sockprintf(sock,prot,session,"RCPT TO:<%s>", toaddr);
if(!sockgetrsp(sock,prot,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,prot,session,"DATA");
if(!sockgetrsp(sock,prot,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 %s sending message text (%lu bytes) begin"
,sock, prot, bytes);
lines=sockmsgtxt(sock, prot, session, &msg,msgtxt, /* max_lines: */-1);
lprintf(LOG_DEBUG,"%04d %s send of message text (%lu bytes, %lu lines) complete, waiting for acknowledgment (250)"
,sock, prot, bytes, lines);
if(!sockgetrsp(sock,prot,session,"250", buf, sizeof(buf))) {
/* Wait doublely-long for the acknowledgment */
if(buf[0] || !sockgetrsp(sock,prot,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 %s Successfully sent message #%u (%lu bytes, %lu lines) from %s to '%s' %s"
,sock, prot, msg.hdr.number, bytes, lines, sender_info, msg.to, toaddr);
/* Now lets mark this message for deletion without corrupting the index */
if((msg.hdr.netattr & MSG_KILLSENT) || msg.from_ext == NULL)
msg.hdr.attr|=MSG_DELETE;
msg.hdr.netattr|=MSG_SENT;
msg.hdr.netattr&=~MSG_INTRANSIT;
if((i=smb_updatemsg(&smb,&msg))!=SMB_SUCCESS)
lprintf(LOG_ERR,"%04d %s !ERROR %d (%s) deleting message #%u"
,sock, prot, i, smb.last_error, msg.hdr.number);
if(msg.hdr.auxattr&MSG_FILEATTACH)
delfattach(&scfg,&msg);
sockprintf(sock,prot,session,"QUIT");
sockgetrsp(sock,prot,session,"221", buf, sizeof(buf));
mail_close_socket(&sock, &session);
if(msg.from_agent==AGENT_PERSON && !(startup->options&MAIL_OPT_NO_AUTO_EXEMPT))
exempt_email_addr("SEND Auto-exempting", sender_info, toaddr);
/* Free up resources here */
if(mail!=NULL)
freemail(mail);
}
if(sock!=INVALID_SOCKET)
mail_close_socket(&sock, &session);
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_INFO, "0000 Waiting for %d child threads to terminate", protected_uint32_value(thread_count)-1);
while(protected_uint32_value(thread_count) > 1) {
mswait(100);
}
lprintf(LOG_INFO, "0000 Done waiting for child threads to terminate");
}
semfile_list_free(&recycle_semfiles);
semfile_list_free(&shutdown_semfiles);
if(mailproc_list!=NULL) {
for(i=0;i<mailproc_count;i++) {

rswindell
committed
if(mailproc_list[i].ar!=NULL)
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 */
listFree(¤t_logins);
listFree(¤t_connections);
if(protected_uint32_value(active_clients))
lprintf(LOG_WARNING,"!!!! Terminating with %d 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_exceeded)
sprintf(str+strlen(str),", %lu exceeded max", stats.connections_exceeded);
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)
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: 1.735 $", "%*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)