#ifndef LINT
static char *rcsid="$Id: clnt_unixd.c,v 1.7 2001/07/20 07:35:54 crosser Exp $";
#endif

/*
	$Log: clnt_unixd.c,v $
	Revision 1.7  2001/07/20 07:35:54  crosser
	more tuning of AF_UNIX address size, now works on FreeBSD
	
	Revision 1.6  2001/07/20 05:48:59  crosser
	address bug with AF_UNIX address size
	fix permissions of unix datagram socket (on Linux, honors umask)
	
	Revision 1.5  1999/10/03 21:18:03  crosser
	First (probably) working version with new run time config parser.
	Also added listenq parameter (backlog size for listen()) and
	renamed wtest to whoson (and with man page).  Release 2.00beta1.
	
	Revision 1.4  1999/08/19 17:22:15  crosser
	Move to new config scheme (does not work yet)
	
	Revision 1.3  1999/07/27 17:30:05  crosser
	fix names
	
	Revision 1.2  1999/01/30 16:44:17  crosser
	add unlink()

	Revision 1.1  1999/01/30 15:43:40  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 <sys/stat.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"

#include "rtc_begin.h"
#include "clnt_unixd_cfg.h"
#include "rtc_middle.h"
#include "clnt_unixd_cfg.h"
#include "rtc_end.h"

#define MAXREQL 1024

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

int wso_unixd_clnt_connect(void *priv,char *buf)
{
	wso_clnt_unixd_cfg *rec=(wso_clnt_unixd_cfg *)(priv);
	struct sockaddr_un server,me,frominet;
	int fd;
	int len,slen;
	fd_set rfds,wfds,efds;
	struct timeval seltimer;
	unsigned long timeout;
	int tries,rereads,rc=0;
	char wbuf[MAXREQL];
	mode_t savemask;

	memset((char *)&server,0,sizeof(server));
	server.sun_family = AF_UNIX;
	strncpy(server.sun_path,rec->port,sizeof(server.sun_path)-1);
	server.sun_path[sizeof(server.sun_path)-1]='\0';
	if ((fd=socket(AF_UNIX,SOCK_DGRAM,0)) < 0) {
		ERRLOG((LOG_ERR,"[WHOSON] socket: %m"))
		return -1;
	}
	/* This may sound stupid, but when using UNIX DGRAM socket, we
	   must explicitely bind() it.  "man 2 socket" says that a unique
	   address will be assigned automatically, but apparently this
	   ony works for INET sockets... */
	memset((char *)&me,0,sizeof(me));
	me.sun_family = AF_UNIX;
	if (tmpnam(me.sun_path) == NULL) {
		ERRLOG((LOG_ERR,"[WHOSON] cannot create temporary socket address: %m"))
		return -1;
	}
	savemask=umask(0);
	if (bind(fd,(struct sockaddr*)&me,sizeof(me)-sizeof(me.sun_path)+
						strlen(me.sun_path)+1) < 0) {
		(void)umask(savemask);
		ERRLOG((LOG_ERR,"[WHOSON] bind: %m"))
		return -1;
	}
	(void)umask(savemask);

	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)-sizeof(server.sun_path)+
					strlen(server.sun_path)+1) != len) {
			ERRLOG((LOG_ERR,"[WHOSON] sendto: %m"))
			close(fd);
			(void)unlink(me.sun_path);
			return -1;
		}

		rereads=0;
reread:

DPRINT(("unixd waiting try=%d(%d max) timeout=%lu (init %u)\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);
			(void)unlink(me.sun_path);
			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);
				(void)unlink(me.sun_path);
				return -1;
			}
			buf[len]='\0';
DPRINT(("recvfrom returned %d bytes: \"%s\"\n",len,buf))
			if (strcmp(frominet.sun_path,server.sun_path) == 0)
				break;
DPRINT(("did not pass address check: from %s, dest was %s\n",
				frominet.sun_path,server.sun_path))
			ERRLOG((LOG_ERR,"[WHOSON] ignore reply from from %s (dest was %s)",
					frominet.sun_path,server.sun_path))
			if (++rereads < MAXREREADS)
				goto reread;
			else
				sprintf(buf,"*Ignoring reply from %s, sent to %s\r\n\r\n",
					frominet.sun_path,server.sun_path);
		}

		timeout *= 2;
	}

	if (rc == 0) {
		ERRLOG((LOG_ERR,"[WHOSON] unixd excessive retries\n"))
		close(fd);
		(void)unlink(me.sun_path);
		return -1;
	}

	close(fd);
	(void)unlink(me.sun_path);
	return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1