/*
 *
 *  X11DEBUG - An X11 Transport Debugging Program for BSD UNIX
 *
 *  Based on the design for the MultiNet X11Debug command
 *  for MultiNet TCP/IP for VMS.
 *
 *	John McMahon
 *	TGV, Incorporated
 *	December 1992
 *
 *	Example Code for MultiNet Version 3.3
 *
 *  To compile under UNIX:
 *
 *	cc x11debug.c -o x11debug
 *
 *  The program will either take the display variable
 *  off of the command line using the "-display" option,
 *  or will use the DISPLAY environment variable.
 *
 *  -verbose provides informational messages as the
 *  program executes.
 *
 *******************************************************
 *
 * 3.3(1) - Initial Version
 *
 */

#define VERSION "3.3(1)"

#include <stdio.h>  /* printf() */
#include <stdlib.h> /* getenv() */
#include <string.h> /* strstr() */

#ifndef VMS
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#else
#include "multinet_root:[multinet.include.sys]types.h"
#include "multinet_root:[multinet.include.sys]ioctl.h"
#include "multinet_root:[multinet.include.sys]socket.h"
#include "multinet_root:[multinet.include.netinet]in.h"
#include "multinet_root:[multinet.include.arpa]inet.h"
#include "multinet_root:[multinet.include]netdb.h"
#include "multinet_root:[multinet.include]errno.h"
#define  perror socket_perror
#define  errno  socket_errno
#define  read   socket_read
#define  write  socket_write
#define  ioctl  socket_ioctl
#endif

#define DEBUG_PRINT if (verbose == 1) printf

adam12()
{
	printf("x11debug: See the X(1) man page for more information on DISPLAY variables\n");
}

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

	char	*display_variable;	/* the DISPLAY environment variable */
	char	*nodename;		/* nodename from DISPLAY */
	char	*server;		/* server number from DISPLAY */
	char	*screen;		/* screen number from DISPLAY */

	char	*zap;			/* workspace */
	char	*zero = "";		/* a zero */

	unsigned int	ip_address;	/* the ip address for nodename */

	struct hostent	*host_info;	/* the hostent structure for nodename */

	int			ip_socket;
	struct sockaddr_in	connect_info;
	int			waiting;

	int			verbose;	/* verbose flag */
	int			display_line;	/* display on cmd line */
	char			*display_arg;	/* ditto */

	char    auth_message[12]; /* basic authentication message for X11 */
	char	buffer[512];

	printf("X11 TCP/IP Transport Debug Program - Version %s\n",VERSION);

        /*
	 * X11 Client Authentication Message
	 * (Lifted from a few TCPDUMP tests
         * and the X11R4 Protocol Specification)
         */

	verbose = 0;
	display_line = 0;
	display_arg = NULL;

	while (++argv,--argc)
	{
		if (!strcmp(*argv,"-verbose"))
		{
			verbose = 1;
			continue;
		}
		if (!strcmp(*argv,"-display"))
		{
			display_line = 1;
			if (++argv,--argc) 
			{
				display_arg = *argv;
				continue;
			}
		}
		printf("x11debug: (error) valid command line options are  \n");
                printf("                                                  \n");
		printf("                    -display display-variable     \n");
		printf("                    -verbose                      \n");
		exit(1);
	}

	auth_message[0]  = 108; /* Byte order - LSB first         */
	auth_message[1]  = 0;   /* Null                           */
	auth_message[2]  = 11;  /* Protocol Major Version - LSB   */
	auth_message[3]  = 0;   /*                        - MSB   */
	auth_message[4]  = 0;   /* Protocol Minor Version - LSB   */
	auth_message[5]  = 0;   /*                        - MSB   */
	auth_message[6]  = 0;   /* Auth Proto Name Length - LSB   */
	auth_message[7]  = 0;   /*                        - MSB   */
	auth_message[8]  = 0;   /* Auth Proto Data Length - LSB   */
	auth_message[9]  = 0;   /*                        - MSB   */
	auth_message[10] = 0;   /* Unused                         */
        auth_message[11] = 0;   /* Unused                         */

	/*
	 *  
	 * Test 1 - Syntax check of the DISPLAY environment variable
	 *
	 * The user should have issued a "setenv DISPLAY foo" command
	 *
	 * or put it on the command line...
	 *
	 */

	DEBUG_PRINT("x11debug: (information) Starting Test 1 - DISPLAY variable syntax \n");

	if (display_arg == NULL)
	{
		display_variable = getenv("DISPLAY");
		if (display_variable == NULL) 
		{
			printf("x11debug: (error) The DISPLAY environment variable has not been\n");
			printf("          defined with the 'setenv' command\n");
			adam12();
			exit(1);
		}
	}
	else
	{
		display_variable = display_arg;
	}
	DEBUG_PRINT("x11debug: (information) DISPLAY variable is '%s'\n",display_variable);

	/*
	 *  Parse the DISPLAY variable
	 */

	nodename = display_variable; /* beginning of the string */
	server = strstr(display_variable,":"); /* start at the colon */
	if (server == NULL)
	{
		printf("x11debug: (error) No server number specified in the\n");
		printf("                  DISPLAY environment variable\n");
		adam12();
		exit(1);
	}
	else
	{
		server++;	/* get rid of the colon prefix */
	}

	screen = strstr(server,"."); /* start at the period after the colon */
	if (screen == NULL)
	{
		screen = "0";
		DEBUG_PRINT("x11debug: (information) No screen number specified in the\n");
		DEBUG_PRINT("                        DISPLAY environment variable,\n");
		DEBUG_PRINT("                        using default of zero\n");
	}
	else
	{
		screen++;	/* get rid of the period prefix */
	}

	/* finish cleaning the nodename string */

	zap = strchr(nodename,':');	/* find the first colon		*/
	strncpy(zap,zero,1);		/* terminate the string		*/
	if (strcmp(nodename,"") == 0)
	{
		printf("x11debug: (error) The hostname is set to NULL.  The transport\n");
		printf("                  being used is probably not TCP/IP          \n");
		adam12();
		exit(1);
	}

	DEBUG_PRINT("x11debug: (information) X server nodename is '%s'\n",nodename);

	/* finish cleaning the server number */

	zap = strchr(server,'.');	/* find the period		*/
	if (zap != NULL) strncpy(zap,zero,1); /* terminate the string		*/

	/* if the server number starts with a ':', then the protocol specified
           was DECnet, not IP.  */

	if (strchr(server,':') != NULL)
	{
		printf("x11debug: (error) The protocol specified in the\n");
                printf("                  DISPLAY environment variable is DECnet\n");
		adam12();
		exit(1);
	}
	else
	{
		DEBUG_PRINT("x11debug: (information) X server number is '%s'\n",server);
	}

	/* no cleaning needed for the screen number */
	
	DEBUG_PRINT("x11debug: (information) X screen number is '%s'\n",screen);

	/*
         *	validate the hostname
         */

	ip_address = inet_addr(nodename);

	if (ip_address == -1)
	{
		/* the nodename contains text */
		host_info = gethostbyname(nodename);
		if (host_info == NULL)
		{
			printf("x11debug: (error) Unable to get the IP address for '%s'\n",nodename);
			exit(1);
		}
		else
		{
			DEBUG_PRINT("x11debug: (information) Information for node '%s' was found\n",nodename);
		}
	}
	else
	{
		/* nope, it's an numeric IP address */
		DEBUG_PRINT("x11debug: (information) The nodename is a numeric IP address\n",screen);
	}

	/*
	 *
         *	Test 2 - Connectivity
         *
         */

	DEBUG_PRINT("x11debug: (information) Starting Test 2 - Connectivity test \n");


	ip_socket = socket(AF_INET,SOCK_STREAM,0); /* create the socket */

	if (ip_socket < 0)
	{
		perror("x11debug: socket");
		printf("x11debug: (error) Error creating TCP/IP socket\n");
		exit(1);
	}

	connect_info.sin_family = AF_INET; /* IP socket */

	if (ip_address == -1)
	{
		/* copy IP address to connect (text hostname) */
         	bcopy(host_info->h_addr,&connect_info.sin_addr,
                      host_info->h_length);
	}
	else
	{
                /* copy IP address to connect (numeric nodename) */
		bcopy(&ip_address,&connect_info.sin_addr,4);
	}

	/* target port number. 6000+server number */
	connect_info.sin_port = htons(6000+atoi(server) ); 

	DEBUG_PRINT("x11debug: (information) Target port number is '%d'\n",6000+atoi(server)    );

	/* here we go... */

	if (connect(ip_socket,&connect_info,sizeof(connect_info),0) < 0)
	{
		perror("x11debug: connect");
		if (errno == ECONNREFUSED)
		{
			printf("x11debug: (error) X Server '%s' on node '%s' is \n",server,nodename);
			printf("                  down, or the wrong server number has been specified\n");
		}
		else
		{
			printf("x11debug: (error) Network connection error.  Take action based on previous\n");
			printf("                  error message\n");
		}
		exit(1);
	}
	DEBUG_PRINT("x11debug: (information) Connected to X Server '%s' on node '%s'\n",server,nodename);

	/*
	 *
         *	Test 3 - Authorization
         *
         */

	/* send the auth message */

	DEBUG_PRINT("x11debug: (information) Starting Test 3 - Authentication test \n");

	if ( write(ip_socket,auth_message,sizeof(auth_message)) < 1)
	{
		perror("x11debug: write");
		printf("x11debug: (error) Network i/o error.  Take action based on previous\n");
		printf("                  error message\n");
		exit(1);
	}
	DEBUG_PRINT("x11debug: (information) Authorization message sent to server\n");

#ifdef notdef

	waiting = 0;

	while (waiting < 9)
	{
		DEBUG_PRINT("x11debug: (information) Waiting for data. \n");
		ioctl(ip_socket,FIONREAD,&waiting);
	}

#endif
                           
	if ( read(ip_socket,buffer,sizeof(buffer)) < 1)
	{
		perror("x11debug: read");
		printf("x11debug: (error) Network i/o error.  Take action based on previous\n");
		printf("                  error message\n");
		exit(1);
	}
	DEBUG_PRINT("x11debug: (information) Data returned from server\n");

	if (buffer[0] == 0) /* success/fail? */
	{
		printf("x11debug: (error) Server refused the connection,\n");
		if (buffer[1] > 0)
		{
			buffer[8+buffer[1]] = 0;
			printf("x11debug:         reason is '%s'\n",buffer+8);
		}
		else
		{
			printf("x11debug:         no reason provided by the server\n");
		}

		exit(1);

	}
	else
	{
		printf("x11debug: (information) Passed all x11debug tests!\n");
	}
	
}
