summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaleb Keithley <kaleb@freedesktop.org>2003-11-14 15:54:53 +0000
committerKaleb Keithley <kaleb@freedesktop.org>2003-11-14 15:54:53 +0000
commit1dea1475a4beb620f350eda4ff0f902c9ec274a9 (patch)
tree08b99411d48a1351e5d7af3cd400b742a464bc3e
R6.6 is the Xorg base-lineXORG-MAINXORG-STABLE
-rw-r--r--io.c1312
-rw-r--r--io.h53
-rw-r--r--misc.c1508
-rw-r--r--misc.h111
-rw-r--r--pm.c538
-rw-r--r--pm.h69
-rw-r--r--transport.c334
-rw-r--r--transport.h70
-rw-r--r--xfwp.c151
-rw-r--r--xfwp.h318
-rw-r--r--xfwp.man389
11 files changed, 4853 insertions, 0 deletions
diff --git a/io.c b/io.c
new file mode 100644
index 0000000..f0b2518
--- /dev/null
+++ b/io.c
@@ -0,0 +1,1312 @@
+/* $Xorg: io.c,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */
+/*
+
+Copyright "1986-1997, 1998 The Open Group
+
+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.
+
+The above copyright notice and the following permission notice
+shall be included in all copies of the Software:
+
+THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
+CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN
+THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group
+shall not be used in advertising or otherwise to promote the use
+or other dealings in this Software without prior written
+authorization from The Open Group.
+
+X Window System is a trademark of The Open Group.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <X11/Xos.h> /* Needed here for SunOS */
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <assert.h>
+
+#include <X11/X.h>
+#include <X11/Xproto.h>
+#include <X11/Xfuncs.h>
+
+#include <X11/ICE/ICElib.h>
+
+#include "xfwp.h"
+#include "misc.h"
+#include "pm.h"
+#include "transport.h"
+#include "io.h"
+
+#ifdef X_NOT_STDC_ENV
+extern int errno;
+#endif
+
+/*
+ * Error messages returned to clients who are denied access
+ */
+static char * server_reason[2] = {
+ "Authentication rejected",
+ "permission denied"
+};
+
+
+static void
+RemoveFDFromServerListenArray (
+ int fd_counter,
+ fd_set * rinit,
+ int num_servers)
+{
+ /*
+ * File descriptor fd_counter had a read failure. If this fd
+ * is associated with an Xserver, then we assume the Xserver is
+ * un-usable (e.g. has died) thus we should no longer listen for
+ * for connections on the client listen port associated with
+ * this Xserver.
+ */
+
+ int i;
+
+ for (i = 0; i < num_servers; i++)
+ {
+ if (server_array[i] != NULL &&
+ server_array[i]->server_fd == fd_counter)
+ {
+ FD_CLR (server_array[i]->client_listen_fd, rinit);
+ (void) close (server_array[i]->client_listen_fd);
+ if (server_array[i]->x_server_hostport)
+ free ((char *) server_array[i]->x_server_hostport);
+ if (server_array[i]->listen_port_string)
+ free ((char *) server_array[i]->listen_port_string);
+ free ((char *) server_array[i]);
+ server_array[i] = NULL;
+ break;
+ }
+ }
+}
+
+static void
+doProcessWritables(
+ int fd_counter,
+ fd_set * rinit,
+ fd_set * winit)
+{
+ int bytes_written;
+ int remainder;
+
+ /*
+ * start off by writing from the selected fd to its connection
+ * partner
+ */
+ if (client_conn_array[fd_counter]->wbytes)
+ {
+ /*
+ * See how much you manage to write
+ */
+ bytes_written = write (fd_counter,
+ client_conn_array[fd_counter]->writebuf,
+ client_conn_array[fd_counter]->wbytes);
+ /*
+ * handle some common error conditions
+ */
+ if (bytes_written == -1)
+ {
+ /*
+ * no process attached to the other end of this socket
+ */
+ if (errno == EPIPE)
+ {
+ (void) fprintf (stderr, "write error - EPIPE\n");
+ perror("socket write");
+ }
+ /*
+ * clean up
+ */
+ FD_CLR(fd_counter, rinit);
+ FD_CLR(fd_counter, winit);
+ (void) close (fd_counter);
+
+ if (client_conn_array[fd_counter]->conn_to != -1)
+ {
+ FD_CLR(client_conn_array[fd_counter]->conn_to, rinit);
+ FD_CLR(client_conn_array[fd_counter]->conn_to, winit);
+ (void) close (client_conn_array[fd_counter]->conn_to);
+ }
+
+ client_conn_array[client_conn_array[fd_counter]->conn_to]->conn_to
+ = -1;
+ client_conn_array[fd_counter]->conn_to = -1;
+ if (client_conn_array[fd_counter]->source)
+ free(client_conn_array[fd_counter]->source);
+ if (client_conn_array[fd_counter]->destination)
+ free(client_conn_array[fd_counter]->destination);
+ free(client_conn_array[fd_counter]);
+ client_conn_array[fd_counter] = NULL;
+ return;
+ } else
+ {
+ /*
+ * no errors on write, but did you write everything in the buffer?
+ */
+ remainder = client_conn_array[fd_counter]->wbytes - bytes_written;
+ if (remainder)
+ {
+ /*
+ * move any remainder to front of writebuffer and adjust
+ * writebuf byte counter
+ */
+ bcopy(client_conn_array[fd_counter]->writebuf + bytes_written,
+ client_conn_array[fd_counter]->writebuf,
+ remainder);
+ client_conn_array[fd_counter]->wbytes = remainder;
+ } else
+ /*
+ * writebuffer *must* be empty, so zero byte counter
+ */
+ client_conn_array[fd_counter]->wbytes = 0;
+
+ /*
+ * take this opportunity to get more read data, if any present
+ */
+ if ((client_conn_array[fd_counter]->conn_to != -1) &&
+ (client_conn_array[client_conn_array[fd_counter]->conn_to]->rbytes))
+ doCopyFromTo(client_conn_array[fd_counter]->conn_to,
+ fd_counter,
+ rinit,
+ winit);
+
+ /*
+ * If the above operation didn't put anything on the writebuffer,
+ * and the readables handler marked the fd ready to close, then
+ * clean up and close the connection; otherwise, simply clear the
+ * fd_set mask for this fd
+ */
+ if (client_conn_array[fd_counter]->wbytes == 0)
+ {
+ if (client_conn_array[fd_counter]->wclose == 1)
+ {
+ FD_CLR(fd_counter, rinit);
+ client_conn_array[fd_counter]->conn_to = -1;
+ client_conn_array[fd_counter]->wclose = 0;
+ (void) close (fd_counter);
+ if (client_conn_array[fd_counter]->source)
+ free(client_conn_array[fd_counter]->source);
+ if (client_conn_array[fd_counter]->destination)
+ free(client_conn_array[fd_counter]->destination);
+ free(client_conn_array[fd_counter]);
+ client_conn_array[fd_counter] = NULL;
+ }
+ FD_CLR(fd_counter, winit);
+ }
+ /*
+ * since we just wrote data to the conn_to fd, mark it as ready
+ * to check for reading when we go through select() the next time
+ */
+ if (client_conn_array[fd_counter]->conn_to != -1)
+ FD_SET(client_conn_array[fd_counter]->conn_to, rinit);
+ } /* end else no errors on write */
+ } else
+ {
+ /*
+ * There was nothing to write on this fd (can't see how we'd get
+ * here if select() returned this fd as writable, but it's in
+ * XForward so who am I to say?!)
+ */
+ if ((client_conn_array[fd_counter]->conn_to != -1) &&
+ (client_conn_array[client_conn_array[fd_counter]->conn_to]->rbytes))
+ {
+ doCopyFromTo (client_conn_array[fd_counter]->conn_to,
+ fd_counter,
+ rinit,
+ winit);
+ /*
+ * if you got anything to write, then proceed to next
+ * iter of select()
+ */
+ if (client_conn_array[fd_counter]->wbytes)
+ return;
+ }
+
+ /*
+ * You didn't get anything from that copy; check to see if it was
+ * because the readables handler marked the fd closed; if so,
+ * close this association; otherwise, simply clear the fd_set
+ * writable mask for this fd
+ */
+ if (client_conn_array[fd_counter]->wclose)
+ {
+ FD_CLR(fd_counter, rinit);
+ client_conn_array[fd_counter]->conn_to = -1;
+ client_conn_array[fd_counter]->wclose = 0;
+ (void) close (fd_counter);
+ if (client_conn_array[fd_counter]->source)
+ free(client_conn_array[fd_counter]->source);
+ if (client_conn_array[fd_counter]->destination)
+ free(client_conn_array[fd_counter]->destination);
+ free(client_conn_array[fd_counter]);
+ client_conn_array[fd_counter] = NULL;
+ }
+ FD_CLR(fd_counter, winit);
+ }
+}
+
+static void
+ProcessNewPMConnection (
+ int * nfds,
+ fd_set * rinit,
+ struct config * config_info,
+ IceListenObj ** listen_objects,
+ int listen_fd)
+
+{
+ IceConn new_ice_conn;
+ IceAcceptStatus accept_status;
+ int temp_sock_fd;
+ IceListenObj * temp_obj;
+ struct timeval time_val;
+ struct timezone time_zone;
+ struct sockaddr_in temp_sockaddr_in;
+ struct sockaddr_in server_sockaddr_in;
+ int retval;
+ int addrlen = sizeof(temp_sockaddr_in);
+ int rule_number;
+ int pm_idx;
+
+ /*
+ * Only continue if there is room for another PM connection.
+ */
+ for (pm_idx = 0; pm_idx < config_info->num_pm_conns; pm_idx++)
+ {
+ if (!pm_conn_array[pm_idx])
+ break;
+ }
+ if (pm_idx >= config_info->num_pm_conns)
+ {
+ (void) fprintf (stderr,
+ "Maximum number of PM connections has been reached (%d)\n",
+ config_info->num_pm_conns);
+
+ /*
+ * Must accept and then close this connection or the PM will
+ * continue to poll.
+ */
+ temp_obj = *listen_objects;
+ new_ice_conn = IceAcceptConnection(temp_obj[listen_fd], &accept_status);
+ if (new_ice_conn)
+ IceCloseConnection(new_ice_conn);
+
+ return;
+ }
+
+ /*
+ * accept the connection if you can, use pm_listen_array
+ * index to index into ICE listen_object list (this is because the
+ * listen_objects list must correspond to the pm_listen_array)
+ */
+ temp_obj = *listen_objects;
+ new_ice_conn = IceAcceptConnection(temp_obj[listen_fd], &accept_status);
+ if (!new_ice_conn)
+ {
+ static int been_here;
+
+ /*
+ * ICE initialization (bug?) makes this happen the
+ * first time readables is hit.
+ */
+ if (!been_here)
+ been_here++;
+ else
+ (void) fprintf(stderr, "IceAcceptConnection failed (%d)\n",
+ accept_status);
+ return;
+ }
+
+ /*
+ * extract the fd from this new connection; remember, the fd of
+ * the listen socket is *not* the fd of the actual connection!
+ */
+ temp_sock_fd = IceConnectionNumber(new_ice_conn);
+
+ /*
+ * before we get any further, do a config check on the new ICE
+ * connection; start by using getpeername() to get endpoint info
+ */
+ retval = getpeername(temp_sock_fd,
+ (struct sockaddr*)&temp_sockaddr_in,
+ &addrlen);
+ if (retval)
+ {
+ IceCloseConnection(new_ice_conn);
+ (void) fprintf(stderr, "getpeername call failed\n");
+ return;
+ }
+
+ assert(temp_sockaddr_in.sin_family == AF_INET);
+
+ /*
+ * Do a configuration check. NOTE: we're not doing anything
+ * with the server_sockaddr_in argument
+ */
+ if ((doConfigCheck(&temp_sockaddr_in,
+ &server_sockaddr_in,
+ config_info,
+ PMGR,
+ &rule_number)) == FAILURE)
+ {
+ /*
+ * close the PM connection
+ *
+ */
+ (void) fprintf(stderr, "The Proxy Manager failed the configuration check\n");
+ IceCloseConnection(new_ice_conn);
+ return;
+ }
+
+ /*
+ * you've started the connection process; allocate a buffer
+ * for this connection, then continue processing other fd's without
+ * blocking while waiting to read the coming PM data; [NOTE:
+ * we use the fd of the connection socket as index into the
+ * pm_conn_array; this saves us much troublesome linked-list
+ * management!]
+ */
+ if ((pm_conn_array[pm_idx] =
+ (struct pm_conn_buf *) malloc(sizeof(struct pm_conn_buf))) == NULL)
+ {
+ (void) fprintf (stderr, "malloc - PM connection object\n");
+ return;
+ }
+
+ /*
+ * save the ICEconn struct for future status checks; also
+ * the fd (although you could extract it from the ICEconn
+ * each time you need it, but that's a pain)
+ */
+ pm_conn_array[pm_idx]->fd = temp_sock_fd;
+ pm_conn_array[pm_idx]->ice_conn = new_ice_conn;
+
+ /*
+ * Set the readables select() to listen for a readable on this
+ * fd; remember, we're not interested in pm writables, since
+ * all the negotiation is handled inside this routine; adjust
+ * the nfds (must do that everytime we get a new socket to
+ * select() on), and then contnue processing current selections
+ */
+ FD_SET(temp_sock_fd, rinit);
+ *nfds = max(*nfds, temp_sock_fd + 1);
+
+ /*
+ * this is where we initialize the current time and timeout on this
+ * pm_connection object
+ */
+ (void) gettimeofday(&time_val, &time_zone);
+ pm_conn_array[pm_idx]->creation_time = time_val.tv_sec;
+ pm_conn_array[pm_idx]->time_to_close = config_info->pm_data_timeout;
+}
+
+static void
+ProcessPMInput (
+ int * nfds,
+ fd_set * rinit,
+ int pm_idx)
+{
+ IceProcessMessagesStatus process_status;
+
+ switch (IceConnectionStatus(pm_conn_array[pm_idx]->ice_conn))
+ {
+ case IceConnectPending:
+ /*
+ * for some reason this connection still isn't ready for
+ * reading, so return and try next readable
+ */
+ (void) IceProcessMessages(pm_conn_array[pm_idx]->ice_conn,
+ NULL, NULL);
+ break;
+
+ case IceConnectAccepted:
+ /*
+ * you're ready to read the PM data, allocate and send back
+ * your client listen port, etc., etc.; do this inside
+ * FWPprocessMessages() by calling IceProcessMessages()
+ * [NOTE: The NULL args set it up for non-blocking]
+ */
+ process_status = IceProcessMessages(pm_conn_array[pm_idx]->ice_conn,
+ NULL, NULL);
+
+ switch (process_status)
+ {
+ case IceProcessMessagesSuccess:
+
+ /*
+ * you read the server data, allocated a listen port
+ * for the remote client and wrote it back to the PM,
+ * so you don't need to do anything more until PM
+ * closes the connection (NOTE: Make sure we don't
+ * do this more than once!!)
+ */
+ break;
+
+ case IceProcessMessagesIOError:
+ IceProcessMessagesConnectionClosed:
+
+ if (process_status == IceProcessMessagesIOError)
+ /*
+ * there was a problem with the connection, close
+ * it explicitly
+ */
+ IceCloseConnection(pm_conn_array[pm_idx]->ice_conn);
+ else
+ /*
+ * the connection somehow closed itself, so don't call
+ * IceCloseConnection
+ */
+ ;
+
+ /*
+ * reset the select() readables mask and nfds, free
+ * the buffer memory on this array element, reset the
+ * pointer to NULL and return
+ */
+ FD_CLR(pm_conn_array[pm_idx]->fd, rinit);
+ *nfds = max(*nfds, pm_conn_array[pm_idx]->fd + 1);
+ free(pm_conn_array[pm_idx]);
+ pm_conn_array[pm_idx] = NULL;
+ break;
+
+ default:
+ /*
+ * Should never get here since all of the return
+ * codes from IceProcessMessages have been checked.
+ */
+ (void) fprintf (stderr, "IceProcessMessages error\n");
+ }
+ break;
+
+ case IceConnectRejected:
+ /*
+ * don't know yet what to do in this case, but for now simply
+ * output diagnostic and return to select() processing
+ */
+ (void) fprintf (stderr, "PM IceConnectRejected\n");
+ break;
+
+ case IceConnectIOError:
+ /*
+ * don't know yet what to do in this case, but for now simply
+ * output diagnostic and return to select() processing
+ */
+ (void) fprintf (stderr, "PM IceConnectIOError\n");
+ break;
+
+ default:
+ /*
+ * Should never get here since all of the return
+ * codes from IceConnectionStatus have been checked.
+ */
+ (void) fprintf (stderr, "IceConnectionStatus error\n");
+ }
+}
+
+static void
+ProcessNewClientConnection (
+ int * nfds,
+ fd_set * rinit,
+ struct config * config_info,
+ int accept_fd,
+ int server_idx)
+{
+ int temp_sock_fd;
+ struct sockaddr_in temp_sockaddr_in;
+ struct timeval time_val;
+ struct timezone time_zone;
+ int rule_number = -1;
+ int server_fd;
+ struct sockaddr_in server_sockaddr_in;
+ int temp_sock_len = sizeof(temp_sockaddr_in);
+
+ /*
+ * The first thing we do is accept() this connection and check it
+ * against configuration data to see whether its origination host
+ * is allowed; next, we connect to the server found in the lookup,
+ * synthesize a proxy connection setup request to be sent
+ * to that server to determine whether it`s a secure server;
+ * we can't block on the server reply so we go ahead and allocate
+ * a data structure for this client connection, and mark its
+ * state PENDING
+ */
+
+ if ((temp_sock_fd = accept(accept_fd,
+ (struct sockaddr *) &temp_sockaddr_in,
+ &temp_sock_len)) < 0)
+ {
+ (void) fprintf (stderr, "accept call for a client failed\n");
+ return;
+ }
+
+ /*
+ * Try to initialize and open a connection to the server. If
+ * an error occurs, those functions will output an appropriate
+ * message
+ */
+ if ((doServerConnectSetup(server_array[server_idx]->x_server_hostport,
+ &server_array[server_idx]->server_fd,
+ &server_sockaddr_in)) == FAILURE)
+ {
+ (void) close (temp_sock_fd);
+ return;
+ }
+ if ((doServerConnect(&server_array[server_idx]->server_fd,
+ &server_sockaddr_in)) == FAILURE)
+ {
+ (void) close (temp_sock_fd);
+ (void) close (server_array[server_idx]->server_fd);
+ return;
+ }
+
+ server_fd = server_array[server_idx]->server_fd;
+
+ /*
+ * do config check on client source and destination (must do
+ * it here because otherwise we don't have a server socket
+ * to query and we may not be able to resolve server name
+ * alone from xfindproxy()
+ */
+ if ((doConfigCheck(&temp_sockaddr_in,
+ &server_sockaddr_in,
+ config_info,
+ CLIENT,
+ &rule_number)) == FAILURE)
+ {
+ /*
+ * log the client connection failure, close client and server
+ * sockets and return
+ */
+ doWriteLogEntry (inet_ntoa(temp_sockaddr_in.sin_addr),
+ inet_ntoa(server_sockaddr_in.sin_addr),
+ CLIENT_REJECT_CONFIG,
+ rule_number,
+ config_info);
+ (void) close (temp_sock_fd);
+ (void) close (server_fd);
+ return;
+ }
+
+ /*
+ * If configured authorization succeeds, go ahead and
+ * allocate a client_conn_buf struct for client connection
+ */
+ if ((client_conn_array[temp_sock_fd] = (struct client_conn_buf *)
+ malloc(sizeof (struct client_conn_buf))) == NULL)
+ {
+ (void) fprintf (stderr, "malloc - client connection buffer\n");
+ return;
+ }
+ bzero (client_conn_array[temp_sock_fd], sizeof (struct client_conn_buf));
+
+ /*
+ * save the source and destination data for this connection (since
+ * the log data struct will go out of scope before we check the
+ * server security extension or other loggable events)
+ */
+ client_conn_array[temp_sock_fd]->source =
+ Malloc(strlen(inet_ntoa(temp_sockaddr_in.sin_addr)) + 1);
+ client_conn_array[temp_sock_fd]->destination =
+ Malloc(strlen(inet_ntoa(server_sockaddr_in.sin_addr)) + 1);
+
+ (void) strcpy(client_conn_array[temp_sock_fd]->source,
+ inet_ntoa(temp_sockaddr_in.sin_addr));
+ (void) strcpy(client_conn_array[temp_sock_fd]->destination,
+ inet_ntoa(server_sockaddr_in.sin_addr));
+
+ /*
+ * allocate a buffer for the X server connection
+ * and create the association between client and server
+ */
+ if ((client_conn_array[server_fd] = (struct client_conn_buf *)
+ malloc(sizeof (struct client_conn_buf))) == NULL)
+ {
+ (void) fprintf (stderr, "malloc - server connectioin buffer\n");
+ return;
+ }
+ bzero (client_conn_array[server_fd], sizeof (struct client_conn_buf));
+
+ client_conn_array[server_fd]->conn_to = temp_sock_fd;
+ client_conn_array[temp_sock_fd]->conn_to = server_fd;
+
+ /*
+ * save this sock fd for future reference (in timeout computation)
+ */
+ client_conn_array[temp_sock_fd]->fd = temp_sock_fd;
+
+ /*
+ * mark this buffer as readable and writable and waiting for
+ * authentication to complete; mark the server conn buffer
+ * with a special state to make sure that its reply to
+ * the authentication request can be read and interpreted
+ * before it is simply forwarded to the client
+ */
+ client_conn_array[temp_sock_fd]->state = CLIENT_WAITING;
+ client_conn_array[server_fd]->state = SERVER_REPLY;
+
+ /*
+ * update the select() fd mask and the nfds
+ */
+ FD_SET(temp_sock_fd, rinit);
+ *nfds = max(*nfds, temp_sock_fd + 1);
+ FD_SET(server_fd, rinit);
+ *nfds = max(*nfds, server_fd + 1);
+
+ /*
+ * this is where we initialize the current time and timeout on this
+ * client_data object
+ */
+ gettimeofday(&time_val, &time_zone);
+ client_conn_array[temp_sock_fd]->creation_time = time_val.tv_sec;
+ client_conn_array[temp_sock_fd]->time_to_close =
+ config_info->client_data_timeout;
+
+ /*
+ * be sure the mark the server side of the association, too
+ */
+ client_conn_array[server_fd]->creation_time = time_val.tv_sec;
+ client_conn_array[server_fd]->time_to_close =
+ config_info->client_data_timeout;
+
+ client_conn_array[server_fd]->fd = server_fd;
+}
+
+static void
+ProcessClientWaiting (
+ fd_set * winit,
+ int client_idx)
+{
+ char * conn_auth_name = "XC-QUERY-SECURITY-1";
+ int conn_auth_namelen;
+ int conn_auth_datalen;
+ xConnClientPrefix client;
+ int endian;
+ int name_remainder;
+ int data_remainder;
+ int idx;
+ char *bufP;
+
+ /*
+ * The remote client is sending more data on an already-
+ * established connection, but we still haven't checked
+ * authentication on this client from the associated
+ * X-server.
+ *
+ * Do the following:
+ *
+ * 1. create the authentication header
+ * 2. mark the server fd writable
+ * 3. copy the header info into the write buffer
+ * 3. set the wbytes field to the header size
+ * 4. mark the state SERVER_WAITING
+ */
+ conn_auth_namelen = strlen(conn_auth_name);
+
+ if (SitePolicyCount == 0)
+ conn_auth_datalen = 0;
+ else
+ {
+ int sitePolicy;
+
+ conn_auth_datalen = 3;
+ for (sitePolicy = 0; sitePolicy < SitePolicyCount; sitePolicy++)
+ {
+ conn_auth_datalen += 1 + strlen(SitePolicies[sitePolicy]);
+ }
+ }
+
+ endian = 1;
+ if (*(char *) &endian)
+ client.byteOrder = '\154'; /* 'l' */
+ else
+ client.byteOrder = '\102'; /* 'B' */
+
+ client.majorVersion = X_PROTOCOL;
+ client.minorVersion = X_PROTOCOL_REVISION;
+ client.nbytesAuthProto = conn_auth_namelen;
+ client.nbytesAuthString = conn_auth_datalen;
+
+ /*
+ * Put the authentication message into the appropriate
+ * client_conn_buf object
+ *
+ * compute required padding for name and data strings
+ */
+ name_remainder = (4 - (conn_auth_namelen % 4)) % 4;
+ data_remainder = (4 - (conn_auth_datalen % 4)) % 4;
+
+ idx = client_conn_array[client_idx]->conn_to;
+
+ bufP = client_conn_array[idx]->writebuf;
+
+ memcpy(bufP, (char *) &client, sizeof(client));
+ bufP += sizeof(client);
+
+ memcpy(bufP, (char *) conn_auth_name, conn_auth_namelen);
+ bufP += conn_auth_namelen;
+
+ bzero(bufP, name_remainder);
+ bufP += name_remainder;
+
+ if (conn_auth_datalen)
+ {
+ int sitePolicy;
+
+ *bufP++ = 0x02; /* Site Policies only at this point */
+ *bufP++ = (SitePolicyPermit == False);
+ *bufP++ = SitePolicyCount;
+
+ for (sitePolicy = 0; sitePolicy < SitePolicyCount; sitePolicy++)
+ {
+ char nameLen = strlen(SitePolicies[sitePolicy]);
+
+ *bufP++ = nameLen;
+ memcpy(bufP, SitePolicies[sitePolicy], nameLen);
+ bufP += nameLen;
+ }
+ bzero(bufP, data_remainder);
+ }
+
+ client_conn_array[idx]->wbytes = sizeof(client) +
+ conn_auth_namelen + name_remainder +
+ conn_auth_datalen + data_remainder;
+
+ /*
+ * Mark this fd as selectable to force a write() operation
+ * of authentication request to server for this client
+ */
+ FD_SET(client_conn_array[client_idx]->conn_to, winit);
+
+ /*
+ * Mark the connection SERVER_WAITING (so that we don't
+ * read any more client data until the authentication
+ * sequence is complete)
+ */
+ client_conn_array[client_idx]->state = SERVER_WAITING;
+}
+
+static void
+ProcessConnectionReady (
+ fd_set * rinit,
+ fd_set * winit,
+ struct config * config_info,
+ int client_fd)
+{
+ /*
+ * We've finished our authentication handshaking and are
+ * forwarding data either from client to server or vice versa
+ */
+ int bytes_read;
+
+ if (client_conn_array[client_fd]->rbytes < RWBUFFER_SIZE)
+ {
+ /*
+ * read what you have room for
+ */
+ bytes_read = read(client_fd,
+ client_conn_array[client_fd]->readbuf +
+ client_conn_array[client_fd]->rbytes, RWBUFFER_SIZE -
+ client_conn_array[client_fd]->rbytes);
+ /*
+ * check for I/O error on this fd; this is our only way
+ * of knowing if remote closed the connection
+ */
+ if (bytes_read == -1)
+ {
+ /*
+ * remote apparently closed the connection;
+ * clear bits in the select() mask, reclaim conn_buffs and
+ * listen port
+ */
+ FD_CLR(client_fd, rinit);
+ FD_CLR(client_fd, winit);
+ FD_CLR(client_conn_array[client_fd]->conn_to, rinit);
+ FD_CLR(client_conn_array[client_fd]->conn_to, winit);
+
+ (void) close (client_conn_array[client_fd]->conn_to);
+ (void) close (client_fd);
+
+ if (client_conn_array[client_fd]->source)
+ free(client_conn_array[client_fd]->source);
+ if (client_conn_array[client_fd]->destination)
+ free(client_conn_array[client_fd]->destination);
+ free(client_conn_array[client_fd]);
+
+ client_conn_array[client_fd] = NULL;
+
+ /*
+ * If this FD is for an Xserver must remove the server's
+ * listen fd so that clients will not attempt to connect
+ * on this fd.
+ */
+ RemoveFDFromServerListenArray (client_fd,
+ rinit,
+ config_info->num_servers);
+
+ /*
+ * exit readables for loop
+ */
+ return;
+ } else if (bytes_read == 0)
+ {
+ /*
+ * make sure we don't try to read on this fd again
+ */
+ FD_CLR(client_fd, rinit);
+ FD_CLR(client_fd, winit);
+
+ (void) close (client_fd);
+
+ if (client_conn_array[client_fd]->conn_to != -1)
+ {
+ /*
+ * mark this conn_fd fd ready to close
+ */
+ int idx = client_conn_array[client_fd]->conn_to;
+
+ client_conn_array[idx]->wclose = 1;
+ client_conn_array[idx]->conn_to = -1;
+
+ /*
+ * but still force a last write on the conn_to connection
+ */
+ FD_SET(client_conn_array[client_fd]->conn_to, winit);
+ }
+
+ /*
+ * and mark this connection for no further activity
+ */
+ client_conn_array[client_fd]->rbytes = 0;
+ client_conn_array[client_fd]->wbytes = 0;
+ client_conn_array[client_fd]->conn_to = -1;
+
+ /*
+ * If this FD is for an Xserver must remove the server's
+ * listen fd so that clients will not attempt to connect
+ * on this fd.
+ */
+ RemoveFDFromServerListenArray (client_fd,
+ rinit,
+ config_info->num_servers);
+
+ } else
+ {
+ /*
+ * Move bytes between buffers on associated fd's
+ * (read data on one becomes write data on other)
+ */
+ client_conn_array[client_fd]->rbytes += bytes_read;
+
+ if (client_conn_array[client_fd]->conn_to != 0)
+ doCopyFromTo(client_fd,
+ client_conn_array[client_fd]->conn_to,
+ rinit,
+ winit);
+
+ /*
+ * check if you have more read data than available space
+ */
+ if (client_conn_array[client_fd]->rbytes >= RWBUFFER_SIZE)
+ {
+ /*
+ * if so, abort the attempted copy until you can
+ * write some data out of the buffer first, and
+ * don't allow any more reading until that's done
+ */
+ FD_CLR(client_fd, rinit);
+ }
+ }
+ }
+}
+
+static void
+ProcessServerReply (
+ fd_set * rinit,
+ fd_set * winit,
+ struct config * config_info,
+ int client_fd)
+{
+ int idx;
+ int endian;
+ int four = 4;
+ int server_reason_remainder;
+ char * server_reason_padded;
+ int server_reason_len;
+ char throw_away[RWBUFFER_SIZE];
+ xConnSetupPrefix prefix;
+
+ if (client_conn_array[client_fd]->state == SERVER_REPLY)
+ {
+ /*
+ * read the server reply to the authentication request
+ */
+ (void) read(client_fd,
+ client_conn_array[client_fd]->readbuf +
+ client_conn_array[client_fd]->rbytes,
+ RWBUFFER_SIZE - client_conn_array[client_fd]->rbytes);
+
+ switch ((BYTE) client_conn_array[client_fd]->readbuf[0])
+ {
+ case SERVER_REPLY_FAILURE:
+#ifdef DEBUG
+ {
+ char * replyP = client_conn_array[client_fd]->readbuf;
+ int reasonLength = *++replyP;
+
+ replyP += 6; /* skip major & minor version, msg len */
+ *(replyP+reasonLength+1) = '\0';
+ (void) fprintf (stderr, "Server replied FAILURE: %s\n", replyP);
+ }
+ /* FALL-THROUGH */
+#endif
+ case SERVER_REPLY_SUCCESS:
+ /*
+ * two possibilities here: either the policy field
+ * passed to the server is unauthorized, or the server
+ * does not support the security extension; in both cases
+ * we read the client fd then synthesize a response
+ * which we forward to the client before closing the
+ * connection
+ */
+ (void) read(client_conn_array[client_fd]->conn_to,
+ throw_away, 1);
+ /*
+ * construct the client response
+ */
+ prefix.success = 0;
+ prefix.lengthReason = server_reason_len =
+ strlen(server_reason
+ [(int) client_conn_array[client_fd]->readbuf[0]]);
+ prefix.majorVersion = X_PROTOCOL;
+ prefix.minorVersion = X_PROTOCOL_REVISION;
+ server_reason_remainder = server_reason_len;
+
+ /*
+ * calculate quadword padding required
+ */
+ while (server_reason_remainder > 0)
+ server_reason_remainder = server_reason_remainder - four;
+ server_reason_remainder = abs(server_reason_remainder);
+
+ /*
+ * allocate the padded buffer
+ */
+ if ((server_reason_padded =
+ (char *) malloc (server_reason_len +
+ server_reason_remainder)) == NULL)
+ {
+ (void) fprintf (stderr, "malloc - server reason\n");
+ return;
+ }
+
+ /*
+ * calculate the "additional data" field
+ */
+ prefix.length = (server_reason_len + server_reason_remainder) /
+ four;
+
+ /*
+ * compare client and xfwp byte ordering and swap prefix fields
+ * as necessary
+ */
+ endian = 1;
+ if (((throw_away[0] == '\154') && !(*(char *) &endian)) ||
+ ((throw_away[0] == '\102') && (*(char *) &endian)))
+ {
+ /*
+ * client and xfwp are different byte order
+ * so swap all fwp 2-byte fields to little endian
+ */
+ swab((char *) &prefix.majorVersion,
+ (char *) &prefix.majorVersion,
+ sizeof(prefix.majorVersion));
+ swab((char *) &prefix.minorVersion,
+ (char *) &prefix.minorVersion,
+ sizeof(prefix.minorVersion));
+ swab((char *) &prefix.length,
+ (char *) &prefix.length,
+ sizeof(prefix.length));
+ }
+
+ /*
+ * load the padded reason
+ */
+ bzero((char *) server_reason_padded,
+ server_reason_len + server_reason_remainder);
+ memcpy((char *) server_reason_padded,
+ (char *) server_reason
+ [(int) client_conn_array[client_fd]->readbuf[0]],
+ server_reason_len);
+ /*
+ * load the complete synthesized server reply (which will
+ * be sent to the client next time the writables are
+ * processed (again, to avoid blocking)
+ */
+ memcpy((char *) client_conn_array[client_fd]->readbuf,
+ (char *) &prefix,
+ sizeof(prefix));
+
+ memcpy((char *) client_conn_array[client_fd]->readbuf +
+ sizeof(prefix),
+ (char *) server_reason_padded,
+ server_reason_len + server_reason_remainder);
+
+ client_conn_array[client_fd]->rbytes = sizeof(prefix) +
+ server_reason_len + server_reason_remainder;
+
+ /*
+ * make sure to zero the wbytes on the client conn object
+ * before forwarding the server reply
+ */
+ idx = client_conn_array[client_fd]->conn_to;
+
+ client_conn_array[idx]->wbytes = 0;
+ doCopyFromTo(client_fd, idx, rinit, winit);
+ client_conn_array[client_fd]->wclose = 1;
+ client_conn_array[idx]->conn_to = -1;
+
+ /*
+ * clear the select() mask for the client socket and for
+ * the server
+ */
+ FD_CLR(client_conn_array[client_fd]->conn_to, rinit);
+ FD_CLR(client_fd, rinit);
+#ifdef DEBUG
+ /*
+ * output a trace message
+ */
+ if (((int) client_conn_array[client_fd]->readbuf[0]) ==
+ SERVER_REPLY_SUCCESS)
+ (void) fprintf (stderr, "Server replied SUCCESS\n");
+#endif
+ /*
+ * clean up memory
+ */
+ free(server_reason_padded);
+
+ doWriteLogEntry (client_conn_array[idx]->source,
+ client_conn_array[idx]->destination,
+ CLIENT_REJECT_SERVER,
+ -1,
+ config_info);
+ break;
+
+ case SERVER_REPLY_AUTHENTICATE:
+ /*
+ * the server supports the security extension; begin
+ * forwarding client and server messages normally
+ */
+ idx = client_conn_array[client_fd]->conn_to;
+
+ client_conn_array[client_fd]->state = CONNECTION_READY;
+ client_conn_array[idx]->state = CONNECTION_READY;
+
+#ifdef DEBUG
+ (void) fprintf (stderr, "Server replied AUTHENTICATE\n");
+#endif
+ doWriteLogEntry (client_conn_array[idx]->source,
+ client_conn_array[idx]->destination,
+ CLIENT_ACCEPT,
+ -1,
+ config_info);
+ break;
+
+ default:
+ (void) fprintf (stderr, "unknown reply from server\n");
+ }
+ }
+}
+
+static void
+doProcessReadables(
+ int fd_counter,
+ int * nfds,
+ fd_set * rinit,
+ fd_set * winit,
+ int pm_listen_array[],
+ struct config * config_info,
+ IceListenObj ** listen_objects)
+{
+ int i;
+
+ /*
+ * Check fd_counter to see if it is one of the PM listen fds
+ */
+ for (i = 0; i < config_info->num_pm_listen_ports; i++)
+ {
+ if (pm_listen_array[i] == fd_counter)
+ {
+ if (!pm_conn_array[fd_counter])
+ {
+ /*
+ * Process this new PM connection
+ */
+ ProcessNewPMConnection (nfds,
+ rinit,
+ config_info,
+ listen_objects,
+ i);
+ return;
+ }
+ break;
+ }
+ }
+
+ /*
+ * If this is an already-accepted PM connection, call
+ * IceProcessMessages() to invoke the FWPprocessMessages
+ * callback
+ */
+ for (i = 0; i < config_info->num_pm_conns; i++)
+ {
+ if (pm_conn_array[i] &&
+ pm_conn_array[i]->fd == fd_counter)
+ {
+ ProcessPMInput (nfds, rinit, i);
+ return;
+ }
+ }
+
+ /*
+ * Check fd_counter to see if it one of the ports that
+ * clients use to connect to a server.
+ */
+ for (i = 0; i < config_info->num_servers; i++)
+ {
+ if (server_array[i] &&
+ server_array[i]->client_listen_fd == fd_counter)
+ {
+ ProcessNewClientConnection (nfds,
+ rinit,
+ config_info,
+ fd_counter,
+ i);
+ return;
+ }
+ }
+
+ /*
+ * At this point the input can be from the following sources:
+ *
+ * 1. An X server replying to an authentication request
+ *
+ * 2. Data coming from a X server that needs to be re-directed
+ * to a client
+ *
+ * 3. Data from a client that needs to be re-directed to
+ * its X server
+ */
+ if (!client_conn_array[fd_counter])
+ {
+ /*
+ * This can happen if the client was marked as closed by
+ * doProcessWritables.
+ */
+ return;
+ }
+
+ switch (client_conn_array[fd_counter]->state)
+ {
+ case CLIENT_WAITING:
+ ProcessClientWaiting (winit,
+ fd_counter);
+ break;
+
+ case CONNECTION_READY:
+ ProcessConnectionReady (rinit,
+ winit,
+ config_info,
+ fd_counter);
+ break;
+
+ case SERVER_WAITING:
+ /*
+ * This is a do-nothing state while we're waiting for the
+ * server reply (see below)
+ */
+ break;
+
+ case SERVER_REPLY:
+ ProcessServerReply (rinit,
+ winit,
+ config_info,
+ fd_counter);
+ break;
+
+ default:
+ /*
+ * All of the values of state have been tested.
+ */
+ (void) fprintf (stderr, "client state error\n");
+ }
+}
+
+void
+doProcessSelect(
+ int * nfds,
+ int * nready,
+ fd_set * readable,
+ fd_set * writable,
+ fd_set * rinit,
+ fd_set * winit,
+ int pm_listen_array[],
+ struct config * config_info,
+ IceListenObj ** listen_objects)
+{
+ int fd_counter;
+
+ /*
+ * Loop through descriptors
+ */
+ for (fd_counter = 0; fd_counter < *nfds && *nready; fd_counter++)
+ {
+ /*
+ * Look at fd's for writables
+ */
+ if (FD_ISSET(fd_counter, writable))
+ {
+ /*
+ * Decrement the list of read/write ready connections
+ */
+ *nready -= 1;
+ doProcessWritables (fd_counter,
+ rinit,
+ winit);
+ }
+
+ /*
+ * Now, do the same thing for the readables
+ */
+ if (FD_ISSET(fd_counter, readable))
+ {
+ /*
+ * Decrement the list of read/write ready connections
+ */
+ *nready -= 1;
+ doProcessReadables (fd_counter,
+ nfds,
+ rinit,
+ winit,
+ pm_listen_array,
+ config_info,
+ listen_objects);
+ }
+ }
+}
diff --git a/io.h b/io.h
new file mode 100644
index 0000000..147b10f
--- /dev/null
+++ b/io.h
@@ -0,0 +1,53 @@
+/* $Xorg: io.h,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */
+/*
+
+Copyright "1986-1997, 1998 The Open Group
+
+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.
+
+The above copyright notice and the following permission notice
+shall be included in all copies of the Software:
+
+THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
+CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN
+THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group
+shall not be used in advertising or otherwise to promote the use
+or other dealings in this Software without prior written
+authorization from The Open Group.
+
+X Window System is a trademark of The Open Group.
+
+*/
+
+
+#ifndef _IO_H
+#define _IO_H
+
+#define SERVER_REPLY_FAILURE 0
+#define SERVER_REPLY_SUCCESS 1
+#define SERVER_REPLY_AUTHENTICATE 2
+
+extern void
+doProcessSelect(
+ int * nfds,
+ int * nready,
+ fd_set * readable,
+ fd_set * writable,
+ fd_set * rinit,
+ fd_set * winit,
+ int pm_listen_array[],
+ struct config * config_info,
+ IceListenObj ** listen_objects);
+
+#endif /* _IO_H */
diff --git a/misc.c b/misc.c
new file mode 100644
index 0000000..93dab82
--- /dev/null
+++ b/misc.c
@@ -0,0 +1,1508 @@
+/* $Xorg: misc.c,v 1.6 2001/02/09 02:05:45 xorgcvs Exp $ */
+
+/*
+Copyright "1986-1997, 1998 The Open Group
+
+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.
+
+The above copyright notice and the following permission notice
+shall be included in all copies of the Software:
+
+THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
+CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN
+THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group
+shall not be used in advertising or otherwise to promote the use
+or other dealings in this Software without prior written
+authorization from The Open Group.
+
+X Window System is a trademark of The Open Group.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <X11/Xos.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <X11/Xfuncs.h> /* Need for bcopy() */
+#include <X11/ICE/ICElib.h>
+#include <X11/PM/PM.h>
+
+#include "xfwp.h"
+#include "misc.h"
+#include "pm.h"
+
+static Bool printConfigVerify = FALSE;
+static Bool HaveSitePolicy = 0;
+
+
+/*ARGSUSED*/
+static void
+BadSyntax(
+ char *msg,
+ int line)
+{
+#ifdef DEBUG
+ (void) fprintf(stderr, "Config error: %s at line %d\n", msg, line);
+#endif
+}
+
+static void
+Usage()
+{
+ (void) fprintf (stderr, "Usage: xfwp [-pdt <#secs>] [-clt <#secs>] \\\n");
+ (void) fprintf (stderr,
+ "\t[-cdt <#secs>] [-pmport <port#>] [-config <path>]\\\n");
+ (void) fprintf (stderr,
+ "\t[-logfile <path>] [-loglevel <0|1>] [-verify]\n");
+ exit (0);
+}
+
+static void
+MallocFailed()
+{
+ (void) fprintf(stderr, "Memory allocation failed, exiting\n");
+ exit(1);
+}
+
+static char*
+Realloc(
+ char *p,
+ int s)
+{
+ if (!p)
+ p = malloc(s);
+ else
+ p = realloc(p, s);
+
+ if (!p)
+ MallocFailed();
+
+ return p;
+}
+
+static void
+BadMalloc(
+ int line)
+{
+ (void) fprintf(stderr, "Error: memory exhaused at line %d\n", line);
+}
+
+static void
+doPrintEval(
+ struct config * config_info,
+ int line_counter)
+{
+ struct config_line *ruleP = config_info->config_file_data[line_counter];
+
+ if (!printConfigVerify)
+ return;
+
+ (void) fprintf(stderr,"matched: %s %s %s %s %s %s %s\n",
+ (ruleP->permit_deny) ? ruleP->permit_deny : "",
+ (ruleP->source_hostname) ? ruleP->source_hostname : "",
+ (ruleP->source_netmask) ? ruleP->source_netmask : "",
+ (ruleP->dest_hostname) ? ruleP->dest_hostname : "",
+ (ruleP->dest_netmask) ? ruleP->dest_netmask : "",
+ (ruleP->operator) ? ruleP->operator : "",
+ (ruleP->service) ? ruleP->service : "");
+}
+
+static Bool
+doConfigRequireDisallow(
+ int line,
+ char* result)
+{
+ Bool permit = (strcmp("require", result) == 0);
+
+ if (((result = strtok(NULL, SEPARATOR1)) == NULL) ||
+ (strcmp(result, "sitepolicy") != 0))
+ {
+ BadSyntax("require/disallow must specify \"sitepolicy\"", line);
+ return 1;
+ }
+
+ if (HaveSitePolicy && (SitePolicyPermit != permit))
+ {
+ BadSyntax("can't mix require and disallow policies", line);
+ return 1;
+ }
+
+ HaveSitePolicy = True;
+ SitePolicyPermit = permit;
+
+ if ((result = strtok(NULL, " \n")) == NULL)
+ {
+ BadSyntax("missing policy string after \"sitepolicy\"", line);
+ return 1;
+ }
+
+ if (SitePolicies)
+ SitePolicies = (char**)realloc((char**)SitePolicies,
+ (SitePolicyCount+1) * sizeof(char*));
+ else
+ SitePolicies = (char**)malloc(sizeof(char*));
+
+ if (!SitePolicies)
+ {
+ BadMalloc(line);
+ return 1;
+ }
+
+ SitePolicies[SitePolicyCount] = malloc(strlen(result) + 1);
+
+ if (!SitePolicies[SitePolicyCount])
+ {
+ BadMalloc(line);
+ return 1;
+ }
+
+ strcpy(SitePolicies[SitePolicyCount++], result);
+
+#ifdef DEBUG
+ (void) fprintf(stderr, "%s %s", permit ? "requiring" : "disallowing", result);
+#endif
+
+ return False;
+}
+
+static int
+doVerifyHostMaskToken(
+ char token[])
+{
+ char * result;
+ int delimiter_count = 0;
+
+ /*
+ * verify there are 3 "." delimiters in the token
+ */
+ while (token)
+ {
+ if ((result = strchr(token, SEPARATOR2)) != NULL)
+ {
+ token = result;
+ delimiter_count++;
+ token ++;
+ } else
+ token = result;
+ }
+ if ((delimiter_count < 3) || (delimiter_count > 3))
+ return 0;
+ else
+ return 1;
+}
+
+static int
+doInitNewRule(
+ struct config *config_info)
+{
+ int rule_number = config_info->rule_count;
+ struct config_line *config_lineP;
+
+ if (rule_number == config_info->lines_allocated)
+ {
+ if ((config_info->config_file_data = (struct config_line**)
+ Realloc((char*)config_info->config_file_data,
+ (config_info->lines_allocated += ADD_LINES) *
+ sizeof(struct config_line *))) == NULL)
+ {
+ (void) fprintf (stderr, "realloc - config_file_data\n");
+ return -1;
+ }
+ }
+
+ if ((config_lineP = (struct config_line *)
+ Malloc (sizeof(struct config_line))) == NULL)
+ {
+ (void) fprintf (stderr, "malloc - config_lineP\n");
+ return -1;
+ }
+
+ config_lineP->permit_deny = NULL;
+ config_lineP->source_hostname = NULL;
+ config_lineP->source_host = 0;
+ config_lineP->source_netmask = NULL;
+ config_lineP->source_net = 0;
+ config_lineP->dest_hostname = NULL;
+ config_lineP->dest_host = 0;
+ config_lineP->dest_netmask = NULL;
+ config_lineP->dest_net = 0;
+ config_lineP->operator = NULL;
+ config_lineP->service = NULL;
+
+ config_info->config_file_data[rule_number] = config_lineP;
+
+ return rule_number;
+}
+
+static int
+doConfigPermitDeny(
+ struct config *config_info,
+ char *result)
+{
+ struct config_line ** config_file_data;
+ int line_number;
+ int bad_token = 0;
+
+ /*
+ * caution; config_info->config_file_data can move in doInitNewRule
+ */
+ if ((line_number = doInitNewRule(config_info)) == -1)
+ return 1;
+
+ config_file_data = config_info->config_file_data;
+
+ if ((config_file_data[line_number]->permit_deny =
+ (char *) malloc (strlen(result) + 1)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - config rule (permit/deny keyword)\n");
+ return 0;
+ }
+ strcpy(config_file_data[line_number]->permit_deny, result);
+
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "first token = %s\n",
+ config_file_data[line_number]->permit_deny);
+#endif
+
+ /*
+ * do the source hostname field
+ */
+ if ((result = strtok(NULL, SEPARATOR1)) != NULL)
+ {
+ char token[64];
+ strcpy(token, result);
+
+ if (doVerifyHostMaskToken(token))
+ {
+ if ((config_file_data[line_number]->source_hostname =
+ (char *) malloc (strlen(result) + 1)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - config rule (source host)\n");
+ return 0;
+ }
+ strcpy(config_file_data[line_number]->source_hostname, result);
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "second token = %s\n",
+ config_file_data[line_number]->source_hostname);
+#endif
+ /*
+ * generate network address format
+ */
+ config_file_data[line_number]->source_host =
+ inet_addr(config_file_data[line_number]->source_hostname);
+ } else
+ bad_token = 1;
+ }
+
+ /*
+ * now the source netmask field
+ */
+ if ((result = strtok(NULL, SEPARATOR1)) != NULL)
+ {
+ char token[64];
+ strcpy(token, result);
+
+ if (doVerifyHostMaskToken(token))
+ {
+ if ((config_file_data[line_number]->source_netmask =
+ (char *) malloc (strlen(result) + 1)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - config rule (source netmask)\n");
+ return 0;
+ }
+ strcpy(config_file_data[line_number]->source_netmask, result);
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "third token = %s\n",
+ config_file_data[line_number]->source_netmask);
+#endif
+ config_file_data[line_number]->source_net =
+ inet_addr(config_file_data[line_number]->source_netmask);
+ } else
+ bad_token = 1;
+ }
+
+ /*
+ * now the destination hostname field
+ */
+ if ((result = strtok(NULL, SEPARATOR1)) != NULL)
+ {
+ char token[64];
+ strcpy(token, result);
+
+ if (doVerifyHostMaskToken(token))
+ {
+ if ((config_file_data[line_number]->dest_hostname =
+ (char *) malloc (strlen(result) + 1)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - config rule (destination host)\n");
+ return 0;
+ }
+ strcpy(config_file_data[line_number]->dest_hostname, result);
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "fourth token = %s\n",
+ config_file_data[line_number]->dest_hostname);
+#endif
+ config_file_data[line_number]->dest_host =
+ inet_addr(config_file_data[line_number]->dest_hostname);
+ } else
+ bad_token = 1;
+ }
+
+ /*
+ * now the destination netmask field
+ */
+ if ((result = strtok(NULL, SEPARATOR1)) != NULL)
+ {
+ char token[64];
+ strcpy(token, result);
+
+ if (doVerifyHostMaskToken(token))
+ {
+ if ((config_file_data[line_number]->dest_netmask =
+ (char *) malloc (strlen(result) + 1)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - config rule (destination mask)\n");
+ return 0;
+ }
+ strcpy(config_file_data[line_number]->dest_netmask, result);
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "fifth token = %s\n",
+ config_file_data[line_number]->dest_netmask);
+#endif
+ config_file_data[line_number]->dest_net =
+ inet_addr(config_file_data[line_number]->dest_netmask);
+ } else
+ bad_token = 1;
+ }
+
+ /*
+ * now the operator field
+ */
+ if ((result = strtok(NULL, SEPARATOR1)) != NULL)
+ {
+ if (!strcmp("eq", result))
+ {
+ if ((config_file_data[line_number]->operator =
+ (char *) malloc (strlen(result) + 1)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - config rule (op)\n");
+ return 0;
+ }
+ strcpy(config_file_data[line_number]->operator, result);
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "sixth token = %s\n",
+ config_file_data[line_number]->operator);
+#endif
+ } else
+ bad_token = 1;
+ }
+
+ /*
+ * and finally the service field
+ */
+ if ((result = strtok(NULL, SEPARATOR1)) != NULL)
+ {
+ if (!(strncmp("pm", result, 2)) ||
+ (!strncmp("fp", result, 2)) ||
+ (!strncmp("cd", result, 2)))
+ {
+ if ((config_file_data[line_number]->service =
+ (char *) malloc (strlen(result) + 1)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - config rule (service)\n");
+ return 0;
+ }
+ strcpy(config_file_data[line_number]->service, result);
+#ifdef DEBUG
+ (void) fprintf(stderr,
+ "seventh token = %s\n",
+ config_file_data[line_number]->service);
+#endif
+ /*
+ * load the appropriate service id
+ */
+ if (!strncmp(config_file_data[line_number]->service, "pm", 2))
+ config_file_data[line_number]->service_id = PMGR;
+ else if (!strncmp(config_file_data[line_number]->service, "fp", 2))
+ config_file_data[line_number]->service_id = FINDPROXY;
+ else
+ if (!strncmp(config_file_data[line_number]->service, "cd", 2))
+ config_file_data[line_number]->service_id = CLIENT;
+ } else
+ bad_token = 1;
+ }
+
+ /*
+ * rules for bad parse
+ */
+ if (bad_token ||
+ (config_file_data[line_number]->permit_deny == NULL) ||
+ ((config_file_data[line_number]->permit_deny != NULL) &&
+ (config_file_data[line_number]->source_hostname == NULL)) ||
+ ((config_file_data[line_number]->source_hostname != NULL) &&
+ (config_file_data[line_number]->source_netmask == NULL)) ||
+ ((config_file_data[line_number]->dest_hostname != NULL) &&
+ (config_file_data[line_number]->dest_netmask == NULL)) ||
+ ((config_file_data[line_number]->operator != NULL) &&
+ (config_file_data[line_number]->service == NULL)))
+ return 1;
+
+ config_info->rule_count++;
+ return 0;
+}
+
+static int
+doProcessLine(
+ char *line,
+ struct config *config_info,
+ int config_line)
+{
+ char * result;
+ int bad_parse = 0;
+
+ if (line[0] == '#' || line[0] == '\n')
+ return 1;
+
+ if ((result = strtok(line, SEPARATOR1)) != NULL)
+ {
+ if (!(strcmp("permit", result)) || (!strcmp("deny", result)))
+ {
+ bad_parse = doConfigPermitDeny(config_info, result);
+ }
+ else
+ if (!strcmp("require", result) || !strcmp("disallow", result))
+ bad_parse = doConfigRequireDisallow(config_line, result);
+
+ else
+ bad_parse = 1;
+ }
+
+ if (bad_parse)
+ return 0;
+ else
+ return 1;
+}
+
+/*
+ * Public functions
+ */
+char*
+Malloc(
+ int s)
+{
+ char *p = malloc(s);
+
+ if (!p)
+ MallocFailed();
+
+ return p;
+}
+
+int
+doConfigCheck(
+ struct sockaddr_in * source_sockaddr_in,
+ struct sockaddr_in * dest_sockaddr_in,
+ struct config * config_info,
+ int context,
+ int * rule_number)
+{
+ int line_counter;
+
+ /*
+ * look through the config file parse tree for a source IP address
+ * that matches this request
+ */
+ for (line_counter = 0; line_counter < config_info->rule_count; line_counter++)
+ {
+ if (config_info->config_file_data[line_counter] != NULL)
+ {
+ if ((source_sockaddr_in->sin_addr.s_addr &
+ (~(config_info->config_file_data[line_counter]->source_net))) ==
+ config_info->config_file_data[line_counter]->source_host)
+ {
+ /*
+ * okay, the source host and netmask fields pass, see if the
+ * config file specifies "permit" or "deny" for this host
+ */
+ if (!strcmp(config_info->config_file_data[line_counter]->permit_deny,
+ "permit"))
+ {
+ /*
+ * check for presence of destination info
+ */
+ if ((config_info->config_file_data[line_counter]->dest_hostname) &&
+ (context != PMGR))
+ {
+ /*
+ * compute destination info restrictions
+ */
+ if ((dest_sockaddr_in->sin_addr.s_addr &
+ (~(config_info->config_file_data[line_counter]->dest_net))) ==
+ config_info->config_file_data[line_counter]->dest_host)
+ {
+ /*
+ * you got a match on the destination, so look at
+ * the operator and service fields to see if the "permit"
+ * might be specific to one particular connection-type only
+ */
+ if (config_info->config_file_data[line_counter]->operator != NULL)
+ {
+ /*
+ * there *is* a service id; see if it matches our current
+ * config check request
+ */
+ if (config_info->config_file_data[line_counter]->service_id ==
+ context)
+ {
+ doPrintEval(config_info, line_counter);
+ /*
+ * if you are permitting, there's no rule match to log
+ */
+ *rule_number = line_counter + 1;
+ return 1;
+ } else
+ /*
+ * we didn't get a match on context; this "permit" doesn't
+ * apply to the current request; so keep trying
+ */
+ continue;
+ } else
+ /*
+ * there's no service qualifier; permit the connection
+ */
+ doPrintEval(config_info, line_counter);
+ *rule_number = line_counter + 1;
+ return 1;
+ } else
+ /*
+ * the destination field doesn't match; keep trying
+ */
+ continue;
+ } else if ((config_info->
+ config_file_data[line_counter]->dest_hostname) &&
+ (context == PMGR))
+ {
+ /*
+ * skip the destination address check and test for
+ * the operator and service_id
+ */
+ if (config_info->config_file_data[line_counter]->operator != NULL)
+ {
+ /*
+ * there *is* a service id; see if it matches our current
+ * config check context
+ */
+ if (config_info->config_file_data[line_counter]->service_id
+ == context)
+ {
+ doPrintEval(config_info, line_counter);
+ /*
+ * not logging PM events so don't save rule match
+ */
+ return 1;
+ } else
+ /*
+ * we didn't get a match on context; this "permit" doesn't
+ * apply to the current client request; so keep trying
+ */
+ continue;
+ } else
+ {
+ /*
+ * there's no service qualifier; permit the connection
+ */
+ doPrintEval(config_info, line_counter);
+ *rule_number = line_counter + 1;
+ return 1;
+ }
+ } else
+ {
+ /*
+ * there's no destination specified; permit the connection
+ */
+ doPrintEval(config_info, line_counter);
+ *rule_number = line_counter + 1;
+ return 1;
+ }
+ }
+ else
+ {
+ /*
+ * we still have to check the destination and service fields
+ * to know exactly what we are denying
+ */
+ if ((config_info->config_file_data[line_counter]->dest_hostname) &&
+ (context != PMGR))
+ {
+ /*
+ * compute destination info restrictions
+ */
+ if ((dest_sockaddr_in->sin_addr.s_addr &
+ (~(config_info->config_file_data[line_counter]->dest_net))) ==
+ config_info->config_file_data[line_counter]->dest_host)
+ {
+ /*
+ * you got a match on the destination, so look at
+ * the operator and service fields to see if the "deny"
+ * might be specific to one particular connection-type only
+ */
+ if (config_info->config_file_data[line_counter]->operator != NULL)
+ {
+ /*
+ * there *is* a service id; see if it matches our current
+ * config check request
+ */
+ if (config_info->config_file_data[line_counter]->service_id ==
+ context)
+ {
+ /*
+ * the match signifies an explicit denial of permission
+ */
+ doPrintEval(config_info, line_counter);
+ /*
+ * save the rule match number before returning
+ */
+ *rule_number = line_counter + 1;
+ return 0;
+ } else
+ /*
+ * we didn't get a match on the service id; the "deny"
+ * operation doesn't apply to this connection, so keep
+ * trying
+ */
+ continue;
+ } else
+ {
+ /*
+ * there's no service qualifier; deny the connection
+ */
+ doPrintEval(config_info, line_counter);
+ /*
+ * save the rule match number before returning
+ */
+ *rule_number = line_counter + 1;
+ return 0;
+ }
+ } else
+ /*
+ * the destination field doesn't match; keep trying
+ */
+ continue;
+ } else if ((config_info->
+ config_file_data[line_counter]->dest_hostname) &&
+ (context == PMGR))
+ {
+ /*
+ * skip the destination address check and test for
+ * the operator and service_id
+ */
+ if (config_info->config_file_data[line_counter]->operator != NULL)
+ {
+ /*
+ * there *is* a service id; see if it matches our current
+ * config check context
+ */
+ if (config_info->config_file_data[line_counter]->service_id ==
+ context)
+ {
+ /*
+ * this is a request to explicitly deny service, so do it
+ */
+ doPrintEval(config_info, line_counter);
+ /*
+ * not logging PM events, but if we were, save rule match here
+ */
+ return 0;
+ } else
+ /*
+ * we didn't get a match on context; this "deny" doesn't
+ * apply to the current client request; so keep trying
+ */
+ continue;
+ } else
+ {
+ /*
+ * there's no service qualifier; deny the connection
+ */
+ doPrintEval(config_info, line_counter);
+ /*
+ * if we were logging PM events ...
+ */
+ return 0;
+ }
+ } else
+ {
+ /*
+ * there's no destination specified; deny the connection
+ */
+ doPrintEval(config_info, line_counter);
+ /*
+ * save rule match
+ */
+ *rule_number = line_counter + 1;
+ return 0;
+ }
+ } /* end else deny */
+ } /* end if match on source */
+ } /* end if valid config line */
+ } /* end all config lines for loop */
+
+ /*
+ * whatever you did not explicitly permit you must deny -- *unless* --
+ * no config file was specified, in which case permit all
+ */
+ if (config_info->config_file_path == NULL)
+ {
+ if (printConfigVerify)
+ (void) fprintf(stderr,
+ "matched default permit 0.0.0.0 255.255.255.255\n");
+ /*
+ * there's no rule match to save
+ */
+ *rule_number = -1;
+ return 1;
+ }
+
+ if (!config_info->config_file_data)
+ {
+ /*
+ * A config file was specified but it had no permit/deny
+ * entries. This can happen if: the file was empty; the
+ * file only contained comment entries; the file only has
+ * require/disallow entries. In any event, this case
+ * should be treated as if no config file was used - that
+ * is allow the connection.
+ */
+ *rule_number = -1;
+ return 1;
+ }
+
+ if (printConfigVerify)
+ (void) fprintf(stderr, "matched default deny 0.0.0.0 255.255.255.255\n");
+
+ /*
+ * not in this case either
+ */
+ *rule_number = -1;
+ return 0;
+}
+
+
+void
+doCheckTimeouts(
+ struct config * config_info,
+ int * nfds_ready,
+ fd_set * rinit,
+ fd_set * winit,
+ fd_set * readable,
+ fd_set * writable)
+{
+ int client_data_counter;
+ int client_listen_counter;
+ int pm_conn_counter;
+ struct timeval current_time;
+ struct timezone current_zone;
+
+ /*
+ * get current time
+ */
+ gettimeofday(&current_time, &current_zone);
+
+ /*
+ * start with the clients; we have to do them all, because a
+ * timeout may occur even if the object's fd is not currently
+ * readable or writable
+ */
+ for (client_data_counter = 0;
+ client_data_counter < config_info->num_client_conns;
+ client_data_counter++)
+ {
+ if (client_conn_array[client_data_counter] != NULL)
+ {
+ /*
+ * do the shutdown time computation
+ */
+ if ((current_time.tv_sec
+ - client_conn_array[client_data_counter]->creation_time)
+ > client_conn_array[client_data_counter]->time_to_close)
+ {
+ /*
+ * time to shut this client conn down; we're not going to be graceful
+ * about it, either; we're just going to clear the select() masks for
+ * the relevant file descriptors, close these fd's and deallocate
+ * the connection objects (for both client and server), and finally
+ * adjust the select() return params as necessary
+ */
+ FD_CLR(client_conn_array[client_data_counter]->fd, rinit);
+ FD_CLR(client_conn_array[client_data_counter]->fd, winit);
+ FD_CLR(client_conn_array[client_data_counter]->conn_to, rinit);
+ FD_CLR(client_conn_array[client_data_counter]->conn_to, winit);
+ close(client_conn_array[client_data_counter]->fd);
+ close(client_conn_array[client_data_counter]->conn_to);
+ free(client_conn_array[client_conn_array[client_data_counter]->conn_to]);
+ if (client_conn_array[client_data_counter]->source)
+ free(client_conn_array[client_data_counter]->source);
+ if (client_conn_array[client_data_counter]->destination)
+ free(client_conn_array[client_data_counter]->destination);
+ free(client_conn_array[client_data_counter]);
+ client_conn_array[client_conn_array[client_data_counter]->conn_to] =
+ NULL;
+ client_conn_array[client_data_counter] = NULL;
+ /*
+ * the nfds_ready value is tricky, because we're not sure if we got
+ * a readable or writable on the associated connection for this
+ * iteration through select(); we'll decrement it one instead of two,
+ * but it really doesn't matter either way given the logic of the
+ * process readables and writables code
+ */
+ *nfds_ready--;
+ /*
+ * if you just shut this connection object down, you don't want
+ * to reset its creation date to now, so go to the next one
+ */
+ continue;
+ }
+ /*
+ * recompute select() timeout to maximize blocking time without
+ * preventing timeout checking
+ */
+ config_info->select_timeout.tv_sec =
+ min(config_info->select_timeout.tv_sec,
+ client_conn_array[client_data_counter]->time_to_close -
+ (current_time.tv_sec -
+ client_conn_array[client_data_counter]->creation_time));
+ /*
+ * this wasn't a shutdown case, so check to see if there's activity
+ * on the fd; if so, then reset the creation time field to now
+ */
+ if (FD_ISSET(client_conn_array[client_data_counter]->fd, readable) ||
+ FD_ISSET(client_conn_array[client_data_counter]->fd, writable))
+ client_conn_array[client_data_counter]->creation_time =
+ current_time.tv_sec;
+ /*
+ * do the same thing with the conn_to connections, but only
+ * if they haven't already been marked for closing
+ */
+ if ((client_conn_array[client_data_counter]->conn_to) > 0)
+ {
+
+ if ((FD_ISSET(client_conn_array[client_data_counter]->conn_to,
+ readable)) ||
+ (FD_ISSET(client_conn_array[client_data_counter]->conn_to,
+ writable)))
+ client_conn_array[client_data_counter]->creation_time =
+ current_time.tv_sec;
+ }
+ }
+ }
+
+ /*
+ * now do the client listen fds; as with the client data objects,
+ * we have to do them all, because a timeout may occur even if the
+ * object's fd is not currently readable or writable
+ */
+ for (client_listen_counter = 0;
+ client_listen_counter < config_info->num_servers;
+ client_listen_counter++)
+ {
+ if (server_array[client_listen_counter] != NULL)
+ {
+ /*
+ * do the shutdown time computation
+ */
+ if ((current_time.tv_sec
+ - server_array[client_listen_counter]->creation_time)
+ > server_array[client_listen_counter]->time_to_close)
+ {
+ /*
+ * time to shut this listener down just like we did above;
+ * we close the server connection here as well but we don't
+ * need to worry about the select() mask because we're not
+ * using the server fd in this object for reading or writing --
+ * only to initialize the client data object server connections
+ */
+ FD_CLR(server_array[client_listen_counter]->client_listen_fd, rinit);
+ FD_CLR(server_array[client_listen_counter]->client_listen_fd, winit);
+ close(server_array[client_listen_counter]->client_listen_fd);
+ free(server_array[client_listen_counter]);
+ server_array[client_listen_counter] = NULL;
+ *nfds_ready--;
+ /*
+ * if you just shut this connection object down, you don't want
+ * to reset its creation date to now, so go to the next one
+ */
+ continue;
+ }
+ /*
+ * recompute select() timeout to maximize blocking time without
+ * preventing timeout checking
+ */
+ config_info->select_timeout.tv_sec =
+ min(config_info->select_timeout.tv_sec,
+ server_array[client_listen_counter]->time_to_close -
+ (current_time.tv_sec -
+ server_array[client_listen_counter]->creation_time));
+ /*
+ * this wasn't a shutdown case, so check to see if there's activity
+ * on the fd; if so, then reset the creation time field to now
+ */
+ if (FD_ISSET(server_array[client_listen_counter]->client_listen_fd,
+ readable) ||
+ FD_ISSET(server_array[client_listen_counter]->client_listen_fd,
+ writable))
+ server_array[client_listen_counter]->creation_time =
+ current_time.tv_sec;
+ }
+ }
+
+ /*
+ * last of all the pm connection fds
+ */
+ for (pm_conn_counter = 0;
+ pm_conn_counter < config_info->num_pm_conns;
+ pm_conn_counter++)
+ {
+ if (pm_conn_array[pm_conn_counter] != NULL)
+ {
+ /*
+ * do the shutdown time computation
+ */
+ if ((current_time.tv_sec
+ - pm_conn_array[pm_conn_counter]->creation_time)
+ > pm_conn_array[pm_conn_counter]->time_to_close)
+ {
+ /*
+ * shut this connection down just like the others
+ */
+ FD_CLR(pm_conn_array[pm_conn_counter]->fd, rinit);
+ FD_CLR(pm_conn_array[pm_conn_counter]->fd, winit);
+ close(pm_conn_array[pm_conn_counter]->fd);
+ free(pm_conn_array[pm_conn_counter]);
+ pm_conn_array[pm_conn_counter] = NULL;
+ *nfds_ready--;
+ /*
+ * if you just shut this connection object down, you don't want
+ * to reset its creation date to now, so go to the next one
+ */
+ continue;
+ }
+ /*
+ * recompute select() timeout to maximize blocking time without
+ * preventing timeout checking
+ */
+ config_info->select_timeout.tv_sec =
+ min(config_info->select_timeout.tv_sec,
+ pm_conn_array[pm_conn_counter]->time_to_close -
+ (current_time.tv_sec -
+ pm_conn_array[pm_conn_counter]->creation_time));
+ /*
+ * this wasn't a shutdown case, so check to see if there's activity
+ * on the fd; if so, then reset the creation time field to now
+ */
+ if (FD_ISSET(pm_conn_array[pm_conn_counter]->fd, readable) ||
+ FD_ISSET(pm_conn_array[pm_conn_counter]->fd, writable))
+ pm_conn_array[pm_conn_counter]->creation_time = current_time.tv_sec;
+ }
+ }
+}
+
+
+int
+doHandleConfigFile (
+ struct config * config_info)
+{
+ FILE * stream;
+ char line[128];
+ int num_chars = 120;
+ int line_number = 0;
+
+ if (!config_info->config_file_path)
+ return 1;
+
+ if ((stream = fopen(config_info->config_file_path, "r")) == NULL)
+ {
+ perror("Could not open config file");
+ return 0;
+ }
+
+ while (1)
+ {
+ if ((fgets(line, num_chars, stream)) == NULL)
+ {
+#ifdef DEBUG
+ (void) fprintf(stderr, "Reading config file - got 0 bytes\n");
+#endif
+ break;
+ }
+
+#ifdef DEBUG
+ (void) fprintf(stderr, line);
+#endif
+
+ line_number++;
+
+ if (!doProcessLine(line, config_info, line_number))
+ {
+ (void) fprintf(stderr,"Config file format error. Parse failed.\n");
+ (void) fprintf(stderr,"\tline: %s\n", line);
+ (void) fclose(stream);
+ return 0;
+ }
+ }
+
+ if (!feof(stream))
+ {
+ (void) fprintf(stderr, "Error parsing config file; not at eof\n");
+ (void) fclose(stream);
+ return 0;
+ }
+
+ if (printConfigVerify)
+ (void) fprintf(stderr, "%d rules read\n", config_info->rule_count);
+
+ (void) fclose(stream);
+ return 1;
+}
+
+void
+doWriteLogEntry(
+ char * source,
+ char * destination,
+ int event,
+ int rule_number,
+ struct config * config_info)
+{
+ FILE * stream;
+ struct timezone current_zone;
+ struct timeval current_time;
+ char * time_stamp;
+ int time_length;
+
+ /*
+ * if no logfile, then return without action
+ */
+ if (!config_info->log_file_path)
+ return;
+
+ /*
+ * The xfwp audit/logging spec says to always log CLIENT_REJECT_CONFIG
+ * events but other events should only be logged if log_level is
+ * > 0
+ */
+ if (event != CLIENT_REJECT_CONFIG && !config_info->log_level)
+ return;
+
+ if ((stream = fopen(config_info->log_file_path, "a")) == NULL)
+ {
+ (void) fprintf(stderr,
+ "Failed to open log file '%s'\n",
+ config_info->log_file_path);
+ return;
+ }
+
+ /*
+ * generate time stamp for this event
+ */
+ gettimeofday(&current_time, &current_zone);
+ time_stamp = ctime((time_t *) &current_time.tv_sec);
+ time_length = strlen(time_stamp);
+
+ /*
+ * eliminate newline character in time stamp
+ */
+ *(&time_stamp[time_length - 1]) = (char) 0;
+
+ (void) fprintf (stream, "%s %2d %s %s %2d\n",
+ time_stamp,
+ event,
+ (source) ? source : "",
+ (destination) ? destination : "",
+ rule_number);
+
+ (void) fclose(stream);
+}
+
+
+void
+doCopyFromTo(
+ int fd_from,
+ int fd_to,
+ fd_set * rinit,
+ fd_set * winit)
+{
+ int ncopy;
+
+ if (client_conn_array[fd_from]->wbytes < RWBUFFER_SIZE)
+ {
+ /*
+ * choose to write either how much you have (from->rbytes),
+ * or how much you can hold (to->wbytes), whichever is
+ * smaller
+ */
+ ncopy = min(client_conn_array[fd_from]->rbytes,
+ RWBUFFER_SIZE - client_conn_array[fd_to]->wbytes);
+ /*
+ * index into existing number bytes into the write buffer
+ * to get the start point for copying
+ */
+ bcopy(client_conn_array[fd_from]->readbuf,
+ client_conn_array[fd_to]->writebuf +
+ client_conn_array[fd_to]->wbytes, ncopy);
+ /*
+ * Then up the to->wbytes counter
+ */
+ client_conn_array[fd_to]->wbytes += ncopy;
+ /*
+ * something has to be done here with the select mask!!
+ */
+ FD_SET(fd_to, winit);
+ if (ncopy == client_conn_array[fd_from]->rbytes)
+ client_conn_array[fd_from]->rbytes = 0;
+ else
+ {
+ bcopy(client_conn_array[fd_from]->readbuf + ncopy,
+ client_conn_array[fd_from]->readbuf,
+ client_conn_array[fd_from]->rbytes - ncopy);
+ client_conn_array[fd_from]->rbytes -= ncopy;
+ }
+ /*
+ * and here
+ */
+ FD_SET(fd_to, rinit);
+ }
+ /*
+ * If there's no room in the fd_to write buffer, do nothing
+ * this iteration (keep iterating on select() until something
+ * gets written from this fd)
+ */
+ return;
+}
+
+
+int
+doCheckServerList(
+ char * server_address,
+ char ** listen_port_string,
+ int num_servers)
+{
+ /*
+ * this routine checks the server_address (provided by XFindProxy
+ * and forwarded through the PM to the FWP) against the list of
+ * servers to which connections have already been established;
+ * it does no format type checking or conversions! (i.e., network-id
+ * vs. hostname representations); if the string received is not an
+ * exact match to one in the list, FWP will open a new connection
+ * to the specified server, even though one may already exist under
+ * a different name-format; all this is in a separate routine in
+ * case we want to check the various formats in the future
+ */
+ int list_counter;
+
+ for (list_counter = 0; list_counter < num_servers; list_counter++)
+ {
+ if (server_array[list_counter] != NULL)
+ {
+ if (!strcmp(server_array[list_counter]->x_server_hostport,
+ server_address))
+ {
+ /*
+ * allocate and return the listen_port_string
+ */
+ if ((*listen_port_string = (char *) malloc
+ (strlen(server_array[list_counter]->listen_port_string) + 1))
+ == NULL)
+ {
+ (void) fprintf(stderr, "malloc - listen_port_string\n");
+ return FAILURE;
+ }
+ strcpy(*listen_port_string,
+ server_array[list_counter]->listen_port_string);
+ return SUCCESS;
+ }
+ }
+ }
+ return FAILURE;
+}
+
+
+void
+doProcessInputArgs (
+ struct config * config_info,
+ int argc,
+ char * argv[])
+{
+ int arg_counter;
+ int break_flag = 0;
+
+ config_info->num_servers = 0;
+ config_info->num_pm_conns = 0;
+ config_info->pm_data_timeout = 0;
+ config_info->client_listen_timeout = 0;
+ config_info->client_data_timeout = 0;
+ config_info->log_level = 0;
+ config_info->rule_count = config_info->lines_allocated = 0;
+ config_info->pm_listen_port = NULL;
+ config_info->config_file_data = NULL;
+ config_info->config_file_path = NULL;
+ config_info->log_file_path = NULL;
+
+ /*
+ * initialize timeout for three port types; if a timeout for a
+ * particular port type (pmdata, clientlisten, clientdata) is
+ * not specified explicitly, then it assumes the hard-coded
+ * default value; initialize other command line options here
+ * as well
+ */
+ for (arg_counter = 1; arg_counter < argc; arg_counter++)
+ {
+ if (argv[arg_counter][0] == '-')
+ {
+ if (!strcmp("-pdt", argv[arg_counter]))
+ {
+ if (arg_counter + 1 == argc)
+ {
+ break_flag = 1;
+ break;
+ }
+ config_info->pm_data_timeout = atoi(argv[arg_counter + 1]);
+ }
+ else if (!strcmp("-clt", argv[arg_counter]))
+ {
+ if (arg_counter + 1 == argc)
+ {
+ break_flag = 1;
+ break;
+ }
+ config_info->client_listen_timeout = atoi(argv[arg_counter + 1]);
+ }
+ else if (!strcmp("-cdt", argv[arg_counter]))
+ {
+ if (arg_counter + 1 == argc)
+ {
+ break_flag = 1;
+ break;
+ }
+ config_info->client_data_timeout = atoi(argv[arg_counter + 1]);
+ }
+ else if (!strcmp("-pmport", argv[arg_counter]))
+ {
+ if (arg_counter + 1 == argc)
+ {
+ break_flag = 1;
+ break;
+ }
+ if (atoi(argv[arg_counter + 1]) > 65536)
+ {
+ break_flag = 1;
+ break;
+ }
+ config_info->pm_listen_port = Malloc(strlen(argv[arg_counter+1])+1);
+ strcpy(config_info->pm_listen_port, argv[arg_counter + 1]);
+ }
+ else if (!strcmp("-max_pm_conns", argv[arg_counter]))
+ {
+ if (arg_counter + 1 == argc)
+ {
+ break_flag = 1;
+ break;
+ }
+ config_info->num_pm_conns = atoi(argv[arg_counter + 1]);
+ }
+ else if (!strcmp("-max_server_conns", argv[arg_counter]))
+ {
+ if (arg_counter + 1 == argc)
+ {
+ break_flag = 1;
+ break;
+ }
+ config_info->num_servers = atoi(argv[arg_counter + 1]);
+ }
+ else if (!strcmp("-config", argv[arg_counter]))
+ {
+ if (arg_counter + 1 == argc)
+ {
+ break_flag = 1;
+ break;
+ }
+ config_info->config_file_path = Malloc(strlen(argv[arg_counter+1])+1);
+ strcpy(config_info->config_file_path, argv[arg_counter + 1]);
+ }
+ else if (!strcmp("-verify", argv[arg_counter]))
+ {
+ printConfigVerify = TRUE;
+ }
+ else if (!strcmp("-logfile", argv[arg_counter]))
+ {
+ if (arg_counter + 1 == argc)
+ {
+ break_flag = 1;
+ break;
+ }
+ config_info->log_file_path = Malloc(strlen(argv[arg_counter+1])+1);
+ strcpy(config_info->log_file_path, argv[arg_counter + 1]);
+ }
+ else if (!strcmp("-loglevel", argv[arg_counter]))
+ {
+ if ((arg_counter + 1 == argc) || (atoi(argv[arg_counter + 1]) > 1))
+ {
+ break_flag = 1;
+ break;
+ }
+ config_info->log_level = atoi(argv[arg_counter + 1]);
+ }
+ else
+ {
+ (void) fprintf(stderr, "Unrecognized command line argument\n");
+ Usage();
+ }
+ }
+ }
+ if (break_flag)
+ Usage();
+
+ /*
+ * Create space for the global connection arrays
+ */
+#ifdef XNO_SYSCONF /* should only be on FreeBSD 1.x and NetBSD 0.x */
+#undef _SC_OPEN_MAX
+#endif
+#ifdef _SC_OPEN_MAX
+ config_info->num_client_conns = sysconf(_SC_OPEN_MAX) - 1;
+#else
+#ifdef hpux
+ config_info->num_client_conns = _NFILE - 1;
+#else
+ config_info->num_client_conns = getdtablesize() - 1;
+#endif
+#endif
+
+ client_conn_array = (struct client_conn_buf **)
+ malloc (config_info->num_client_conns * sizeof (struct client_conn_buf *));
+ if (!client_conn_array)
+ {
+ (void) fprintf (stderr, "malloc - client connection array\n");
+ exit (1);
+ }
+
+ if (!config_info->num_pm_conns)
+ config_info->num_pm_conns = MAX_PM_CONNS;
+ pm_conn_array = (struct pm_conn_buf **)
+ malloc (config_info->num_client_conns * sizeof (struct pm_conn_buf *));
+ if (!pm_conn_array)
+ {
+ (void) fprintf (stderr, "malloc - PM connection array\n");
+ exit (1);
+ }
+
+ if (!config_info->num_servers)
+ config_info->num_servers = MAX_SERVERS;
+ server_array = (struct server_list **)
+ malloc (config_info->num_servers * sizeof (struct server_list *));
+ if (!server_array)
+ {
+ (void) fprintf (stderr, "malloc - server listen array\n");
+ exit (1);
+ }
+
+ /*
+ * check timeout values; if still zero then apply defaults
+ */
+ if (config_info->pm_data_timeout <= 0)
+ config_info->pm_data_timeout = PM_DATA_TIMEOUT_DEFAULT;
+ if (config_info->client_listen_timeout <= 0)
+ config_info->client_listen_timeout = CLIENT_LISTEN_TIMEOUT_DEFAULT;
+ if (config_info->client_data_timeout <= 0)
+ config_info->client_data_timeout = CLIENT_DATA_TIMEOUT_DEFAULT;
+ if (config_info->pm_listen_port == NULL)
+ {
+ config_info->pm_listen_port = Malloc(strlen(PM_LISTEN_PORT) + 1);
+ strcpy(config_info->pm_listen_port, PM_LISTEN_PORT);
+ }
+}
+
+
+int
+doInitDataStructs(
+ struct config * config_info,
+ struct ICE_setup_info * pm_conn_setup)
+{
+ int i;
+
+ /*
+ * Initialize select() timeout; start with a high value, which will
+ * be overridden by the minimum timeout value of all fd's taken
+ * together;this heuristic allows us to block inside select()
+ * as much as possible (avoiding CPU spin cycles), as well as
+ * to periodically check timeouts on open ports and thereby recover
+ * them
+ */
+ config_info->select_timeout.tv_usec = 0;
+ config_info->select_timeout.tv_sec = 180000;
+
+ /*
+ * NULL the connection arrays
+ */
+ for (i = 0; i < config_info->num_client_conns; i++)
+ client_conn_array[i] = NULL;
+ for (i = 0; i < config_info->num_pm_conns; i++)
+ pm_conn_array[i] = NULL;
+ for (i = 0; i < config_info->num_servers; i++)
+ server_array[i] = NULL;
+
+ /*
+ * init ICE connection setup data
+ */
+ pm_conn_setup->opcode = 0;
+ pm_conn_setup->versionCount = 1;
+ pm_conn_setup->PMVersions->major_version = 1;
+ pm_conn_setup->PMVersions->minor_version = 0;
+ pm_conn_setup->PMVersions->process_msg_proc =
+ (IcePaProcessMsgProc) FWPprocessMessages;
+ /*
+ * Register for protocol setup
+ */
+ if ((pm_conn_setup->opcode = IceRegisterForProtocolReply(
+ PM_PROTOCOL_NAME,
+ "XC",
+ "1.0",
+ pm_conn_setup->versionCount,
+ pm_conn_setup->PMVersions,
+ 0, /* authcount */
+ NULL, /* authname */
+ NULL, /* authprocs */
+ FWPHostBasedAuthProc, /* force non-auth */
+ FWPprotocolSetupProc,
+ NULL, /* protocol activate proc */
+ NULL /* IceIOErrorProc */ )) < 0)
+ {
+ /*
+ * Log this message?
+ */
+ (void) fprintf(stderr, "Could not register PROXY_MANAGEMENT ICE protocol");
+ return FAILURE;
+ }
+
+ global_data.major_opcode = pm_conn_setup->opcode;
+
+ return SUCCESS;
+}
+
diff --git a/misc.h b/misc.h
new file mode 100644
index 0000000..f1e7064
--- /dev/null
+++ b/misc.h
@@ -0,0 +1,111 @@
+/* $Xorg: misc.h,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */
+/*
+
+Copyright "1986-1997, 1998 The Open Group
+
+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.
+
+The above copyright notice and the following permission notice
+shall be included in all copies of the Software:
+
+THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
+CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN
+THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group
+shall not be used in advertising or otherwise to promote the use
+or other dealings in this Software without prior written
+authorization from The Open Group.
+
+X Window System is a trademark of The Open Group.
+
+*/
+
+#ifndef _MISC_H
+#define _MISC_H
+
+/*
+ * the following are timeout defaults (in seconds) on the three
+ * types of ports (pm-data, client-listen and client-data)
+ */
+#define PM_DATA_TIMEOUT_DEFAULT 3600 /* 1 hour */
+#define CLIENT_LISTEN_TIMEOUT_DEFAULT 86400 /* 24 hours */
+#define CLIENT_DATA_TIMEOUT_DEFAULT 604800 /* 1 week */
+
+#define PM_LISTEN_PORT "4444"
+
+/*
+ * allocate ADD_LINES entries at a time when loading the configuration
+ */
+#define ADD_LINES 20
+#define SEPARATOR1 " \t\n"
+#define SEPARATOR2 '.'
+
+
+extern char*
+Malloc(
+ int s);
+
+extern int
+doConfigCheck(
+ struct sockaddr_in * source_sockaddr_in,
+ struct sockaddr_in * dest_sockaddr_in,
+ struct config * config_info,
+ int context,
+ int * rule_number);
+
+extern void
+doCheckTimeouts(
+ struct config * config_info,
+ int * nfds_ready,
+ fd_set * rinit,
+ fd_set * winit,
+ fd_set * readable,
+ fd_set * writable);
+
+extern int
+doHandleConfigFile (
+ struct config * config_info);
+
+extern void
+doWriteLogEntry(
+ char * source,
+ char * destination,
+ int event,
+ int rule_number,
+ struct config * config_info);
+
+extern void
+doCopyFromTo(
+ int fd_from,
+ int fd_to,
+ fd_set * rinit,
+ fd_set * winit);
+
+extern int
+doCheckServerList(
+ char * server_address,
+ char ** listen_port_string,
+ int num_servers);
+
+extern void
+doProcessInputArgs(
+ struct config * config_info,
+ int argc,
+ char * argv[]);
+
+extern int
+doInitDataStructs(
+ struct config * config_info,
+ struct ICE_setup_info * PM_conn_setup);
+
+#endif /* _MISC_H */
diff --git a/pm.c b/pm.c
new file mode 100644
index 0000000..4df1634
--- /dev/null
+++ b/pm.c
@@ -0,0 +1,538 @@
+/* $Xorg: pm.c,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */
+/*
+
+Copyright "1986-1997, 1998 The Open Group
+
+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.
+
+The above copyright notice and the following permission notice
+shall be included in all copies of the Software:
+
+THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
+CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN
+THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group
+shall not be used in advertising or otherwise to promote the use
+or other dealings in this Software without prior written
+authorization from The Open Group.
+
+X Window System is a trademark of The Open Group.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xos.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <signal.h>
+#include <X11/Xproto.h>
+
+#include <X11/ICE/ICElib.h>
+#include <X11/ICE/ICEmsg.h>
+#include <X11/ICE/ICEproto.h>
+
+#include <X11/PM/PM.h>
+#include <X11/PM/PMproto.h>
+
+#include "xfwp.h"
+#include "pm.h"
+#include "transport.h"
+#include "misc.h"
+
+void FWPprocessMessages(
+ IceConn iceConn,
+ IcePointer * client_data,
+ int opcode,
+ unsigned long length,
+ Bool swap)
+{
+ switch (opcode)
+ {
+ /*
+ * this is really the only opcode we care about -- the one
+ * which indicates an XFindProxy request for a connection
+ * to a specified server
+ */
+ case PM_GetProxyAddr:
+ {
+ pmGetProxyAddrMsg *pMsg;
+ char *pData, *pStart;
+ char *serviceName = NULL, *serverAddress = NULL;
+ char *hostAddress = NULL, *startOptions = NULL;
+ char *authName = NULL, *authData = NULL;
+ int authLen;
+ struct clientDataStruct * program_data;
+ char * listen_port_string;
+ int pm_send_msg_len;
+ pmGetProxyAddrReplyMsg * pReply;
+ char * pReplyData;
+ struct hostent * hostptr;
+ struct sockaddr_in server_sockaddr_in;
+ struct sockaddr_in dummy_sockaddr_in;
+ char * server_name_base;
+ char * config_failure = "unrecognized server or permission denied";
+ char * tmp_str;
+ int rule_number = -1;
+ char * colon;
+ char * tmpAddress = NULL;
+
+ /*
+ * this is where we need and get access to that client data we
+ * went through such contortions to set up earlier!
+ */
+ program_data = (struct clientDataStruct *) client_data;
+ /*
+ * initial check on expected message size
+ */
+ CHECK_AT_LEAST_SIZE (iceConn, global_data.major_opcode, opcode,
+ length, SIZEOF (pmGetProxyAddrMsg), IceFatalToProtocol);
+
+ IceReadCompleteMessage (iceConn, SIZEOF (pmGetProxyAddrMsg),
+ pmGetProxyAddrMsg, pMsg, pStart);
+
+ if (!IceValidIO (iceConn))
+ {
+ IceDisposeCompleteMessage (iceConn, pStart);
+ return;
+ }
+
+ authLen = swap ? lswaps (pMsg->authLen) : pMsg->authLen;
+
+ pData = pStart;
+
+ SKIP_STRING (pData, swap); /* proxy-service */
+ SKIP_STRING (pData, swap); /* server-address */
+ SKIP_STRING (pData, swap); /* host-address */
+ SKIP_STRING (pData, swap); /* start-options */
+ if (authLen > 0)
+ {
+ SKIP_STRING (pData, swap); /* auth-name */
+ pData += (authLen + PAD64 (authLen)); /* auth-data */
+ }
+ /*
+ * now a detailed check on message size
+ */
+ CHECK_COMPLETE_SIZE (iceConn, global_data.major_opcode, opcode,
+ length, pData - pStart + SIZEOF (pmGetProxyAddrMsg),
+ pStart, IceFatalToProtocol);
+
+ pData = pStart;
+ /*
+ * extract message data, based on known characteristics
+ * of this message type
+ */
+ EXTRACT_STRING (pData, swap, serviceName);
+ EXTRACT_STRING (pData, swap, serverAddress);
+ EXTRACT_STRING (pData, swap, hostAddress);
+ EXTRACT_STRING (pData, swap, startOptions);
+ if (authLen > 0)
+ {
+ EXTRACT_STRING (pData, swap, authName);
+ authData = (char *) malloc (authLen);
+ memcpy (authData, pData, authLen);
+ }
+#ifdef DEBUG
+ (void) fprintf (stderr,
+ "Got GetProxyAddr, serviceName = %s, serverAddr = %s\n",
+ serviceName, serverAddress);
+ (void) fprintf (stderr,
+ "\thostAddr = %s, options = %s, authLen = %d\n",
+ hostAddress, startOptions, authLen);
+ if (authLen > 0)
+ (void) fprintf (stderr, "\tauthName = %s\n", authName);
+#endif
+ /*
+ * need to copy the host port string because strtok() changes it
+ */
+ if ((tmp_str = strdup (serverAddress)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - serverAddress copy\n");
+ goto sendFailure;
+ }
+
+ /*
+ * before proceeding we want to verify that we are allowed to
+ * accept connections from the host who called xfindproxy();
+ * the thing is, we don't get that host name from Proxy Manager
+ * even if the "-host <hostname>" command-line option was present
+ * in xfindproxy (and even if it was we shouldn't rely on it --
+ * much better to have ProxyMngr query the xfindproxy connect
+ * socket for its origin); the upshot of all this that we do
+ * a configuration check *only* on the destination (which we
+ * assume in this case to be the serverAddress passed in by
+ * xfindproxy(); so get the destination IP address!
+ */
+ server_name_base = strtok(tmp_str, ":");
+ if ((hostptr = gethostbyname(server_name_base)) == NULL)
+ {
+ (void) fprintf(stderr, "gethostbyname (%s) failed\n", server_name_base);
+ goto sendFailure;
+ }
+ memset(&server_sockaddr_in, 0, sizeof(server_sockaddr_in));
+ memset(&dummy_sockaddr_in, 0, sizeof(dummy_sockaddr_in));
+ memcpy((char *) &server_sockaddr_in.sin_addr,
+ hostptr->h_addr,
+ hostptr->h_length);
+
+ /*
+ * need to initialize dummy to something, but doesn't matter
+ * what (should eventually be the true host address);
+ * NOTE: source configuration will always match (see XFWP man
+ * page) unless sysadmin explicitly chooses to deny
+ */
+ memcpy((char *) &dummy_sockaddr_in.sin_addr,
+ hostptr->h_addr,
+ hostptr->h_length);
+
+ if ((doConfigCheck(&dummy_sockaddr_in,
+ &server_sockaddr_in,
+ global_data.config_info,
+ FINDPROXY,
+ &rule_number)) == FAILURE)
+ {
+ (void) fprintf(stderr, "xfindproxy failed config check\n");
+ sendFailure:
+ /*
+ * report failure back to the ProxyMgr
+ *
+ */
+ pm_send_msg_len = STRING_BYTES(config_failure)
+ + STRING_BYTES(NULL);
+ IceGetHeaderExtra(iceConn,
+ program_data->major_opcode,
+ PM_GetProxyAddrReply,
+ SIZEOF(pmGetProxyAddrReplyMsg),
+ WORD64COUNT (pm_send_msg_len),
+ pmGetProxyAddrReplyMsg,
+ pReply,
+ pReplyData);
+ pReply->status = PM_Failure;
+ STORE_STRING(pReplyData, NULL);
+ STORE_STRING(pReplyData, config_failure);
+ IceFlush(iceConn);
+ free(tmp_str);
+ return;
+ }
+
+ /*
+ * okay, you got what you need from the PM to proceed,
+ * so extract the fd of the selected connection and use
+ * it to set up the remote client listen port and add
+ * the name of the X server to your list of server connections
+ */
+
+ /*
+ * Before checking to see if you already have a PM connection
+ * request for this server, make serverAddress a
+ * FQDN so that synonomous names like oregon:0 and oregon.com:0
+ * will be recognized as the same Xserver. If this server
+ * already exists, don't allocate another listen port for it -
+ * use the already allocated one
+ */
+ colon = strchr (serverAddress, ':');
+ if (colon)
+ {
+ struct hostent *hostent;
+
+ *colon = '\0';
+ hostent = gethostbyname (serverAddress);
+ *colon = ':';
+
+ if (hostent && hostent->h_name) {
+ tmpAddress = (char *) malloc (strlen (hostent->h_name) +
+ strlen (colon) + 1);
+ (void) sprintf (tmpAddress, "%s%s", hostent->h_name, colon);
+ serverAddress = tmpAddress;
+ }
+ }
+
+ if ((doCheckServerList(serverAddress,
+ &listen_port_string,
+ program_data->config_info->num_servers)) == FAILURE)
+ {
+ /*
+ * this server name isn't in your list; so set up a new
+ * remote client listen port for it; extract the fd from
+ * the connection and pass it in as index to array lookup
+ */
+ if ((doSetupRemClientListen(&listen_port_string,
+ program_data,
+ serverAddress)) == FAILURE)
+ {
+ goto sendFailure;
+ }
+ }
+ if (tmpAddress)
+ free (tmpAddress);
+
+ /*
+ * the PM-sent server address *was* in your list, so send back
+ * the rem client listen port you had already associated with
+ * that server (it will presumably be forwarded to the remote
+ * client through some other channel)
+ * use IceGetHeaderExtra() and the
+ */
+ pm_send_msg_len = STRING_BYTES(listen_port_string)
+ + STRING_BYTES(NULL);
+ IceGetHeaderExtra(iceConn,
+ program_data->major_opcode,
+ PM_GetProxyAddrReply,
+ SIZEOF(pmGetProxyAddrReplyMsg),
+ WORD64COUNT (pm_send_msg_len),
+ pmGetProxyAddrReplyMsg,
+ pReply,
+ pReplyData);
+ pReply->status = PM_Success;
+ STORE_STRING(pReplyData, listen_port_string);
+ STORE_STRING(pReplyData, NULL);
+ IceFlush(iceConn);
+ /*
+ * before leaving this routine, change the select() timeout
+ * here to be equal to the configured client listen timeout
+ * (otherwise you'll never *get* to your listen timeout
+ * if it's shorter than the startup select() default
+ */
+ program_data->config_info->select_timeout.tv_sec =
+ program_data->config_info->client_listen_timeout;
+ break;
+ }
+
+ case ICE_Error:
+ {
+ iceErrorMsg *pMsg;
+ char *pStart;
+
+ CHECK_AT_LEAST_SIZE (iceConn, global_data.major_opcode, ICE_Error,
+ length, sizeof(iceErrorMsg), IceFatalToProtocol);
+
+ IceReadCompleteMessage (iceConn, SIZEOF (iceErrorMsg),
+ iceErrorMsg, pMsg, pStart);
+
+ if (!IceValidIO (iceConn))
+ {
+ IceDisposeCompleteMessage (iceConn, pStart);
+ return;
+ }
+
+ if (swap)
+ {
+ pMsg->errorClass = lswaps (pMsg->errorClass);
+ pMsg->offendingSequenceNum = lswapl (pMsg->offendingSequenceNum);
+ }
+
+ (void) fprintf(stderr, "Proxy Manager reported ICE Error:\n");
+ (void) fprintf(stderr, "\tclass = 0x%x, offending minor opcode = %d\n",
+ pMsg->errorClass, pMsg->offendingMinorOpcode);
+ (void) fprintf(stderr, "\tseverity = %d, sequence = %d\n",
+ pMsg->severity, pMsg->offendingSequenceNum);
+
+ IceDisposeCompleteMessage (iceConn, pStart);
+
+ break;
+ }
+
+ default:
+ break;
+ } /* end switch */
+}
+
+
+/*ARGSUSED*/
+Bool
+FWPHostBasedAuthProc (
+ char * hostname)
+{
+ /*
+ * don't worry about config for now
+ *
+ * this routine gets called *after* IceAcceptConnection
+ * is called but *before* that routine returns its status;
+ * it is therefore the logical place to check configuration
+ * data on which PM connections (from which hosts) will be
+ * accepted; so do it and return either 0 to terminate
+ * connection (automatically informing PM) or 1 to proceed
+ *
+ * the PM host is not allowed; terminate connection and inform
+ * requestor why (handled automatically by ICElib)
+ */
+
+ return True;
+}
+
+
+/*ARGSUSED*/
+Status
+FWPprotocolSetupProc(
+ IceConn iceConn,
+ int major_version,
+ int minor_version,
+ char * vendor,
+ char * release,
+ IcePointer * clientDataRet,
+ char ** failureReasonRet)
+{
+ /*
+ * This routine gets invoked when the remote ICE originator
+ * (in this case PM) calls IceRegisterSetup() on *its* end
+ * of the FWP connection; this is where the pointer to
+ * client data should be initialized, so that this data
+ * can be accessed when FMprocessMessages is called by
+ * IceProcessMessages()
+ */
+ struct clientDataStruct * client_data;
+ if ((client_data = (struct clientDataStruct *)
+ malloc (sizeof (struct clientDataStruct))) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - client data object\n");
+ return (0);
+ }
+ /*
+ * setup the client data struct; we need this object in order to
+ * avoid making these variables global so they can be accessed in the
+ * ICE FWPprocessMessages() callback; now you see that our global_data
+ * struct was the only way of getting program data into the
+ * protocolReply setup routine!
+ */
+ client_data->config_info = global_data.config_info;
+ client_data->nfds = global_data.nfds;
+ client_data->rinit = global_data.rinit;
+ client_data->winit = global_data.winit;
+ client_data->major_opcode = global_data.major_opcode;
+ *clientDataRet = client_data;
+
+ return (1);
+}
+
+int
+doSetupPMListen(
+ char * pm_port,
+ int * size_pm_listen_array,
+ int ** pm_listen_array,
+ IceListenObj ** listen_objects,
+ int * nfds,
+ fd_set * rinit)
+{
+ int num_fds_returned;
+ char errormsg[256];
+ int fd_counter;
+ IceListenObj * temp_obj;
+
+ /*
+ * establish PM listeners
+ */
+ if (!IceListenForWellKnownConnections(pm_port,
+ &num_fds_returned,
+ listen_objects,
+ 256,
+ errormsg))
+ {
+ (void) fprintf(stderr, "IceListenForWellKnowConnections error: %s\n",
+ errormsg);
+ return 0;
+ }
+
+ /*
+ * Create space for pm_listen_array
+ */
+ *pm_listen_array = (int *) malloc (num_fds_returned * sizeof (int *));
+ if (!pm_listen_array)
+ {
+ (void) fprintf (stderr, "malloc - pm_listen_array\n");
+ return 0;
+ }
+ *size_pm_listen_array = num_fds_returned;
+
+ /*
+ * obtain the PM listen fd's for the connection objects
+ */
+ for (fd_counter = 0; fd_counter < num_fds_returned; fd_counter++)
+ {
+ /*
+ * get fd(s) for PM listen (could be more than one if different
+ * transport mechanisms)
+ */
+ temp_obj = *listen_objects;
+ IceSetHostBasedAuthProc(temp_obj[fd_counter], FWPHostBasedAuthProc);
+ (*pm_listen_array)[fd_counter] =
+ IceGetListenConnectionNumber(temp_obj[fd_counter]);
+
+ /*
+ * set all read mask bits on which we are going to select();
+ * [NOTE: We don't care about write bits here because we don't
+ * use select() to manage writing to the PM]
+ */
+ FD_SET((*pm_listen_array)[fd_counter], rinit);
+
+ /*
+ * compute nfds for select()
+ */
+ *nfds = max(*nfds, (*pm_listen_array)[fd_counter] + 1);
+ }
+ return 1;
+}
+
+
+/*
+ * The real way to handle IO errors is to check the return status
+ * of IceProcessMessages. xsm properly does this.
+ *
+ * Unfortunately, a design flaw exists in the ICE library in which
+ * a default IO error handler is invoked if no IO error handler is
+ * installed. This default handler exits. We must avoid this.
+ *
+ * To get around this problem, we install an IO error handler that
+ * does a little magic. Since a previous IO handler might have been
+ * installed, when we install our IO error handler, we do a little
+ * trick to get both the previous IO error handler and the default
+ * IO error handler. When our IO error handler is called, if the
+ * previous handler is not the default handler, we call it. This
+ * way, everyone's IO error handler gets called except the stupid
+ * default one which does an exit!
+ */
+
+static IceIOErrorHandler prev_handler;
+
+static void
+MyIoErrorHandler (
+ IceConn ice_conn)
+
+{
+ if (prev_handler)
+ (*prev_handler) (ice_conn);
+}
+
+void
+doInstallIOErrorHandler ()
+{
+ IceIOErrorHandler default_handler;
+
+ prev_handler = IceSetIOErrorHandler (NULL);
+ default_handler = IceSetIOErrorHandler (MyIoErrorHandler);
+ if (prev_handler == default_handler)
+ prev_handler = NULL;
+#ifdef X_NOT_POSIX
+ signal(SIGPIPE, SIG_IGN);
+#else
+ {
+ struct sigaction act;
+
+ (void) sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = SIG_IGN;
+ (void) sigaction(SIGPIPE, &act, NULL);
+ }
+#endif
+}
+
diff --git a/pm.h b/pm.h
new file mode 100644
index 0000000..ae110cd
--- /dev/null
+++ b/pm.h
@@ -0,0 +1,69 @@
+/* $Xorg: pm.h,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */
+/*
+
+Copyright "1986-1997, 1998 The Open Group
+
+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.
+
+The above copyright notice and the following permission notice
+shall be included in all copies of the Software:
+
+THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
+CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN
+THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group
+shall not be used in advertising or otherwise to promote the use
+or other dealings in this Software without prior written
+authorization from The Open Group.
+
+X Window System is a trademark of The Open Group.
+
+*/
+
+#ifndef _PM_H
+#define _PM_H
+
+extern void FWPprocessMessages(
+ IceConn iceConn,
+ IcePointer * client_data,
+ int opcode,
+ unsigned long length,
+ Bool swap);
+
+extern Bool
+FWPHostBasedAuthProc (
+ char * hostname);
+
+extern Status
+FWPprotocolSetupProc(
+ IceConn iceConn,
+ int major_version,
+ int minor_version,
+ char * vendor,
+ char * release,
+ IcePointer * clientDataRet,
+ char ** failureReasonRet);
+
+extern int
+doSetupPMListen(
+ char * pm_port,
+ int * size_pm_listen_array,
+ int ** pm_listen_array,
+ IceListenObj ** listen_objects,
+ int * nfds,
+ fd_set * rinit);
+
+extern void
+doInstallIOErrorHandler ();
+
+#endif /* _PM_H */
diff --git a/transport.c b/transport.c
new file mode 100644
index 0000000..864ab6e
--- /dev/null
+++ b/transport.c
@@ -0,0 +1,334 @@
+/* $Xorg: transport.c,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */
+/*
+
+Copyright "1986-1997, 1998 The Open Group
+
+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.
+
+The above copyright notice and the following permission notice
+shall be included in all copies of the Software:
+
+THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
+CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN
+THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group
+shall not be used in advertising or otherwise to promote the use
+or other dealings in this Software without prior written
+authorization from The Open Group.
+
+X Window System is a trademark of The Open Group.
+
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <X11/Xos.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <X11/Xfuncs.h> /* Need for bzero() */
+
+#ifdef X_NOT_STDC_ENV
+extern int errno;
+#endif
+
+#include <X11/ICE/ICElib.h> /* Need typedef for Bool */
+
+#include "xfwp.h"
+#include "transport.h"
+
+int
+doSetupRemClientListen(
+ char ** listen_port_string,
+ struct clientDataStruct * program_data,
+ char * server_address)
+{
+ int this_server;
+ int one = 1;
+ struct sockaddr_in rem_sockaddr_in;
+ int listen_port;
+ char hostname[MAX_HOSTNAME_LEN];
+ struct timeval time_val;
+ struct timezone time_zone;
+ int num_servers = program_data->config_info->num_servers;
+
+ /*
+ * ugh. This really shouldn't be kept as a sparse list but no time...
+ */
+ for (this_server = 0;
+ this_server < num_servers && server_array[this_server] != NULL;
+ this_server++);
+ if (this_server == num_servers)
+ {
+ (void) fprintf(stderr,
+ "Maximum number of server connections has been reached (%d)\n",
+ program_data->config_info->num_servers);
+ return FAILURE;
+ }
+
+ /*
+ * offset listen port into the X protocol range;
+ * must be > X_SERVER_PORT_BASE < 65535
+ */
+ listen_port = this_server + X_SERVER_PORT_BASE + 1;
+
+ /*
+ * allocate the server_array struct and init the fd elements;
+ * can't use the PM connection fd as an index into this array, since
+ * there could be multiple servers per PM connection
+ */
+ if ((server_array[this_server] =
+ (struct server_list *) malloc(sizeof(struct server_list))) == NULL)
+ {
+ (void) fprintf(stderr,"malloc - server_array\n");
+ return FAILURE;
+ }
+
+ if ((server_array[this_server]->client_listen_fd =
+ socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ (void) fprintf(stderr,"socket call failed\n");
+ free(server_array[this_server]);
+ server_array[this_server] = NULL;
+ return FAILURE;
+ }
+
+ /*
+ * this is where we initialize the current time and timeout on this
+ * client_listen object
+ */
+ gettimeofday(&time_val, &time_zone);
+ server_array[this_server]->creation_time = time_val.tv_sec;
+ server_array[this_server]->time_to_close =
+ global_data.config_info->client_listen_timeout;
+
+ /*
+ * set up the rest of the remote client listener
+ */
+ bzero((char * ) &rem_sockaddr_in, sizeof(rem_sockaddr_in));
+ rem_sockaddr_in.sin_family = AF_INET;
+#ifdef BSD44SOCKETS
+ rem_sockaddr_in.sin_len = sizeof rem_sockaddr_in;
+#endif
+
+#ifdef SO_REUSEADDR
+ if (setsockopt(server_array[this_server]->client_listen_fd,
+ SOL_SOCKET, SO_REUSEADDR,
+ (char *) &one, sizeof(int)) < 0)
+ {
+ (void) fprintf(stderr, "setsockopt(SO_REUSEADDR) failed\n");
+ returnFailure:
+ close(server_array[this_server]->client_listen_fd);
+ free(server_array[this_server]);
+ server_array[this_server] = NULL;
+ return FAILURE;
+ }
+#endif /* SO_REUSEADDR */
+
+ while (True) {
+ rem_sockaddr_in.sin_port = htons(listen_port);
+ if (bind(server_array[this_server]->client_listen_fd,
+ (struct sockaddr *)&rem_sockaddr_in,
+ sizeof(rem_sockaddr_in)) == 0)
+ break;
+ if (errno != EADDRINUSE)
+ {
+ (void) fprintf(stderr,"bind call failed\n");
+ goto returnFailure;
+ }
+ listen_port++;
+
+ /*
+ * Cann't keep going forever.
+ *
+ * Why 65535 - it's the same value used by the LBXProxy.
+ */
+ if (listen_port > 65535)
+ {
+ (void) fprintf(stderr,"failed to find a valid port for bind\n");
+ goto returnFailure;
+ }
+ }
+
+#ifdef DEBUG
+ (void) fprintf (stderr, "Client connect port: %d\n", listen_port);
+#endif
+
+ if (listen(server_array[this_server]->client_listen_fd, SOMAXCONN) < 0)
+ {
+ (void) fprintf(stderr, "listen call failed\n");
+ goto returnFailure;
+ }
+
+ /*
+ * update the nfds
+ */
+ *(program_data->nfds) = max(*(program_data->nfds),
+ server_array[this_server]->client_listen_fd + 1);
+
+ /*
+ * get fully qualified name of host on which FWP is running
+ */
+ if ((gethostname(hostname, MAX_HOSTNAME_LEN)) < 0)
+ {
+ (void) fprintf(stderr, "gethostname call failed\n");
+ goto returnFailure;
+ }
+
+ /*
+ * allocate and convert the listen_port string for return to PM;
+ * string equals address of host on which FWP is running
+ * plus ":<listen_port - X_SERVER_PORT_BASE> (up to xxx)"
+ */
+ if (((*listen_port_string) =
+ (char *) malloc (strlen(hostname) + 10)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - proxy address\n");
+ goto returnFailure;
+ }
+ (void) sprintf (*listen_port_string, "%s:%d", hostname,
+ listen_port - X_SERVER_PORT_BASE);
+
+ /*
+ * add the server name associated with the current PM request
+ * to the list
+ */
+ if ((server_array[this_server]->x_server_hostport =
+ strdup (server_address)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - server_array string\n");
+ free(*listen_port_string);
+ *listen_port_string = NULL;
+ goto returnFailure;
+ }
+
+ /*
+ * add the client listen port associated with the current PM connection
+ * to the list
+ */
+ if ((server_array[this_server]->listen_port_string =
+ strdup (*listen_port_string)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - server_array string\n");
+ free(server_array[this_server]->x_server_hostport);
+ free(*listen_port_string);
+ *listen_port_string = NULL;
+ goto returnFailure;
+ }
+
+ /*
+ * set the select() read mask for this descriptor
+ */
+ FD_SET(server_array[this_server]->client_listen_fd, program_data->rinit);
+
+ return SUCCESS;
+}
+
+void
+doSelect(struct
+ config * config_info,
+ int * nfds,
+ int * nready,
+ fd_set * readable,
+ fd_set * writable)
+{
+ if ((*nready = select(*nfds,
+ readable,
+ writable,
+ NULL,
+ &config_info->select_timeout)) == -1)
+ {
+ if (errno == EINTR)
+ return;
+ (void) fprintf(stderr, "select call failed\n");
+ perror("select");
+ exit(1);
+ }
+}
+
+int
+doServerConnectSetup(
+ char * x_server_hostport,
+ int * server_connect_fd,
+ struct sockaddr_in * server_sockaddr_in)
+{
+ struct hostent * hostptr;
+ char * server_name_base;
+ char server_port_base[10];
+ int server_port;
+ int i = 0;
+ char * tmp_str;
+ char * tmp_hostport_str;
+
+ /*
+ * need to copy the host port string because strtok() changes it
+ */
+ if ((tmp_hostport_str = strdup (x_server_hostport)) == NULL)
+ {
+ (void) fprintf(stderr, "malloc - hostport copy\n");
+ return FAILURE;
+ }
+
+ tmp_str = x_server_hostport;
+ while (tmp_str[i] != ':')
+ tmp_str++;
+ tmp_str++;
+ strcpy(server_port_base, tmp_str);
+ server_name_base = strtok(tmp_hostport_str,":");
+ server_port = atoi(server_port_base) + X_SERVER_PORT_BASE;
+ hostptr = gethostbyname(server_name_base);
+ free(tmp_hostport_str);
+
+ if (hostptr == NULL)
+ {
+ (void) fprintf(stderr, "gethostbyname call failed\n");
+ return FAILURE;
+ }
+
+ if ((*server_connect_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ {
+ (void) fprintf(stderr, "socket call for server failed: %s\n",
+ strerror(errno));
+ return FAILURE;
+ }
+
+ memset(server_sockaddr_in, 0, sizeof(*server_sockaddr_in));
+ server_sockaddr_in->sin_family = hostptr->h_addrtype;
+#ifdef BSD44SOCKETS
+ server_sockaddr_in->sin_len = sizeof server_sockaddr_in;
+#endif
+ memcpy((char *) &server_sockaddr_in->sin_addr,
+ hostptr->h_addr,
+ hostptr->h_length);
+ server_sockaddr_in->sin_port = htons(server_port);
+
+ return SUCCESS;
+}
+
+int
+doServerConnect(
+ int * server_connect_fd,
+ struct sockaddr_in * server_sockaddr_in)
+{
+ if(connect(*server_connect_fd, (struct sockaddr * )server_sockaddr_in,
+ sizeof(*server_sockaddr_in)) < 0)
+ {
+ (void) fprintf(stderr, "connect call to server failed: %s\n",
+ strerror(errno));
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
diff --git a/transport.h b/transport.h
new file mode 100644
index 0000000..99f3892
--- /dev/null
+++ b/transport.h
@@ -0,0 +1,70 @@
+/* $Xorg: transport.h,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */
+/*
+
+Copyright "1986-1997, 1998 The Open Group
+
+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.
+
+The above copyright notice and the following permission notice
+shall be included in all copies of the Software:
+
+THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
+AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE
+FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN
+CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN
+THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group
+shall not be used in advertising or otherwise to promote the use
+or other dealings in this Software without prior written
+authorization from The Open Group.
+
+X Window System is a trademark of The Open Group.
+
+*/
+
+#ifndef _TRANSPORT_H
+#define _TRANSPORT_H
+
+#ifndef SOMAXCONN
+#define SOMAXCONN 128
+#endif
+
+#define MAX_HOSTNAME_LEN 256
+
+#define X_SERVER_PORT_BASE 6000
+
+
+extern int
+doSetupRemClientListen(
+ char ** listen_port_string,
+ struct clientDataStruct * program_data,
+ char * server_address);
+
+extern void
+doSelect(struct
+ config * config_info,
+ int * nfds,
+ int * nready,
+ fd_set * readable,
+ fd_set * writable);
+
+extern int
+doServerConnectSetup(
+ char * x_server_hostport,
+ int * server_connect_fd,
+ struct sockaddr_in * server_sockaddr_in);
+
+extern int
+doServerConnect(
+ int * server_connect_fd,
+ struct sockaddr_in * server_sockaddr_in);
+
+#endif /* _TRANSPORT_H */
diff --git a/xfwp.c b/xfwp.c
new file mode 100644
index 0000000..de2f17d
--- /dev/null
+++ b/xfwp.c
@@ -0,0 +1,151 @@
+/* $Xorg: xfwp.c,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */
+
+/*
+Copyright 1996, 1998 The Open Group
+
+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.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <X11/Xos.h>
+#include <netinet/in.h>
+
+#include <X11/ICE/ICElib.h>
+
+#include "xfwp.h"
+#include "io.h"
+#include "misc.h"
+#include "pm.h"
+#include "transport.h"
+
+/*
+ * Global variables
+ */
+struct clientDataStruct global_data; /* for ICE callbacks */
+
+struct pm_conn_buf ** pm_conn_array;
+struct server_list ** server_array;
+struct client_conn_buf ** client_conn_array;
+
+char **SitePolicies = NULL; /* list of site security policy strings */
+
+int SitePolicyCount = 0; /* count of elements in SitePolicies */
+
+Bool SitePolicyPermit = 0; /* True := permit iff server supports
+ * at least one listed policy,
+ * False := deny if server has any of
+ * the listed policies.
+ */
+
+int
+main (
+ int argc,
+ char * argv[])
+{
+ int * pm_listen_array;
+ fd_set readable, writable, rinit, winit;
+ int nfds = 0;
+ int nready = 0;
+ struct ICE_setup_info pm_conn_setup;
+ IceListenObj * listen_objects;
+ struct config * config_info;
+
+ /*
+ * setup the global client data struct; we need to do this in order
+ * to access program data in the ICE FWPprocessMessages() callback
+ * without making everything global! See FWPprotocolSetupProc() for
+ * the rest of what we are doing
+ */
+ config_info = (struct config *) Malloc(sizeof(struct config));
+
+ global_data.config_info = config_info;
+ global_data.nfds = &nfds;
+ global_data.rinit = &rinit;
+ global_data.winit = &winit;
+
+ /*
+ * complete the setup
+ */
+ doProcessInputArgs(config_info, argc, argv);
+
+ if ((doHandleConfigFile(config_info)) == FAILURE)
+ exit(1);
+
+ if ((doInitDataStructs(config_info,
+ &pm_conn_setup)) == FAILURE)
+ exit(1);
+
+ /*
+ * install the ICE i/o error handler
+ */
+ doInstallIOErrorHandler();
+
+ /*
+ * zero the select() read/write masks
+ */
+ FD_ZERO(&readable);
+ FD_ZERO(&writable);
+ FD_ZERO(&rinit);
+ FD_ZERO(&winit);
+
+ /*
+ * create listener socket(s) for PM connections
+ */
+ if (!doSetupPMListen(config_info->pm_listen_port,
+ &config_info->num_pm_listen_ports,
+ &pm_listen_array,
+ &listen_objects,
+ &nfds,
+ &rinit))
+ exit(1);
+
+ while(1)
+ {
+ readable = rinit;
+ writable = winit;
+
+ doSelect (config_info,
+ &nfds,
+ &nready,
+ &readable,
+ &writable);
+
+ doCheckTimeouts (config_info,
+ &nready,
+ &rinit,
+ &winit,
+ &readable,
+ &writable);
+
+ doProcessSelect (&nfds,
+ &nready,
+ &readable,
+ &writable,
+ &rinit,
+ &winit,
+ pm_listen_array,
+ config_info,
+ &listen_objects);
+ }
+}
diff --git a/xfwp.h b/xfwp.h
new file mode 100644
index 0000000..c34fcc6
--- /dev/null
+++ b/xfwp.h
@@ -0,0 +1,318 @@
+/* $Xorg: xfwp.h,v 1.6 2001/02/09 02:05:46 xorgcvs Exp $ */
+
+/*
+Copyright 1996, 1998 The Open Group
+
+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.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of The Open Group shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from The Open Group.
+*/
+
+#ifndef _XFWP_H
+#define _XFWP_H
+
+#define FALSE 0
+#define TRUE 1
+
+#define min(a,b) ((a) < (b) ? (a) : (b))
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+/*
+ * Default connection array sizes
+ */
+#define MAX_PM_CONNS 10
+#define MAX_SERVERS 100
+
+#define RWBUFFER_SIZE 2048
+
+enum CLIENT_CONN_STATE {
+ CLIENT_WAITING,
+ SERVER_WAITING,
+ SERVER_REPLY,
+ CONNECTION_READY
+};
+
+enum PM_CONN_STATE {
+ START,
+ WAIT_SERVER_INFO,
+ SENT_PORT_INFO,
+ PM_EXCHANGE_DONE
+};
+
+enum CONFIG_CHECK {
+ FAILURE,
+ SUCCESS
+};
+
+enum CONFIG_TYPE {
+ PM,
+ REM_CLIENT
+};
+
+enum LISTEN_STATE {
+ AVAILABLE,
+ IN_USE
+};
+
+enum SERVICE_ID_TYPES {
+ CLIENT,
+ PMGR,
+ FINDPROXY
+};
+
+enum LOG_EVENTS {
+ CLIENT_ACCEPT, /* event 0: client connection granted */
+ CLIENT_REJECT_CONFIG, /* event 1: client conn rejected by config file */
+ CLIENT_REJECT_SERVER /* event 2: client conn rejected by server query */
+};
+
+typedef void fp1();
+typedef Bool fp2();
+
+struct ICE_setup_info
+{
+
+ int opcode;
+ int versionCount;
+ IcePaVersionRec PMVersions[1];
+ void (*fp1) ();
+ Bool (*fp2) ();
+};
+
+struct client_conn_buf
+{
+ char readbuf[RWBUFFER_SIZE];
+ char writebuf[RWBUFFER_SIZE];
+ int rbytes;
+ int wbytes;
+ int conn_to;
+ int wclose;
+ int state;
+ int time_to_close;
+ int creation_time;
+ int fd;
+ char * source;
+ char * destination;
+};
+
+struct pm_conn_buf
+{
+ char readbuf[RWBUFFER_SIZE];
+ int rbytes;
+ int state;
+ int fd;
+ IceConn ice_conn;
+ int creation_time;
+ int time_to_close;
+} ;
+
+struct config
+{
+ int num_client_conns;
+ int num_pm_conns;
+ int num_servers;
+ int num_pm_listen_ports;
+ int idle_timeout;
+ int pm_data_timeout;
+ int client_listen_timeout;
+ int client_data_timeout;
+ struct timeval select_timeout;
+ char * pm_listen_port;
+ char * config_file_path;
+ char * log_file_path;
+ int lines_allocated;
+ int rule_count;
+ struct config_line ** config_file_data;
+ int log_level;
+};
+
+struct server_list
+{
+ char * x_server_hostport;
+ char * listen_port_string;
+ int client_listen_fd;
+ int server_fd;
+ int done_accept;
+ int creation_time;
+ int time_to_close;
+};
+
+struct clientDataStruct
+{
+ int * nfds;
+ fd_set * rinit;
+ fd_set * winit;
+ int major_opcode;
+ struct config * config_info;
+};
+
+struct config_line
+{
+ char * permit_deny;
+ char * source_hostname;
+ unsigned int source_host;
+ char * source_netmask;
+ unsigned int source_net;
+ char * dest_hostname;
+ unsigned int dest_host;
+ char * dest_netmask;
+ unsigned int dest_net;
+#if defined(__cplusplus) || defined(c_plusplus)
+ char * c_operator;
+#else
+ char * operator;
+#endif
+ char * service;
+ int service_id;
+};
+
+
+/*
+ * Global variables
+ */
+extern struct clientDataStruct global_data; /* for ICE callbacks */
+
+extern struct pm_conn_buf ** pm_conn_array;
+extern struct server_list ** server_array;
+extern struct client_conn_buf ** client_conn_array;
+
+extern char **SitePolicies;
+extern int SitePolicyCount;
+extern int SitePolicyPermit;
+
+/*
+ * Handy ICE message parsing macros
+ */
+
+/*
+ * Pad to a 64 bit boundary
+ */
+
+#define PAD64(_bytes) ((8 - ((unsigned int) (_bytes) % 8)) % 8)
+
+#define PADDED_BYTES64(_bytes) (_bytes + PAD64 (_bytes))
+
+
+/*
+ * Number of 8 byte units in _bytes.
+ */
+
+#define WORD64COUNT(_bytes) (((unsigned int) ((_bytes) + 7)) >> 3)
+
+
+/*
+ * Compute the number of bytes for a STRING representation
+ */
+
+#define STRING_BYTES(_str) (2 + (_str ? strlen (_str) : 0) + \
+ PAD64 (2 + (_str ? strlen (_str) : 0)))
+
+
+
+#define SKIP_STRING(_pBuf, _swap) \
+{ \
+ CARD16 _len; \
+ EXTRACT_CARD16 (_pBuf, _swap, _len); \
+ _pBuf += _len; \
+ if (PAD64 (2 + _len)) \
+ _pBuf += PAD64 (2 + _len); \
+}
+
+/*
+ * STORE macros
+ */
+
+#define STORE_CARD16(_pBuf, _val) \
+{ \
+ *((CARD16 *) _pBuf) = _val; \
+ _pBuf += 2; \
+}
+
+#define STORE_STRING(_pBuf, _string) \
+{ \
+ int _len = _string ? strlen (_string) : 0; \
+ STORE_CARD16 (_pBuf, _len); \
+ if (_len) { \
+ memcpy (_pBuf, _string, _len); \
+ _pBuf += _len; \
+ } \
+ if (PAD64 (2 + _len)) \
+ _pBuf += PAD64 (2 + _len); \
+}
+
+
+/*
+ * EXTRACT macros
+ */
+
+#define EXTRACT_CARD16(_pBuf, _swap, _val) \
+{ \
+ _val = *((CARD16 *) _pBuf); \
+ _pBuf += 2; \
+ if (_swap) \
+ _val = lswaps (_val); \
+}
+
+#define EXTRACT_STRING(_pBuf, _swap, _string) \
+{ \
+ CARD16 _len; \
+ EXTRACT_CARD16 (_pBuf, _swap, _len); \
+ _string = (char *) malloc (_len + 1); \
+ memcpy (_string, _pBuf, _len); \
+ _string[_len] = '\0'; \
+ _pBuf += _len; \
+ if (PAD64 (2 + _len)) \
+ _pBuf += PAD64 (2 + _len); \
+}
+
+
+/*
+ * Byte swapping
+ */
+
+/* byte swap a long literal */
+#define lswapl(_val) ((((_val) & 0xff) << 24) |\
+ (((_val) & 0xff00) << 8) |\
+ (((_val) & 0xff0000) >> 8) |\
+ (((_val) >> 24) & 0xff))
+
+/* byte swap a short literal */
+#define lswaps(_val) ((((_val) & 0xff) << 8) | (((_val) >> 8) & 0xff))
+
+
+#define CHECK_AT_LEAST_SIZE(_iceConn, _majorOp, _minorOp, _expected_len, _actual_len, _severity) \
+ if ((((_actual_len) - SIZEOF (iceMsg)) >> 3) > _expected_len) \
+ { \
+ _IceErrorBadLength (_iceConn, _majorOp, _minorOp, _severity); \
+ return; \
+ }
+
+
+#define CHECK_COMPLETE_SIZE(_iceConn, _majorOp, _minorOp, _expected_len, _actual_len, _pStart, _severity) \
+ if (((PADDED_BYTES64((_actual_len)) - SIZEOF (iceMsg)) >> 3) \
+ != _expected_len) \
+ { \
+ _IceErrorBadLength (_iceConn, _majorOp, _minorOp, _severity); \
+ IceDisposeCompleteMessage (iceConn, _pStart); \
+ return; \
+ }
+
+#endif /* _XFWP_H */
diff --git a/xfwp.man b/xfwp.man
new file mode 100644
index 0000000..b830d08
--- /dev/null
+++ b/xfwp.man
@@ -0,0 +1,389 @@
+.\" $Xorg: xfwp.man,v 1.4 2001/02/09 02:05:46 xorgcvs Exp $
+.\" Copyright 1996, 1998 The Open Group
+.\"
+.\" 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.
+.\"
+.\" The above copyright notice and this permission notice shall be included
+.\" in all copies or substantial portions of the Software.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+.\" IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
+.\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+.\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+.\" OTHER DEALINGS IN THE SOFTWARE.
+.\"
+.\" Except as contained in this notice, the name of The Open Group shall
+.\" not be used in advertising or otherwise to promote the sale, use or
+.\" other dealings in this Software without prior written authorization
+.\" from The Open Group.
+.\"
+.nh
+.TH XFWP 1 "Release 6.4" "X Version 11"
+.SH NAME
+xfwp - X firewall proxy
+.SH SYNOPSIS
+.B xfwp
+[option ...]
+.PP
+.SH COMMAND LINE OPTIONS
+The command line options that can be specified are:
+.PP
+.TP 8
+.B \-cdt \fInum_secs\fP
+Used to override the default time-to-close (604800 seconds) for xfwp client
+data connections on which there is no activity (connections over which
+X protocol is already being relayed by xfwp)
+.PP
+.TP 8
+.B \-clt \fInum_secs\fP
+Used to override the default time-to-close (86400 seconds) for xfwp client
+listen ports (ports on xfwp to which X clients first connect when trying to
+reach an X server)
+.PP
+.TP 8
+.B \-pdt \fInum_secs\fP
+Used to override the default time-to-close (3600 seconds) for Proxy Manager
+connections on which there is no activity
+.PP
+.TP 8
+.B \-config \fIfile_name\fP
+Used to specify the configuration the name of the configuration file
+.PP
+.TP 8
+.B \-pmport \fIport_number\fP
+Used to override the default port address (4444) for proxy manager connections
+.PP
+.TP 8
+.B \-verify
+Used to display the configuration file rule that was actually matched for
+each service request
+.PP
+.TP 8
+.B \-logfile \fIfile_name\fP
+Used to specify the name of a file where audit information should be logged.
+The format of a logged entry is: time of day; event code; source IP address;
+destination IP address; and configuration rule number. The event codes
+are: "0" for a successful connection; "1" if a connection is denied because of
+a configuration rule; and "2" if a connection is denied because of an
+authorization failure. If the event code is "1", and a configuration file
+is used, the configuration rule number is the line number of the
+configuration file where the match was made (see the section
+CONFIGURATION FILE for more information). If the event code is not "1",
+or if no configuration file is used, the configuration rule number is "-1".
+.PP
+.TP 8
+.B \-loglevel \fI{0,1}\fP
+Used to specify the amount of audit detail that should be logged. If "0",
+all connections are logged. If "1", only unsuccessful connections are logged.
+.PP
+.TP 8
+.B \-max_pm_conns \fInum_connections\fP
+Used to specify the maximum number of Proxy Manager connections. The
+default is 10.
+.PP
+.TP 8
+.B \-max_pm_conns \fInum_connections\fP
+Used to specify the maximum number of X server connections. The
+default is 100.
+.PP
+.SH DESCRIPTION
+The X firewall proxy (xfwp) is an application layer gateway proxy
+that may be run on a network firewall host to forward X traffic
+across the firewall. Used in conjunction with the X server Security
+extension and authorization checking, xfwp constitutes a safe, simple,
+and reliable mechanism both to hide the addresses of X servers located
+on the Intranet and to enforce a server connection policy. Xfwp cannot
+protect against mischief originating on the Intranet; however, when
+properly configured it can guarantee that only trusted clients originating
+on authorized external Internet hosts will be allowed inbound access to
+local X servers.
+
+To use xfwp there must be an X proxy manager running in the local environment
+which has been configured at start-up to know the location of the xfwp.
+[NOTE: There may be more than one xfwp running in a local environment;
+see notes below on load balancing for further discussion.] Using the
+xfindproxy utility (which relays its requests through the proxy manager)
+a user asks xfwp to allocate a client listen port for a particular X server,
+which is internally associated with all future connection requests for that
+server. This client listen port address is returned by the proxy manager
+through xfindproxy. The xfwp hostname and port number is then passed
+out-of-band (i.e., via a Web browser) to some remote X client, which will
+subsequently connect to xfwp instead of to the target X server.
+
+When an X client connection request appears on one of xfwp's listen ports,
+xfwp connects to the X server associated with this listen port and performs
+authorization checks against the server as well as against its own configurable
+access control list for requesting clients. If these checks fail, or if
+the requested server does not support the X Security Extension, the client
+connection is refused. Otherwise, the connection is accepted and all ensuing
+data between client and server is relayed by xfwp until the client terminates
+the connection or, in the case of an inactive client, until a configured
+timeout period is exceeded. Xfwp is designed to block while waiting for
+activity on its connections, thereby minimizing demand for system cycles.
+
+If xfwp is run without a configuration file and thus no sitepolicy is
+defined, if xfwp is using an X server where xhost + has been run to turn
+off host-based authorization checks, when a client tries to connect to
+this X server via xfwp, the X server will deny the connection. If xfwp
+does not define a sitepolicy, host-based authorization must be turned on
+for clients to connect to an X server via the xfwp.
+.PP
+.SH INTEROPERATION WITH IP PACKET-FILTERING ROUTERS
+The whole purpose of the xfwp is to provide reliable control over access
+to Intranet X servers by clients originating outside the firewall. At
+the present time, such access control is typically achieved by firewall
+configurations incorporating IP packet-filtering routers. Frequently,
+the rules for such filters deny access to X server ports (range 6000 -
+6xxx) for all Intranet host machines.
+
+In order for xfwp to do its job, restrictions on access for ports 6001 - 6xxx
+must be removed from the rule-base of the IP packet-filtering router. [NOTE:
+xfwp only assigns ports in the range beginning with 6001; access to port
+6000 on all Intranet hosts may continue to be denied.] This does not
+mean the Intranet firewall will be opened for indiscriminate entry by X
+clients. Instead, xfwp supports a fully configurable rule-based access
+control system, similar to that of the IP packet-filter router itself.
+Xfwp in effect adds another level of packet-filtering control which is
+fully configurable and applies specifically to X traffic. See section
+entitled CONFIGURATION FILE, below, for further details.
+.PP
+.SH INSTALLATION, SETUP AND TROUBLESHOOTING
+Xfwp is typically run as a background process on the Intranet firewall host.
+It can be launched using any of the command-line options described above.
+As noted above, xfwp works only in conjunction with proxy manager and the
+xfindproxy utility. It can also be configured to support a user-defined
+X server site security policy, in which the X server is required to indicate
+to xfwp whether or not it supports the particular policy. Consult the
+X server man pages for further information on these components. Xfwp
+diagnostics can be turned on by compiling with the -DDEBUG switch.
+Connection status can be recorded by using the -logfile and -loglevel
+command line options.
+.PP
+.SH PERFORMANCE, LOAD BALANCING AND RESOURCE MANAGEMENT
+Xfwp manages four different kinds of connections: proxy manager (PM) data,
+X client listen, X client data, and X server. The sysadmin employing xfwp
+must understand how the resources for each of these connection types are
+allocated and reclaimed by xfwp in order to optimize the availability of
+xfwp service.
+
+Each connection-type has a default number of allocation slots and
+a default timeout. The number of allocation slots for PM connections
+and X server connections is configurable via command line options.
+Connection timeouts are also configurable via command line options.
+Each connection timeout represents the period the connection
+will be allowed to remain open in the absence of any activity on that
+connection. Whenever there is activity on a connection, the time-to-close
+is automatically reset. The default distribution of total process connection
+slots across the four connection types, as well as the choice of default
+timeouts for the connection types, is governed by a number of assumptions
+embedded in the xfwp use model.
+
+
+The default number of PM connections is 10 and the
+default duration for PM connections is 3,600
+seconds (1 hour) for each connection after time of last activity.
+At start-up, xfwp listens for PM connection requests on any non-reserved
+port (default of 4444 if not specified on the xfwp command-line). The PM
+normally connects to xfwp only when a call is made to the PM with xfindproxy.
+Thereafter, the PM remains connected to xfwp, even after the messaging between
+them has been completed, for the default connection duration period. In some
+cases this may result in depletion of available PM connection slots.
+If the sysadmin expects connections to a single xfwp from many PM's,
+xfwp should be started using the -pdt command line option, with a timeout
+value reflecting the desired duration that inactive connections will be
+permitted to remain open.
+
+Xfwp client listeners are set up by a call to xfindproxy and continue to
+listen for X client connection requests for a default duration of 86,400
+seconds (24 hours) from the point of last activity. After this time they
+are automatically closed and their fd's recovered for future allocation.
+In addressing the question of how to choose some alternative timeout
+value which will guarantee the availability of client listen ports,
+sysadmins should take into consideration the expected delay between
+the time when the listener was allocated (using xfindproxy) and the time
+when a client actually attempts to connect to xfwp, as well the likelihood
+that client listeners will be re-used after the initial client data
+connection is closed.
+
+Each client connection is allocated a default lifetime of 604,800
+seconds (7 * 24 hours)
+from the point when it last saw activity. After this time it is
+automatically closed and its fd's recovered for future allocation.
+Because server connections are not actually established until a connection
+request from a remote X client arrives at one of the xfwp's client listen
+ports, the client data timeout applies both to client-xfwp connections as well
+as to xfwp-server connections. If the system administrator expects many
+client data connections through xfwp, an overriding of the default timeout
+should be considered.
+.PP
+.SH CONFIGURATION FILE
+The xfwp configuration file resides on the xfwp host machine and is
+used to determine whether X client data connection requests will be
+permitted or denied. The path to the file is specified at start-up
+time. If no configuration file is specified, all X client data
+connection requests routed through xfwp will be by default permitted,
+assuming that other X server authorization checks are successful. If
+a configuration file is supplied but none of its entries matches the
+connection request then the connection is by default denied.
+
+If a line in the configuration file begins with the '#' character
+or a new-line character, the line is ignored and the evaluator will
+skip the line.
+
+The configuration file supports two entirely independent authorization
+checks: one which is performed by xfwp itself, and a second which is the
+result of xfwp's querying the target X server. For the first of these,
+the configuration file employs a syntax and semantic similar to that of IP
+packet-filtering routers. It contains zero or more source-destination
+rules of the following form:
+.PP
+{permit | deny} <src> <src mask> [<dest> <dest mask> [<operator> <service>]]
+.sp
+.IP permit/deny 12
+the keywords ``permit'' or ``deny'' indicate whether the
+rule will enable or disable access, respectively
+.IP src 12
+the IP address against the host who originated the
+connection request will be matched, expressed in IP
+format (x.x.x.x)
+.IP "src mask" 12
+a subnet mask, also in IP format, for further qualifying
+the source mask. Bits set in the mask indicate bits of the
+incoming address to be \fIignored\fP when comparing to the specified src
+.IP dest 12
+the IP address against which the destination of the
+incoming connection request (i.e. the host IP of the
+X server to which the incoming client is attempting to
+connect) will be matched
+.IP "dest mask" 12
+a subnet mask, also in IP format, for further qualifying
+the destination mask. Bits set in the mask indicate bits of the
+destination address to be \fIignored\fP when comparing to the specified dest
+.IP operator 12
+always ``eq'' (if the service field is not NULL)
+.IP service 12
+one of the following three strings: ``pm'', ``fp'', or
+``cd'', corresponding to proxy manager, xfindproxy, or
+client data, respectively
+.PP
+For the second type of authorization check, the configuration file contains
+zero or more site policy rules of the following form:
+.PP
+{require | disallow} sitepolicy <site_policy>
+.sp
+.IP require 12
+specifies that the X server \fImust\fP be configured with \fIat least one\fP
+of the corresponding site policies, else it must refuse the connection.
+.IP disallow 12
+specifies that the X server \fImust not\fP be configured with \fIany\fP of
+the corresponding site policies, else it must refuse the connection.
+.IP sitepolicy 12
+a required keyword
+.IP "<site_policy>" 12
+specifies the policy string. The string may contain any
+combination of alphanumeric characters subject
+only to interpretation by the target X server
+.PP
+.SH RULES FOR EVALUATING THE XFWP CONFIGURATION FILE ENTRIES
+For the first type of configurable authorization checking, access
+can be permitted or denied for each connection type based upon
+source and, optionally, destination and service. Each file entry must
+at a minimum specify the keyword ``permit'' or ``deny'' and the two
+source fields. The
+destination and service fields can be used to provide finer-grained
+access control if desired.
+.PP
+The algorithm for rule-matching is as follows:
+.PP
+.RS 3
+ while (more entries to check)
+ {
+ if ((<originator IP> AND (NOT <src mask>)) == src)
+ [if ((<dest X server IP> AND (NOT <dest mask>)) == dest)]
+ [if (service fields present and matching)]
+ do either permit or deny connection depending on keyword
+ else
+ continue
+ }
+ if (no rule matches)
+ deny connection
+.RE
+.PP
+If a permit or deny rule does not specify a service and operation, then
+the rule applies to all services. If a configuration file is specified
+and it contains at least one valid deny or permit rule, then a host
+that is not explicitly permitted will be denied a connection.
+.PP
+Site policy configuration checking constitutes a separate (and X server
+only) authorization check on incoming connection requests. Any number of
+require or disallow rules may be specified, but all rules must be of the
+same type; that is, a single rule file cannot have both ``require'' and
+``disallow'' keywords. The algorithm for this check is as follows:
+.PP
+.RS 3
+ if (X server recognizes any of the site policy strings)
+ if (keyword == require)
+ permit connection
+ else
+ deny connection
+ else
+ if (keyword == require)
+ deny connection
+ else
+ permit connection
+.RE
+.PP
+The site policy check is performed by xfwp only if the source-destination
+rules permit the connection.
+.PP
+.SH
+EXAMPLES
+.PP
+.sp
+\fC
+.nf
+# if and only if server supports one of these policies then authorize
+# connections, but still subject to applicable rule matches
+#
+require sitepolicy policy1
+require sitepolicy policy2
+#
+# deny pm connections originating on 8.7.6.5 [NOTE: If pm service
+# is explicitly qualified, line must include destination fields as
+# shown.]
+#
+deny 8.7.6.5 0.0.0.0 0.0.0.0 255.255.255.255 eq pm
+#
+# permit xfindproxy X server connects to anywhere [NOTE: If
+# fp service is explicitly qualified, line must include source fields
+# as shown.]
+#
+permit 0.0.0.0 255.255.255.255 0.0.0.0 255.255.255.255 eq fp
+#
+# permit all connection types originating from the 192.0.0.0
+# IP domain only
+#
+permit 192.0.0.0 0.255.255.255
+.fi
+\fP
+.PP
+Care should be taken that source-destination rules are written in the correct
+order, as the first matching rule will be applied. In addition to parser
+syntax checking, a special command-line switch (-verify) has been provided
+to assist the sysadmin in determining which rule was actually matched.
+.PP
+.SH BUGS
+.PP
+Xfwp should check server site policy and security extension before
+allocating a listen port.
+.PP
+.SH SEE ALSO
+xfindproxy (1), Proxy Management Protocol spec V1.0, proxymngr(1), Xserver(1)
+.SH AUTHOR
+Reed Augliere, consulting to X Consortium, Inc.