#ifndef LINT
static char *rcsid="clnt_udp.c,v 1.1.1.1 1999/08/12 19:36:46 woods Exp";
#endif

/*
	clnt_udp.c,v
	Revision 1.1.1.1  1999/08/12 19:36:46  woods
	whoson version 1.08
	
	Revision 1.10  1999/01/30 14:45:43  crosser
	def for buffers

	Revision 1.9  1999/01/30 14:41:05  crosser
	fix reporting addresses (used to overwrite static buffer)

	Revision 1.8  1998/07/28 17:51:53  crosser
	make 64bit architecure happy

	Revision 1.7  1998/07/26 14:06:40  crosser
	Add source address checking in UDP client

	Revision 1.6  1998/07/12 16:43:57  crosser
	Change protocol: responce now is terminated with empty line

	Revision 1.5  1998/07/05 00:26:18  crosser
	Change copyright

	Revision 1.4  1998/07/03 11:10:39  crosser
	fix include sys/time.h

	Revision 1.3  1998/07/02 18:01:15  crosser
	change error reporting to syslog

	Revision 1.2  1998/07/02 15:37:07  crosser
	check perms
	fix bug with excessive retries

	Revision 1.1  1998/07/01 21:55:16  crosser
	Initial revision

	Revision 1.2  1998/07/01 05:18:09  crosser
	minor warnings fix

	Revision 1.1  1998/07/01 05:01:22  crosser
	Initial revision

	Revision 1.1  1998/05/05 19:08:16  crosser
	Initial revision

*/

/*
	WHAT IS IT:
		Implementation of experimental "whoson" protocol
	AUTHOR:
		Eugene G. Crosser <crosser@average.org>
	COPYRIGHT:
		Public domain
*/

#include "config.h"

#include <sys/types.h>
#include <time.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <netdb.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "whoson.h"
#include "rtconfig.h"
#include "checkperm.h"
#include "clnt_common.h"
#include "report.h"

#define MAXREQL 1024

#define INITTIMEOUT 100000
#define MAXTRIES 5
#define MAXREREADS 20

struct _udp_serv_rec {
	int port;
	struct _perm *perms;
	char addr[16];
	unsigned long inittimeout;
	int maxtries;
};

int wso_udp_serv_connect(void *priv,char *buf)
{
	struct _udp_serv_rec *rec=(struct _udp_serv_rec *)(priv);
	struct sockaddr_in server,frominet;
	struct protoent *pe;
	int protonum;
	int fd;
	int len,slen;
	fd_set rfds,wfds,efds;
	struct timeval seltimer;
	unsigned long timeout;
	int tries,rereads,rc=0;
	char wbuf[MAXREQL];
	char addr1a[16],addr2a[16];

	memset((char *)&server,0,sizeof(server));
	server.sin_family = AF_INET;
	if ((pe=getprotobyname("udp"))) protonum=pe->p_proto;
	else protonum=17;
	server.sin_port=htons(rec->port);
	server.sin_addr.s_addr=inet_addr(rec->addr);
	if ((fd=socket(AF_INET,SOCK_DGRAM,protonum)) < 0) {
		ERRLOG((LOG_ERR,"[WHOSON] server socket: %m"))
		return -1;
	}

	strncpy(wbuf,buf,sizeof(wbuf)-1);
	wbuf[sizeof(wbuf)-1]='\0';
	timeout=rec->inittimeout;
	for (tries=0;tries<(rec->maxtries);tries++) {
		len=strlen(wbuf);
		if (sendto(fd,wbuf,len,0,(struct sockaddr *)&server,
				sizeof(server)) != len) {
			ERRLOG((LOG_ERR,"[WHOSON] sendto: %m"))
			close(fd);
			return -1;
		}

		rereads=0;
reread:

DPRINT(("udp waiting try=%d(%d max) timeout=%lu (init %lu)\n",
			tries,rec->maxtries,timeout,rec->inittimeout))

		seltimer.tv_sec=timeout/1000000L;
		seltimer.tv_usec=timeout%1000000L;
DPRINT(("seltimer.tv_sec=%lu, seltimer.tv_usec=%lu\n",
			(long)seltimer.tv_sec,
			(long)seltimer.tv_usec))
		FD_ZERO(&rfds);
		FD_ZERO(&wfds);
		FD_ZERO(&efds);
		FD_SET(fd,&rfds);

		rc=select(fd+1,&rfds,&wfds,&efds,&seltimer);
		if (rc < 0) {
			ERRLOG((LOG_ERR,"[WHOSON] select: %m"))
			close(fd);
			return -1;
		} else if (rc > 0) {
			slen=sizeof(frominet);
			if ((len=recvfrom(fd,buf,MAXREQL-1,0,
					(struct sockaddr *) &frominet, &slen)) < 0) {
				ERRLOG((LOG_ERR,"[WHOSON] recvfrom: %m"))
				close(fd);
				return -1;
			}
			buf[len]='\0';
			if (rec->perms) {
				if (wso_perm_check(rec->perms,
					ntohl(frominet.sin_addr.s_addr)))
					break;
			} else {
				if (memcmp(&frominet.sin_addr.s_addr,
					&server.sin_addr.s_addr,
					sizeof(frominet.sin_addr.s_addr)) == 0)
					break;
			}
			strcpy(addr1a,inet_ntoa(frominet.sin_addr));
			strcpy(addr2a,inet_ntoa(server.sin_addr));
			ERRLOG((LOG_ERR,"[WHOSON] ignore from %s (dest was %s)",
					addr1a,addr2a))
			if (++rereads < MAXREREADS)
				goto reread;
			else
				sprintf(buf,"*Ignoring from %s, sent to %s\r\n\r\n",
					addr1a,addr2a);
		}

		timeout *= 2;
	}

	if (rc == 0) {
		ERRLOG((LOG_ERR,"[WHOSON] udp excessive retries\n"))
		close(fd);
		return -1;
	}

	close(fd);
	return 0;
}

int wso_udp_serv_cfg_init(void **priv)
{
	struct _udp_serv_rec *rec;

	if ((rec=(struct _udp_serv_rec *)malloc
				(sizeof(struct _udp_serv_rec)))) {
		memset(rec,0,sizeof(struct _udp_serv_rec));
		(*priv)=(void*)rec;
		rec->inittimeout=INITTIMEOUT;
		rec->maxtries=MAXTRIES;
		return 0;
	} else {
		ERRLOG((LOG_ERR,"[WHOSON] allocating struct _udp_serv_rec: %m"))
		return 1;
	}
}

int wso_udp_serv_cfg_next(char *key,char *val,void **priv)
{
	struct _udp_serv_rec *rec=(struct _udp_serv_rec *)(*priv);
	struct _perm *perms;

	if (strcasecmp(key,"port") == 0) {
		rec->port=atoi(val);
	} else if (strcasecmp(key,"address") == 0) {
		strncpy(rec->addr,val,sizeof(rec->addr)-1);
		rec->addr[sizeof(rec->addr)-1]='\0';
	} else if (strcasecmp(key,"allow") == 0) {
		if ((perms=wso_perm_parse(1,val))) {
			perms->next=rec->perms;
			rec->perms=perms;
		} else {
			ERRLOG((LOG_ERR,"unparsable allow value \"%s\"\n",val))
		}
	} else if (strcasecmp(key,"deny") == 0) {
		if ((perms=wso_perm_parse(0,val))) {
			perms->next=rec->perms;
			rec->perms=perms;
		} else {
			ERRLOG((LOG_ERR,"unparsable deny value \"%s\"\n",val))
		}
	} else if (strcasecmp(key,"inittimeout") == 0) {
		rec->inittimeout=atol(val);
	} else if (strcasecmp(key,"maxtries") == 0) {
		rec->maxtries=atoi(val);
	} else {
		ERRLOG((LOG_ERR,"[WHOSON] bad keyword \"%s\"\n",key))
		return 1;
	}

	return 0;
}

int wso_udp_serv_cfg_end(void **priv)
{
	struct _udp_serv_rec *rec=(struct _udp_serv_rec *)(*priv);
	int d1,d2,d3,d4;

	if (rec->port == 0) {
		ERRLOG((LOG_ERR,"[WHOSON] bad port value \"%d\"\n",rec->port))
		return 1;
	} else if (rec->inittimeout == 0L) {
		ERRLOG((LOG_ERR,"[WHOSON] bad init timeout value \"%lu\"\n",
					rec->inittimeout))
		return 1;
	} else if (rec->maxtries == 0) {
		ERRLOG((LOG_ERR,"[WHOSON] bad init maxtries value \"%d\"\n",
					rec->maxtries))
		return 1;
	} else if (sscanf(rec->addr,"%d.%d.%d.%d",&d1,&d2,&d3,&d4) != 4) {
		ERRLOG((LOG_ERR,"[WHOSON] bad addr value \"%s\"\n",rec->addr))
		return 1;
	} else
		return 0;
}
