Skip to content
Snippets Groups Projects
mailsrvr.c 100 KiB
Newer Older
					lprintf("0000 !SEND ERROR %d obtaining MX records for %s from %s"
						,i,p,startup->dns_server);
					sprintf(err,"Error %d obtaining MX record for %s",i,p);
					bounce(&smb,&msg,err,FALSE);
					continue;
				}
				server=mx;
			}
			if((sock=mail_open_socket(SOCK_STREAM))==INVALID_SOCKET) {
				lprintf("0000 !SEND ERROR %d opening socket", ERROR_VALUE);
				continue;
			}

			memset(&addr,0,sizeof(addr));
			addr.sin_addr.s_addr = htonl(startup->interface_addr);
			addr.sin_family = AF_INET;

			if(startup->seteuid!=NULL)
				startup->seteuid(FALSE);
			i=bind(sock,(struct sockaddr *)&addr, sizeof(addr));
			if(startup->seteuid!=NULL)
			if(i!=0) {
				lprintf("%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++) {
				if(j) {
					if(startup->options&MAIL_OPT_RELAY_TX || !mx2[0])
						break;
					server=mx2;	/* Give second mx record a try */
				}
				
				lprintf("%04d SEND resolving SMTP hostname: %s", sock, server);
				ip_addr=resolve_ip(server);
				if(ip_addr==INADDR_NONE) {
					sprintf(err,"Failed to resolve SMTP hostname: %s",server);
					continue;
				}

				memset(&server_addr,0,sizeof(server_addr));
				server_addr.sin_addr.s_addr = ip_addr;
				server_addr.sin_family = AF_INET;
				server_addr.sin_port = htons(port);
				lprintf("%04d SEND connecting to port %u on %s [%s]"
					,server,inet_ntoa(server_addr.sin_addr));
				if((i=connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)))!=0) {
					i=ERROR_VALUE;
					lprintf("%04d !SEND ERROR %d connecting to SMTP server: %s"
					sprintf(err,"Error %d connecting to SMTP server: %s"
					continue;
				}
				success=TRUE;
			}
			if(!success) {	/* Failed to send, so bounce */
				bounce(&smb,&msg,err,FALSE);	
				continue;
			}

			lprintf("%04d SEND connected to %s",sock,server);

			/* HELO */
			if(!sockgetrsp(sock,"220",buf,sizeof(buf))) {
				sprintf(err,badrsp_err,server,buf,"220");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			sockprintf(sock,"HELO %s",startup->host_name);
			if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
				sprintf(err,badrsp_err,server,buf,"250");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			/* MAIL */
			if(msg.from_net.type==NET_INTERNET && msg.reverse_path!=NULL)
				SAFECOPY(fromaddr,msg.reverse_path);
				usermailaddr(&scfg,fromaddr,msg.from);
			if(fromaddr[0]=='<')
				sockprintf(sock,"MAIL FROM: %s",fromaddr);
			else
				sockprintf(sock,"MAIL FROM: <%s>",fromaddr);
			if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
				sprintf(err,badrsp_err,server,buf,"250");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			/* RCPT */
			if((p=strrchr((char*)msg.to_net.addr,'<'))!=NULL)
			sockprintf(sock,"RCPT TO: <%s>", toaddr);
			if(!sockgetrsp(sock,"25", buf, sizeof(buf))) {
				sprintf(err,badrsp_err,server,buf,"25*");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			/* DATA */
			sockprintf(sock,"DATA");
			if(!sockgetrsp(sock,"354", buf, sizeof(buf))) {
				sprintf(err,badrsp_err,server,buf,"354");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			lprintf("%04d SEND sending message text (%u bytes)"
				,sock, strlen(msgtxt));
			lines=sockmsgtxt(sock,&msg,msgtxt,-1);
			if(!sockgetrsp(sock,"250", buf, sizeof(buf))) {
				sprintf(err,badrsp_err,server,buf,"250");
				bounce(&smb,&msg,err,buf[0]=='5');
				continue;
			}
			lprintf("%04d SEND message transfer complete (%lu lines)", sock, lines);

			msg.hdr.attr|=MSG_DELETE;
			msg.idx.attr=msg.hdr.attr;
			if((i=smb_lockmsghdr(&smb,&msg))!=0) 
				lprintf("%04d !SEND ERROR %d (%s) locking message header #%lu"
					,i, smb.last_error, msg.hdr.number);
			if((i=smb_putmsg(&smb,&msg))!=0)
				lprintf("%04d !SEND ERROR %d (%s) deleting message #%lu"
					,i, smb.last_error, msg.hdr.number);
			if(msg.hdr.auxattr&MSG_FILEATTACH)
				delfattach(&scfg,&msg);
			smb_unlockmsghdr(&smb,&msg);

			/* QUIT */
			sockprintf(sock,"QUIT");
			sockgetrsp(sock,"221", buf, sizeof(buf));
			sock=INVALID_SOCKET;
		}				
		status(STATUS_WFC);
	}
	if(sock!=INVALID_SOCKET)

	smb_freemsgtxt(msgtxt);
	smb_freemsgmem(&msg);
	smb_close(&smb);

	if(active_sendmail!=0)
		active_sendmail=0,update_clients();
	thread_down();
	lprintf("0000 SendMail thread terminated (%u threads remain)", thread_count);
	if(server_socket!=INVALID_SOCKET) {
    	lprintf("%04d MAIL Terminate: closing socket",server_socket);
	    server_socket=INVALID_SOCKET;
    }
}

static void cleanup(int code)
{
	if(server_socket!=INVALID_SOCKET) {
		server_socket=INVALID_SOCKET;
	}

	if(pop3_socket!=INVALID_SOCKET) {
		pop3_socket=INVALID_SOCKET;
	}

	update_clients();

	if(WSAInitialized && WSACleanup()!=0) 
		lprintf("0000 !WSACleanup ERROR %d",ERROR_VALUE);
	status("Down");
    lprintf("#### Mail Server thread terminated (%u threads remain, %lu clients served)"
		,thread_count, served);
	if(startup!=NULL && startup->terminated!=NULL)
		startup->terminated(code);
}

const char* DLLCALL mail_ver(void)
{
	static char ver[256];
	char compiler[32];

	DESCRIBE_COMPILER(compiler);
	sscanf("$Revision$", "%*s %s", revision);

	sprintf(ver,"Synchronet Mail Server %s%s  SMBLIB %s  "
		"Compiled %s %s with %s"
#ifdef _DEBUG
		," Debug"
#else
		,""
#endif
		,smb_lib_ver()
		,__DATE__, __TIME__, compiler
		);

	return(ver);
}

void DLLCALL mail_server(void* arg)
	char			compiler[32];
	SOCKADDR_IN		server_addr;
	SOCKADDR_IN		client_addr;
	socklen_t		client_addr_len;
	SOCKET			client_socket;
	int				i;
	int				result;
	ulong			l;
	time_t			t;
	time_t			start;
	fd_set			socket_set;
	pop3_t*			pop3;
	smtp_t*			smtp;
	startup=(mail_startup_t*)arg;

    if(startup==NULL) {
    	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;
	}

	/* Setup intelligent defaults */
	if(startup->relay_port==0)				startup->relay_port=IPPORT_SMTP;
	if(startup->smtp_port==0)				startup->smtp_port=IPPORT_SMTP;
	if(startup->pop3_port==0)				startup->pop3_port=IPPORT_POP3;
	if(startup->rescan_frequency==0)		startup->rescan_frequency=3600;	/* 60 minutes */
	if(startup->max_delivery_attempts==0)	startup->max_delivery_attempts=50;
	if(startup->max_inactivity==0) 			startup->max_inactivity=120; /* seconds */
	if(startup->max_recipients==0) 			startup->max_recipients=100;
	if(startup->proc_cfg_file[0]==0)			
		sprintf(startup->proc_cfg_file,"%smailproc.cfg",scfg.ctrl_dir);
	startup->recycle_now=FALSE;
		memset(&scfg, 0, sizeof(scfg));
		lprintf("Synchronet Mail Server Revision %s%s"
		DESCRIBE_COMPILER(compiler);

		lprintf("Compiled %s %s with %s", __DATE__, __TIME__, compiler);
		lprintf("SMBLIB %s (format %x.%02x)",smb_lib_ver(),smb_ver()>>8,smb_ver()&0xff);
		t=time(NULL);
		lprintf("Initializing on %.24s with options: %lx"
			,CTIME_R(&t,str),startup->options);

		/* Initial configuration and load from CNF files */
		SAFECOPY(scfg.ctrl_dir,startup->ctrl_dir);
		lprintf("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("!ERROR %s",error);
			lprintf("!Failed to load configuration files");
			cleanup(1);
			return;
		}
			SAFECOPY(startup->host_name,scfg.sys_inetaddr);
		if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&MAIL_OPT_LOCAL_TIMEZONE)) {
			if(putenv("TZ=UTC0"))
				lprintf("!putenv() FAILED");
			tzset();

			if((t=checktime())!=0) {   /* Check binary time */
				lprintf("!TIME PROBLEM (%ld)",t);
				cleanup(1);
				return;
			}
		}

		if(startup->max_clients==0) {
			startup->max_clients=scfg.sys_nodes;
			if(startup->max_clients<10)
				startup->max_clients=10;
		}
		lprintf("Maximum clients: %u",startup->max_clients);
		lprintf("Maximum inactivity: %u seconds",startup->max_inactivity);
		active_clients=0;
		update_clients();
		/* open a socket and wait for a client */
		server_socket = mail_open_socket(SOCK_STREAM);
		if(server_socket == INVALID_SOCKET) {
			lprintf("!ERROR %d opening socket", ERROR_VALUE);
			cleanup(1);
			return;
		}
		lprintf("%04d SMTP socket opened",server_socket);

		/*****************************/
		/* Listen for incoming calls */
		/*****************************/
		memset(&server_addr, 0, sizeof(server_addr));

		server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
		server_addr.sin_family = AF_INET;
		server_addr.sin_port   = htons(startup->smtp_port);
		if(startup->seteuid!=NULL)
			startup->seteuid(FALSE);
		result = bind(server_socket,(struct sockaddr *)&server_addr,sizeof(server_addr));
		if(startup->seteuid!=NULL)
			startup->seteuid(TRUE);
		if(result != 0) {
			lprintf("%04d !ERROR %d (%d) binding SMTP socket to port %u"
				,server_socket, result, ERROR_VALUE, startup->smtp_port);
			lprintf("%04d %s",server_socket, BIND_FAILURE_HELP);
		lprintf("%04d SMTP socket bound to port %u"
			,server_socket, startup->smtp_port);
		result = listen (server_socket, 1);
		if(result != 0) {
			lprintf("%04d !ERROR %d (%d) listening on socket"
				,server_socket, result, ERROR_VALUE);
		if(startup->options&MAIL_OPT_ALLOW_POP3) {
			/* open a socket and wait for a client */
			pop3_socket = mail_open_socket(SOCK_STREAM);
			if(pop3_socket == INVALID_SOCKET) {
				lprintf("!ERROR %d opening POP3 socket", ERROR_VALUE);
				cleanup(1);
				return;
			}
			lprintf("%04d POP3 socket opened",pop3_socket);
			/*****************************/
			/* Listen for incoming calls */
			/*****************************/
			memset(&server_addr, 0, sizeof(server_addr));
			server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
			server_addr.sin_family = AF_INET;
			server_addr.sin_port   = htons(startup->pop3_port);
			if(startup->seteuid!=NULL)
				startup->seteuid(FALSE);
			result = bind(pop3_socket,(struct sockaddr *)&server_addr,sizeof(server_addr));
			if(startup->seteuid!=NULL)
				startup->seteuid(TRUE);
			if(result != 0) {
				lprintf("%04d !ERROR %d (%d) binding POP3 socket to port %u"
					,pop3_socket, result, ERROR_VALUE, startup->pop3_port);
				lprintf("%04d %s",pop3_socket,BIND_FAILURE_HELP);
				cleanup(1);
				return;
			}

			lprintf("%04d POP3 socket bound to port %u"
				,pop3_socket, startup->pop3_port);

			result = listen (pop3_socket, 1);

			if(result != 0) {
				lprintf("%04d !ERROR %d (%d) listening on POP3 socket"
					,pop3_socket, result, ERROR_VALUE);
				cleanup(1);
				return;
		/* signal caller that we've started up successfully */
		if(startup->started!=NULL)
    		startup->started();
		if(!(startup->options&MAIL_OPT_NO_SENDMAIL))
			_beginthread (sendmail_thread, 0, NULL);
		lprintf("%04d Mail Server thread started",server_socket);
		status(STATUS_WFC);

		if(initialized==0) {
			sprintf(path,"%smailsrvr.rec",scfg.ctrl_dir);
			t=fdate(path);
			if(t!=-1 && t>initialized)
				initialized=t;
		}
			if(!(startup->options&MAIL_OPT_NO_RECYCLE)) {
				sprintf(path,"%smailsrvr.rec",scfg.ctrl_dir);
				t=fdate(path);
				if(!active_clients && t!=-1 && t>initialized) {
					lprintf("0000 Recycle semaphore file (%s) detected",path);
					initialized=t;
					break;
				}
				if(!active_clients && startup->recycle_now==TRUE) {
					lprintf("0000 Recycle semaphore signaled");
					startup->recycle_now=FALSE;
					break;
				}
			/* now wait for connection */

			FD_ZERO(&socket_set);
			FD_SET(server_socket,&socket_set);
			high_socket_set=server_socket+1;
			if(startup->options&MAIL_OPT_ALLOW_POP3 
				&& pop3_socket!=INVALID_SOCKET) {
				FD_SET(pop3_socket,&socket_set);
				if(pop3_socket+1>high_socket_set)
					high_socket_set=pop3_socket+1;
			if((i=select(high_socket_set,&socket_set,NULL,NULL,&tv))<1) {
				if(i==0) {
					mswait(1);
					continue;
				}
				if(ERROR_VALUE==EINTR)
					lprintf("0000 Mail Server listening interrupted");
				else if(ERROR_VALUE == ENOTSOCK)
            		lprintf("0000 Mail Server sockets closed");
				else
					lprintf("0000 !ERROR %d selecting sockets",ERROR_VALUE);
				break;
			if(server_socket!=INVALID_SOCKET
				&& FD_ISSET(server_socket,&socket_set)) {
				client_addr_len = sizeof(client_addr);
				client_socket = accept(server_socket, (struct sockaddr *)&client_addr
        			,&client_addr_len);

				if(client_socket == INVALID_SOCKET)
					if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINVAL)
            			lprintf("0000 SMTP socket closed while listening");
					else
						lprintf("%04d !ERROR %d accept failed", server_socket, ERROR_VALUE);
					break;
				}
				if(startup->socket_open!=NULL)
					startup->socket_open(TRUE);
				sockets++;

				if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) {
					mail_close_socket(client_socket);
					continue;
				}

				if(active_clients>=startup->max_clients) {
					lprintf("%04d !MAXMIMUM CLIENTS (%u) reached, access denied"
						,client_socket, startup->max_clients);
					sockprintf(client_socket,"421 Maximum active clients reached, please try again later.");
					mswait(3000);
					mail_close_socket(client_socket);
					continue;
				}
				if((i=ioctlsocket(client_socket, FIONBIO, &l))!=0) {
					lprintf("%04d !ERROR %d (%d) disabling blocking on socket"
						,client_socket, i, ERROR_VALUE);
					mail_close_socket(client_socket);
					continue;
				}
				if((smtp=malloc(sizeof(smtp_t)))==NULL) {
					lprintf("%04d !ERROR allocating %u bytes of memory for smtp_t"
						,client_socket, sizeof(smtp_t));
					mail_close_socket(client_socket);
					continue;
				}

				smtp->socket=client_socket;
				smtp->client_addr=client_addr;
				_beginthread (smtp_thread, 0, smtp);
			if(pop3_socket!=INVALID_SOCKET
				&& FD_ISSET(pop3_socket,&socket_set)) {
				client_addr_len = sizeof(client_addr);
				client_socket = accept(pop3_socket, (struct sockaddr *)&client_addr
        			,&client_addr_len);
				if(client_socket == INVALID_SOCKET)
					if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINVAL)
            			lprintf("%04d POP3 socket closed while listening",pop3_socket);
					else
						lprintf("%04d !ERROR %d accept failed", pop3_socket, ERROR_VALUE);
					break;
				}
				if(startup->socket_open!=NULL)
					startup->socket_open(TRUE);
				sockets++;

				if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) {
					mail_close_socket(client_socket);
					continue;
				}

				if(active_clients>=startup->max_clients) {
					lprintf("%04d !MAXMIMUM CLIENTS (%u) reached, access denied"
						,client_socket, startup->max_clients);
					sockprintf(client_socket,"-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("%04d !ERROR %d (%d) disabling blocking on socket"
						,client_socket, i, ERROR_VALUE);
					sockprintf(client_socket,"-ERR System error, please try again later.");
					mswait(3000);
					mail_close_socket(client_socket);
					continue;
				}

				if((pop3=malloc(sizeof(pop3_t)))==NULL) {
					lprintf("%04d !ERROR allocating %u bytes of memory for pop3_t"
						,client_socket,sizeof(pop3_t));
					sockprintf(client_socket,"-ERR System error, please try again later.");
					mswait(3000);
					mail_close_socket(client_socket);
					continue;
				}
				pop3->socket=client_socket;
				pop3->client_addr=client_addr;

				_beginthread (pop3_thread, 0, pop3);
		if(active_clients) {
			lprintf("0000 Waiting for %d active clients to disconnect...", active_clients);
			start=time(NULL);
			while(active_clients) {
				if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
					lprintf("!TIMEOUT waiting for %u active clients ",active_clients);
					break;
				}
				mswait(100);
		if(sendmail_running) {
			mail_close_socket(server_socket);
			server_socket=INVALID_SOCKET; /* necessary to terminate sendmail_thread */
			mswait(2000);
		}
		if(sendmail_running) {
			lprintf("0000 Waiting for SendMail thread to terminate...");
			start=time(NULL);
			while(sendmail_running) {
				if(time(NULL)-start>TIMEOUT_THREAD_WAIT) {
					lprintf("!TIMEOUT waiting for sendmail thread to "
            			"terminate");
					break;
				}
				mswait(100);
		if(recycle_server) {
			lprintf("Recycling server...");