/* THE REALAUDIO FIREWALL PROXY KIT 2.0 BETA 2 */
/* This file: $Date: 1996/04/17 18:25:19 $ $Revision: 1.11 $ */
/*
 * 
 * Copyright (c) 1996 Progressive Networks, Inc.
 * 
 * By clicking on the "Accept" button or opening the package, you are
 * consenting to be bound by this Agreement.  If you do not agree to all of
 * the terms of this agreement, click the "Do Not Accept" button and, if
 * you received the Software by package, return the product to the place of
 * purchase for a full refund.
 * 
 * 1. GRANT OF LICENSE.
 * 
 *  
 * Progressive Networks, Inc. (PN) grants to Recipient a limited,
 * non-exclusive, royalty-free license to use, modify, or redistribute the
 * Software without a fee, subject to the conditions stated below.
 * 
 * All other rights are reserved to PN. 
 * 
 * 
 * 2.  CONDITIONS
 * The above grant of license is subject to the following conditions:
 * 
 *        A) The copyright notice ("Copyright (c) 1996 Progressive
 *           Networks, Inc. ")  and this license appear on all copies of the
 *           Software and/or any derivatives of the Software.
 * 
 *        B) Recipient uses the Software or any derivatives only to build a
 *           firewall proxy for use with RealAudio products from PN.
 * 
 *        C) Recipient acknowledges and agrees that PN is and shall be the
 *           exclusive owner of all right, title and interest, including
 *           copyright, in the Software.
 * 
 * 3. SOFTWARE  MAINTENANCE. 
 * 
 * PN is not obligated to provide maintenance or updates to Recipient for
 * the Software. However, any maintenance or updates provided by PN shall
 * be covered by this Agreement.
 * 
 * 4. DISCLAIMER OF WARRANTY.
 * 
 * Software is deemed accepted by Recipient.  Because PN is providing
 * Recipient the Software for free, the Software is provided to Recipient
 * AS IS, WITHOUT WARRANTY OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED
 * BYAPPLICABLE LAW, PROGRESSIVE NETWORKS FURTHER DISCLAIMS ALL
 * WARRANTIES,INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
 * NONINFRINGEMENT. THE ENTIRE RISK ARISING OUT OF THE USE OR PERFORMANCE
 * OF THE SOFTWARE REMAINS WITH RECIPIENT. TO THE MAXIMUM EXTENT PERMITTED
 * BY APPLICABLE LAW, IN NO EVENT SHALL PROGRESSIVE NETWORKS OR ITS
 * SUPPLIERS BE LIABLE FOR ANY CONSEQUENTIAL, INCIDENTAL, DIRECT, INDIRECT,
 * SPECIAL, PUNITIVE, OR OTHER DAMAGES WHATSOEVER (INCLUDING, WITHOUT
 * LIMITATION, DAMAGES FOR LOSS OF BUSINESS PROFITS, BUSINESS INTERRUPTION,
 * LOSS OF BUSINESS INFORMATION, OR OTHER PECUNIARY LOSS) ARISING OUT OF
 * THIS AGREEMENT OR THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF
 * PROGRESSIVE NETWORKS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGES. BECAUSE SOME STATES/JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR
 * LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, THE
 * ABOVE LIMITATION MAY NOT APPLY TO RECIPIENT.
 * 
 * 5. INDEMNIFICATION. 
 * 
 * Recipient hereby agrees to defend, indemnify, and hold PN, its
 * directors, officers, employees and agents, harmless from any and all
 * claims, damages, and expenses (including attorneys fees and costs) of
 * any nature arising out of the use, modification, or redistribution the
 * Software or any derivatives.
 * 
 * 6. U.S. GOVERNMENT RESTRICTED RIGHTS AND EXPORT RESTRICTIONS. 
 * 
 * The Software is provided with RESTRICTED RIGHTS. Use, duplication, or
 * disclosure by the Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of The Rights in Technical Data and Computer
 * Software clause of DFARS 252.227-7013 or subparagraphs (c)(i) and (2) of
 * the Commercial Computer Software-Restricted Rights at 48 CFR 52.227-19,
 * as applicable. Manufacturer is Progressive Networks, 616 First Avenue,
 * Suite 701, Seattle, Washington 98104-2258. Recipient acknowledges that
 * none of the Software or underlying information or technology may be
 * downloaded or otherwise exported or re-exported (i) into (or to a
 * national or resident of) Cuba, Iraq, Libya, Yugoslavia, North Korea,
 * Iran, Syria or any other country to which the U.S. has embargoed goods;
 * or (ii) to anyone on the U.S. Treasury Department's list of Specially
 * Designated Nationals or the U.S. Commerce Department's Table of Denial
 * Orders.  By using the Software, you are agreeing to the foregoing and
 * you are representing and warranting that you are not located in, under
 * the control of, or a national or resident or resident of any such
 * country or on any such list.
 * 
 * 7. GOVERNING LAW; ATTORNEYS FEES. 
 * 
 * This agreement shall be governed by the laws of the State of Washington
 * and Recipient further consents to jurisdiction by the state and federal
 * courts sitting in the State of Washington. If either PN or Recipient
 * employs attorneys to enforce any rights arising out of or relating to
 * this Agreement, the prevailing party shall be entitled to recover
 * reasonable attorneys' fees.
 * 
 * 8. ENTIRE AGREEMENT. 
 * 
 * This agreement constitutes the complete and exclusive agreement between
 * PN and Recipient with respect to the subject matter hereof, and
 * supersedes all prior oral or written understandings, communications or
 * agreements not specifically incorporated herein.  This agreement may not
 * be modified except in a writing duly signed by an authorized
 * representative of PN and Recipient.
 *
 */
/*
 *  Usage Notes: 
 *  - we assume we're being called from inetd or in some way that
 *	the control connection is on fd 0.  
 *
 *  -  Go through the proxy dialog with the player and get "useful" info
 *  -  Setup control connection to the "real" server.
 *  -  For non tcp connections, setup UDP backchannel passthrough stream.
 *  -  Copy control data to/from player/server
 *  -  For udp, copy data stream from server to player.
 */


/* use tabstop=4 */
#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/time.h>
#include	<sys/socket.h>
#include	<netdb.h>
#include	<netinet/in.h>
#include	<sys/signal.h>
#include	<syslog.h>
#include	"raproxy.h"

#ifdef USE_SOCKS
#define connect Rconnect
#define accept  Raccept
#define getsockname Rgetsockname
#define bind Rbind
#define listen Rlisten
#endif  /* USE_SOCKS */

#ifdef SYSV
/* if you don't have bzero/bcopy, define SYSV. */
#define bcopy(src, dest, size) memcpy(dest, src, size)
#define bzero(buf,size) memset(buf, '\0', size);
#endif /* SYSV */

/* define the logging facility/level for the proxy */
#define LOG_FACILITY	LOG_DAEMON
#define LOG_LVL	LOG_NOTICE

#ifdef LOAD_LIMIT
#define LOAD_OK			0	/* load limit not reached */
#define LOAD_EXCEEDED		1	/* load limit exceeded */
#define LOAD_PROBLEM		2	/* locking/counting problems */
/* flag to indicate whether max audio streams are being used */
int load_limit_reached = LOAD_PROBLEM;
#endif /* LOAD_LIMIT */

/* socket to audio server */
int serverfd;

#ifndef NO_UDP
/* udp sockets for audio */
int serveraudiofd, playeraudiofd;
/* client/player address for udp stream */
struct sockaddr_in playerinfo;
#endif /* !NO_UDP */

main(argc, argv)
int argc;
char **argv;
{

	fd_set		readfds, dummy;
	char		buf[2048];
	struct		timeval *timeout = (struct timeval *)0;
	int		use_tcp;
	extern		int errno;


#ifdef DEBUG
	/* 
	 * define DEBUG if you want to run this via a debugger 
	 * SEE debugit() below.
	 */
	debugit();
#endif /* DEBUG */

	openlog("raproxy", LOG_PID|LOG_NDELAY,LOG_FACILITY);

#ifdef USE_SOCKS
	SOCKSinit(argv[0]);
#endif /* USE_SOCKS */

#ifdef LOAD_LIMIT
	/* increment load count */
	load_limit_reached = raloadlimit(1);
#endif /* LOAD_LIMIT */

	/* 
	 * do initial "proxy dialog" with client and connect to audio server
	 *  (globally sets serverfd)
	 */
	if ( (use_tcp = proxydialog()) < 0 ) {
		/* 
		 * some PC/MAC stacks disconnect immediately - we'll add a
		 *  delay to make sure they get all messages.
		 */
		sleep(2);
		close(0);
		if ( serverfd )
			close(serverfd);
		ra_exit(1);
	}

#ifndef NO_UDP
	/* 
	 * For udp, then setup udp backchannel sockets/data etc
	 * (globally sets, playeraudiofd, serveraudiofd and playerinfo)
	 */
	if ( use_tcp == 0 ) {
		if ( udpsetup() < 0 ) {
			sleep(2); /* for problem stacks - see above */
			ra_exit(1);
		}
	}
#endif /* !NO_UDP */

	/* 
	 * Main loop
	 *  - we've done all the proxy setup
	 *  - we have descriptors for sending/receiving control info
	 *    to/from player/server 
	 *	  - 0 is control socket to/from player (setup by inetd)
	 *	  - serverfd is control socket to/from server (setup from proxydialog)
	 *  - for udp connections we also have descriptors for 
	 *    - sending/receiving audio info. (setup in udpsetup)
	 *	  - playeryaudiofd is socket for sending udp audio
	 *	  - serveraudiofd is socket for receiving udp audio
	 *    - playerinfo has address info for udp "write"
	 *
	 *  - Now just sit and wait for info and copy it to the
	 *    appropriate place.
	 */

	FD_ZERO(&dummy);
	FD_SET(0, &dummy);
	FD_SET(serverfd, &dummy);
#ifndef NO_UDP
	if (use_tcp == 0 ) {
		FD_SET(serveraudiofd, &dummy);
	}
#endif /* !NO_UDP */
	while (1) {
		struct 		sockaddr_in from;
		int 		fromlen = sizeof(from);
		int 		count;

		(void) bcopy( &dummy, &readfds, sizeof(fd_set) );
		if ( select(FD_SETSIZE, &readfds, (fd_set *)0, (fd_set *)0,timeout) <= 0) {
			/* syslog timeout goes here */
			break;
		}
		/*  read/write control info (and sometimes audio) from server to player */
		if ( FD_ISSET(serverfd, &readfds) ) {
			if ( (count = read( serverfd, buf, sizeof(buf))) <= 0 )
				break;
			if ( write(0, buf, count) != count )
				break;
		}
		/*  read/write control info from player to server */
		if ( FD_ISSET(0, &readfds) ) {
			if ( (count = read(0, buf, sizeof(buf)) ) <= 0 )
				break;
			if ( write(serverfd, buf, count) != count )
				break;
		}
#ifndef NO_UDP
		if (use_tcp == 0 ) {
				/* write audio data from server to player */
				if ( FD_ISSET(serveraudiofd, &readfds) ) {
				/* 
				 * you may want to check the source/from address to see if
				 * the stream is coming from the "right" place.  Given
				 * this is udp it could be easily spoofed. (All
				 * you would get is "garbage" in the audio stream).
				 */
				if( (count = recvfrom(serveraudiofd, buf, sizeof(buf),
				 			0, (struct sockaddr *)&from, &fromlen) ) <= 0 ) {
					break;
				}
				if( sendto(playeraudiofd, buf, count, 0, 
				 (struct sockaddr *)&playerinfo, sizeof(playerinfo)) != count )
					break;
				}
		}
#endif /* !NO_UDP */
	}
	ra_exit(0);
}

ra_exit(error)
int error;
{
#ifdef LOAD_LIMIT
	/* decrement load count */
	if ( load_limit_reached == LOAD_OK )
		raloadlimit(0);
#endif /* LOAD_LIMIT */
	exit(error);
}

/* storage for "proxy info" from client - need by udpsetup() */
static unsigned short backportoffset;

/*
 *  Send version info to player then wait for appropriate info
 *	to validate proxy dialog.
 */
int
proxydialog()
{
	unsigned char opcode;
	unsigned char version = RA_PROXYVERSION;
	unsigned char connecttype = 0;
	int gothostname = 0, gotserverport = 0, gotbackport = 0, gotconnecttype = 0;
	unsigned short serverport = 0;
	short msgbytes;
	int done;
	char *hostname;
	void timeoutexit(), signalerror();
	extern int errno;


	/* setup a timeout in case proxy dialog doesn't finish */
	signal(SIGALRM, timeoutexit);
	signal(SIGPIPE, signalerror);
	alarm(RA_TIMEOUT);

	/* send our proxy version # */
	msgbytes = htons(2);
	if ( write(0, &msgbytes, 2) != 2 ) {
		/* syslog error */
		syslog(LOG_LVL, "raproxy: error sending version to client 1: %m");
		return(-1);
	}
	opcode = RA_VERSION;
	if ( write(0, &opcode, 1) != 1 ) {
		/* syslog error */
		syslog(LOG_LVL, "raproxy: error sending version to client 2: %m");
		return(-1);
	}
	if ( write(0, &version, 1) != 1 ) {
		/* syslog error */
		syslog(LOG_LVL, "raproxy: error sending version to client 3: %m");
		return(-1);
	}

	/* 
	 * read proxy info from client 
	 *  - procotol version comes first
	 *  the following info can be in any order:
	 *  - server hostname
	 *  - server port number
	 *  - offset of "backchannel data port" in subsequent control stream
	 *  - is this a udp or tcp connection.
	 */

	
	/* check if first info is version # */
	if ( getopcode(&msgbytes) != RA_VERSION ) {
		/* syslog error? */
		syslog(LOG_LVL, "raproxy: bad proxy protocol version.");
		sendstatus(RA_BADCODE, 0);
		return(-1);
	}
	if ( read( 0, &version, 1) != 1 ) {
		syslog(LOG_LVL, "raproxy: error reading version from client: %m");
		return(-1);
	}

	if ( version != RA_PROXYVERSION ) {
		syslog(LOG_LVL, "raproxy: bad version from client: %m");
		sendstatus(RA_BADVERSION,0);
		/* return status error to player TBD */
		return(-1);
	}

	done = 0;
	while ( !done ) {

		switch(getopcode(&msgbytes))
		{
			case RA_HOSTNAME:
				if ( (hostname = (char *)malloc(msgbytes + 1)) == (char *) NULL ) {
					/* syslog error - out of memory ? */
				 	syslog(LOG_LVL, "raproxy: malloc() out of memory.");
					return(-1);
				}
					
				if ( read( 0, hostname, msgbytes) != msgbytes ) {
					/* syslog error? */
					syslog(LOG_LVL, "raproxy: error reading hostname from client: %m");
					return(-1);
				}
				hostname[msgbytes] = '\0';
				gothostname = 1;
				break;
			case RA_SERVERPORT:
				if ( read( 0, &serverport, msgbytes) != msgbytes ) {
					/* syslog error? */
					syslog(LOG_LVL, "raproxy: error reading server port from client: %m");
					return(-1);
				}
				serverport = ntohs(serverport);
				gotserverport = 1;
				break;
			case RA_BACKPORTOFFSET:
				if ( read( 0, &backportoffset, msgbytes) != msgbytes ) {
					/* syslog error? */
					syslog(LOG_LVL, "raproxy: error reading backport offset from client: %m");
					return(-1);
				}
				backportoffset = ntohs(backportoffset);
				gotbackport = 1;
				break;
			case RA_CONNECTTYPE:
				if ( read( 0, &connecttype, msgbytes) != msgbytes ) {
					/* syslog error? */
					syslog(LOG_LVL, "raproxy: error reading connection type from client: %m");
					return(-1);
				}
				gotconnecttype = 1;
				break;
			case RA_END:
				/* got all the info we need?  If not, send error */
				
				if ( gothostname && gotserverport && gotconnecttype  
									&& (gotbackport ^ connecttype) ) {
				 	done = 1;
					break;
				} else {
					/* syslog error? */
					syslog(LOG_LVL, "raproxy: not enough proxy info");
					sendstatus(RA_BADINFO, 0);
					return(-1);
				} 
				
			default:
				syslog(LOG_LVL, "raproxy: invalid proxy opcode.");
				/* syslog error? */
				/* return unknown code status error to player */
				sendstatus(RA_BADCODE, 0);
				return(-1);
			/* NOT REACHED */
		}
	}

	alarm(0);  /* "turn-off" alarm */
#ifdef NO_UDP
	/* if UDP specified and not compiled for UDP, send an error */
	if ( connecttype == 0 ) {
		sendstatus(RA_NOUDP, 0);
		syslog(LOG_LVL, "raproxy: UDP audio streams not supported.");
		return(-1);
	}
#endif /* NO_UDP */
#ifdef LOAD_LIMIT
	if (load_limit_reached == LOAD_EXCEEDED) {
		sendstatus(RA_LIMIT, 0);
		syslog(LOG_LVL, "raproxy: Audio streams limit reached.");
		return(-1);
	}
	if (load_limit_reached == LOAD_PROBLEM) {
		sendstatus(RA_LIMITPROB, 0);
		syslog(LOG_LVL, "raproxy: Problem accessing/locking limit lockfile.");
		return(-1);
	}
#endif /* LOAD_LIMIT */
	/* Can we connect to the server */
	if ( (serverfd = serverconnect(hostname, serverport)) < 0 ) {
		/* errno is still set from previous call */
		sendstatus(RA_NOCONNECT, errno);
		return(-1);
	} else {
		/* send success message to player */
		sendstatus(RA_SUCCESS, 0);
	}
	return(connecttype);
}

void signalerror(signal)
int signal;
{
	/*syslog(LOG_LVL, "raproxy: SIGPIPE received.");*/
	ra_exit(1);
}

void timeoutexit()
{
	/* syslog error? */
	syslog(LOG_LVL, "raproxy: Timeout waiting for proxy info");
	ra_exit(1);
}



#include <ctype.h> 
/*
 * Connect to a server
 *
 *  Args:
 * 	Hostname:  null terminated string with name/IP address
 *	Port:	   port number to connect to.
 * 
 *  Return Value:  file descriptor for connection
 *			or 
 *		-1 for various errors 
 *		(Should probably have multiple errors for different
 *			problems)
 *		
 */
int
serverconnect(hostname,port)
char *hostname;
int port;
{
	struct hostent		*hp;
	struct sockaddr_in	addr;
	int			fd;
	unsigned long 		ipaddr;


	/* Check if hostname is an IP address (ie. only digits and dots) */
	ipaddr = inet_addr(hostname);
	if (ipaddr != (unsigned long) -1) {
		(void) bcopy((char *)&ipaddr, (char *)&addr.sin_addr, sizeof(ipaddr));
	} else {
		/* must be a real hostname, do lookup */
		if ( (hp = gethostbyname(hostname)) == (struct hostent *)0 )
			return(-1);
		(void) bcopy(hp->h_addr, (char *)&addr.sin_addr, hp->h_length);
	}
	addr.sin_port = htons(port);
	addr.sin_family = AF_INET;
	/* setup socket */
	fd = socket(AF_INET, SOCK_STREAM, 0);
	if ( fd < 0 ) {
		return(-1);
	}
	/* connect to server - errno will still be set if we need it later */
	if ( connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0 ) {
		return(-1);
	}
	return(fd);
}

/*
 * send proxy protocol status message to client
 * (It would also be useful to append an OS error string for some cases)
 */
int 
sendstatus(code, os_errno)
unsigned char code, os_errno;
{
	short	size;
	int		msgsize;
	char 	*msg;
	unsigned char	opcode;
	char	buf[2048];
	char	*strerror();


	msg = statusmsgs[code];
	/* if we have an errno, append the error to the message */
		
	if ( msg != (char *)NULL ) {
		strncpy(buf, msg, strlen(msg) + 1);
		if ( os_errno )  {
			char *p = strerror(os_errno);

			strncat(buf, p, strlen(p) + 1);
		}
		msgsize = strlen(buf) + 1;
		size = htons(msgsize + 2);
	} else {
		size = htons(2);  
	}

	/* write out message size */
	if ( write(0, &size, 2) != 2 ) {
		return(-1);
	}
	opcode = RA_STATUS;
	if ( write(0, &opcode, 1) != 1 ) {
		return(-1);
	}
	/* write out status code */
	if ( write(0, &code, 1) != 1 ) {
		return(-1);
	}
	if ( msg != (char *)NULL ) {
		/* write out status message */
		if ( write(0, buf, msgsize) != msgsize ) {
			return(-1);
		}
	}
	return(0);
}

#ifndef NO_UDP
/*
 * setup udp backchannel sockets from server to client 
 */
int
udpsetup()
{
	int		size, count, temp;
	unsigned short 	player_audioport;
	struct sockaddr_in	addr, info;

	/* setup bind/listen etc for udp backchannel from server */
	if ( (serveraudiofd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ) {
		/* syslog  error */
		syslog(LOG_LVL, "raproxy: can't setup socket: %m");
		return(-1);		/* Probably out of sockets */
	}
	(void) bzero (&addr, sizeof (addr));
	addr.sin_family = AF_INET;
	/* 
	 * port to receive audio from server
	 * if you want this to be in some specific port range
	 * then add your code to to that here.
	 */
	addr.sin_port = htons(0);
	if ( bind(serveraudiofd, (struct sockaddr *)&addr, sizeof (addr)) < 0 ) {
		syslog(LOG_LVL, "raproxy: can't bind socket: %m");
		/* syslog error ? */
		return(-1);
	}

	/* lets get socket info so later we can find out our local port */
	size = sizeof(info);
	if ( getsockname(serveraudiofd, (struct sockaddr *)&info, &size)  < 0 ) {
		/* syslog error ? */
		syslog(LOG_LVL, "raproxy: can't getsockname(): %m");
		return(-1);
	}
	/* 
	 * now read tcp control stream from player until we reach backchannel 
	 * port info.  Copy info out to the server as we go along.
	 */
	count = 0;
	while ( count < backportoffset ) {
		if ( read(0, &temp, 1) != 1) {
			syslog(LOG_LVL, "raproxy: read error from client getting backport info.");
			/* syslog error ? */
			return(-1);
		}
		if ( write(serverfd, &temp, 1) != 1) {
			/* syslog error ? */
			syslog(LOG_LVL, "raproxy: write error to server setting up backport.");
			return(-1);
		}
		count++;
	}

	/* send server the port number it should use to talk to us */
	if ( write(serverfd, &info.sin_port, 2) != 2) {
		/* syslog error ? */
		syslog(LOG_LVL, "raproxy: error writing backport to server.");
		return(-1);
	}
	
	/* get port # we should use to send player audio data */
	if ( read(0, &player_audioport, 2) != 2) {
		/* syslog error ? */
		syslog(LOG_LVL, "raproxy: error reading backport from client.");
		return(-1);
	}
	/* create socket for talking to player audio port */
	if ( (playeraudiofd = socket (AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0 ) {
		/* syslog error */
		syslog(LOG_LVL, "raproxy: error creating audio socket to client: %m");
		return(-1);		
	}
	/* get inet info of client/player - we need this later  */
	size = sizeof(playerinfo);
	if ( getpeername(0, (struct sockaddr *)&playerinfo, &size) < 0 ) {
		/* syslog error ? */
		syslog(LOG_LVL, "raproxy: error getting peername of client: %m");
		return(-1);
	}
	/* now point back to audio port on player */
	playerinfo.sin_port = player_audioport;
	return(0);
}
#endif /* !NO_UDP */


/*
 * get proxy protocol opcode from control channel
 *
 * Args: msgbytes - pointer to store number of bytes in message
 *			stream.
 *
 * Returns: -1 on error
 *		    opcode that was read
 */
int
getopcode(msgbytes)
short *msgbytes;
{
	unsigned char opcode = 0;

	*msgbytes = 0;
	/* read byte count from stream */
	if ( read(0, msgbytes, 2) != 2 ) {
		return(-1);
	}
	/* read opcode from stream */
	if ( read( 0, &opcode, 1) != 1 ) {
		return(-1);
	}
	/* bogus opcode */
	if ( opcode > RA_STATUS ) {
		/* syslog error for this? */
		return(-1);
	}
	/* 
	 * convert # of bytes in message to local byte order (and remove
	 * the byte that was for the opcode)
	 */
	*msgbytes = ntohs(*msgbytes) - 1;
	/* check if # bytes is correct */
	if ( (opcodebytes[opcode] == -1) )
		return(opcode);
 	if (opcodebytes[opcode] != *msgbytes) {
		/* syslog error for this? */
		return(-1);
	}
	return(opcode);
}

#ifdef	DEBUG
/*
 * Define DEBUG and set the define below to the port
 * that you want the proxy to bind to.  This can be useful
 * for running the proxy with a debugger.
 * 
 */
#define DEBUGPORT	5556
debugit()
{
	struct	sockaddr_in	addr;
	int	sock, newfd;

	if( (sock = socket(AF_INET,SOCK_STREAM,0)) < 0 ) {
		perror("socket problem");
		exit(1);
	}
	bzero(&addr,sizeof(addr));
	addr.sin_port = htons(DEBUGPORT);
	addr.sin_family = AF_INET;
	if( bind( sock, (struct sockaddr *)&addr, sizeof(addr)) ) {
		perror("bind problem");
		exit(1);
	}
	if( listen(sock, 1) < 0 ) {
		perror("listen problem");
		exit(1);
	}
	if( ( newfd=accept(sock, 0, 0) ) < 0 ) {
		perror("accept");
		exit(1);
	}
	/* close stdin/out and dup to socket we just setup */
	close(0);
	dup(newfd);
	close(1);
	dup(newfd);
}

dump(buf, count)
char *buf;
int count;
{
	char *p = buf;
	while ( count--)  {
		fprintf(stderr, "0x%x ", (unsigned int)*p++);
		if ( (count %10) == 0)
			fprintf(stderr, "\n");

    	}
	fprintf(stderr, "\n");
}
#endif /* DEBUG */


#ifdef SUNOS
static char unknown_err[] = "Unknown Error.";

char *
strerror(errnum)
int errnum;
{
        extern int sys_nerr;
        extern char *sys_errlist[];

        if ( errnum >= sys_nerr )
                return (unknown_err);
        else
                return ( sys_errlist[errnum] );
}

#endif /* SUNOS */

#ifdef LOAD_LIMIT

/*
 *  "Rudimentary" code for limiting the number of audio streams/proxies
 *  running at the same time.  (LOAD_LIMIT must be defined for this
 *  code to be compiled in.  You also need to #define FLOCK if your
 *  OS uses FLOCK locking,  otherwise FCNTL locking will be used.  Most
 *  people should define FLOCK unless you are straight System V).
 *
 *  We keep a count of the # of proxies running.  This count is kept
 *  as a binary integer in the file mentioned in raproxy.h
 *  At boot time the program raloadinit, needs to be called to
 *  create and initialise this file.
 *
 *  Once the lock/count file is created, we increment/decrement the count
 *  on entry and exit.  If the limit is exceeded, the user receives
 *  an appropriate proxy error message.
 *
 *
 *	Args:  direction  - 1 increment
 *					    0 decrement
 *
 *	Returns:	 0 everything successful
 *				 1 limit exceeded, locking problem, decrement count on exit
 *				 2 some problem, DON'T decrement count on exit
 *
 *
 *  
 */

#include <sys/file.h>


int raloadlimit(direction)
int direction;
{
	int fd;
	int numstreams = 0;

	if ( (fd = open(ralockfile, O_RDWR)) < 0 ) {
		syslog(LOG_LVL, "raproxy: no lock file for streams: %m");
		return(LOAD_PROBLEM);
	}
	if ( lockfile(fd) != 0 ) {
		syslog(LOG_LVL, "raproxy: couldn't lock file for UDP stream limiting: %m");
		return(LOAD_PROBLEM);
	}
	if ( read(fd, &numstreams, ranumbytes) != ranumbytes ) {
		syslog(LOG_LVL, "raproxy: error on lock file for streams: %m");
		return(LOAD_PROBLEM);
	}
	if ( direction )
		++numstreams;
	else
		--numstreams;
	if ( numstreams < 0 ) {
		syslog(LOG_LVL, "raproxy: number of Audio streams < 0????.");
		return(LOAD_PROBLEM);
	}
	if ( numstreams > maxaudiostreams ) {
		syslog(LOG_LVL, "raproxy: Exceeded max number of Audio streams.");
		return(LOAD_EXCEEDED);
	}
	lseek(fd, SEEK_SET, 0);
	if ( write(fd, &numstreams, ranumbytes) != ranumbytes ) {
		syslog(LOG_LVL, "raproxy: write error on lock file for streams: %m");
		return(LOAD_PROBLEM);
	}
	if ( unlockfile(fd) != 0 ) {
		syslog(LOG_LVL, "raproxy: couldn't lock file for UDP stream limiting: %m");
		return(LOAD_PROBLEM);
	}
	if ( close(fd) < 0 ) {
		syslog(LOG_LVL, "raproxy: write error on lock file for streams: %m");
		return(LOAD_PROBLEM);
	}
	return(LOAD_OK);
}

#include <sys/file.h>

int
lockfile(fd)
int fd;
{

#ifdef FLOCK
	if ( flock(fd, LOCK_EX) < 0 )
		return(1);
	return(0);

#else /* !FLOCK */
	struct flock lock;

	bzero( &lock, sizeof(lock));
	lock.l_type = F_WRLCK;
	if ( fcntl( fd, F_SETLKW, &lock) < 0 ) {
		return(1);
	}
	return(0);

#endif /* FLOCK */
}

int
unlockfile(fd)
int fd;
{
#ifdef FLOCK
	if ( flock(fd, LOCK_UN) < 0 )
		return(1);
	return(0);

#else /* !FLOCK */
	struct flock lock;

	bzero( &lock, sizeof(lock));
	lock.l_type = F_UNLCK;
	if ( fcntl( fd, F_SETLKW, &lock) < 0 ) {
		return(1);
	}
	return(0);

#endif /* FLOCK */
}
#endif /* LOAD_LIMIT */

