static char IDudpserver[] = "@(#)udpserver 07/10/15";
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* udpserver [-/+options] [port] [host]
*
* This program creates a datagram socket, binds an address to it, then
* reads  from  the socket.  It takes a hostname and port number on its
* command line; the defaults are `hostname` and 2345.
*
* Options may start with '+' or '-' to enable/disable  features;  with
* the  options  for which this doesn't make sense, either may be used.
* An option letter may be upper or lower case.
*
* The options recognized are:
*
* +e
*   echo:  Send each message back to the client.
*
* -n<n>
*   number of messages: accept only <n> messages and exit; the default
*   is 100.
*
* -s<s>
*   sleep time:  wait <s> seconds between messages; the default is  1.
*   This is a debug option, and limits tcpserver's responsiveness.
*
* -t<t>
*   timeout: waits <t> seconds for replies and then exits; the default
*   is 60. This is intended as a debugging option; a real server would
*   rarely do this.
*
* +x
*   exec:   Send each message to /bin/sh for execution.  The process's
*   stdio files will be attached to the TCP socket, so the client will
*   receive  the output.  This option is a security risk, as it allows
*   the client to exec any program  in  the  system  with  tcpserver's
*   permissions.   It  is  for  demo and prototype purposes only.  You
*   should NEVER leave tcpserver running  with  this  option  for  any
*   length of time.
*/
#include "V.h"
#include "sys_socket.h"
#include "sys_in.h"
#include "sys_netdb.h"

#define BUF 1024
#define MAXNAME 256

char  buf[BUF];
Flag  echo = 0;		/* Echo messages back to client */
char *hostname = 0;	/* The hostname to bind to */
char *ifmt = "%d";	/* How to decode numbers */
int   msgs = 0;		/* Number of messages received so far */
int   msglim = 100;	/* Number of messages to read before exiting */
TIMV  tmout = {1};	/* How long to wait in select() */
char *portname = 0;	/* Symbolic version of port number */
int   port = 0;		/* UDP port number */
U32   now, then;	/* Timestamps */
U32   rmask = 0;	/* Select read mask */
int   timelimit = 60;	/* Seconds to wait for messages */
SKin  sndr = {0};	/* Address of sender */
int   sndl = 0;		/* Actual size of sndr */
int   wsleep = 1;	/* Seconds between writes */
Flag  exec = 0;		/* Attempt to exec messages */

main(ac,av)
	int   ac;
	char**av;
{	int   r=0;	/* Result (exit value) */
	char *p;
	int   c0, c1;
	int   a, i, n, sock;
	int   length;
	SKin  addr;
	HOST *hp, *gethostbyname();

	ac = Vinit(ac,av);

	for (a=1; a<ac; a++) {
		V3 "Arg %2d: \"%s\"",a,av[a] D;
		switch(c0 = av[a][0]) {
		case '-':
		case '+':
			V3 "Opt %2d: \"%s\"",a,av[a] D;
			switch (c1 = av[a][1]) {
			case 'e':
			case 'E':
				echo = (c0 == '+');
				V2 "Echo %sabled.",echo?"en":"dis" D;
				break;
			case 'n':
			case 'N':
				if (sscanf(av[a]+2,ifmt,&msglim) < 1)
					++n;
				V2 "Accepting %d messages.",msglim D;
				break;
			case 's':
			case 'S':
				if (sscanf(av[a]+2,ifmt,&wsleep) < 1)
					++n;
				V2 "Wait %d seconds between messages.",wsleep D;
				break;
			case 't':
			case 'T':
				if (sscanf(av[a]+2,ifmt,&timelimit) < 1)
					++n;
				V2 "Wait %d seconds for replies.",timelimit D;
				break;
			case 'x':
			case 'X':
				exec = (c0 == '+');
				V2 "Exec %sabled.",exec?"en":"dis" D;
				break;
			default:
				V1 "Arg %2d: \"%s\" ignored.",a,av[a] D;
				break;
			}
			break;
		default:
			if (!portname && (sscanf(av[a],ifmt,&port) > 0)) {
				portname = av[a];
				V2 "Port: \"%s\"",portname D;
				V3 "port: %d.",port D;
			} else
			if (!hostname) {
				hostname = av[a];
				V2 "Host: \"%s\"",hostname D;
			} else {
				V1 "Arg %2d: \"%s\" ignored.",a,av[a] D;
			}
			break;
		}
	}
	if (!port) port = 2345;
/*
* Create socket from which to read.
*/
	sock = Socket(AF_INET, SOCK_DGRAM, 0);
	if (sock < 0) {
		V1 "Can't get DGRAM socket [Err %d=%s=%s]",Errinfo D;
		r = 2;
		Fail;
	}
/*
* Create address with wildcards.
*/
	if (!hostname) {
		if (!(hostname = (char*)MallocM(MAXNAME,"hostname"))) {
			r = errno;
			Fail;
		}
		if (Gethostname(hostname, MAXNAME)) {
			r = errno;
			V1 "Can't get hostname [Err %d=%s=%s]",Errinfo D;
			Fail;
		}
	}
	V3 "Host: \"%s\"",hostname D;
	hp = Gethostbyn(hostname);
	Bcopy(hp->h_addr,&(addr.sin_addr.s_addr),hp->h_length);
	addr.sin_family = AF_INET;
	addr.sin_port = htons(port);
	if (Vlvl > 3) DmpSKin(&addr);
	H6(&addr,sizeof(addr),"addr");
	if (Bind(sock,&addr,sizeof(addr)) < 0) {
		V1 "Can't bind to address %s.%d [Err %d=%s=%s]",
			ipdot(&addr.sin_addr),
			ntohs( addr.sin_port),
			Errinfo D;
		r = 2;
		Fail;
	}
/*
* Find assigned port value and print it out.
*/
	length = sizeof(addr);
	if (Getsockname(sock, &addr, &length)) {
		V1 "Can't get socket address for file %d [Err %d=%s=%s]",sock,Errinfo D;
		r = 2;
		Fail;
	}
	H6(&addr,sizeof(addr),"addr");
	printf("UDP socket has address %s port %d.\n",
		ipdot(&addr.sin_addr),
		ntohs( addr.sin_port));
/*
* Read from the socket
*/
	then = UnixTime(0);
loop:
	now = UnixTime(0);
	if (timelimit && now - then > timelimit) {
		V2 "Time limit of %d seconds expired.",timelimit D;
		Done;
	}
	rmask = 1 << sock;
	if (Select(sock+1, &rmask,0,0,0) > 0) {
		V3 "Got input, rmask=%08X",rmask D;
		sndl = sizeof(sndr);
		if ((n = Recvfrom(sock, buf, BUF, 0, &sndr, &sndl)) > 0) {
			buf[n] = 0;
			V2 "Recv UDP %d bytes file %d addr %s \"%s\"\n",
				n, sock,
				SymSockAddr(&sndr),
				Dsps(buf,n) D;
			H3(buf,n,"Got");
			if (Vlvl)
				fprintf(stderr,"Got: \"%s\"\n",Dsps(buf,n));
			if (exec) {
				V3 "Exec \"%s\" ...",Dsps(buf,n) D;
				system(buf);
			}
			if (echo) {
				V3 "Echo message back to sender ..." D;
				H3(buf,n,"Put");
				if (Sendto(sock, buf, n, 0, &sndr, sndl) < 0) {
					V2 "Can't send reply to %d.%d [Err %d=%s=%s]",
						ipdot(&sndr.sin_addr),
						ntohs( sndr.sin_port),
						Errinfo D;
				}
				if (Vlvl)
					fprintf(stderr,"Put: \"%s\"\n",Dsps(buf,n));
			}
			if (++ msgs > msglim) {
				V2 "Message limit of %d reached, exiting.",msglim D;
				Done;
			}
			Loop;
		}
	}
	V2 "Can't read from socket [Err %d=%s=%s]",Errinfo D;
fail:
done:
	Close(sock);
	Exit(r);
}
DmpSKin(p)
	SKin *p;
{
	V1 "\tfmly = %d.",p->sin_family D;
	V1 "\taddr = %08X=%s.",p->sin_addr,ipdot(&p->sin_addr) D;
	V1 "\tport = %04X=%d.",p->sin_port,ntohs(p->sin_port) D;
}

