/*****************************************************************************

 *                                                                           *

 * DH_NET.C                                                                  *

 *                                                                           *

 * Freely redistributable and modifiable.  Use at your own risk.             *

 *                                                                           *

 * Copyright 1994 The Downhill Project                                       *

 *                                                                           *

 *****************************************************************************/





/* Include stuff *************************************************************/

#include "INCLUDE/DH.H"

#include <sys/types.h>

#include <sys/socket.h>

#include <sys/file.h>

#include <netinet/in.h>

#include <stdio.h>

#include <netdb.h>

#include <errno.h>

#include <sys/param.h>

#include <strings.h>





/* Define stuff **************************************************************/

#define DOWNHILL_RCMD_TIMEOUT  32

#define DOWNHILL_REXEC_TIMEOUT 16





/* Function stuff ************************************************************/



/* Report a host error ===================================================== */

#if (_DOWNHILL_POSIX_SOURCE == 0)

void herror(char* error_Prefix)

{

	/* Use perror(), 'cause there isn't an h_error */

	perror(error_Prefix);

}

#endif



/* Get user and password information from .netrc =========================== */

#if (_DOWNHILL_POSIX_SOURCE == 0)

static char* downhill_Ruserpass_GetWord(FILE* file_Fs)

{

	static char word_Text[1024];

	int         word_Position;

	static char line_Text[1024];

	static int  line_Position = 0;



	strcpy(word_Text,"");

	word_Position = 0;



	/* Loop to find a word */

	do

	{

		/* Get a new line */

		if (line_Text[line_Position] == '\0')

		{

			if (fgets(line_Text,sizeof(line_Text),file_Fs) == NULL)

			{

				return NULL;

			}

			line_Position = 0;

		}



		/* Skip any white space */

		while ((line_Text[line_Position] == ' ') ||

		 (line_Text[line_Position] == '\t') ||

		 (line_Text[line_Position] == '\n'))

		{

			line_Position++;

		}

	

		/* Get the word */

		while ((line_Text[line_Position] != ' ') &&

		 (line_Text[line_Position] != '\t') &&

		 (line_Text[line_Position] != '\n') &&

		 (line_Text[line_Position] != '\0'))

		{

			word_Text[word_Position] = line_Text[line_Position];

			word_Text[word_Position+1] = '\0';

			word_Position++;

			line_Position++;

		}

	} while (!strcmp(word_Text,""));



	/* Return what we got */

	return word_Text;

}

void ruserpass(char* host_Name,char** user_Name,char** user_Password)

{

	FILE*        file_Fs;

	char         netrc_File[MAXPATHLEN+1];

	char*        netrc_Word;

	static char  netrc_Name[32];

	static char  netrc_Password[32];

	int          machine_Match = 0;

	int          machine_Matched = 0;



	/* Open the file */

	strcpy(netrc_File,Downhill_User_Dir);

	strcat(netrc_File,"\\");

	strcat(netrc_File,_DOWNHILL_NETRC_FILE);

	file_Fs = fopen(netrc_File,"rt");

	if (file_Fs == NULL)

	{

		return;

	}



	/* Loop through the file */

	do

	{

		netrc_Word = downhill_Ruserpass_GetWord(file_Fs);

		if (netrc_Word == NULL)

		{

			break;

		}

		else if (!strcasecmp(netrc_Word,"machine"))

		{

			netrc_Word = downhill_Ruserpass_GetWord(file_Fs);

			if (netrc_Word != NULL)

			{

				machine_Match = (!strcasecmp(netrc_Word,

				 host_Name));

				if (machine_Match)

				{

					machine_Matched = 1;

				}

			}

		}

		else if (!strcasecmp(netrc_Word,"default"))

		{

			if (!machine_Matched)

			{

				machine_Match = 1;

			}

			else

			{

				machine_Match = 0;

			}

		}

		else if (!strcasecmp(netrc_Word,"login"))

		{

			netrc_Word = downhill_Ruserpass_GetWord(file_Fs);

			if ((netrc_Word != NULL) && (machine_Match))

			{

				strcpy(netrc_Name,netrc_Word);

				*user_Name = netrc_Name;

			}

		}

		else if (!strcasecmp(netrc_Word,"password"))

		{

			netrc_Word = downhill_Ruserpass_GetWord(file_Fs);

			if ((netrc_Word != NULL) && (machine_Match))

			{

				strcpy(netrc_Password,netrc_Word);

				*user_Password = netrc_Password;

			}

		}

	} while (netrc_Word != NULL);



	return;

}

#endif



/* Execute a password-protected remote command ============================= */

/* This is based on the BSD rexec. */

#if (_DOWNHILL_POSIX_SOURCE == 0)

int rexec(char** server_Host,unsigned short server_Port,char* server_User,

     char* server_Password,char* server_Command,int* client_Stderr)

{

	struct hostent*    server_Info;

	struct sockaddr_in client_AddrStdout;

	int                client_Stdout;

	struct sockaddr_in client_AddrStderr;

	unsigned short     client_PortStderr = 0;

	int                sec_Count = 0;

	char               result_Character;

	

	/* Normalize the host */

	server_Info = gethostbyname(*server_Host);

	if (server_Info == NULL)

	{

		herror(*server_Host);

		return -1;

	}

	*server_Host = server_Info->h_name;



	/* Get any additional information */

	ruserpass(server_Info->h_name,&server_User,&server_Password);



retry:

	/* Open stdout ----------------------------------------------------- */

	client_Stdout = socket(AF_INET,SOCK_STREAM,0);

	if (client_Stdout < 0)

	{

		perror("rexec: socket");

		return -1;

	}

	client_AddrStdout.sin_family = server_Info->h_addrtype;

	client_AddrStdout.sin_port = htons(server_Port);

	memcpy((caddr_t)(&client_AddrStdout.sin_addr),server_Info->h_addr,

	 server_Info->h_length);

	if (connect(client_Stdout,(struct sockaddr*)(&client_AddrStdout),

	 sizeof(client_AddrStdout)) < 0)

	{

		/* Close the socket */

		closesocket(client_Stdout);



		/* Should we retry? */

		if ((errno == ECONNREFUSED) &&

		 (sec_Count <= DOWNHILL_REXEC_TIMEOUT))

		{

			/* Wait a bit */

			sleep(sec_Count);

			sec_Count *= 2;

			goto retry;

		}



		/* Couldn't get the connection */

		perror(server_Info->h_name);

		return -1;

	}



	/* Open stderr ----------------------------------------------------- */

	if (client_Stderr == NULL)

	{

		send(client_Stdout,"\0",1,0);

		client_PortStderr = 0;

	}

	else

	{

		struct sockaddr_in temp_Addr;

		int                temp_AddrSize;

		char               temp_Port[8];

		int                temp_Socket;

		int                temp_Socket2;

		

		temp_Socket = socket(AF_INET,SOCK_STREAM,0);

		if (temp_Socket < 0)

		{

			goto bad;

		}

		listen(temp_Socket,1);

		temp_AddrSize = sizeof(client_AddrStderr);

		if ((getsockname(temp_Socket,

		 (struct sockaddr*)(&client_AddrStderr),&temp_AddrSize) < 0) ||

		 (temp_AddrSize != sizeof(client_AddrStderr)))

		{

			perror("getsockname");

			closesocket(temp_Socket);

			goto bad;

		}

		client_PortStderr =

		 ntohs((unsigned short)client_AddrStderr.sin_port);

		sprintf(temp_Port,"%u",client_PortStderr);

		write(client_Stdout,temp_Port,strlen(temp_Port)+1);

		temp_AddrSize = sizeof(temp_Addr);

		temp_Socket2 = accept(temp_Socket,

		 (struct sockaddr*)(&temp_Addr),&temp_AddrSize);

		closesocket(temp_Socket);

		if (temp_Socket2 < 0)

		{

			perror("accept");

			client_PortStderr = 0;

			goto bad;

		}

		*client_Stderr = temp_Socket2;

	}



	/* Send the command ------------------------------------------------ */

	send(client_Stdout,server_User,strlen(server_User)+1,0);

	send(client_Stdout,server_Password,strlen(server_Password)+1,0);

	send(client_Stdout,server_Command,strlen(server_Command)+1,0);



	/* Handle result --------------------------------------------------- */

	if (recv(client_Stdout,&result_Character,1,0) != 1)

	{

		perror(*server_Host);

		goto bad;

	}

	if (result_Character != 0)

	{

		/* Read the fail message */

		while (recv(client_Stdout,&result_Character,1,0) == 1)

		{

			write(fileno(stderr),&result_Character,1);

			if (result_Character == '\n')

			{

				break;

			}

		}

		goto bad;

	}



	return client_Stdout;



bad:

	if (client_PortStderr)

	{

		closesocket(*client_Stderr);

	}

	closesocket(client_Stdout);

	return -1;

}

#endif



/* Open a socket to a reserved port ======================================== */

#if (_DOWNHILL_POSIX_SOURCE == 0)

int rresvport(unsigned short* client_Port)

{

	int                client_Sd;

	struct sockaddr_in client_Addr;



	/* Loop 'til we get something */

	(*client_Port)++;

	for (;;)

	{

		(*client_Port)--;

		if (*client_Port < 0)

		{

			errno = EAGAIN;

			return -1;

		}



		/* Open the socket */

		client_Sd = socket(PF_INET,SOCK_STREAM,0);

		if (client_Sd == -1)

		{

			return -1;

		}



		/* Bind to the port */

		client_Addr.sin_family = AF_INET;

		client_Addr.sin_port = htons(*client_Port);

		client_Addr.sin_addr.s_addr = INADDR_ANY;

		if (bind(client_Sd,(struct sockaddr*)(&client_Addr),

		 sizeof(client_Addr)) == -1)

		{

			int error_Number = GetLastError();



			/* Close the socket */

			closesocket(client_Sd);



			/* Try again */

			if (error_Number == EADDRINUSE)

			{

				continue;

			}



			return -1;

		}



		break;

	}



	/* Return the socket */

	return client_Sd;

}

#endif



/* Execute a remote command ================================================ */

/* This is based on the BSD rcmd. */

#if (_DOWNHILL_POSIX_SOURCE == 0)

int rcmd(char** server_Host,unsigned short server_Port,char* client_User,

     char* server_User,char* server_Command,int* client_Stderr)

{

	int                client_Stdout;

	unsigned short     client_Port = IPPORT_RESERVED-1;

	struct sockaddr_in client_AddrStdout;

	struct sockaddr_in client_AddrStderr;

	struct hostent*    server_Info;

	int                sec_Count = 1;

	fd_set             sock_Set;

	char               result_Character;

	int                error_Number;



	/* The the info on the server */

	server_Info = gethostbyname(*server_Host);

	if (server_Info == NULL)

	{

		return -1;

	}

	*server_Host = server_Info->h_name;



	/* Find a socket --------------------------------------------------- */

	for (;;)

	{

		client_Stdout = rresvport(&client_Port);

		if (client_Stdout == -1)

		{

			if (errno == EAGAIN)

			{

				fprintf(stderr,"socket: All ports in use\n");

			}

			else

			{

				perror("rcmd: socket");

			}

			return -1;

		}



		/* Set up the socket */

		client_AddrStdout.sin_family = server_Info->h_addrtype;

		memcpy((caddr_t)(&client_AddrStdout.sin_addr),

		 server_Info->h_addr_list[0],server_Info->h_length);

		client_AddrStdout.sin_port = htons(server_Port);



		/* Connect */

		if (connect(client_Stdout,

		 (struct sockaddr*)(&client_AddrStdout),

		 sizeof(client_AddrStdout)) >= 0)

		{

			/* Connect worked */

			break;

		}

		error_Number = GetLastError();



		/* Connect failed */

		closesocket(client_Stdout);



		/* Refused? */

		if ((error_Number == ECONNREFUSED) &&

		 (sec_Count <= DOWNHILL_RCMD_TIMEOUT))

		{

			/* Wait a bit and try again */

			sleep(sec_Count);

			sec_Count *= 2;

			continue;

		}



		/* If there's other addresses, try 'em */

		if (server_Info->h_addr_list[1] != NULL)

		{

			int errno_Old = errno;



			fprintf(stderr,"connect to address %s: ",

			 inet_ntoa(client_AddrStdout.sin_addr));

			errno = errno_Old;

			perror(NULL);



			server_Info->h_addr_list++;

			memcpy((caddr_t)(&client_AddrStdout.sin_addr),

			 server_Info->h_addr_list[0],server_Info->h_length);

			fprintf(stderr,"Trying %s...\n",

			 inet_ntoa(client_AddrStdout.sin_addr));



			continue;

		}



		/* Couldn't find any */

		perror(server_Info->h_name);

		return -1;

	}



	/* Open stderr ----------------------------------------------------- */

	if (client_Stderr == NULL)

	{

		send(client_Stdout,"",1,0);

		client_Port = 0;

	}

	else

	{

		/* Create the stderr socket */

		char sock_Port[8];

		int  sock_Temp;

		int  client_Stderr2;

		int  len;



		client_Port--;

		sock_Temp = rresvport(&client_Port);

		if (sock_Temp == -1)

		{

			goto bad;

		}

		if (listen(sock_Temp,1) == -1)

		{

			goto bad;

		}



		/* Tell the host were the stderr port is */

		sprintf(sock_Port,"%d",client_Port);

		if (send(client_Stdout,sock_Port,strlen(sock_Port)+1,0) !=

		 (int)strlen(sock_Port)+1)

		{

			perror("send: setting up stderr");

			closesocket(sock_Temp);

			goto bad;

		}



		FD_ZERO(&sock_Set);

		FD_SET(client_Stdout,&sock_Set);

		FD_SET(sock_Temp,&sock_Set);

		errno = 0;

		if ((select(FD_SETSIZE,&sock_Set,(fd_set *)NULL,(fd_set *)NULL,

		 (struct timeval *)NULL) < 1) ||

		 (!FD_ISSET(sock_Temp,&sock_Set)))

		{

			if (errno != 0)

			{

				perror("select: setting up stderr");

			}

			else

			{

				fprintf(stderr,

				 "select: protocol failure in setup.\n");

			}

			closesocket(sock_Temp);

			goto bad;

		}



		/* Wait for the host to connect */

		len = sizeof(client_AddrStderr);

		client_Stderr2 = accept(sock_Temp,

		 (struct sockaddr*)&client_AddrStderr,&len);

		closesocket(sock_Temp);

		if (client_Stderr2 == -1)

		{

			perror("accept");

			client_Port = 0;

			goto bad;

		}

		*client_Stderr = client_Stderr2;



		/* Make sure we connected right */

		client_AddrStderr.sin_port = ntohs(client_AddrStderr.sin_port);

		if ((client_AddrStderr.sin_family != AF_INET) ||

		 (client_AddrStderr.sin_port >= IPPORT_RESERVED) ||

		 (client_AddrStderr.sin_port < IPPORT_RESERVED/2))

		{

			fprintf(stderr,

			 "socket: protocol failure in circuit setup.\n");

			goto bad2;

		}

	}



	/* Send the command ------------------------------------------------ */

	send(client_Stdout,client_User,strlen(client_User)+1,0);

	send(client_Stdout,server_User,strlen(server_User)+1,0);

	send(client_Stdout,server_Command,strlen(server_Command)+1,0);



	/* Handle the result ----------------------------------------------- */

	if (recv(client_Stdout,&result_Character,1,0) != 1)

	{

		perror(*server_Host);

		goto bad2;

	}

	if (result_Character != 0)

	{

		/* The command failed, read the result */

		while (recv(client_Stdout,&result_Character,1,0) == 1)

		{

			write(fileno(stderr),&result_Character,1);

			if (result_Character == '\n')

			{

				break;

			}

		}

		goto bad2;

	}



	return client_Stdout;



bad2:

	if (client_Port)

	{

		closesocket(*client_Stderr);

	}

bad:

	closesocket(client_Stdout);

	return -1;

}

#endif



/* Check to see if the hostnames match ===================================== */

static int downhill_Ruserok_CheckHost(char* hostname_Client,

            char* hostname_Candidate)

{

	char hostname_CandidateFull[MAXHOSTNAMELEN+1];



	/* Build the name */

	strcpy(hostname_CandidateFull,hostname_Candidate);



	/* Does the candidate end in a "."? */

	if (hostname_CandidateFull[strlen(hostname_CandidateFull)-1] == '.')

	{

		char hostname_Local[MAXHOSTNAMELEN+1];



		/* Get the local domain */

		if (gethostname(hostname_Local,MAXHOSTNAMELEN) == 0)

		{

			char* domain_Local = strchr(hostname_Local,'.');



			/* If there's a domain, use it */

			if (domain_Local != NULL)

			{

				strcat(hostname_CandidateFull,domain_Local);

			}

		}

	}



	/* Disallowed? */

	if ((hostname_Candidate[0] == '-') &&

	 (!strcasecmp(hostname_Client,&(hostname_CandidateFull[1]))))

	{

		return FALSE;

	}



	/* Allowed? */

	if ((!strcmp("+",hostname_CandidateFull)) ||

	 (!strcasecmp(hostname_Client,hostname_CandidateFull)))

	{

		return TRUE;

	}



	/* Couldn't find */

	return FALSE;

}



/* Check if host and user are valid ======================================== */

static int downhill_Ruserok_CheckHostUser(FILE* file_Fs,char* hostname_Client,

            char* username_Server,char* username_Client)

{

	char          line_Text[MAXHOSTNAMELEN+1];

	unsigned int  line_Index = 0;

	char          hostname_Candidate[MAXHOSTNAMELEN+1];

	unsigned int  hostname_Index = 0;

	char          username_Candidate[MAXHOSTNAMELEN+1];

	unsigned int  username_Index;



	/* Loop through all the hostnames in the file */

	while (fgets(line_Text,sizeof(line_Text),file_Fs))

	{

		/* Clear out the candidates */

		strcpy(hostname_Candidate,"");

		hostname_Index = 0;



		/* Clear out any leading white-space */

		line_Index = 0;

		while ((line_Text[line_Index] == ' ') ||

		 (line_Text[line_Index] == '\t'))

		{

			line_Index++;

		}



		/* Get the host name */

		while ((line_Text[line_Index] != '\n') &&

		 (line_Text[line_Index] != '\t') &&

		 (line_Text[line_Index] != ' ') &&

		 (line_Text[line_Index] != '\0'))

		{

			hostname_Candidate[hostname_Index] =

			 line_Text[line_Index];



			line_Index++;

			hostname_Index++;

		}

		hostname_Candidate[hostname_Index] = '\0';



		/* Do the host names match? */

		if (downhill_Ruserok_CheckHost(hostname_Client,

		 hostname_Candidate))

		{

			/* Is there a user name? */

			while ((line_Text[line_Index] == ' ') ||

			 (line_Text[line_Index] == '\t'))

			{

				line_Index++;

			}

			if ((line_Text[line_Index] == '\n') ||

			 (line_Text[line_Index] == '\0'))

			{

				/* If the client user is the same as the server

				   user, they we're OK */

				if (!strcasecmp(username_Client,

				 username_Server))

				{

					return TRUE;

				}

			}

			else

			{

				/* Loop through all the users */

				while ((line_Text[line_Index] != '\n') &&

				 (line_Text[line_Index] != '\0'))

				{

					/* Clear any white space */

					while ((line_Text[line_Index] == ' ')

					 || (line_Text[line_Index] == '\t'))

					{

						line_Index++;

					}

					if ((line_Text[line_Index] == '\n') ||

					 (line_Text[line_Index] == '\0'))

					{

						break;

					}



					/* Clear out the candidate */

					username_Index = 0;



					/* Get the user name */

					while ((line_Text[line_Index] != '\n')

					 && (line_Text[line_Index] != '\t') &&

					 (line_Text[line_Index] != ' ') &&

					 (line_Text[line_Index] != '\0'))

					{

						username_Candidate[

						 username_Index] = line_Text[

						 line_Index];

	

						line_Index++;

						username_Index++;

					}

					username_Candidate[username_Index] =

					 '\0';



					/* Are we explicitly excluded? */

					if ((username_Candidate[0] == '-') &&

					 (!strcasecmp(username_Client,

					 &(username_Candidate[1]))))

					{

						return FALSE;

					}



					/* Do we allowed? */

					if ((!strcmp("+",username_Candidate))

					 || (!strcasecmp(username_Client,

					 username_Candidate)))

					{

						return TRUE;

					}

				}

			}

		}

	}



	return FALSE;

}



/* Check if a remote user can execute commands on this server ============== */

#if (_DOWNHILL_POSIX_SOURCE == 0)

int ruserok(char* hostname_Client,int root_Want,char* user_Client,

     char* user_Server)

{

	FILE* file_Fs;

	char  rhosts_File[MAXPATHLEN+1];



	/* If they don't want to be root, open up the "hosts.equiv" file */

	file_Fs = root_Want?(FILE*)NULL:fopen(_DOWNHILL_HOSTSEQUIV_PATH,"rt");



	/* If the "hosts.equiv" file opened up, check it */

	if (file_Fs != NULL)

	{

		if (downhill_Ruserok_CheckHostUser(file_Fs,hostname_Client,

		 user_Server,user_Client))

		{

			fclose(file_Fs);

			return 0;

		}

		fclose(file_Fs);

	}



	/* Try the .rhosts file */

	strcpy(rhosts_File,Downhill_User_Dir);

	strcat(rhosts_File,"\\");

	strcat(rhosts_File,_DOWNHILL_RHOSTS_FILE);

	file_Fs = fopen(rhosts_File,"rt");



	/* If it opened up, check it */

	if (file_Fs != NULL)

	{

		if (downhill_Ruserok_CheckHostUser(file_Fs,hostname_Client,

		 user_Server,user_Client))

		{

			fclose(file_Fs);

			return 0;

		}

		fclose(file_Fs);

	}



	/* Not allowed */

	return -1;

}

#endif

