Previous patch

Home

Next patch

./src/transdata.c

Patch: Additional logging in NTP

+/***************************************
+
+    This is part of frox: A simple transparent FTP proxy
+    Copyright (C) 2000 James Hollingshead
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  transdata.c -- System independent code for transparently proxying
+                 data connections
+  
+  ***************************************/
+
+#include <signal.h>
+#include <sys/un.h>
+
+#include "common.h"
+#include "transdata.h"
+
+static void serve_requests(int listen);
+static int td_listenfd;
+static int td_reqfd;
+
+
+void transdata_newsocketpair()
+{
+  int fds[2];
+
+  if(!config.transdata)
+    return;
+  if(config.inetd)
+    write_log(IMPORT, "Transdata not recommended when running "
+        "out of inetd");
+  if(socketpair(PF_UNIX, SOCK_STREAM, 0, fds) == -1) {
+    config.transdata = FALSE;
+    return;
+  }
+  td_reqfd = fds[0];
+  send_fd(td_listenfd, fds[1], 'N');
+  close(fds[1]);
+}
+
+/* ------------------------------------------------------------- **
+**  Make a connection to dest which appears to have come from src. If
+**  we can't then we just make the connection anyway.
+**  ------------------------------------------------------------- */
+int transp_connect(struct sockaddr_in dest, struct sockaddr_in src)
+{
+  struct fd_request req;
+  int ret;
+
+  write_log(VERBOSE, " TD: transp_connect(). Setting up req structure");
+  req.type = CONNECT;
+  req.local = src;
+  req.remote = dest;
+  req.ports[0] = config.actvports[0];
+  req.ports[1] = config.actvports[1];
+
+  write_log(VERBOSE, " TD: transp_connect(). Sending fd request.");
+  send(td_reqfd, &req, sizeof(req), 0);
+  recv_fd(td_reqfd, &ret);
+  write_log(VERBOSE, " TD: transp_connect(). Received fd.");
+  return ret;
+}
+
+/* ------------------------------------------------------------- **
+**  Listen in order to intercept any connections that are headed for
+**  realdest, on a port from the range specified by use. You must call
+**  il_free at some point before exiting to remove the iptables rule
+**  this function adds under 2.4. Current implementation of il_free
+**  means you can only have one intercept_listening socket per
+**  process.
+**  ------------------------------------------------------------- */
+int intercept_listen(struct sockaddr_in intercept,
+         struct sockaddr_in listen_on, int portrange[2])
+{
+  struct fd_request req;
+  int ret;
+
+  write_log(VERBOSE,
+      " TD: intercept_listen(). Setting up req structure");
+  req.type = LISTEN;
+  req.local = listen_on;
+  req.remote = intercept;
+  req.ports[0] = portrange[0];
+  req.ports[1] = portrange[1];
+
+  write_log(VERBOSE, " TD: intercept_listen(). Sending fd request.");
+  send(td_reqfd, &req, sizeof(req), 0);
+  recv_fd(td_reqfd, &ret);
+  write_log(VERBOSE, " TD: intercept_listen(). Received fd.");
+
+  return ret;
+}
+
+/* ------------------------------------------------------------- **
+**  Call this to remove the iptables rule introduced by a previous
+**  intercept_listen.
+**  ------------------------------------------------------------- */
+int il_free(void)
+{
+  struct fd_request req;
+
+  if(!config.transdata)
+    return 0;
+  req.type = UNLISTEN;
+  send(td_reqfd, &req, sizeof(req), 0);
+  return 0;
+}
+
+void transdata_flush(void)
+{
+  if(!config.transdata)
+    return;
+  send(td_listenfd, "F", 1, 0);
+}
+
+/*******************************************************************
+ *  Code below here runs as root in a separate process. It accepts
+ *  connections from the socket, and then returns fds back across the
+ *  connections as and when requested.
+ ******************************************************************/
+
+void transdata_setup()
+{
+  int fds[2];
+
+  if(!config.transdata)
+    return;
+  if(kernel_transdata_setup() == -1) {
+    write_log(ERROR, "Failed to setup transparent data. "
+        "Will not do it");
+    config.transdata = FALSE;
+    return;
+  }
+  if(socketpair(PF_UNIX, SOCK_STREAM, 0, fds) == -1) {
+    config.transdata = FALSE;
+    return;
+  }
+
+  switch ((tdatapid = fork())) {
+  case -1:
+    tdatapid = 0;
+    config.transdata = FALSE;
+    return;
+  case 0:
+    signal(SIGHUP, SIG_IGN);
+    write_log(VERBOSE, "TDS: Running transdata server");
+    break;
+  default:
+    close(fds[1]);
+    td_listenfd = fds[0];
+    return;
+  }
+  close(fds[0]);
+
+  /*Drop privileges while we can */
+  setgid(config.gid);
+  setgid(config.gid);
+  seteuid(config.uid);
+  serve_requests(fds[1]);
+
+  exit(0);
+}
+
+struct td_client {
+  int fd;
+  struct fd_request req;
+  struct td_client *next;
+};
+
+void serve_client(struct td_client *p);
+void purge_clients(void);
+
+struct td_client *head = NULL;
+
+static void serve_requests(int listen)
+{
+  int fd;
+  struct td_client *p;
+
+  do {
+    fd_set reads;
+    FD_ZERO(&reads);
+    for(p = head; p != NULL; p = p->next)
+      FD_SET(p->fd, &reads);
+    FD_SET(listen, &reads);
+
+    select(FD_SETSIZE, &reads, NULL, NULL, NULL);
+    if(FD_ISSET(listen, &reads)) {
+      switch (recv_fd(listen, &fd)) {
+      case 0:
+        write_log(IMPORT, "Transdata exiting");
+        exit(0);
+      case 'F':
+        kernel_td_flush();
+        break;
+      case 'N':
+        write_log(VERBOSE,
+            "TDS: Accepted new client with fd=%d",
+            fd);
+        p = malloc(sizeof(struct td_client));
+        p->next = head;
+        p->fd = fd;
+        p->req.type = NONE;
+        head = p;
+        break;
+      }
+    }
+    for(p = head; p != NULL; p = p->next) {
+      if(FD_ISSET(p->fd, &reads)) {
+        if(p->req.type == LISTEN) {
+          kernel_td_unlisten(p->req);
+          p->req.type = NONE;
+        }
+        if(recv(fd, &p->req, sizeof(p->req),
+          MSG_WAITALL) <= 1) {
+          write_log(VERBOSE,
+              "TDS: Closing fd %d",
+              p->fd);
+          close(p->fd);
+          p->fd = -1;
+          continue;
+        }
+        serve_client(p);
+      }
+    }
+    purge_clients();
+  } while(TRUE);
+}
+
+void serve_client(struct td_client *p)
+{
+  int ret;
+  switch (p->req.type) {
+  case CONNECT:
+    ret = kernel_td_connect(p->req);
+    break;
+  case LISTEN:
+    ret = kernel_td_listen(p->req);
+    break;
+  case UNLISTEN:
+    /*Don't need to do anything - kernel_td_unlisten()
+       already called from serve_requests before dealing
+       with this one. */
+    return;
+  default:
+    return;
+  }
+  write_log(VERBOSE, "TDS: Sending fd %d", ret);
+  if(ret == -1)
+    write(p->fd, "X", 1);
+  else
+    send_fd(p->fd, ret, 'x');
+  close(ret);
+}
+
+void purge_clients(void)
+{
+  struct td_client *p, *pp;
+
+  while(head && head->fd == -1) {
+    pp = head;
+    head = pp->next;
+    free(pp);
+  }
+  for(p = head; p && p->next; p = p->next) {
+    if(p->next->fd == -1) {
+      pp = p->next;
+      p->next = p->next->next;
+      free(pp);
+    }
+  }
+}
+-