Skip to content
Snippets Groups Projects
mailsrvr.c 180 KiB
Newer Older
			/* reset recipient list */
			if((rcptlst=freopen(rcptlst_fname,"w+",rcptlst))==NULL) {
				lprintf(LOG_ERR,"%04d !SMTP ERROR %d re-opening %s"
				sockprintf(socket,session,sys_error);
			content_encoding=ENCODING_NONE;
			memset(mailproc_to_match,FALSE,sizeof(BOOL)*mailproc_count);
			sockprintf(socket,session,ok_rsp);
#if 0	/* No one uses this command */
		if(!strnicmp(buf,"VRFY",4)) {
			p=buf+4;
				sockprintf(socket,session,"550 No user specified.");
		/* Add to Recipient list */
		if(!strnicmp(buf,"RCPT TO:",8)) {

rswindell's avatar
rswindell committed
			if(state<SMTP_STATE_MAIL_FROM) {
				lprintf(LOG_WARNING,"%04d !SMTP MISSING 'MAIL' command",socket);
				sockprintf(socket,session, badseq_rsp);
rswindell's avatar
rswindell committed
			p=buf+8;
rswindell's avatar
rswindell committed
			SAFECOPY(rcpt_to,p);
			SAFECOPY(str,p);
			truncstr(str,">");	/* was truncating at space too */
			forward=FALSE;
			no_forward=FALSE;
			if(!strnicmp(p,FORWARD,strlen(FORWARD))) {
				forward=TRUE;		/* force forward to user's netmail address */
				p+=strlen(FORWARD);
			}
			if(!strnicmp(p,NO_FORWARD,strlen(NO_FORWARD))) {
				no_forward=TRUE;	/* do not forward to user's netmail address */
				p+=strlen(NO_FORWARD);
			}

				lprintf(LOG_NOTICE,"%04d !SMTP NO RECIPIENT SPECIFIED"
				sockprintf(socket,session, "500 No recipient specified");
			SAFECOPY(rcpt_addr,p);
			/* Check recipient counter */
rswindell's avatar
rswindell committed
			if(startup->max_recipients) {
				if(rcpt_count>=startup->max_recipients) {
					lprintf(LOG_NOTICE,"%04d !SMTP MAXIMUM RECIPIENTS (%d) REACHED"
						,socket, startup->max_recipients);
					SAFEPRINTF(tmp,"Maximum recipient count (%d)",startup->max_recipients);
					spamlog(&scfg, "SMTP", "REFUSED", tmp
						,host_name, host_ip, rcpt_addr, reverse_path);
					sockprintf(socket,session, "452 Too many recipients");
rswindell's avatar
rswindell committed
					stats.msgs_refused++;
					continue;
				}
				if(relay_user.number!=0 && !(relay_user.exempt&FLAG('M'))
					&& rcpt_count+(waiting=getmail(&scfg,relay_user.number,/* sent: */TRUE, /* SPAM: */FALSE)) > startup->max_recipients) {
rswindell's avatar
rswindell committed
					lprintf(LOG_NOTICE,"%04d !SMTP MAXIMUM PENDING SENT EMAILS (%u) REACHED for User #%u (%s)"
						,socket, waiting, relay_user.number, relay_user.alias);
					sockprintf(socket,session, "452 Too many pending emails sent");
rswindell's avatar
rswindell committed
					stats.msgs_refused++;
					continue;
				}
rswindell's avatar
rswindell committed
			if(relay_user.number && (relay_user.etoday+rcpt_count) >= scfg.level_emailperday[relay_user.level]
				&& !(relay_user.exempt&FLAG('M'))) {
rswindell's avatar
rswindell committed
				lprintf(LOG_NOTICE,"%04d !SMTP EMAILS PER DAY LIMIT (%u) REACHED FOR USER #%u (%s)"
					,socket, scfg.level_emailperday[relay_user.level], relay_user.number, relay_user.alias);
rswindell's avatar
rswindell committed
				SAFEPRINTF2(tmp,"Maximum emails per day (%u) for %s"
					,scfg.level_emailperday[relay_user.level], relay_user.alias);
				spamlog(&scfg, "SMTP", "REFUSED", tmp
					,host_name, host_ip, rcpt_addr, reverse_path);
				sockprintf(socket,session, "452 Too many emails today");
rswindell's avatar
rswindell committed
				stats.msgs_refused++;
				continue;
			}
				
			/* Check for SPAM bait recipient */
			if((spam_bait_result=findstr(rcpt_addr,spam_bait))==TRUE) {
				char	reason[256];
				SAFEPRINTF(reason,"SPAM BAIT (%s) taken", rcpt_addr);
				lprintf(LOG_NOTICE,"%04d SMTP %s by: %s"
					,socket, reason, reverse_path);
				if(relay_user.number==0) {
					if(dnsbl_result.s_addr==0						/* Don't double-filter */
rswindell's avatar
rswindell committed
						&& !spam_block_exempt)	{ 
						lprintf(LOG_NOTICE,"%04d !BLOCKING IP ADDRESS: %s in %s", socket, host_ip, spam_block);
						filter_ip(&scfg, "SMTP", reason, host_name, host_ip, reverse_path, spam_block);
						strcat(tmp," and BLOCKED");
					}
					spamlog(&scfg, "SMTP", tmp, "Attempted recipient in SPAM BAIT list"
						,host_name, host_ip, rcpt_addr, reverse_path);
					dnsbl_result.s_addr=0;
				}
				sockprintf(socket,session,ok_rsp);
				state=SMTP_STATE_RCPT_TO;
				continue;
			if(relay_user.number==0
				&& !chk_email_addr(socket,rcpt_addr,host_name,host_ip,rcpt_addr,reverse_path,"RECIPIENT")) {
				sockprintf(socket,session, "550 Unknown User: %s", rcpt_to);
rswindell's avatar
rswindell committed
				stats.msgs_refused++;
			if(relay_user.number==0 && dnsbl_result.s_addr && startup->options&MAIL_OPT_DNSBL_BADUSER) {
				lprintf(LOG_NOTICE,"%04d !SMTP REFUSED MAIL from blacklisted server (%u total)"
					,socket, ++stats.sessions_refused);
				SAFEPRINTF2(str,"Listed on %s as %s", dnsbl, inet_ntoa(dnsbl_result));
				spamlog(&scfg, "SMTP", "REFUSED", str, host_name, host_ip, rcpt_addr, reverse_path);
				sockprintf(socket,session
					,"550 Mail from %s refused due to listing at %s"
					,host_ip, dnsbl);
			if(spy==NULL 
				&& (trashcan(&scfg,reverse_path,"smtpspy")
					|| trashcan(&scfg,rcpt_addr,"smtpspy"))) {
				SAFEPRINTF(path,"%ssmtpspy.txt", scfg.logs_dir);
				spy=fopen(path,"a");
			}

			p=alias(&scfg,p,alias_buf);
			if(p==alias_buf) 
				lprintf(LOG_DEBUG,"%04d SMTP ADDRESS ALIAS: %s (for %s)"
rswindell's avatar
rswindell committed

			tp=strrchr(p,'@');
			if(cmd==SMTP_CMD_MAIL && tp!=NULL) {
deuce's avatar
deuce committed
				dest_port=inet_addrport(&server_addr);
				SAFECOPY(dest_host,tp+1);
rswindell's avatar
rswindell committed
				if(relay_user.number && scfg.total_faddrs) {
					char* ftn_tld = strstr(dest_host, FIDO_TLD);
					if(ftn_tld != NULL && ftn_tld[strlen(FIDO_TLD)] == 0) {
						fidoaddr_t faddr = scfg.faddr[0];
						faddr.point = 0;
						if((sscanf(dest_host,"p%hu.f%hu.n%hu.z%hu.fidonet"
							,&faddr.point
							,&faddr.node
							,&faddr.net
							,&faddr.zone)==4
							||
							sscanf(dest_host,"f%hu.n%hu.z%hu.fidonet"
							,&faddr.node
							,&faddr.net
							,&faddr.zone)==3
							) && faddr.zone) {

							lprintf(LOG_INFO,"%04d SMTP %s relaying to FidoNet address: %s (%s)"
								,socket, relay_user.alias, tp+1, smb_faddrtoa(&faddr, NULL));

							fprintf(rcptlst,"[%u]\n",rcpt_count++);
							fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
							fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_FIDO);
							fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),smb_faddrtoa(&faddr,NULL));

							sockprintf(socket,session,ok_rsp);
rswindell's avatar
rswindell committed
							state=SMTP_STATE_RCPT_TO;
							continue;
						}
					}
				}
				cp=strrchr(dest_host,':');
				if(cp!=NULL) {
					*cp=0;
					dest_port=atoi(cp+1);
				}
				SAFEPRINTF(domain_list,"%sdomains.cfg",scfg.ctrl_dir);
				if((stricmp(dest_host,scfg.sys_inetaddr)!=0
						&& stricmp(dest_host,startup->host_name)!=0
						&& findstr(dest_host,domain_list)==FALSE)
deuce's avatar
deuce committed
					|| dest_port!=inet_addrport(&server_addr)) {
					SAFEPRINTF(relay_list,"%srelay.cfg",scfg.ctrl_dir);
					if(relay_user.number==0 /* not authenticated, search for IP */
						&& startup->options&MAIL_OPT_SMTP_AUTH_VIA_IP) { 
						relay_user.number=userdatdupe(&scfg, 0, U_IPADDR, LEN_IPADDR, host_ip, /* del */FALSE, /* next */FALSE, NULL, NULL);
						if(relay_user.number) {
							getuserdat(&scfg,&relay_user);
							if(relay_user.laston < time(NULL)-(60*60))	/* logon in past hour? */
								relay_user.number=0;
						}
					} else
						getuserdat(&scfg,&relay_user);
					if(p!=alias_buf /* forced relay by alias */ &&
						(!(startup->options&MAIL_OPT_ALLOW_RELAY)
							|| relay_user.number==0
							|| relay_user.rest&(FLAG('G')|FLAG('M'))) &&
						!findstr(host_name,relay_list) && 
						!findstr(host_ip,relay_list)) {
						lprintf(LOG_WARNING,"%04d !SMTP ILLEGAL RELAY ATTEMPT from %s [%s] to %s"
							,socket, reverse_path, host_ip, p);
						SAFEPRINTF(tmp,"Relay attempt to: %s", p);
						spamlog(&scfg, "SMTP", "REFUSED", tmp, host_name, host_ip, rcpt_addr, reverse_path);
						if(startup->options&MAIL_OPT_ALLOW_RELAY)
							sockprintf(socket,session, "553 Relaying through this server "
							"Please authenticate before sending.");
							sockprintf(socket,session, "550 Relay not allowed.");
					if(relay_user.number==0)
						SAFECOPY(relay_user.alias,"Unknown User");

					lprintf(LOG_INFO,"%04d SMTP %s relaying to external mail service: %s"
						,socket, relay_user.alias, tp+1);
					fprintf(rcptlst,"[%u]\n",rcpt_count++);
					fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
					fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_INTERNET);
					fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),p);

					sockprintf(socket,session,ok_rsp);
rswindell's avatar
rswindell committed
					state=SMTP_STATE_RCPT_TO;
rswindell's avatar
rswindell committed
			if(tp!=NULL)
				*tp=0;	/* truncate at '@' */
			tp=strchr(p,'!');	/* Routed QWKnet mail in <qwkid!user@host> format */
			if(tp!=NULL) {
				*(tp++)=0;
				truncstr(tp,"\"");				/* Strip '"' */
				SAFECOPY(rcpt_addr,tp);
			FIND_ALPHANUMERIC(p);				/* Skip '<' or '"' */
rswindell's avatar
rswindell committed
			truncstr(p,"\"");
			p=alias(&scfg,p,name_alias_buf);
				lprintf(LOG_DEBUG,"%04d SMTP NAME ALIAS: %s (for %s)"
			/* Check if message is to be processed by an external mail processor */

				if(!mailproc_list[i].process_dnsbl && dnsbl_result.s_addr)
					continue;

				if(!mailproc_list[i].process_spam && spam_bait_result)
					continue;

				if(!chk_ar(&scfg,mailproc_list[i].ar,&relay_user,&client))
					continue;

				if(findstr_in_list(p, mailproc_list[i].to) || findstr_in_list(rcpt_addr, mailproc_list[i].to)) {
					mailproc_to_match[i]=TRUE;
rswindell's avatar
rswindell committed
				}
			mailproc_match=i;

			if(!strnicmp(p,"sub:",4)) {		/* Post on a sub-board */
				p+=4;
				for(i=0;i<scfg.total_subs;i++)
					if(!stricmp(p,scfg.sub[i]->code))
						break;
				if(i>=scfg.total_subs) {
					lprintf(LOG_NOTICE,"%04d !SMTP UNKNOWN SUB-BOARD: %s", socket, p);
					sockprintf(socket,session, "550 Unknown sub-board: %s", p);
				sockprintf(socket,session,ok_rsp);
			/* destined for a (non-passthru) external mail processor */
				fprintf(rcptlst,"[%u]\n",rcpt_count++);
				fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
#if 0	/* should we fall-through to the sysop account? */
				fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTEXT),1);
#endif
				lprintf(LOG_INFO,"%04d SMTP Routing mail for %s to External Mail Processor: %s"
					,socket, rcpt_addr, mailproc_list[mailproc_match].name);
				sockprintf(socket,session,ok_rsp);
			usernum=0;	/* unknown user at this point */
				SAFECOPY(qwkid,p);
				truncstr(qwkid,"/");
				/* Search QWKnet hub-IDs for route destination */
				for(i=0;i<scfg.total_qhubs;i++) {
					if(!stricmp(qwkid,scfg.qhub[i]->id))
				if(i<scfg.total_qhubs) {	/* found matching QWKnet Hub */
					lprintf(LOG_INFO,"%04d SMTP Routing mail for %s <%s> to QWKnet Hub: %s"
						,socket, rcpt_addr, p, scfg.qhub[i]->id);

					fprintf(rcptlst,"[%u]\n",rcpt_count++);
					fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
					fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_QWK);
					fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),p);
					sockprintf(socket,session,ok_rsp);
			if((p==alias_buf || p==name_alias_buf || startup->options&MAIL_OPT_ALLOW_RX_BY_NUMBER)
				&& isdigit((uchar)*p)) {
				usernum=atoi(p);			/* RX by user number */
				/* verify usernum */
				username(&scfg,usernum,str);
				if(!str[0] || !stricmp(str,"DELETED USER"))
					usernum=0;
				p=str;
			} else {
				/* RX by "user alias", "user.alias" or "user_alias" */
deuce's avatar
deuce committed
				usernum=smtp_matchuser(&scfg,p,startup->options&MAIL_OPT_ALLOW_SYSOP_ALIASES,FALSE);	

				if(!usernum) { /* RX by "real name", "real.name", or "sysop.alias" */
					
					/* convert "user.name" to "user name" */
					SAFECOPY(rcpt_name,p);
					for(tp=rcpt_name;*tp;tp++)	
						if(*tp=='.') *tp=' ';

					if(!stricmp(p,scfg.sys_op) || !stricmp(rcpt_name,scfg.sys_op))
					if(!usernum && scfg.msg_misc&MM_REALNAME)	/* RX by "real name" */
deuce's avatar
deuce committed
						usernum=smtp_matchuser(&scfg, p, FALSE, TRUE);	
					if(!usernum && scfg.msg_misc&MM_REALNAME)	/* RX by "real.name" */
deuce's avatar
deuce committed
						usernum=smtp_matchuser(&scfg, rcpt_name, FALSE, TRUE);	
			if(!usernum && startup->default_user[0]) {
				usernum=matchuser(&scfg,startup->default_user,TRUE /* sysop_alias */);
					lprintf(LOG_INFO,"%04d SMTP Forwarding mail for UNKNOWN USER to default user: '%s' #%u"
						,socket,startup->default_user,usernum);
					lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN DEFAULT USER: '%s'"
						,socket,startup->default_user);
			}

deuce's avatar
deuce committed
			if(usernum==UINT_MAX) {
rswindell's avatar
rswindell committed
				lprintf(LOG_INFO,"%04d SMTP Blocked tag: %s", socket, rcpt_to);
				sockprintf(socket,session, "550 Unknown User: %s", rcpt_to);
deuce's avatar
deuce committed
				continue;
			}
			if(!usernum) {
				lprintf(LOG_WARNING,"%04d !SMTP UNKNOWN USER: '%s'", socket, rcpt_to);
				sockprintf(socket,session, "550 Unknown User: %s", rcpt_to);
				continue;
			}
			user.number=usernum;
			if((i=getuserdat(&scfg, &user))!=0) {
				lprintf(LOG_ERR,"%04d !SMTP ERROR %d getting data on user #%u (%s)"
					,socket, i, usernum, p);
				sockprintf(socket,session, "550 Unknown User: %s", rcpt_to);
				continue;
			}
			if(user.misc&(DELETED|INACTIVE)) {
				lprintf(LOG_WARNING,"%04d !SMTP DELETED or INACTIVE user #%u (%s)"
					,socket, usernum, p);
				sockprintf(socket,session, "550 Unknown User: %s", rcpt_to);
rswindell's avatar
rswindell committed
			if(cmd==SMTP_CMD_MAIL) {
				if((user.rest&FLAG('M')) && relay_user.number==0) {
					lprintf(LOG_NOTICE,"%04d !SMTP M-restricted user #u (%s) cannot receive unauthenticated SMTP mail"
						,socket, user.number, user.alias);
					sockprintf(socket,session, "550 Closed mailbox: %s", rcpt_to);
rswindell's avatar
rswindell committed
					stats.msgs_refused++;
					continue;
				}
				if(startup->max_msgs_waiting && !(user.exempt&FLAG('W')) 
					&& (waiting=getmail(&scfg, user.number, /* sent: */FALSE, /* spam: */FALSE)) > startup->max_msgs_waiting) {
rswindell's avatar
rswindell committed
					lprintf(LOG_NOTICE,"%04d !SMTP User #%u (%s) mailbox (%u msgs) exceeds the maximum (%u) msgs waiting"
						,socket, user.number, user.alias, waiting, startup->max_msgs_waiting);
					sockprintf(socket,session, "450 Mailbox full: %s", rcpt_to);
rswindell's avatar
rswindell committed
					stats.msgs_refused++;
					continue;
				}
			}
			else if(cmd==SMTP_CMD_SEND) { /* Check if user online */
				for(i=0;i<scfg.sys_nodes;i++) {
					getnodedat(&scfg, i+1, &node, 0);
					if(node.status==NODE_INUSE && node.useron==user.number
						&& !(node.misc&NODE_POFF))
						break;
				}
				if(i>=scfg.sys_nodes) {
					lprintf(LOG_WARNING,"%04d !Attempt to send telegram to unavailable user #%u (%s)"
					sockprintf(socket,session,"450 User unavailable");
rswindell's avatar
rswindell committed
			if(cmd!=SMTP_CMD_MAIL)
			fprintf(rcptlst,"[%u]\n",rcpt_count++);
			fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENT),rcpt_addr);
			fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTEXT),user.number);

			/* Forward to Internet */
			tp=strrchr(user.netmail,'@');
				&& tp!=NULL && smb_netaddr_type(user.netmail)==NET_INTERNET 
				&& !strstr(tp,scfg.sys_inetaddr)) {
				lprintf(LOG_INFO,"%04d SMTP Forwarding to: %s"
					,socket, user.netmail);
				fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_INTERNET);
				fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),user.netmail);
				sockprintf(socket,session,"251 User not local; will forward to %s", user.netmail);
			} else { /* Local (no-forward) */
				if(routed) { /* QWKnet */
					fprintf(rcptlst,"%s=%u\n",smb_hfieldtype(RECIPIENTNETTYPE),NET_QWK);
					fprintf(rcptlst,"%s=%s\n",smb_hfieldtype(RECIPIENTNETADDR),user.alias);
				}						
				sockprintf(socket,session,ok_rsp);
rswindell's avatar
rswindell committed
			state=SMTP_STATE_RCPT_TO;
		/* Message Data (header and body) */
		if(!strnicmp(buf,"DATA",4)) {
rswindell's avatar
rswindell committed
			if(state<SMTP_STATE_RCPT_TO) {
				lprintf(LOG_WARNING,"%04d !SMTP MISSING 'RCPT TO' command", socket);
				sockprintf(socket,session, badseq_rsp);
				fclose(msgtxt), msgtxt=NULL;
			if((msgtxt=fopen(msgtxt_fname,"w+b"))==NULL) {
				lprintf(LOG_ERR,"%04d !SMTP ERROR %d opening %s"
					,socket, errno, msgtxt_fname);
				sockprintf(socket,session, insuf_stor);
			/* These vars are potentially over-written by parsing an RFC822 header */
			/* get sender_addr */
			truncstr(sender_addr,">");
			if(truncstr(sender,"@")==NULL)
			sockprintf(socket,session, "354 send the mail data, end with <CRLF>.<CRLF>");
			if(telegram)
				state=SMTP_STATE_DATA_BODY;	/* No RFC headers in Telegrams */
			else
				state=SMTP_STATE_DATA_HEADER;
			lprintf(LOG_INFO,"%04d SMTP Receiving %s message from: %s to %s"
				,socket, telegram ? "telegram":"mail", reverse_path, rcpt_addr);
		if(!stricmp(buf,"STARTTLS")) {
			if (get_ssl_cert(&scfg, NULL) == -1) {
				lprintf(LOG_ERR, "Unable to get certificate");
				sockprintf(socket, session, "454 TLS not available");
				continue;
			}
			if (cryptCreateSession(&session, CRYPT_UNUSED, CRYPT_SESSION_SSL_SERVER) != CRYPT_OK) {
				lprintf(LOG_ERR, "Unable to create TLS session");
				sockprintf(socket, session, "454 TLS not available");
				continue;
			}
			if (cryptSetAttribute(session, CRYPT_SESSINFO_SSL_OPTIONS, CRYPT_SSLOPTION_DISABLE_CERTVERIFY) != CRYPT_OK) {
				lprintf(LOG_ERR, "Unable to disable certificate verification");
				cryptDestroySession(session);
				session = -1;
				sockprintf(socket, session, "454 TLS not available");
				continue;
			}
			if (cryptSetAttribute(session, CRYPT_SESSINFO_PRIVATEKEY, scfg.tls_certificate) != CRYPT_OK) {
				lprintf(LOG_ERR, "Unable to set private key");
				cryptDestroySession(session);
				session = -1;
				sockprintf(socket, session, "454 TLS not available");
				continue;
			}
			nodelay = TRUE;
			setsockopt(socket,IPPROTO_TCP,TCP_NODELAY,(char*)&nodelay,sizeof(nodelay));
			nb=0;
			ioctlsocket(socket,FIONBIO,&nb);
			if ((rd = cryptSetAttribute(session, CRYPT_SESSINFO_NETWORKSOCKET, socket)) != CRYPT_OK) {
				lprintf(LOG_ERR, "Unable to set network socket");
				cryptDestroySession(session);
				session = -1;
				sockprintf(socket, session, "454 TLS not available");
				continue;
			}
			sockprintf(socket, -1, "220 Ready to start TLS");
			if (cryptSetAttribute(session, CRYPT_SESSINFO_ACTIVE, 1) != CRYPT_OK) {
				lprintf(LOG_ERR, "Unable to set session active");
				cryptDestroySession(session);
				mail_close_socket(socket);
				thread_down();
				protected_uint32_adjust(&active_clients, -1);
				update_clients();
				free(mailproc_to_match);
			}
			if (startup->max_inactivity) {
				if (cryptSetAttribute(session, CRYPT_OPTION_NET_READTIMEOUT, startup->max_inactivity) != CRYPT_OK) {
					lprintf(LOG_ERR, "Unable to set max inactivity");
					cryptDestroySession(session);
					mail_close_socket(socket);
					thread_down();
					protected_uint32_adjust(&active_clients, -1);
					update_clients();
					free(mailproc_to_match);
					break;
				}
			}
			continue;
		}
		sockprintf(socket,session,"500 Syntax error");
		lprintf(LOG_WARNING,"%04d !SMTP UNSUPPORTED COMMAND: '%s'", socket, buf);
			lprintf(LOG_WARNING,"%04d !TOO MANY INVALID COMMANDS (%u)",socket,badcmds);
	}

	/* Free up resources here */
	smb_freemsgmem(&msg);

		fclose(msgtxt);
	if(!(startup->options&MAIL_OPT_DEBUG_RX_BODY))
		remove(msgtxt_fname);
	if(rcptlst!=NULL)
		fclose(rcptlst);
	if(spy!=NULL)
		fclose(spy);
	js_cleanup(js_runtime, js_cx, &js_glob);
	protected_uint32_adjust(&active_clients, -1);
	client_off(socket);

	{
		int32_t remain = thread_down();
		lprintf(LOG_INFO,"%04d SMTP Session thread terminated (%u threads remain, %lu clients served)"
			,socket, remain, ++stats.smtp_served);
	}
deuce's avatar
deuce committed
	free(mailproc_to_match);
	if (session != -1)
		cryptDestroySession(session);
	/* Must be last */
	mail_close_socket(socket);
BOOL bounce(SOCKET sock, smb_t* smb, smbmsg_t* msg, char* err, BOOL immediate)
	char		msgid[256];
	lprintf(LOG_WARNING,"%04d !SEND Delivery attempt #%u FAILED (%s) for message #%lu from %s to %s"
		,sock
		,msg->hdr.delivery_attempts
		,err
		,msg->hdr.number
		,msg->from
		,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"
			,sock, i, smb->last_error);
	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);
		lprintf(LOG_ERR,"%04d !SEND BOUNCE ERROR %d (%s) deleting message"
			,sock, i, smb->last_error);
rswindell's avatar
rswindell committed
	if(msg->from_agent==AGENT_SMTPSYSMSG	/* don't bounce 'bounce messages' */
		|| (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);
	newmsg.hfield=NULL;
	newmsg.hfield_dat=NULL;
	newmsg.total_hfields=0;
	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);
rswindell's avatar
rswindell committed
	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 */
		SAFEPRINTF(attempts,"after %u attempts", msg->hdr.delivery_attempts);
	SAFEPRINTF2(str,"%s reporting delivery failure of message %s"
	smb_hfield_str(&newmsg, SMB_COMMENT, str);
	SAFEPRINTF2(str,"from %s to %s\r\n"
		,(char*)msg->to_net.addr);
	smb_hfield_str(&newmsg, SMB_COMMENT, str);
	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 !BOUNCE ERROR %d (%s) adding message header"
			,sock,i,smb->last_error);
		lprintf(LOG_WARNING,"%04d !SEND Delivery failure notification (message #%ld) 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 #%lu"
			,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 #%lu"
			,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;

deuce's avatar
deuce committed
	sprintf(dns_server,"%.*s",(int)len,startup->dns_server);
		if((list=getNameServerList())!=NULL) {
			if((count=strListCount(list))>0) {
deuce's avatar
deuce committed
				sprintf(dns_server,"%.*s",(int)len,list[xp_random(count)]);
				lprintf(LOG_DEBUG,"0000 SEND using auto-detected DNS server address: %s"
					,dns_server);
			}
			freeNameServerList(list);
		}
	}
}

deuce's avatar
deuce committed
/* TODO: IPv6 etc. */
#ifdef __BORLANDC__
#pragma argsused
#endif
static void sendmail_thread(void* arg)
{
	int			i,j;
	char		to[128];
	char		mx[128];
	char		mx2[128];
	char		buf[512];
	char		fromaddr[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*		server;
	char*		msgtxt=NULL;
	char*		p;
	ulong		last_msg=0;
	ulong		ip_addr;
	ulong		dns;
	SOCKET		sock=INVALID_SOCKET;
	SOCKADDR_IN	addr;
deuce's avatar
deuce committed
	union xp_sockaddr	server_addr;
deuce's avatar
deuce committed
	char		server_ip[INET6_ADDRSTRLEN];
	time_t		last_scan=0;
	smb_t		smb;
	smbmsg_t	msg;
	BOOL		sending_locally=FALSE;
	CRYPT_SESSION session = -1;
	BOOL nodelay=TRUE;
	ulong nb = 0;
	BOOL		tls_failed;
	lprintf(LOG_INFO,"0000 SendMail thread started");

	memset(&msg,0,sizeof(msg));
	memset(&smb,0,sizeof(smb));

	listInit(&failed_server_list, /* flags: */0);

deuce's avatar
deuce committed
	while((!terminated) && !terminate_sendmail) {
		if(startup->options&MAIL_OPT_NO_SENDMAIL) {
			sem_trywait_block(&sendmail_wakeup_sem,1000);
			active_sendmail=0, update_clients();
		if (session != -1) {
			cryptDestroySession(session);
			session = -1;
		}
		if(sock!=INVALID_SOCKET) {
			sock=INVALID_SOCKET;
		}

		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)
		smb_unlocksmbhdr(&smb);
		if(smb.status.last_msg==last_msg && time(NULL)-last_scan<startup->rescan_frequency)
			continue;
		lprintf(LOG_DEBUG, "0000 SEND last_msg=%u, smb.status.last_msg=%u, elapsed=%u"
			,last_msg, smb.status.last_msg, time(NULL)-last_scan);
		last_msg=smb.status.last_msg;
		last_scan=time(NULL);
		mail=loadmail(&smb,&msgs,/* to network */0,MAIL_YOUR,0);
				active_sendmail=0, update_clients();
deuce's avatar
deuce committed
			if(terminated || terminate_sendmail)	/* server stopped */
			if (session != -1) {
				cryptDestroySession(session);
				session = -1;
			}
			if(sock!=INVALID_SOCKET) {
				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) {
				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) {
			if(!(startup->options&MAIL_OPT_SEND_INTRANSIT) && msg.hdr.netattr&MSG_INTRANSIT) {
rswindell's avatar
rswindell committed
				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
			SAFEPRINTF2(str,"Sending (%u of %u)", u+1, msgs);
#ifdef _WIN32
			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);
			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);
			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;