summaryrefslogtreecommitdiff
path: root/scope.c
diff options
context:
space:
mode:
Diffstat (limited to 'scope.c')
-rw-r--r--scope.c692
1 files changed, 692 insertions, 0 deletions
diff --git a/scope.c b/scope.c
new file mode 100644
index 0000000..9511f5a
--- /dev/null
+++ b/scope.c
@@ -0,0 +1,692 @@
+/* ******************************************************
+ * *
+ * A spy program to reveal X11 traffic *
+ * *
+ * James Peterson, 1988 *
+ * Copyright (C) 1988 MCC
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of MCC not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. MCC makes no
+ * representations about the suitability of this software for any purpose. It
+ * is provided "as is" without express or implied warranty.
+ *
+ * MCC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL MCC BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ * *
+ ***************************************************** */
+
+#include "scope.h"
+
+#include <sys/types.h> /* needed by sys/socket.h and netinet/in.h */
+#include <sys/uio.h> /* for struct iovec, used by socket.h */
+#include <sys/socket.h> /* for AF_INET, SOCK_STREAM, ... */
+#include <sys/ioctl.h> /* for FIONCLEX, FIONBIO, ... */
+#include <netinet/in.h> /* struct sockaddr_in */
+#include <netdb.h> /* struct servent * and struct hostent * */
+#include <errno.h> /* for EINTR, EADDRINUSE, ... */
+extern int errno;
+
+/* ********************************************** */
+/* */
+/* ********************************************** */
+
+#define DefaultPort 6000
+
+char ServerHostName[255];
+long ServerBasePort = DefaultPort;
+long ServerInPort = 1;
+long ServerOutPort = 0;
+long ServerDisplay = 0;
+
+
+/* ********************************************** */
+/* */
+/* */
+/* ********************************************** */
+
+short GetServerport ()
+{
+ short port;
+
+ enterprocedure("GetServerport");
+
+ port = ServerBasePort + ServerOutPort + ServerDisplay;
+ debug(4,(stderr, "Server service is on port %d\n", port));
+ return(port);
+}
+
+short GetScopePort ()
+{
+ short port;
+
+ enterprocedure("GetScopePort");
+
+ port = ServerBasePort + ServerInPort + ServerDisplay;
+ debug(4,(stderr, "scope service is on port %d\n", port));
+ return(port);
+}
+
+/* ********************************************** */
+/* */
+/* ********************************************** */
+
+Usage()
+{
+ fprintf(stderr, "Usage: xscope\n");
+ fprintf(stderr, " [-h<server-host>]\n");
+ fprintf(stderr, " [-i<in-port>]\n");
+ fprintf(stderr, " [-o<out-port>]\n");
+ fprintf(stderr, " [-d<display-number>]\n");
+ fprintf(stderr, " [-v<n>] -- verbose output\n");
+ fprintf(stderr, " [-q] -- quiet output\n");
+ fprintf(stderr, " [-D<debug-level>]\n");
+ exit(1);
+}
+
+
+char *OfficialName(); /* forward type declaration */
+
+ScanArgs(argc, argv)
+ int argc;
+ char **argv;
+{
+ NoExtensions = false /* default is to allow extensions */;
+ Verbose = 1 /* default verbose-ness level */;
+ RequestSync = false /* default is to just copy blocks */;
+
+ /* Scan argument list */
+ while (--argc > 0)
+ {
+ ++argv;
+ if (**argv == '-')
+ switch (*++*argv)
+ {
+ /*
+ debug levels:
+ 2 - trace each procedure entry
+ 4 - I/O, connections
+ 8 - Scope internals
+ 16 - Message protocol
+ 32 - 64 - malloc
+ 128 - 256 - really low level
+ */
+ case 'D':
+ debuglevel = atoi(++*argv);
+ if (debuglevel == 0)
+ debuglevel = 255;
+ debuglevel |= 1;
+ Verbose = 7;
+ debug(1,(stderr, "debuglevel = %d\n", debuglevel));
+ break;
+
+ case 'q': /* quiet mode */
+ Verbose = 0;
+ debug(1,(stderr, "Verbose = %d\n", Verbose));
+ break;
+
+ case 'v': /* verbose mode */
+ Verbose = atoi(++*argv);
+ debug(1,(stderr, "Verbose = %d\n", Verbose));
+ break;
+
+ case 's': /* synchronous mode */
+ RequestSync = true;
+ debug(1,(stderr, "RequestSync true\n"));
+ break;
+
+ case 'o': /* output port */
+ ServerOutPort = atoi(++*argv);
+ if (ServerOutPort <= 0)
+ ServerOutPort = 0;
+ debug(1,(stderr, "ServerOutPort = %d\n", ServerOutPort));
+ break;
+
+ case 'd':
+ ServerDisplay = atoi(++*argv);
+ if (ServerDisplay <= 0)
+ ServerDisplay = 0;
+ debug(1,(stderr, "ServerDisplay=%d\n", ServerDisplay));
+ break;
+
+ case 'i':
+ ServerInPort = atoi(++*argv);
+ if (ServerInPort <= 0)
+ ServerInPort = 0;
+ debug(1,(stderr, "ServerInPort = %d\n", ServerInPort));
+ break;
+
+ case 'h':
+ if (++*argv != NULL && **argv != '\0')
+ (void)strcpy(ServerHostName, OfficialName(*argv));
+ debug(1,(stderr, "ServerHostName=%s\n", ServerHostName));
+ break;
+
+ case 'x':
+ NoExtensions = true;
+ debug(1,(stderr, "NoExtensions\n"));
+ break;
+
+ default:
+ fprintf(stderr, "Unknown option %c\n", **argv);
+ Usage();
+ break;
+
+ }
+ else
+ {
+ /* file argument to scope -- error */
+ Usage();
+ }
+ }
+
+ /* check for different port numbers or different machines */
+ if (ServerInPort == ServerOutPort)
+ if (ServerHostName[0] == '\0')
+ {
+ fprintf(stderr, "Can't have xscope on same port as server (%d)\n",
+ ServerInPort);
+ Usage();
+ }
+
+}
+
+
+/* ********************************************** */
+/* */
+/* ********************************************** */
+
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ ScanArgs(argc, argv);
+ InitializeFD();
+ InitializeX11();
+ SetUpStdin();
+ SetUpConnectionSocket(GetScopePort());
+ SetSignalHandling();
+
+ MainLoop();
+ exit(0);
+}
+
+TimerExpired()
+{
+ debug(16,(stderr, "Timer tick\n"));
+}
+
+/* ********************************************** */
+/* */
+/* ********************************************** */
+
+/*
+ here is where we would add code to allow control from
+ the keyboard. We would want to read a command and
+ interpret it. Possibilties:
+
+ (a) verbose level setting
+ (b) reset time
+ (c) save X requests to a file.
+ (d) replay X requests from a file.
+ (e) allow fake events, errors to be generated.
+*/
+
+ReadStdin(fd)
+ FD fd;
+{
+ char buf[2048];
+ long n;
+
+ enterprocedure("ReadStdin");
+ n = read(fd, buf, 2048);
+ debug(4,(stderr, "read %d bytes from stdin\n", n));
+}
+
+SetUpStdin()
+{
+ enterprocedure("SetUpStdin");
+ /* UsingFD(fileno(stdin), ReadStdin); */
+}
+
+/* ************************************************************ */
+/* */
+/* */
+/* ************************************************************ */
+
+/*
+ xscope is really meant to look at one client at a time. However,
+ it can easily handle multiple clients and servers. To do so,
+ we need to have a pair of FDs: one for the client and one for the
+ server for that client. If either goes away, so does the other.
+ We need to be able to identify the other FD of a pair, so that if
+ we get input from one, we can write it to the other.
+*/
+
+struct fdinfo
+{
+ Boolean Server;
+ long ClientNumber;
+ FD pair;
+};
+
+static long ClientNumber = 0;
+struct fdinfo FDinfo[StaticMaxFD];
+
+SetUpPair(client, server)
+ FD client;
+ FD server;
+{
+ if (client >= 0)
+ {
+ ClientNumber += 1;
+ FDinfo[client].Server = false;
+ FDinfo[client].pair = server;
+ FDinfo[client].ClientNumber = ClientNumber;
+ if (server >= 0)
+ {
+ FDinfo[server].Server = true;
+ FDinfo[server].pair = client;
+ FDinfo[server].ClientNumber = FDinfo[client].ClientNumber;
+ }
+ }
+ else if (server >= 0)
+ {
+ (void)close(server);
+ NotUsingFD(server);
+ }
+}
+
+
+CloseConnection(fd)
+ FD fd;
+{
+ debug(4,(stderr, "close %d and %d\n", fd, FDPair(fd)));
+ StopClientConnection(ServerHalf(fd));
+ StopServerConnection(ClientHalf(fd));
+
+ (void)close(fd);
+ NotUsingFD(fd);
+ (void)close(FDPair(fd));
+ NotUsingFD(FDPair(fd));
+}
+
+/* ************************************************************ */
+
+FD FDPair(fd)
+ FD fd;
+{
+ return(FDinfo[fd].pair);
+}
+
+FD ClientHalf(fd)
+ FD fd;
+{
+ if (FDinfo[fd].Server)
+ return(FDinfo[fd].pair);
+ return(fd);
+}
+
+FD ServerHalf(fd)
+ FD fd;
+{
+ if (FDinfo[fd].Server)
+ return(fd);
+ return(FDinfo[fd].pair);
+}
+
+char *ClientName (fd)
+ FD fd;
+{
+ static char name[20];
+
+ (void)sprintf(name, " %s %d",
+ (FDinfo[fd].Server ? "Server" : "Client"),
+ FDinfo[fd].ClientNumber);
+ return(name);
+}
+
+
+/* ********************************************** */
+/* */
+/* ********************************************** */
+
+/* when we get data from a client, we read it in, copy it to the
+ server for this client, then dump it to the client. Note, we don't
+ have to have a server, if there isn't one. */
+
+DataFromClient(fd)
+ FD fd;
+{
+ unsigned char buf[2048];
+ long n;
+ FD ServerFD;
+
+ enterprocedure("DataFromClient");
+ n = read(fd, (char *)buf, 2048);
+ debug(4,(stderr, "read %d bytes from %s\n", n, ClientName(fd)));
+ if (n < 0)
+ {
+ PrintTime();
+ perror("Client --> read error:");
+ CloseConnection(fd);
+ return;
+ }
+ if (n == 0)
+ {
+ PrintTime();
+ fprintf(stdout, "%s --> EOF\n", ClientName(fd));
+ CloseConnection(fd);
+ return;
+ }
+ if (debuglevel > 4)
+ {
+ fflush(stdout); fflush(stderr);
+ DumpHexBuffer(buf, n);
+ fprintf(stdout,"\n");
+ fflush(stdout); fflush(stderr);
+ }
+
+ ServerFD = FDPair(fd);
+ if (ServerFD < 0)
+ {
+ ServerFD = ConnectToServer(false);
+ SetUpPair(fd, ServerFD);
+ }
+
+ /* write bytes from client to server, allow for server to fail */
+ if (!RequestSync) WriteBytes(ServerFD, buf, n);
+
+ /* also report the bytes to standard out */
+ ReportFromClient(fd, buf, n);
+}
+
+/* ********************************************** */
+/* */
+/* ********************************************** */
+
+/* similar situation for the server, but note that if there is no client,
+ we close the connection down -- don't need a server with no client. */
+
+DataFromServer(fd)
+ FD fd;
+{
+ unsigned char buf[2048];
+ long n;
+ FD ClientFD;
+
+ enterprocedure("DataFromServer");
+ n = read(fd, (char *)buf, 2048);
+ debug(4,(stderr, "read %d bytes from %s\n", n, ClientName(fd)));
+ if (n < 0)
+ {
+ PrintTime();
+ perror("read error <- Server");
+ CloseConnection(fd);
+ return;
+ }
+ if (n == 0)
+ {
+ PrintTime();
+ fprintf(stdout, "EOF <-- %s\n", ClientName(fd));
+ CloseConnection(fd);
+ return;
+ }
+ if (debuglevel > 4)
+ {
+ fflush(stdout); fflush(stderr);
+ DumpHexBuffer(buf, n);
+ fprintf(stdout,"\n");
+ fflush(stdout); fflush(stderr);
+ }
+
+ ClientFD = FDPair(fd);
+ if (ClientFD < 0)
+ {
+ CloseConnection(fd);
+ return;
+ }
+
+ if (NoExtensions)
+ {
+ if (buf[0] == 1) /* reply code */
+ {
+ short SequenceNumber = IShort (&buf[2]);
+ short Request = CheckReplyTable (fd, SequenceNumber);
+ if (Request == 98) /* Query Extension */
+ if (buf[8] == 1)
+ buf[8] = 0;
+ }
+ }
+
+ /* write bytes from server to client, allow for client to fail */
+ WriteBytes(ClientFD, buf, n);
+
+ /* also report the bytes to standard out */
+ ReportFromServer(fd, buf, n);
+}
+
+
+
+/* ************************************************************ */
+/* */
+/* */
+/* ************************************************************ */
+
+WriteBytes(fd, buf, n)
+FD fd;
+unsigned char *buf;
+long n;
+{
+ long BytesToWrite = n;
+ unsigned char *p = buf;
+
+ if (fd < 0) return;
+ if (!ValidFD(fd)) return;
+
+ while (BytesToWrite > 0)
+ {
+ int n1 = write (fd, (char *)p, (int)BytesToWrite);
+ debug(4,(stderr, "write %d bytes to %s\n", n1, ClientName(fd)));
+ if (n1 > 0)
+ {
+ BytesToWrite -= n1;
+ p += n1;
+ }
+ else
+ {
+ char message[255];
+ sprintf(message,"Error on write to %s", ClientName(fd));
+ perror(message);
+ CloseConnection(fd);
+ BytesToWrite = 0;
+ }
+ }
+}
+
+
+/* ************************************************************ */
+/* */
+/* Create New Connection to a client program and to Server */
+/* */
+/* ************************************************************ */
+
+static int ON = 1 /* used in ioctl */ ;
+
+NewConnection(fd)
+ FD fd;
+{
+ FD ServerFD = -1;
+ FD ClientFD = -1;
+
+ ClientFD = ConnectToClient(fd);
+ ServerFD = ConnectToServer(true);
+ SetUpPair(ClientFD, ServerFD);
+}
+
+
+/* ************************************************************ */
+
+FD ConnectToClient(ConnectionSocket)
+ FD ConnectionSocket;
+{
+ FD ClientFD;
+ struct sockaddr_in from;
+ size_t len = sizeof (from);
+
+ enterprocedure("ConnectToClient");
+
+ ClientFD = accept(ConnectionSocket, (struct sockaddr *)&from, &len);
+ debug(4,(stderr, "Connect To Client: FD %d\n", ClientFD));
+ if (ClientFD < 0 && errno == EWOULDBLOCK)
+ {
+ debug(4,(stderr, "Almost blocked accepting FD %d\n", ClientFD));
+ panic("Can't connect to Client");
+ }
+ if (ClientFD < 0)
+ {
+ debug(4,(stderr, "NewConnection: error %d\n", errno));
+ panic("Can't connect to Client");
+ }
+
+ UsingFD(ClientFD, DataFromClient);
+#ifdef FIOCLEX
+ (void)ioctl(ClientFD, FIOCLEX, 0);
+#endif /* #ifdef FIOCLEX */
+ (void)ioctl(ClientFD, FIONBIO, &ON);
+ StartClientConnection(ClientFD);
+ return(ClientFD);
+}
+
+
+
+/* ************************************************************ */
+/* */
+/* */
+/* ************************************************************ */
+
+
+
+FD ConnectToServer(report)
+ Boolean report;
+{
+ FD ServerFD;
+ struct sockaddr_in sin;
+ struct hostent *hp;
+#ifndef SO_DONTLINGER
+ struct linger linger;
+#endif /* #ifndef SO_DONTLINGER */
+
+ enterprocedure("ConnectToServer");
+
+ /* establish a socket to the name server for this host */
+ bzero((char *)&sin, sizeof(sin));
+ ServerFD = socket(AF_INET, SOCK_STREAM, 0);
+ if (ServerFD < 0)
+ {
+ perror("socket() to Server failed");
+ debug(1,(stderr, "socket failed\n"));
+ panic("Can't open connection to Server");
+ }
+ (void) setsockopt(ServerFD, SOL_SOCKET, SO_REUSEADDR, (char *) NULL, 0);
+#ifdef SO_USELOOPBACK
+ (void) setsockopt(ServerFD, SOL_SOCKET, SO_USELOOPBACK,(char *) NULL, 0);
+#endif /* #ifdef SO_USELOOPBACK */
+#ifdef SO_DONTLINGER
+ (void) setsockopt(ServerFD, SOL_SOCKET, SO_DONTLINGER, (char *) NULL, 0);
+#else /* #ifdef SO_DONTLINGER */
+ linger.l_onoff = 0;
+ linger.l_linger = 0;
+ (void) setsockopt(ServerFD, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof linger);
+#endif /* #ifdef SO_DONTLINGER */
+
+ /* determine the host machine for this process */
+ if (ServerHostName[0] == '\0')
+ (void) gethostname(ServerHostName, sizeof (ServerHostName));
+ debug(4,(stderr, "try to connect on %s\n", ServerHostName));
+
+ hp = gethostbyname(ServerHostName);
+ if (hp == 0)
+ {
+ perror("gethostbyname failed");
+ debug(1,(stderr, "gethostbyname failed for %s\n", ServerHostName));
+ panic("Can't open connection to Server");
+ }
+
+ sin.sin_family = AF_INET;
+ bcopy((char *)hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
+ sin.sin_port = htons(GetServerport());
+
+ if ((sin.sin_port == ScopePort)
+ && strcmp(ServerHostName, ScopeHost) == 0)
+ {
+ char error_message[100];
+ (void)sprintf(error_message, "Trying to attach to myself: %s,%d\n",
+ ServerHostName, sin.sin_port);
+ panic(error_message);
+ }
+
+ /* ******************************************************** */
+ /* try to connect to Server */
+
+ if (connect(ServerFD, (struct sockaddr *)&sin, sizeof(sin)) < 0)
+ {
+ debug(4,(stderr, "connect returns errno of %d\n", errno));
+ if (errno != 0)
+ if (report)
+ perror("connect");
+ switch (errno)
+ {
+ case ECONNREFUSED:
+ /* experience says this is because there is no Server
+ to connect to */
+ (void)close(ServerFD);
+ debug(1,(stderr, "No Server\n"));
+ if (report)
+ warn("Can't open connection to Server");
+ return(-1);
+
+ default:
+ (void)close(ServerFD);
+ panic("Can't open connection to Server");
+ }
+ }
+
+ debug(4,(stderr, "Connect To Server: FD %d\n", ServerFD));
+ if (ServerFD >= 0)
+ {
+ UsingFD(ServerFD, DataFromServer);
+ StartServerConnection(ServerFD);
+ }
+ return(ServerFD);
+}
+
+
+/* ********************************************** */
+/* */
+/* ********************************************** */
+
+char *OfficialName(name)
+char *name;
+{
+ struct hostent *HostEntry;
+
+ HostEntry = gethostbyname(name);
+ if (HostEntry == NULL)
+ {
+ perror("gethostbyname");
+ exit(-1);
+ }
+ debug(4,(stderr, "Official name of %s is %s\n", name, HostEntry->h_name));
+ return(HostEntry->h_name);
+}
+