Previous patch

Home

Next patch

./src/ccp.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
+
+  ccp.c -- ftp-proxy like command control program. This file is
+           something of a mess now because it contains two completely
+           different implementations plus wrappers to call one or the
+           other depending on the "UseOldCCP" config file variable.
+           The ccp_old_... functions will die in the next major
+           release.
+
+           Note that the interface here is low level. A badly written
+           ccp script can cause the connection to hang. CCP scripts
+           will also get relatively unsanitised data. Commands should
+           fit ftp command syntax, and be less than MAX_LINE_LEN bytes
+           long with all non printable characters purged, but that is
+           all that is guaranteed.
+  
+  ***************************************/
+
+#include <sys/wait.h>
+#include <stdio.h>
+#include "common.h"
+#include "control.h"
+#include "ccp.h"
+
+#ifndef HAVE_SETENV
+#define setenv(A, B, C)
+#endif
+
+static int exec_ccp_daemon(void);
+static void check_serverok(void);
+static int process_reply(sstr * cmd, sstr * arg);
+
+static void ccp_new_changedest(void);
+static int ccp_new_allowcmd(sstr * cmd, sstr * arg);
+static int ccp_new_allowmsg(int *code, sstr * msg);
+static void ccp_old_init(void);
+static int ccp_old_allowcmd(sstr * cmd, sstr * arg);
+
+static int stdin_fd;
+static FILE *stdout_fp;
+
+static int dont_reenter = 0;
+
+void ccp_changedest(void)
+{
+  if(config.oldccp)
+    ccp_old_init();
+  else
+    ccp_new_changedest();
+}
+
+int ccp_allowcmd(sstr * cmd, sstr * arg)
+{
+  if(config.oldccp)
+    return ccp_old_allowcmd(cmd, arg);
+  else
+    return ccp_new_allowcmd(cmd, arg);
+}
+
+int ccp_allowmsg(int *code, sstr * msg)
+{
+  if(config.oldccp)
+    return TRUE;
+  else
+    return ccp_new_allowmsg(code, msg);
+}
+
+/*************************
+ **** New interface ******
+ *************************/
+
+static void ccp_new_changedest(void)
+{
+  if(!config.ccpcmd)
+    return;
+
+  exec_ccp_daemon();
+  check_serverok();
+}
+
+static int exec_ccp_daemon(void)
+{
+  int stdin_fds[2], stdout_fds[2];
+  static char *argv[2];
+
+  argv[0] = config.ccpcmd;
+  argv[1] = NULL;
+
+  pipe(stdin_fds);
+  pipe(stdout_fds);
+  switch (fork()) {
+  case 0:    /*Child */
+    close(stdout_fds[0]);
+    close(stdin_fds[1]);
+    dup2(stdout_fds[1], 1);
+    dup2(stdin_fds[0], 0);
+    close(stdout_fds[1]);
+    close(stdin_fds[0]);
+    execvp(argv[0], argv);
+    die(ERROR, "Failed to exec ccp prog", 0, NULL, -1);
+  case -1:
+    debug_err("Error");
+    die(ERROR, "Unable to fork", 0, NULL, -1);
+  default:
+    break;
+  }
+  close(stdout_fds[1]);
+  close(stdin_fds[0]);
+  stdout_fp = fdopen(stdout_fds[0], "r");
+  stdin_fd = stdin_fds[1];
+  return 0;
+}
+
+static void check_serverok(void)
+{
+  sstr *buf;
+
+  buf = sstr_init(MAX_LINE_LEN + 10);
+
+  sstr_apprintf(buf, "I %s ",
+          inet_ntoa(info->client_control.address.sin_addr));
+  sstr_apprintf(buf, "%s %s",
+          inet_ntoa(info->server_control.address.sin_addr),
+          sstr_len(info->server_name) ?
+          sstr_buf(info->server_name) : "X");
+  sstr_apprintf(buf, " %d\n",
+          htons(info->server_control.address.sin_port));
+
+  sstr_write(stdin_fd, buf, 0);
+
+  sstr_free(buf);
+  process_reply(NULL, NULL);
+}
+
+static int ccp_new_allowcmd(sstr * cmd, sstr * arg)
+{
+  sstr *buf;
+
+  if(!config.ccpcmd)
+    return TRUE;
+  if(dont_reenter)
+    return dont_reenter--;
+
+  buf = sstr_init(MAX_LINE_LEN + 10);
+  sstr_apprintf(buf, "C %s %s\n", sstr_buf(cmd), sstr_buf(arg));
+  sstr_write(stdin_fd, buf, 0);
+
+  sstr_free(buf);
+  if(process_reply(cmd, arg) != 'C')
+    return TRUE;
+
+  dont_reenter = 1;
+  send_message(sstr_atoi(cmd), arg);
+  return FALSE;
+}
+
+static int ccp_new_allowmsg(int *code, sstr * msg)
+{
+  sstr *buf;
+
+  if(!config.ccpcmd)
+    return TRUE;
+  if(dont_reenter)
+    return dont_reenter--;
+
+  buf = sstr_init(MAX_LINE_LEN + 10);
+  sstr_apprintf(buf, "S %d %s\n", *code, sstr_buf(msg));
+  sstr_write(stdin_fd, buf, 0);
+
+  switch (process_reply(buf, msg)) {
+  case 'S':
+    dont_reenter = 1;
+    parse_client_cmd(buf, msg);
+    sstr_free(buf);
+    return FALSE;
+  case 'C':
+    *code = sstr_atoi(buf);
+    sstr_free(buf);
+    return TRUE;
+  default:
+    return TRUE;
+  }
+
+}
+
+static int process_reply(sstr * cmd, sstr * arg)
+{
+  sstr *buf;
+  buf = sstr_init(MAX_LINE_LEN + 10);
+  for(;;) {
+    sstr_fgets(buf, stdout_fp);
+    switch (sstr_getchar(buf, 0)) {
+    case 'R':  /*Redirect */
+      sstr_split(buf, NULL, 0, 2);
+      sstr_split(buf, NULL, sstr_len(buf) - 1, 1);
+      inet_aton(sstr_buf(buf),
+          &info->server_control.address.sin_addr);
+      info->final_server_address =
+        info->server_control.address;
+      sstr_free(buf);
+      return 0;
+    case 'S':  /* A command to write to the server */
+      sstr_split(buf, NULL, 0, 2);
+      sstr_token(buf, cmd, " ", 0);
+      sstr_token(buf, arg, "\r\n", 0);
+      sstr_free(buf);
+      return 'S';
+    case 'C':  /* A message to write to the client */
+      sstr_split(buf, NULL, 0, 2);
+      sstr_token(buf, cmd, " ", 0);
+      sstr_token(buf, arg, "\r\n", 0);
+      sstr_free(buf);
+      return 'C';
+    case 'L':  /* A log message. Action to follow */
+      sstr_split(buf, NULL, 0, 2);
+      sstr_split(buf, NULL, sstr_len(buf) - 1, 1);
+      write_log(IMPORT, sstr_buf(buf));
+      break;
+    case 'Q':  /* Close session */
+      die(ERROR, "CCP requested exit. Closing session", 0,
+          NULL, 0);
+      break;
+    case 'X':  /* No change */
+      sstr_free(buf);
+      return 0;
+    default:
+      die(ERROR, "Unknown code from CCP progeam", 0, 0, 0);
+      break;
+    }
+  }
+}
+
+/*************************
+ **** Old interface ******
+ *************************/
+/*This will all go sometime soon. Maybe it will be replaced with a
+  wrapper to ues around legacy ccps.*/
+
+#define EPR "FROX_"
+
+static int exec_old_ccp(void);
+
+static void ccp_old_init(void)
+{
+  sstr *buf;
+
+  if(!config.ccpcmd)
+    return;
+
+  buf = sstr_init(MAX_LINE_LEN);
+
+  setenv(EPR "CLIENT",
+         inet_ntoa(info->client_control.address.sin_addr), 1);
+  setenv(EPR "SERVER",
+         inet_ntoa(info->server_control.address.sin_addr), 1);
+  setenv(EPR "SERVERNAME", sstr_buf(info->server_name), 1);
+  sstr_apprintf(buf, "%lu-%u", time(NULL), getpid());
+  setenv(EPR "SESSION", sstr_buf(buf), 1);
+
+  setenv(EPR "COMMAND", "+NEW", 1);
+  exec_old_ccp();
+
+  sstr_free(buf);
+}
+
+static int ccp_old_allowcmd(sstr * cmd, sstr * arg)
+{
+  if(!config.ccpcmd)
+    return TRUE;
+
+  if(!sstr_cmp2(cmd, "USER")) {
+    setenv(EPR "SERVER",
+           inet_ntoa(info->server_control.address.sin_addr), 1);
+    setenv(EPR "SERVERNAME", sstr_buf(info->server_name), 1);
+    setenv(EPR "USER", sstr_buf(arg), 1);
+  }
+  setenv(EPR "COMMAND", sstr_buf(cmd), 1);
+  setenv(EPR "PARAMETER", sstr_buf(arg), 1);
+
+  return (exec_old_ccp());
+}
+
+static int exec_old_ccp(void)
+{
+  int log_fds[2], message_fds[2], i;
+  static char *argv[2];
+  sstr *buf;
+
+  argv[0] = config.ccpcmd;
+  argv[1] = NULL;
+
+  pipe(log_fds);
+  pipe(message_fds);
+  switch (fork()) {
+  case 0:    /*Child */
+    close(log_fds[0]);
+    close(message_fds[0]);
+    dup2(log_fds[1], 1);
+    dup2(message_fds[1], 2);
+    execvp(argv[0], argv);
+    die(ERROR, "Failed to exec ccp prog", 0, NULL, 0);
+  case -1:
+    close(log_fds[1]);
+    close(message_fds[1]);
+    debug_err("Error");
+    break;
+   /*FIXME*/ default:
+    break;
+  }
+  wait(&i);
+  if(!WIFEXITED(i)) {
+    close(log_fds[0]);
+    close(message_fds[0]);
+    die(ERROR, "CCP program exited abnormally", 0, NULL, -1);
+  }
+
+  buf = sstr_init(MAX_LINE_LEN);
+  sstr_append_read(log_fds[0], buf, MAX_LINE_LEN);
+  if(sstr_len(buf) > 0)
+    write_log(IMPORT, sstr_buf(buf));
+  close(log_fds[0]);
+
+  sstr_empty(buf);
+  sstr_append_read(message_fds[0], buf, MAX_LINE_LEN);
+  if(sstr_len(buf) > 0)
+    sstr_write(info->client_control.fd, buf, 0);
+  close(message_fds[0]);
+  sstr_free(buf);
+
+  switch (WEXITSTATUS(i)) {
+  case 0:
+    return TRUE;
+  case 1:
+    return FALSE;
+  case 2:
+    die(ERROR, "CCP requested exit. Closing session", 0, NULL, 0);
+  }
+  die(ERROR, "CCP exited with unknown exit code", 0, NULL, -1);
+  return FALSE;
+}
+-