Logo Search packages:      
Sourcecode: tcpreen version File versions  Download package

main.cpp

/*
 * main.cpp - main() function for tcpreen - command line parsing and
 * basic sanity checks.
 * $Id: main.cpp 190 2006-03-18 20:16:14Z remi $
 */

/***********************************************************************
 *  Copyright (C) 2002-2005 Remi Denis-Courmont.                       *
 *  This program is free software; you can redistribute and/or modify  *
 *  it under the terms of the GNU General Public License as published  *
 *  by the Free Software Foundation; version 2 of the license.         *
 *                                                                     *
 *  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, you can get it from:              *
 *  http://www.gnu.org/copyleft/gpl.html                               *
 ***********************************************************************/

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

#include "secstdio.h"
#include <stdlib.h> /* getenv(), strtol(), stroul() */
#include <string.h> /* memset() */
#include <limits.h> /* LONG_MAX, INT_MAX */

#include <sys/types.h>
#include <unistd.h> /* geteuid(), getuid(), seteuid() */
#ifdef HAVE_GETOPT_H
/*
 * Temporary dirty fix for C++-incompatible GNU <getopt.h>
 * on non-GNU platforms (namely FreeBSD+libgnugetopt).
 *
 * I hope I can get rid of that silly thing soon.
 */
# if (defined(HAVE_GETOPT_LONG) && !defined(__GNU_LIBRARY__))
#  define getopt gnugetopt_broken
# endif
# include <getopt.h> /* getopt_long() */
# undef getopt
#endif
#ifdef HAVE_PWD_H
# include <pwd.h> /* getpwnam() */
#endif
#ifdef HAVE_SYSLOG_H
# include <syslog.h>
#endif
#ifdef HAVE_LOCALE_H
# include <locale.h>
#endif
#include "gettext.h"

#include "tcpreen.h"
#include "format.h"
#include "proto.h"

// Possible return values
#define MAIN_NOERR      0
#define MAIN_SHORTCIRCUIT -1 // for internal use
#define MAIN_PARMPROB   1 // parameter problem
#define MAIN_IOERR      2 // I/O error

static int
usage (void)
{
      puts (_(
"Usage: tcpreen [OPTION]... SERVER_PORT [LOCAL_PORT]\n"
"Establishes a bridge between two TCP ports then monitors TCP sessions.\n"
"\n"
"  -a, --bind     listen for connections on the following address\n"
"  -b, --bytes    limit maximum sessions length in bytes; e.g.: -b 1024\n"
"  -c, --connect  connect to the client instead of listening for it\n"
"  -d, --daemon   run in the background\n"
"  -f, --format   selects log file(s) output format\n"
"  -F, --fork     specify number of client processes to spawn\n"
"  -h, --help     display this help and exit\n"
"  -l, --listen   listen for the server instead of connecting to it\n"
"  -m, --maxconn  limit number of monitored connections (faster)\n"
"  -n, --numeric  disable reverse DNS lookup\n"
/*
"  -i, --iface    specify which network interface(s) to use (if applicable)\n"
*/
"  -o, --output   open a log file; e.g.: -o mylog.txt\n"
"  -p, --protocol use the following protocol for connections\n"
"  -q, --quiet    do not write to stdout\n"
"  -s, --server   connect to this host (local host by default)\n"
"  -u, --user     specify an unprivilieged username to be used\n"
"  -v, --verbose  increase verbosity - cumulative\n"
"  -V, --version  display program version and exit\n"));

      fprint_proto_list (stdout);
      fprint_format_list (stdout);
      fputc ('\n', stdout);

      printf (_("Report any bug to: <%s>.\n"), PACKAGE_BUGREPORT);
      return MAIN_SHORTCIRCUIT;
}

static int
version (void)
{
#ifndef VERSION
# define VERSION "unknown version"
#endif
      puts (
"TCP re-engineering tool "VERSION" ("PACKAGE_HOST")\n"
" built "__DATE__" on "PACKAGE_BUILD_HOSTNAME" ("PACKAGE_BUILD")\n"
"Copyright (C) 2002-2004 Remi Denis-Courmont");
      puts (_(
"This is free software; see the source for copying conditions.\n"
"There is NO warranty; not even for MERCHANTABILITY or\n"
"FITNESS FOR A PARTICULAR PURPOSE.\n"));
      printf (_("Written by %s.\nConfigured with: %s\n"), "Remi Denis-Courmont",
            PACKAGE_CONFIGURE_INVOCATION);
      return MAIN_SHORTCIRCUIT;
}


static int
quick_usage (void)
{
      fputs (_("Try \"tcpreeen -h | more\" for more information.\n"), stderr);
      return MAIN_PARMPROB;
}


/*
 * Generic error handling
 */
static int
error_gen (const char *path, const char *msg)
{
      fprintf (stderr, "%s: %s.\n", path, _(msg));
      return quick_usage ();
}


static int
error_qty (const char *quantity)
{
      return error_gen (quantity,
                        N_("invalid number (or capacity exceeded)"));
}


static int
error_extra (const char *extra)
{
      return error_gen (extra, N_("unexpected extra parameter"));
}


/*
 * Parses an user name.
 * Returns (uid_t)(-1) on failure.
 */
static uid_t
parse_user (const char *username)
{
      if ((username == NULL) || (username[0] == 0))
            return (uid_t)(-1);

      struct passwd *pw = getpwnam (username);
      return (pw != NULL) ? pw->pw_uid : (uid_t)(-1);
}


/*
 * Command line options parsing.
 * argc, argv and loglist must be kept valid at all cost.
 */
#define VERBOSE_MAX 10
static int
parse_args (int argc, char *argv[], struct bridgeconf *conf,
            DataLogListMaker& logsmaker)
{
      int check, verbose = 1;
      struct option longopts[] =
      {
            { "accept", 1, NULL, 'a' },
            { "bind",   1, NULL, 'a' },
            { "bytes",  1, NULL, 'b' },
            { "connect",      0, NULL, 'c' },
            { "daemon", 0, NULL, 'd' },
            { "engine", 1, NULL, 'e' },
            { "format", 1, NULL, 'f' },
            { "fork",   1, NULL, 'F' },
            { "help",   0, NULL, 'h' },
            { "listen", 0, NULL, 'l' },
            //{ "iface",      1, NULL, 'i' },
            //{ "interface",  1, NULL, 'i' },
            { "max",    1, NULL, 'm' },
            { "maxconn",      1, NULL, 'm' },
            { "numeric",      0, NULL, 'n' },
            { "output", 1, NULL, 'o' },
            { "protocol",     1, NULL, 'p' },
            { "quiet",  0, NULL, 'q' },
            { "server", 1, NULL, 's' },
            { "user",   1, NULL, 'u' },
            { "verbose",      0, NULL, 'v' },
            { "version",      0, NULL, 'V' },
            { NULL,     0, NULL, 0 }
      };
      DataLog *(*logmaker) (void) = NULL;

      while ((check = getopt_long (argc, argv,
            "a:b:cde:f:F:hlLm:no:p:qs:u:vV", longopts, NULL)) != EOF)
      {
            switch (check)
            {
                  case 'a':
                        conf->bridgename = optarg;
                        break;

                  case 'b':
                  {
                        char *end;
                        long lim;

                        lim = strtol (optarg, &end, 0);
                        if ((*end) || (lim < 0) || (lim == LONG_MAX))
                              return error_qty (optarg);
                        conf->bytelimit = lim;
                  }
                        break;

                  case 'c':
                        conf->mode &= ~tcpreen_listen_client;
                        if (conf->totalclients == -1)
                              conf->totalclients = 1;
                        break;

                  case 'd':
                        conf->mode |= tcpreen_daemon;
                        break;

                  case 'f':
                        logmaker = findlogmakerbyname (optarg);
                        if (logmaker == NULL)
                              return error_gen (optarg, _("Unrecognized log format"));
                        break;

                  case 'F':
                  {
                        char *end;
                        long num;

                        num = strtoul (optarg, &end, 0);
                        if (*end || (num > INT_MAX) || (num == 0))
                              return error_qty (optarg);
                        conf->maxclients = (int)num;
                  }
                        break;

                  case 'h': /* help */
                        return usage();

                  case 'l':
                        conf->mode |= tcpreen_listen_server;
                        break;

                  case 'm':
                        {
                              char *end;
                              conf->totalclients = strtol (optarg,
                                                     &end, 0);
                              if (*end)
                                    return error_qty (optarg);
                        }
                        break;

                  case 'n':
                        conf->mode |= tcpreen_numeric;
                        break;

                  case 'o':
                  {
                        if (logmaker == NULL)
                              logmaker = default_format (1);

                        int val = (strcmp (optarg, "-") ?
                               logsmaker.AddLogMaker (logmaker, optarg)
                              : logsmaker.AddLogMaker (logmaker));

                        if (val)
                        {
                              perror (_("Fatal error"));
                              return MAIN_IOERR;
                        }
                  }
                        break;

                  case 'p':
                        if (parse_proto (optarg, &conf->serveraf,
                                          &conf->bridgeaf))
                              return error_gen (optarg, N_("unknown or unsupported protocol(s)"));

                        break;

                  case 'q':
                        verbose = 0;
                        break;

                  case 's':
                        conf->servername = optarg;
                        break;

                  case 'u':
                        if (conf->user)
                              return error_gen (optarg, N_("only root can select an user"));
                        else
                        {
                              uid_t uid = parse_user (optarg);
                              if (uid == (uid_t)(-1))
                                    return error_gen (optarg, N_("invalid user"));
                              conf->user = uid;
                        }
                        break;

                  case 'v':
                        verbose ++;
                        if (verbose > VERBOSE_MAX)
                              verbose = VERBOSE_MAX;
                        break;

                  case 'V':
                        return version();

                  default: // never happens
                  case '?': // error: unrecognized option
                        return quick_usage ();
            }
      }

      // Reads service names/port numbers
      conf->serverservice = (optind < argc) ? argv[optind++] : NULL;
      conf->bridgeservice = (optind < argc) ? argv[optind++] : NULL;

      if (optind < argc)
            return error_extra (argv[optind]);

      // Handles verbosity setting
      if (conf->mode & tcpreen_daemon)
            verbose = 0;
      if (verbose)
      {
            conf->mode |= tcpreen_verbose;
            if (logmaker == NULL)
                  logmaker = default_format (verbose > 1);

            int check = logsmaker.AddLogMaker (logmaker);
            if (check)
            {
                  perror (_("Fatal error"));
                  return MAIN_IOERR;
            }
      }

      /*
       * Sanity checks
       */
      if ((conf->serverservice == NULL)
       && !(conf->mode & tcpreen_listen_server))
            return error_gen (argv[0], N_("no server port specified"));
      if ((conf->bridgeservice == NULL)
       && !(conf->mode & tcpreen_listen_client))
            return error_gen (argv[0], N_("no client port specified with -c option"));
      if (!(conf->mode & tcpreen_speak)
       && ((conf->bridgeservice == NULL) || (conf->serverservice == NULL)))
            return error_gen (argv[0], N_("dynamically allocated port but nowhere to tell you which one"));

      return MAIN_NOERR;
}


/*
 * Default security settings
 */
static int
preconf_security (bridgeconf *conf)
{
      uid_t user = getuid (), effuser = geteuid ();

      if (user == 0)
      {
            if (effuser)
                  /* In case we are Real UID root and Set UID non-root */
                  user = effuser;
            else
            {
                  /* Support for sudo (http://www.sudo.ws/)
                   * (if, and only if, we are running as **REAL**
                   * UID root) */
                  const char *sudo_user = getenv ("SUDO_USER");
                  if (sudo_user != NULL)
                  {
                        user = parse_user (sudo_user);
                        if (user == (uid_t)(-1))
                              return -1;
                  }
            }
      }

      conf->user = user;
      return 0;
}


/*
 * Main function
 */
#ifdef WINSOCK
extern "C"
#endif
int main (int argc, char *argv[])
{
      struct bridgeconf conf;
      int val;

      /* Default settings */
      memset (&conf, 0, sizeof (conf));
      conf.bytelimit = -1;
      conf.totalclients = -1;
      conf.maxclients = 1;
      conf.mode = tcpreen_listen_client;
      if (preconf_security (&conf))
            return 1;

      /* Use low privileges during initialization *
       * (in particular to open log files)          */
      if (seteuid (conf.user))
            return 1;
      /* Now we can assume that "seteuid (conf.user)" will always work. */

      /* Initialization */
      setlocale (LC_ALL, "");
      bindtextdomain (PACKAGE, LOCALEDIR);
      textdomain (PACKAGE);

      DataLogListMaker logsmaker;
      conf.logsmaker = &logsmaker;

      /* Command line parsing and checking */
      val = parse_args (argc, argv, &conf, logsmaker);

      /* Let's come to serious things... */
      if (!val)
      {
            // Opens system log
            if (conf.mode & tcpreen_daemon)
            {
                  openlog ("tcpreen", LOG_PID, LOG_DAEMON);
                  syslog (LOG_NOTICE, _("starting\n"));
            }

            val = bridge_main (&conf);

            if (conf.mode & tcpreen_daemon)
            {
                  syslog (LOG_NOTICE, _("stopping\n"));
                  closelog ();
            }
      }

      return (val >= 0) ? val : 0;
}

Generated by  Doxygen 1.6.0   Back to index