/* Miscellaneous Internet servers: discard, echo and remote
 * Copyright 1991 Phil Karn, KA9Q
 */
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "socket.h"
#include "proc.h"
#include "remote.h"
#include "smtp.h"
#include "tcp.h"
#include "commands.h"
#include "hardware.h"
#include "mailbox.h"

char *Rempass = "";	/* Remote access password */

static int chkrpass __ARGS((struct mbuf *bp));
static void discserv __ARGS((int s,void *unused,void *p));
static void echoserv __ARGS((int s,void *unused,void *p));

static int Rem = -1;
static int Sdisc = -1;
static int Secho = -1;

/* Start up TCP discard server */
int
dis1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct sockaddr_in lsocket;
	int s;

	if(Sdisc != -1){
		return 0;
	}
	psignal(Curproc,0); 	/* Don't keep the parser waiting */
	chname(Curproc,"Discard listener");

	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	if(argc < 2)
		lsocket.sin_port = IPPORT_DISCARD;
	else
		lsocket.sin_port = atoi(argv[1]);
	Sdisc = socket(AF_INET,SOCK_STREAM,0);
	bind(Sdisc,(char *)&lsocket,sizeof(lsocket));
	listen(Sdisc,1);
	for(;;){
		if((s = accept(Sdisc,NULLCHAR,(int *)NULL)) == -1)
			break;	/* Service is shutting down */

		if(availmem() < Memthresh){
			shutdown(s,1);
		} else
			/* Spawn a server */
			newproc("Discard server",576,discserv,s,NULL,NULL,0);
	}
	return 0;
}
static void
discserv(s,unused,p)
int s;
void *unused;
void *p;
{
	struct mbuf *bp;

	sockowner(s,Curproc);
	log(s,"open discard");
	while(recv_mbuf(s,&bp,0,NULLCHAR,NULL) > 0)
		free_p(bp);
	log(s,"close discard");
	close_s(s);
}
/* Stop discard server */
int
dis0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	close_s(Sdisc);
	Sdisc = -1;
	return 0;
}
/* Start up TCP echo server */
int
echo1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct sockaddr_in lsocket;
	int s;

	if(Secho != -1){
		return 0;
	}
	psignal(Curproc,0); 	/* Don't keep the parser waiting */
	chname(Curproc,"Echo listener");

	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	if(argc < 2)
		lsocket.sin_port = IPPORT_ECHO;
	else
		lsocket.sin_port = atoi(argv[1]);
	Secho = socket(AF_INET,SOCK_STREAM,0);
	bind(Secho,(char *)&lsocket,sizeof(lsocket));
	listen(Secho,1);
	for(;;){
		if((s = accept(Secho,NULLCHAR,(int *)NULL)) == -1)
			break;	/* Service is shutting down */

		if(availmem() < Memthresh){
			shutdown(s,1);
		} else
			/* Spawn a server */
			newproc("Echo server",2048,echoserv,s,NULL,NULL,0);
	}
	return 0;
}
static void
echoserv(s,unused,p)
int s;
void *unused;
void *p;
{
	struct mbuf *bp;

	sockowner(s,Curproc);
	log(s,"open echo");
	while(recv_mbuf(s,&bp,0,NULLCHAR,NULL) > 0)
		send_mbuf(s,bp,0,NULLCHAR,0);

	log(s,"close echo");
	close_s(s);
}
/* stop echo server */
int
echo0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	close_s(Secho);
	Secho = -1;
	return 0;
}
/* Start remote exit/reboot server */
int
rem1(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	struct sockaddr_in lsocket,fsock;
	int i;
	int command;
	struct mbuf *bp;
	int32 addr;

	if(Rem != -1){
		return 0;
	}
	psignal(Curproc,0);
	chname(Curproc,"Remote listener");
	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	if(argc < 2)
		lsocket.sin_port = IPPORT_REMOTE;
	else
		lsocket.sin_port = atoi(argv[1]);
	
	Rem = socket(AF_INET,SOCK_DGRAM,0);
	bind(Rem,(char *)&lsocket,sizeof(lsocket));
	for(;;){
		i = sizeof(fsock);
		if(recv_mbuf(Rem,&bp,0,(char *)&fsock,&i) == -1)
			break;
		command = PULLCHAR(&bp);

		switch(command){
#ifdef	MSDOS	/* Only present on PCs running MSDOS */
		case SYS_RESET:
			i = chkrpass(bp);
			log(Rem,"%s - Remote reset %s",
			 psocket((struct sockaddr *)&fsock),
			 i == 0 ? "PASSWORD FAIL" : "" );
			if(i != 0){
				iostop();
				sysreset();	/* No return */
			}
			break;
#endif
		case SYS_EXIT:
			i = chkrpass(bp);
			log(Rem,"%s - Remote exit %s",
			 psocket((struct sockaddr *)&fsock),
			 i == 0 ? "PASSWORD FAIL" : "" );
			if(i != 0){
				iostop();
				exit(0);
			}
			break;
		case KICK_ME:
			if(len_p(bp) >= sizeof(int32))
				addr = pull32(&bp);
			else
				addr = fsock.sin_addr.s_addr;
			kick(addr);
			smtptick((void *)addr);
			break;
		}
		free_p(bp);
	}
	close_s(Rem);
	Rem = -1;
	return 0;
}
/* Check remote password */
static int
chkrpass(bp)
struct mbuf *bp;
{
	char *lbuf;
	int16 len;
	int rval = 0;

	len = len_p(bp);
	if(strlen(Rempass) != len)
		return rval;
	lbuf = mallocw(len);
	pullup(&bp,lbuf,len);
	if(strncmp(Rempass,lbuf,len) == 0)
		rval = 1;
	free(lbuf);
	return rval;
}
int
rem0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	close_s(Rem);
	return 0;
}

