/* Miscellaneous Internet servers: discard, echo and remote
 * Copyright 1991 Phil Karn, KA9Q
 */
#include "global.h"
#include "commands.h"
#ifndef MSDOS
#include <time.h>
#endif
#include "netuser.h"
#include "remote.h"
#include "smtp.h"
#include "tcp.h"
#include "nr4.h"
#include "mailbox.h"
#include "stats.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: smisc.c,v 1.31 1997/09/07 21:18:28 root Exp $";
#endif

char const *Rempass = "";	/* Remote access password */
char const *RemRTpass = "";	/* Remote access password for adding routes */

extern char *MMotd;

#ifdef ALLSERV
static void discserv (int s, void *unused, void *p);
static void echoserv (int s, void *unused, void *p);
static void quoteserv (int s, void *unused, void *p);
static void timeserv (int s, void *unused, void *p);
static void daytimeserv (int s, void *unused, void *p);
static void handleserver (int s, const char *name, int type);
#endif

static int chkrpass (struct mbuf * bp, int isaxip);

#ifdef TRACESERVER
static void traceserv (int s, void *unused, void *p);
#endif

#ifdef ALLSERV
char *getquote (void);
#endif

#ifdef TUTOR
static void tutorserver (int s, void *mode, void *p);
#endif

static const char deletenone[] = "The server is not active - nothing to stop!\n";
static const char openstr[] = "open %s";
static const char quotehdr[] = "\nQuote-of-the-Day at '%s' on %s\n";
static const char failedstr[] = "%s failed";
static const char closestr[] = "close %s";
static const char discardlistener[] = "Discard listener";
static const char discardserver[] = "Discard server";
static const char discardstr[] = "discard";
static const char timelistener[] = "Time listener";
static const char timeserver[] = "Time server";
static const char timestr[] = "time";
static const char daytimelistener[] = "Daytime listener";
static const char daytimeserver[] = "Daytime server";
static const char daytimestr[] = "daytime";
static const char echolistener[] = "Echo listener";
static const char echoserver[] = "Echo server";
static const char echostr[] = "echo";
static const char tracelistener[] = "Trace listener";
static const char traceserver[] = "Trace server";
static const char opentrace[] = "open trace";
static const char remotelistener[] = "Remote listener";
static const char passwdfail[] = "PASSWORD FAIL";
static const char remoteexit[] = "Remote exit %s %s";
static const char remotekick[] = "Remote kick by %s for host %s";
static const char remoteroute[] = "Remote route %s attempt from %s for '%s' %s";
static const char quotelistener[] = "Quote listener";
static const char quoteserver[] = "Quote server";
static const char quotestr[] = "quote";
static const char noopen[] = "Couldn't open '%s'\n";
static const char telnetstr[] = "telnet";

#ifdef MSDOS
static const char remotereset[] = "Remote reset %s %s";
#endif

#ifdef RLOGINSERV
static const char RLoginlistener[] = "RLogin listener";
static const char RLoginserver[] = "RLogin server";
#endif

#ifdef TUTOR
static const char tutopen[] = "open %s server";
static const char nonamestr[] = "noname";
static const char tutclose[] = "close %s server";
static const char tutorlistener[] = "Tutor listener";
static const char Tutorserver[] = "Tutor server";
static const char infolistener[] = "Info listener";
static const char Infoserver[] = "Info server";
static const char newslistener[] = "News listener";
static const char Newsserver[] = "News server";
#endif


static int Rem = -1;


void
server_disconnect_io (void)
{
	if (Curproc->input != -1)	{
		close_s (Curproc->input);
		Curproc->input = -1;
	}
	if (Curproc->output != -1)	{
		close_s (Curproc->output);
		Curproc->output = -1;
	}
}



int
deleteserver (int *mysocket)
{
	if (*mysocket != -1) {
		close_s (*mysocket);
		*mysocket = -1;
	} else
		tputs (deletenone);
	return 0;
}



/* Start up a TCP server */
int
installserver (
int argc,
char *argv[],
int *mysocket,
const char *listener,
int defport,
uint32 defaddress,
const char *procname,
void (*proc) (int, void *, void *),
unsigned int size,
void *parm
) {
struct sockaddr_in lsocket;
int s;

	if (*mysocket != -1)
		return 0;

	ksignal (Curproc, 0);	/* Don't keep the parser waiting */
	chname (Curproc, listener);

	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = defaddress;
	if (argc < 2)
		lsocket.sin_port = (int16) defport;
	else
		lsocket.sin_port = (int16) atoi (argv[1]);
	*mysocket = socket (AF_INET, SOCK_STREAM, 0);
	(void) bind (*mysocket, (char *) &lsocket, sizeof (lsocket));
	(void) listen (*mysocket, 1);
	server_disconnect_io ();
	for (;;) {
		if ((s = accept (*mysocket, NULLCHAR, (int *) NULL)) == -1)
			break;	/* Service is shutting down */

		/* Low mem check now done in tcpin.c - WG7J */
		/* Spawn a server */
		(void) newproc (procname, (unsigned int) size, proc, s, parm, NULL, 0);
	}
	return 0;
}



#ifdef RLOGINSERV
static int Srlogin = -1;
#endif

#ifdef TRACESERVER
static int Strace = -1;
#endif

#ifdef TUTOR
int Stutor = -1;
int Sinfo = -1;
int Snews = -1;
#endif

#ifdef ALLSERV
static int Sdisc = -1;
static int Stime = -1;
static int Squote = -1;
static int Sdaytime = -1;
static int Secho = -1;

#define DIFFTIME 2208988800LU

#define T_TIME 		0
#define T_DAYTIME	1
#define	T_DISCARD	2
#define T_ECHO		3
#define T_QUOTE		4



static void
handleserver (int s, const char *name, int type)
{
struct mbuf *bp;
time_t currtime;
char buf[32];
long adjust;
struct tm *tmp;

	(void) sockowner (s, Curproc);
	sprintf (buf, openstr, name);
	log (s, buf);
	switch (type) {
		case T_DISCARD:
			while (recv_mbuf (s, &bp, 0, NULLCHAR, NULL) > 0)
				free_p (bp);
			break;
		case T_QUOTE:
			{
			char buf2[512], *cp;

				cp = getquote ();
				currtime = time (&currtime);
				sprintf (buf2, quotehdr, Hostname, ctime (&currtime));
				bp = qdata ((unsigned char *) buf2, (int16) strlen (buf2));
				(void) send_mbuf (s, bp, 0, NULLCHAR, 0);
				bp = qdata ((unsigned char *) cp, (int16) strlen (cp));
				free (cp);
			}
			goto common;
		case T_TIME:
		case T_DAYTIME:
			currtime = time (&currtime);
			tmp = localtime (&currtime);
			if (type == T_DAYTIME)		{	/* daytime protocol */
				(void) strftime (buf, 32, "%a %b %d, %Y %X %Z\n", tmp);
				bp = qdata ((unsigned char *) buf, (int16) strlen(buf));
			} else {
				/*
				 * Change 1970 start time to 1900 start time,
				 * and put it in network order
				 */
#if defined(HAVE_TM_ISDST) && !defined(MSDOS)
				adjust = (timezone - (tmp->tm_isdst * 3600L)) / -1L;
#else
				adjust = tmp->tm_gmtoff;
#endif
				(void) put32 ((unsigned char *) buf, (uint32) (((uint32)currtime + DIFFTIME) + adjust));	/*lint !e737 */

				/* enqueue for transmission */
				bp = qdata ((unsigned char *) buf, sizeof (int32));
			}
common:
			if (send_mbuf (s, bp, 0, NULLCHAR, 0) == -1) {
				sprintf (buf, failedstr, name);
				log (s, buf);
			}
			break;
		case T_ECHO:
			while (recv_mbuf (s, &bp, 0, NULLCHAR, NULL) > 0)
				(void) send_mbuf (s, bp, 0, NULLCHAR, 0);
			break;
		default:
			break;
	}
	sprintf (buf, closestr, name);
	log (s, buf);
	close_s (s);
}



/* Start up TCP discard server */
int
dis1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Sdisc, discardlistener, IPPORT_DISCARD,
		INADDR_ANY, discardserver, discserv, 576, NULL));
}



static void
discserv (int s, void *unused OPTIONAL, void *p OPTIONAL)
{
#ifdef STATS_USE
	STATS_adduse (1);
	MiscUsers++;
#endif
	handleserver (s, discardstr, T_DISCARD);
#ifdef STATS_USE
	MiscUsers--;
#endif
}



/* Stop discard server */
int
dis0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Sdisc));
}



/* Start up TCP time server */
int
time1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Stime, timelistener, IPPORT_TIME,
		INADDR_ANY, timeserver, timeserv, 512, NULL));
}



static void
timeserv (int s, void *unused OPTIONAL, void *p OPTIONAL)
{
	(void) sockmode (s, SOCK_BINARY);
	handleserver (s, timestr, T_TIME);
}



/* Stop time server */
int
time0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Stime));
}



/* Start up TCP daytime server */
int
daytime1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Sdaytime, daytimelistener, IPPORT_DAYTIME,
		INADDR_ANY, daytimeserver, daytimeserv, 576, NULL));
}



static void
daytimeserv (int s, void *unused OPTIONAL, void *p OPTIONAL)
{
	handleserver (s, daytimestr, T_DAYTIME);
}



/* Stop time server */
int
daytime0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Sdaytime));
}



/* Start up TCP echo server */
int
echo1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Secho, echolistener, IPPORT_ECHO,
		INADDR_ANY, echoserver, echoserv, 2048, NULL));
}



static void
echoserv (int s, void *unused OPTIONAL, void *p OPTIONAL)
{
#ifdef STATS_USE
	STATS_adduse (1);
	MiscUsers++;
#endif
	handleserver (s, echostr, T_ECHO);
#ifdef STATS_USE
	MiscUsers--;
#endif
}



/* stop echo server */
int
echo0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Secho));
}
#endif /*ALLSERV*/




#if defined(TRACESERVER) || defined(ENCAP)
extern struct cmds Cmds[];
#endif


#ifdef TRACESERVER

/* Start up TCP trace server */
int
trace1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Strace, tracelistener, IPPORT_TRACE,
		INADDR_ANY, traceserver, traceserv, 1024, NULL));
}



/* Stop trace server */
int
trace0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Strace));
}



static void
traceserv (int s, void *unused OPTIONAL, void *p OPTIONAL)
{
#define CMDLINE 80
char *cmd;
int outsave, insave;

#ifdef STATS_USE
	STATS_adduse (1);
	MiscUsers++;
#endif
	(void) sockowner (s, Curproc);
	log (s, opentrace);
	outsave = Curproc->output;
	Curproc->output = s;
	insave = Curproc->input;
	Curproc->input = s;

	/* allocate the command buffer */
	cmd = mallocw (CMDLINE + 1);

	while (recvline (s, (unsigned char *) cmd, CMDLINE) > 0) {
		if (!strnicmp ("tr", cmd, 2))	/* only allow 'trace' command ! */
			(void) cmdparse (Cmds, cmd, NULL);
		if (!strnicmp ("ex", cmd, 2))
			break;
	}
	removetrace ();
	Curproc->input = insave;
	Curproc->output = outsave;
	log (s, "close trace");
#ifdef STATS_USE
	MiscUsers--;
#endif
	close_s (s);
}
#endif /* TRACESERVER */



/* Start remote exit/reboot server */
int
rem1 (int argc, char *argv[], void *p OPTIONAL)
{
struct sockaddr_in lsocket, fsock;
int i;
int command;
struct mbuf *bp, **bpp;
uint32 addr;
char temp[80], cmdbuf[128];
int len;
int action;
char *ab, *cp;
int badaddr;

	if (Rem != -1)
		return 0;
	ksignal (Curproc, 0);
	chname (Curproc, remotelistener);
	server_disconnect_io ();
	lsocket.sin_family = AF_INET;
	lsocket.sin_addr.s_addr = INADDR_ANY;
	if (argc < 2)
		lsocket.sin_port = IPPORT_REMOTE;
	else
		lsocket.sin_port = (int16) atoi (argv[1]);

	Rem = socket (AF_INET, SOCK_DGRAM, 0);
	(void) bind (Rem, (char *) &lsocket, sizeof (lsocket));
	for (;;) {
		i = sizeof (fsock);
		if (recv_mbuf (Rem, &bp, 0, (char *) &fsock, &i) == -1)
			break;
		bpp = &bp;
		command = PULLCHAR (bpp);

		switch (command) {
#ifdef	MSDOS			/* Only present on PCs running MSDOS */
			case SYS_RESET:
				i = chkrpass (bp, 0);
				log (Rem, remotereset, psocket ((struct sockaddr *) &fsock),
				     i == 0 ? passwdfail : "");
				if (i != 0) {
					/*Indicate a hardware reset*/
					where_outta_here (2, "rem1");
				}
				break;
#endif
			case SYS_EXIT:
				i = chkrpass (bp, 0);
				log (Rem, remoteexit, psocket ((struct sockaddr *) &fsock), (i == 0) ? passwdfail : "");
				if (i != 0) 	/*No reset*/
					where_outta_here (0, "rem1");
				break;
			case KICK_ME:
				if (len_p (bp) >= sizeof (int32))
					addr = pull32 (&bp);
				else
					addr = fsock.sin_addr.s_addr;

				/* get round strange printf bug */
				strncpy (temp, inet_ntoa (addr), 20);

				log (Rem, remotekick, psocket ((struct sockaddr *) &fsock), temp);
#ifdef NETROM
				donodetick ();	/* g3rra's idea!..hmmm */
#endif /* NETROM */
				(void) kick (addr);
				smtptick ((void *) addr);
				break;
#ifdef ENCAP
			case ROUTE_ADD:
			case ROUTE_DROP:
				action = (command == ROUTE_ADD) ? 1 : 0;

				/* first, extract the route string */
				len = PULLCHAR (bpp);	/* get the length of the name portion */
				if (len > 18)
					break;
				(void) pullup (bpp, (unsigned char *) temp, (int16) len);
				temp[len] = 0;

				/* now check the password */
				i = chkrpass (bp, 1);

				ab = psocket ((struct sockaddr *) &fsock);
				if ((cp = strchr (ab, ':')) != NULLCHAR)
					*cp = 0;
				badaddr = (strcmp (ab, "127.0.0.1")) ? 0 : 1;
				if (badaddr && action)
					tcmdprintf ("Attempt to add remote route to 127.0.0.1 - ignored\n");

				log (Rem, remoteroute, (action) ? "add" : "drop", ab, temp, (badaddr) ? "cannot route to 127.0.0.1" : (i == 0) ? passwdfail : "");

				if (strncmp (temp, "44.", 3) || !strncmp (ab, "44.", 3) || aton(temp) == 0)
					i = 0;		/* disable if route not for amprnet or from amprnet (error) */
	
				if (i != 0)	{
					/* add/drop the route */
					if (action)
						sprintf (cmdbuf, "route addp %s encap %s", temp, ab);
					else
						sprintf (cmdbuf, "route drop %s", temp);
					(void) cmdparse (Cmds, cmdbuf, NULL);
				}
				break;
#endif
			default:
				break;
		}
#if 0	/* deliberate memory leak */
		free_p (bp);
#endif
	}
	close_s (Rem);
	Rem = -1;
	return 0;
}



/* Check remote password */
static int
chkrpass (struct mbuf *bp, int isaxip)
{
char *lbuf;
int16 len;
int rval = 0;

	len = len_p (bp);
	if (!isaxip)	{
		if (strlen (Rempass) == 0 || strlen (Rempass) != len)
			return rval;
	} else {
		if (strlen (RemRTpass) == 0 || strlen (RemRTpass) != len)
			return rval;
	}
	lbuf = mallocw (len);
	(void) pullup (&bp, (unsigned char *) lbuf, len);
	if (strncmp ((!isaxip) ? Rempass : RemRTpass, lbuf, len) == 0)
		rval = 1;
	free (lbuf);
	return rval;
}



int
rem0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	close_s (Rem);
	return 0;
}



#ifdef ALLSERV
/* Start up TCP quote server */
int
quote1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Squote, quotelistener, IPPORT_QUOTE,
		INADDR_ANY, quoteserver, quoteserv, 1536, NULL));
}



static void
quoteserv (int s, void *unused OPTIONAL, void *p OPTIONAL)
{
	handleserver (s, quotestr, T_QUOTE);
}



/* Stop quote server */
int
quote0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Squote));
}



char *
getquote ()
{
char buf2[512];
char searchstr[8];
char buf3[4096];
char *cptr;
FILE *fp;
struct date dt;

	if (Squote == -1)
		return (NULLCHAR);
	if ((fp = fopen (Quotes, READ_TEXT)) == NULLFILE) {
		if (MMotd != NULLCHAR)
			return (strdup (MMotd));
		else
			return (NULLCHAR);
	}
	tnos_getdate (&dt);
	sprintf (searchstr, "[%-d]", dt.da_day);
	while (fgets (buf2, 512, fp) != NULLCHAR) {
		if (!strncmp (searchstr, buf2, strlen (searchstr)))
			break;
	}
	buf3[0] = 0;
	if ((cptr = strchr (buf2, '%')) != NULLCHAR) {
		rip (buf2);
		(void) fclose (fp);
		if ((fp = fopen (++cptr, READ_TEXT)) == NULLFILE) {
			tcmdprintf (noopen, &buf2[1]);
			return (NULLCHAR);
		}
	}
	while (fgets (buf2, 512, fp) != NULLCHAR) {
		if (*buf2 == '[')
			break;
		strcat (buf3, buf2);
	}
	(void) fclose (fp);
	return (strdup (buf3));
}
#endif /* ALLSERV */



/* Execute user quote command */
int
doquote (int argc OPTIONAL, char *argv[], void *p)
{
	free (argv[0]);
	argv[0] = strdup (telnetstr);
	if (argv[2])
		free (argv[2]);
	argv[2] = strdup ("17");
	if (!argv[1])
		argv[1] = strdup (Hostname);
	return dotelnet (3, argv, p);
}



#ifdef RLOGINSERV
/* Start up RLOGIN server */
int
rlogin1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Srlogin, RLoginlistener, IPPORT_RLOGIN,
		INADDR_ANY, RLoginserver, pbbs_incom, 2048, (void *) RLOGIN_LINK));
}



/* Stop RLOGIN server */
int
rlogin0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Srlogin));
}
#endif



#ifdef TUTOR
extern void tutorserv (const char *, struct mbx *, int, int, int);
static const char *tut[] = {"tutor", "info", "news"};



static void
tutorserver (int s, void *mode, void *p OPTIONAL)
{
int outsave, insave;

	(void) sockmode (s, SOCK_ASCII);
	(void) sockowner (s, Curproc);
	log (s, tutopen, tut[(int) mode]);
	outsave = Curproc->output;
	Curproc->output = s;
	insave = Curproc->input;
	Curproc->input = s;
	tutorserv (nonamestr, NULLMBX, (int) mode, 0, 1);
	Curproc->input = insave;
	Curproc->output = outsave;
	log (s, tutclose, tut[(int) mode]);
	close_s (s);
}



/* Start up TCP tutorial server */
int
tutor1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Stutor, tutorlistener, IPPORT_TUTOR,
		INADDR_ANY, Tutorserver, tutorserver, 2048, (void *) 0));
}



/* Stop tutorial server */
int
tutor0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Stutor));
}



/* Start up TCP tutorial server */
int
info1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Sinfo, infolistener, IPPORT_INFO,
		INADDR_ANY, Infoserver, tutorserver, 2048, (void *) 1));
}



/* Stop tutorial server */
int
info0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Sinfo));
}



/* Start up TCP news server */
int
news1 (int argc, char *argv[], void *p OPTIONAL)
{
	return (installserver (argc, argv, &Snews, newslistener, IPPORT_NEWS,
		INADDR_ANY, Newsserver, tutorserver, 2048, (void *) 2));
}



/* Stop news server */
int
news0 (int argc OPTIONAL, char *argv[] OPTIONAL, void *p OPTIONAL)
{
	return (deleteserver (&Snews));
}



static char WhichTutor = 0;



/* Execute user tutor command */
int
dotutor (int argc OPTIONAL, char *argv[], void *p)
{
char buf[6];
int i = WhichTutor;

	WhichTutor = 0;
	free (argv[0]);
	argv[0] = strdup (telnetstr);
	if (argv[2])
		free (argv[2]);
	sprintf (buf, "%-d", IPPORT_TUTOR - i);
	argv[2] = strdup (buf);
	if (!argv[1])
		argv[1] = strdup (Hostname);
	return dotelnet (3, argv, p);
}



/* Execute user info command */
int
doinfo (int argc, char *argv[], void *p)
{
	WhichTutor = 1;
	return (dotutor (argc, argv, p));
}



/* Execute user info command */
int
donews (int argc, char *argv[], void *p)
{
	WhichTutor = 2;
	return (dotutor (argc, argv, p));
}


#endif


