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

redir.c

/* $Id$
 *
 * redir    - a utility for redirecting tcp connections
 *
 * Author:  Nigel Metheringham
 *          Nigel.Metheringham@ThePLAnet.net
 *
 * Based on, but much modified from, code originally written by
 * sammy@freenet.akron.oh.us - original header is below.
 *
 * redir is released under the GNU General Public license,
 * version 2, or at your discretion, any later version.
 *
 */

/* 
 * redir is currently maintained by Sam Creasey (sammy@oh.verio.com).
 * Please send patches, etc. there.
 *
 */

/* 980601: dl9sau
 * added some nice new features:
 *
 *   --bind_addr=my.other.ip.address
 *       forces to use my.other.ip.address for the outgoing connection
 *   
 *   you can also specify, that redir listens not on all IP addresses of
 *   your system but only for the given one, i.e.:
 *      if my host has the addresses
 *        irc.thishost.my.domain  and  mail.thishost.my.domain
 *      but you want that your users do connect for the irc redir service
 *      only on irc.thishost.my.domain, then do it this way:
 *        redir irc.fu-berlin.de irc.thishost.mydomain:6667 6667
 *   my need was that:
 *        addr1.first.domain  6667 redirects to irc.first.net  port 6667
 *   and  addr2.second.domain 6667 redirects to irc.second.net port 6667
 *   while addr1 and addr2 are the same maschine and the ports can be equal.
 *
 *  enjoy it!
 *    - thomas  <thomas@x-berg.in-berlin.de>, <dl9sau@db0tud.ampr.org>
 *
 *  btw: i tried without success implementing code for the following scenario:
 *    redir --force_addr irc.fu-berlin.de 6667 6667
 *  if "--force_addr" is given and a user connects to my system, that address
 *  of my system will be used on the outgoing connection that the user
 *  connected to.
 *  i was not successful to determine, to which of my addresses the user
 *  has connected.
 */
 
/* 990320 added support for ftp connection done by the client, now this code 
 *        should work for all ftp clients.
 *      
 *   - harald <harald.holzer@eunet.at>
 */
 
/* 991221 added options to simulate a slow connection and to limit
 *      bandwidth.
 *
 *   - Emmanuel Chantréau <echant@maretmanu.org>
 */

#define  VERSION "2.2.1"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <getopt.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>

#ifdef USE_TCP_WRAPPERS
#include <tcpd.h>
#endif

#define debug(x)  if (dodebug) fprintf(stderr, x)
#define debug1(x,y)     if (dodebug) fprintf(stderr, x, y)

/* let's set up some globals... */
int dodebug = 0;
int dosyslog = 0;
unsigned char reuse_addr = 1;
unsigned char linger_opt = 0;
char * bind_addr = NULL;
struct sockaddr_in addr_out;
int timeout = 0;

#ifndef NO_FTP
int ftp = 0;
#endif

int transproxy = 0;

#ifndef NO_SHAPER
int max_bandwidth = 0;
int random_wait = 0;
int wait_in_out=3; /* bit 0: wait for "in", bit 1: wait for "out" */
int wait_in=1;
int wait_out=1;
#endif

unsigned int bufsize=4096;
char *connect_str = NULL;     /* CONNECT string passed to proxy */
char * ident = NULL;

#ifndef NO_FTP
/* what ftp to redirect */
#define FTP_PORT 1
#define FTP_PASV 2
#endif

#ifdef USE_TCP_WRAPPERS
struct request_info request;
int     allow_severity = LOG_INFO;
int     deny_severity = LOG_WARNING;
#endif /* USE_TCP_WRAPPERS */

#ifdef NEED_STRRCHR
#define strrchr rindex
#endif /* NEED_STRRCHR */

#define REDIR_IN 1
#define REDIR_OUT 0

/* prototype anything needing it */
void do_accept(int servsock, struct sockaddr_in *target);
int bindsock(char *addr, int port, int fail);

#ifndef NO_SHAPER
/* Used in this program to write something in a socket, it has the same
   parameters and return value as "write", but with the flag "in": true if
   it's the "in" socket and false if it's the "out" socket */
static inline ssize_t redir_write (int fd, const void *buf, size_t size, int in)
{
  ssize_t result;
  int wait;

  wait=in ? wait_in : wait_out;
  if( random_wait > 0 && wait) {
    fd_set empty;
    struct timeval waitbw; /* for bandwidth */
    int rand_time;
    
    FD_ZERO(&empty);

    rand_time=rand()%(random_wait*2);
    debug1("random wait: %u\n", rand_time);
    waitbw.tv_sec=rand_time/1000;
    waitbw.tv_usec=rand_time%1000;

    select (1, &empty, NULL, NULL, &waitbw);
  }

  result=write(fd, buf, size);

  if( max_bandwidth > 0 && wait) {
    fd_set empty;
    unsigned long bits;
    struct timeval waitbw; /* for bandwidth */

    FD_ZERO(&empty);

      /* wait to be sure tu be below the allowed bandwidth */
    bits=size*8;
    debug1("bandwidth wait: %lu\n", 1000*bits/max_bandwidth);
    waitbw.tv_sec=bits/max_bandwidth;
    waitbw.tv_usec=(1000*(bits%max_bandwidth))/max_bandwidth;

    select (1, &empty, NULL, NULL, &waitbw);
  }

  return result;
}
#else
/* macro if traffic shaper is disabled */
#define redir_write(fd, buf, size, in) write(fd, buf,size)
#endif


#ifdef NEED_STRDUP
char *
strdup(char * str)
{
      char * result;

      if (result = (char *) malloc(strlen(str) + 1))
            strcpy(result, str);

      return result;
}
#endif /* NEED_STRDUP */

void
redir_usage(char *name)
{
      fprintf(stderr,"usage:\n");
      fprintf(stderr, 
            "\t%s --lport=<n> --cport=<n> [options]\n", 
            name);
      fprintf(stderr, "\t%s --inetd --cport=<n>\n", name);
      fprintf(stderr, "\n\tOptions are:-\n");
      fprintf(stderr, "\t\t--lport=<n>\t\tport to listen on\n");
      fprintf(stderr, "\t\t--laddr=IP\t\taddress of interface to listen on\n");
      fprintf(stderr, "\t\t--cport=<n>\t\tport to connect to\n");
      fprintf(stderr, "\t\t--caddr=<host>\t\tremote host to connect to\n");
      fprintf(stderr, "\t\t--inetd\t\trun from inetd\n");
      fprintf(stderr, "\t\t--debug\t\toutput debugging info\n");
      fprintf(stderr, "\t\t--timeout=<n>\tset timeout to n seconds\n");
      fprintf(stderr, "\t\t--syslog\tlog messages to syslog\n");
      fprintf(stderr, "\t\t--name=<str>\ttag syslog messages with 'str'\n");
      fprintf(stderr, "\t\t--connect=<str>\tCONNECT string passed to proxy server\n");
#ifdef USE_TCP_WRAPPERS
      fprintf(stderr, "\t\t            \tAlso used as service name for TCP wrappers\n");
#endif /* USE_TCP_WRAPPERS */
      fprintf(stderr, "\t\t--bind_addr=IP\tbind() outgoing IP to given addr\n");

#ifndef NO_FTP
      fprintf(stderr, "\t\t--ftp=<type>\t\tredirect ftp connections\n");
      fprintf(stderr, "\t\t\twhere type is either port, pasv, both\n");
#endif

      fprintf(stderr, "\t\t--transproxy\trun in linux's transparent proxy mode\n");
#ifndef NO_SHAPER
        /* options for bandwidth */
        fprintf(stderr, "\t\t--bufsize=<octets>\tsize of the buffer\n");
        fprintf(stderr, "\t\t--maxbandwidth=<bit-per-sec>\tlimit the bandwidth\n");
        fprintf(stderr, "\t\t--random_wait=<millisec>\twait before each packet\n");
        fprintf(stderr, "\t\t--wait_in_out=<flag>\t1 wait for in, 2 out, 3 in&out\n");
        /* end options for bandwidth */
#endif
      fprintf(stderr, "\n\tVersion %s.\n", VERSION);
      exit(2);
}

void
parse_args(int argc,
         char * argv[],
         char ** target_addr,
         int * target_port,
           char ** local_addr,
         int * local_port,
         int * timeout,
         int * dodebug,
         int * inetd,
         int * dosyslog,
         char ** bind_addr,
#ifndef NO_FTP
         int * ftp,
#endif
         int *transproxy,
#ifndef NO_SHAPER
           unsigned int * bufsize,
           int * max_bandwidth,
           int * random_wait,
           int * wait_in_out,
#endif
         char **connect_str)
{
      static struct option long_options[] = {
            {"lport", required_argument, 0, 'l'},
            {"laddr", required_argument, 0, 'a'},
            {"cport", required_argument, 0, 'r'},
            {"caddr", required_argument, 0, 'c'},
            {"bind_addr", required_argument, 0, 'b'},
            {"debug",    no_argument,       0, 'd'},
            {"timeout",  required_argument, 0, 't'},
            {"inetd",    no_argument,       0, 'i'},
            {"ident",    required_argument, 0, 'n'},
            {"name",     required_argument, 0, 'n'},
            {"syslog",   no_argument,       0, 's'},
            {"ftp",      required_argument,       0, 'f'},
            {"transproxy", no_argument,     0, 'p'},
            {"connect", required_argument, 0, 'x'},
                {"bufsize",  required_argument,       0, 'z'},
                {"max_bandwidth",  required_argument,       0, 'm'},
                {"random_wait",  required_argument,       0, 'w'},
                {"wait_in_out",  required_argument,       0, 'o'},
            {0,0,0,0}         /* End marker */
      };
      
      int option_index = 0;
      extern int optind;
      int opt;
      struct servent *portdesc;
      char *lport = NULL;
      char *tport = NULL;
#ifndef NO_FTP
      char *ftp_type = NULL;
#endif
 
      *local_addr = NULL;
      *target_addr = NULL;
      *target_port = 0;
      *local_port = 0;

      while ((opt = getopt_long(argc, argv, "disfpn:t:b:a:l:r:c:x:z:m:w:o:", 
                          long_options, &option_index)) != -1) {
            switch (opt) {
            case 'x':
                  *connect_str = optarg;
                  break;
            case 'a':
                  *local_addr = optarg;
                  break;

            case 'l':
                  lport = optarg;
                  break;

            case 'r':
                  tport = optarg;
                  break;

            case 'c':
                  *target_addr = optarg;
                  break;

            case 'b':
                  *bind_addr = optarg;
                  break;

            case 'd':
                  (*dodebug)++;
                  break;

            case 't':
                  *timeout = atol(optarg);
                  break;

            case 'i':
                  (*inetd)++;
                  break;

            case 'n':
                  /* This is the ident which is added to syslog messages */
                  ident = optarg;
                  break;

            case 's':
                  (*dosyslog)++;
                  break;

#ifndef NO_FTP        
            case 'f':
                  ftp_type = optarg;
                  if(!ftp_type) {
                        redir_usage(argv[0]);
                        exit(1);
                  }
                  break;
#endif           

            case 'p':
                  (*transproxy)++;
                  break;

#ifndef NO_SHAPER
                case 'z':
                  *bufsize = (unsigned int)atol(optarg);
                  break;
 
                case 'm':
                  *max_bandwidth = atol(optarg);
                  break;
 
                case 'w':
                  *random_wait = atol(optarg);
                  break;
 
                case 'o':
                  *wait_in_out = atol(optarg);
                  wait_in=*wait_in_out & 1;
                  wait_out=*wait_in_out & 2;
                  break;
#endif 
            default:
                  redir_usage(argv[0]);
                  exit(1);
                  break;
            }
      }

      if(tport == NULL)
      {
            redir_usage(argv[0]);
            exit(1);
      }

      if ((portdesc = getservbyname(tport, "tcp")) != NULL) {
            *target_port = ntohs(portdesc->s_port);
      } else {
            *target_port = atol(tport);
      }
    
      /* only check local port if not running from inetd */
      if(!(*inetd)) {
            if(lport == NULL)
            {
                  redir_usage(argv[0]);
                  exit(1);
            }
       
            if ((portdesc = getservbyname(lport, "tcp")) != NULL) 
                  *local_port = ntohs(portdesc->s_port);
            else
                  *local_port = atol(lport);
      } /* if *inetd */

      if (!ident) {
            if ((ident = (char *) strrchr(argv[0], '/'))) {
                  ident++;
            } else {
                  ident = argv[0];
            }
      }

#ifndef NO_FTP
      /* some kind of ftp being forwarded? */
      if(ftp_type) {
            if(!strncasecmp(ftp_type, "port", 4)) 
                  *ftp = FTP_PORT;
            else if(!strncasecmp(ftp_type, "pasv", 4))
                  *ftp = FTP_PASV;
            else if(!strncasecmp(ftp_type, "both", 4))
                  *ftp = FTP_PORT | FTP_PASV;
            else {
                  redir_usage(argv[0]);
                  exit(1);
            }
      }
#endif            
    
      openlog(ident, LOG_PID, LOG_DAEMON);

      return;
}

#ifndef NO_FTP
/* with the --ftp option, this one changes passive mode replies from
   the ftp server to point to a new redirector which we spawn,
   now it also change the PORT commando when the client accept the
   dataconnection */
   
void ftp_clean(int send, char *buf, unsigned long *bytes, int ftpsrv)
{

      char *port_start;
      int rporthi, lporthi;
      int lportlo, rportlo;
      int lport, rport;
      int remip[4];
      int localsock;
      int socksize = sizeof(struct sockaddr_in);

      struct sockaddr_in newsession;
      struct sockaddr_in sockname;

      if (ftpsrv == 0)
      {
            /* is this a port commando ? */
            if(strncmp(buf, "PORT", 4)) {
                  redir_write(send, buf, (*bytes), REDIR_OUT);
                  return;
            }
            /* parse the old address out of the buffer */
            port_start = strchr(buf, ' ');

            sscanf(port_start, " %d,%d,%d,%d,%d,%d", &remip[0], &remip[1],
                   &remip[2], &remip[3], &rporthi, &rportlo);
      } else {
            /* is this a passive mode return ? */
            if(strncmp(buf, "227", 3)) {
                  redir_write(send, buf, (*bytes), REDIR_OUT);
                  return;
            }
            
            /* parse the old address out of the buffer */
            port_start = strchr(buf, '(');
            
            sscanf(port_start, "(%d,%d,%d,%d,%d,%d", &remip[0], &remip[1],
                   &remip[2], &remip[3], &rporthi, &rportlo);
      }
    
      /* get the outside interface so we can listen */
      if(getsockname(send, (struct sockaddr *)&sockname, &socksize) != 0) {
            perror("getsockname");
            exit(1);
      }

      rport = (rporthi << 8) | rportlo;

      /* we need to listen on a port for the incoming connection.
         we will use the port 0, so let the system pick one. */

      localsock = bindsock(inet_ntoa(sockname.sin_addr), 0, 1);

      
      /* get the real info */
      if(getsockname(localsock, (struct sockaddr *)&sockname, &socksize) < 0) {
            perror("getsockname");
            if (dosyslog)
                  syslog(LOG_ERR, "getsockname failed: %m");
            exit(1);
      }

      lport = ntohs(sockname.sin_port);

      lporthi=(lport >> 8 ) & 0xff;
      lportlo=lport & 0xff;

      /* check to see if we bound */
      if(localsock == -1) {
            fprintf(stderr, "ftp: unable to bind new listening address\n");
            exit(1);
      }
      if (ftpsrv == 0) {
            /* send the new port and ipaddress to the server */
            (*bytes) = sprintf(buf, "PORT %d,%d,%d,%d,%d,%d\n",
                           sockname.sin_addr.s_addr & 0xff, 
                           (sockname.sin_addr.s_addr >> 8) & 0xff, 
                           (sockname.sin_addr.s_addr >> 16) & 0xff,
                           sockname.sin_addr.s_addr >> 24, lporthi, lportlo);
      } else {
            /* send the new port and ipaddress to the client */
            (*bytes) = sprintf(buf, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\n",
                           sockname.sin_addr.s_addr & 0xff, 
                           (sockname.sin_addr.s_addr >> 8) & 0xff, 
                           (sockname.sin_addr.s_addr >> 16) & 0xff,
                           sockname.sin_addr.s_addr >> 24, lporthi, lportlo);
      }
      newsession.sin_port = htons(rport);
      newsession.sin_family = AF_INET;
      newsession.sin_addr.s_addr = remip[0] | (remip[1] << 8)
            | (remip[2] << 16) | (remip[3] << 24);

      debug1("ftpdata server ip: %s\n", inet_ntoa(newsession.sin_addr));
      debug1("ftpdata server port: %d\n", rport);
      debug1("listening for ftpdata on port %d\n", lport);
      debug1("listening for ftpdata on addr %s\n", inet_ntoa(sockname.sin_addr));


      /* now that we're bound and listening, we can safely send the new
         string without fear of them getting a connection refused. */
      redir_write(send, buf, (*bytes), REDIR_OUT);     

      /* make a new process to handle the dataconnection correctly,
         for the PASV mode this isn't a problem because after sending the 
         PASV command, the data connection, get active. For the PORT command
         the server must send a success, if starting here with the copyloop
         the success command never arrive the client.*/
      
      switch(fork())
      {
      case -1: /* Error */
            syslog(LOG_ERR, "Couldn't fork: %m");
            _exit(1);
      case 0:  /* Child */
      {
            /* turn off ftp checking while the data connection is active */
            ftp = 0;
            do_accept(localsock, &newsession);
            close(localsock);
            _exit(0);
      }
      default: /* Parent */
      { close(localsock); }
      }
      return;
}
#endif


void
copyloop(int insock, 
       int outsock,
       int timeout_secs)
{
      fd_set iofds;
      fd_set c_iofds;
      int max_fd;             /* Maximum numbered fd used */
      struct timeval timeout;
      unsigned long bytes;
      unsigned long bytes_in = 0;
      unsigned long bytes_out = 0;
      unsigned int start_time, end_time;
      char buf[bufsize];

      /* Record start time */
      start_time = (unsigned int) time(NULL);

      /* Set up timeout */
      timeout.tv_sec = timeout_secs;
      timeout.tv_usec = 0;

      /* file descriptor bits */
      FD_ZERO(&iofds);
      FD_SET(insock, &iofds);
      FD_SET(outsock, &iofds);

    
      if (insock > outsock) {
            max_fd = insock;
      } else {
            max_fd = outsock;
      }

      debug1("Entering copyloop() - timeout is %d\n", timeout_secs);
      while(1) {
            (void) memcpy(&c_iofds, &iofds, sizeof(iofds));


            if (select(max_fd + 1,
                     &c_iofds,
                     (fd_set *)0,
                     (fd_set *)0,
                     (timeout_secs ? &timeout : NULL)) <= 0) {
                  /*        syslog(LLEV,"connection timeout: %d sec",timeout.tv_sec);*/
                  break;
            }

            if(FD_ISSET(insock, &c_iofds)) {
                  if((bytes = read(insock, buf, sizeof(buf))) <= 0)
                        break;
#ifndef NO_FTP
                  if (ftp & FTP_PORT)
                        /* if we're correcting FTP, lookup for a PORT commando
                           in the buffer, if yes change this and establish 
                           a new redirector for the data */
                        ftp_clean(outsock, buf, &bytes,0); 
                  else
#endif
                        if(redir_write(outsock, buf, bytes, REDIR_OUT) != bytes)
                              break;
                  bytes_out += bytes;
            }
            if(FD_ISSET(outsock, &c_iofds)) {
                  if((bytes = read(outsock, buf, sizeof(buf))) <= 0)
                        break;
                  /* if we're correcting for PASV on ftp redirections, then
                     fix buf and bytes to have the new address, among other
                     things */
#ifndef NO_FTP
                  if(ftp & FTP_PASV)
                        ftp_clean(insock, buf, &bytes,1);
                  else 
#endif
                        if(redir_write(insock, buf, bytes, REDIR_IN) != bytes)
                              break;
                  bytes_in += bytes;
            }
      }
      debug("Leaving main copyloop\n");

/*
  setsockopt(insock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
  setsockopt(insock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); 
  setsockopt(outsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
  setsockopt(outsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); 
*/

      shutdown(insock,0);
      shutdown(outsock,0);
      close(insock);
      close(outsock);
      debug("copyloop - sockets shutdown and closed\n");
      end_time = (unsigned int) time(NULL);
      debug1("copyloop - connect time: %8d seconds\n", end_time - start_time);
      debug1("copyloop - transfer in:  %8ld bytes\n", bytes_in);
      debug1("copyloop - transfer out: %8ld bytes\n", bytes_out);
      if (dosyslog) {
            syslog(LOG_NOTICE, "disconnect %d secs, %ld in %ld out",
                   (end_time - start_time), bytes_in, bytes_out);
      }
      return;
}

void doproxyconnect(int socket)
{
      char buf[128];
      int x;
      /* write CONNECT string to proxy */
      sprintf((char *) &buf, "CONNECT %s HTTP/1.0\n\n", connect_str);
      x = write(socket, (char *) &buf, strlen(buf));
      if (x < 1) {
            perror("doproxyconnect: failed");
            exit(1);
      }
      /* now read result */
      x = read(socket, (char *) &buf, sizeof(buf));
      if (x < 1) {
            perror("doproxyconnect: failed reading fra proxy");
            exit(1);
      }
      /* no more error checking for now -- something should be added later */
      /* HTTP/1.0 200 Connection established */
}


/* lwait for a connection and move into copyloop...  again,
   ftp redir will call this, so we don't dupilcate it. */

void
do_accept(int servsock, struct sockaddr_in *target)
{

      int clisock;
      int targetsock;
      struct sockaddr_in client;
      int clientlen = sizeof(client);
      int accept_errno;
     
      debug("top of accept loop\n");
      if ((clisock = accept(servsock, (struct  sockaddr  *) &client, 
                        &clientlen)) < 0) {

            accept_errno = errno;
            perror("server: accept");

            if (dosyslog)
                  syslog(LOG_ERR, "accept failed: %m");

            /* determine if this error is fatal */
            switch(accept_errno) {
                  /* non-fatal errors */
            case EHOSTUNREACH:
            case ECONNRESET:
            case ETIMEDOUT:
                  return;

                  /* all other errors assumed fatal */
            default:
                  exit(1);
            }

      }
     
      debug1("peer IP is %s\n", inet_ntoa(client.sin_addr));
      debug1("peer socket is %d\n", client.sin_port);

      /*
       * Double fork here so we don't have to wait later
       * This detaches us from our parent so that the parent
       * does not need to pick up dead kids later.
       *
       * This needs to be done before the hosts_access stuff, because
       * extended hosts_access options expect to be run from a child.
       */
      switch(fork())
      {
      case -1: /* Error */
            perror("(server) fork");

            if (dosyslog)
                  syslog(LOG_ERR, "(server) fork failed: %m");

            _exit(1);
      case 0:  /* Child */
            break;
      default: /* Parent */
      {
            int status;
        
            /* Wait for child (who has forked off grandchild) */
            (void) wait(&status);

            /* Close sockets to prevent confusion */
            close(clisock);
      
            return;
      }
      }

      /* We are now the first child. Fork again and exit */
        
      switch(fork())
      {
      case -1: /* Error */
            perror("(child) fork");

            if (dosyslog)
                  syslog(LOG_ERR, "(child) fork failed: %m");

            _exit(1);
      case 0:  /* Child */
            break;
      default: /* Parent */
            _exit(0);
      }
     
      /* We are now the grandchild */

#ifdef USE_TCP_WRAPPERS
      request_init(&request, RQ_DAEMON, ident, RQ_FILE, clisock, 0);
      sock_host(&request);
      sock_hostname(&request);
      sock_hostaddr(&request);

      if (!hosts_access(&request)) {
            refuse(&request);
            _exit(0);
      }

      if (dosyslog)
            syslog(LOG_INFO, "accepted connect from %s", eval_client(&request));
#endif /* USE_TCP_WRAPPERS */

      if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        
            perror("target: socket");
        
            if (dosyslog)
                  syslog(LOG_ERR, "socket failed: %m");
            
            _exit(1);
      }

      if(transproxy) {
            memcpy(&addr_out, &client, sizeof(struct sockaddr_in));
            addr_out.sin_port = 0;
      }
          
      if (bind_addr || transproxy) {
            /* this only makes sense if an outgoing IP addr has been forced;
             * at this point, we have a valid targetsock to bind() to.. */
            /* also, if we're in transparent proxy mode, this option
               never makes sense */

            if (bind(targetsock, (struct  sockaddr  *) &addr_out, 
                   sizeof(struct sockaddr_in)) < 0) {
                  perror("bind_addr: cannot bind to forcerd outgoing addr");

/* the port parameter fetch the really port we are listening, it should
   only be different if the input value is 0 (let the system pick a 
   port) */
                  if (dosyslog)
                        syslog(LOG_ERR, "bind failed: %m");

                  _exit(1);
            }
            debug1("outgoing IP is %s\n", inet_ntoa(addr_out.sin_addr));
      }

      if (connect(targetsock, (struct  sockaddr  *) target, 
                sizeof(struct sockaddr_in)) < 0) {
            perror("target: connect");

            if (dosyslog)
                  syslog(LOG_ERR, "bind failed: %m");

            _exit(1);
      }
     
      debug1("connected to %s\n", inet_ntoa(target->sin_addr));

      /* thanks to Anders Vannman for the fix to make proper syslogging
         happen here...  */

      if (dosyslog) {
            char tmp1[20], tmp2[20];
            strcpy(tmp1, inet_ntoa(client.sin_addr));
            strcpy(tmp2, inet_ntoa(target->sin_addr));
        
            syslog(LOG_NOTICE, "connecting %s/%d to %s/%d",
                   tmp1, client.sin_port,
                   tmp2, target->sin_port);
      }

      /* do proxy stuff */
      if (connect_str)
            doproxyconnect(targetsock);

#ifndef NO_SHAPER
        /* initialise random number if necessary */
        if( random_wait > 0 ) {
          srand(getpid());
        }
#endif

      copyloop(clisock, targetsock, timeout);
      exit(0);    /* Exit after copy */
}

/* bind to a new socket, we do this out here because passive-fixups
   are going to call it too, and there's no sense dupliciting the
   code. */
/* fail is true if we should just return a -1 on error, false if we
   should bail. */

int bindsock(char *addr, int port, int fail) 
{

      int servsock;
      struct sockaddr_in server;
     
      /*
       * Get a socket to work with.  This socket will
       * be in the Internet domain, and will be a
       * stream socket.
       */
     
      if ((servsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            if(fail) {
                  return -1;
            }
            else {
                  perror("server: socket");

                  if (dosyslog)
                        syslog(LOG_ERR, "socket failed: %m");

                  exit(1);
            }
      }

      memset(&server, 0, sizeof(server));
      server.sin_family = AF_INET;
      server.sin_port = htons(port);
      if (addr != NULL) {
            struct hostent *hp;
        
            debug1("listening on %s\n", addr);
            if ((hp = gethostbyname(addr)) == NULL) {
                  fprintf(stderr, "%s: cannot resolve hostname.\n", addr);
                  exit(1);
            }
            memcpy(&server.sin_addr, hp->h_addr, hp->h_length);
      } else {
            debug("local IP is default\n");
            server.sin_addr.s_addr = htonl(inet_addr("0.0.0.0"));
      }
     
      setsockopt(servsock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr));
      setsockopt(servsock, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(SO_LINGER)); 
     
      /*
       * Try to bind the address to the socket.
       */
     
      if (bind(servsock, (struct  sockaddr  *) &server, 
             sizeof(server)) < 0) {
            if(fail) {
                  close(servsock);
                  return -1;
            } else {
                  perror("server: bind");

                  if (dosyslog)
                        syslog(LOG_ERR, "bind failed: %m");

                  exit(1);
            }
      }
     
      /*
       * Listen on the socket.
       */
     
      if (listen(servsock, 10) < 0) {
            if(fail) {
                  close(servsock);
                  return -1;
            } else {
                  perror("server: listen");

                  if (dosyslog)
                        syslog(LOG_ERR, "listen failed: %m");

                  exit(1);
            }
      }
     
      return servsock;
}

int
main(int argc, char *argv[])
{

      struct sockaddr_in target;
      char *target_addr;
      int target_port;
      char *local_addr;
      int local_port;
      int inetd = 0;
      char * target_ip;
      char * ip_to_target;

      debug("parse args\n");
      parse_args(argc, argv, &target_addr, &target_port, &local_addr, 
               &local_port, &timeout, &dodebug, &inetd, &dosyslog, &bind_addr,
#ifndef NO_FTP
               &ftp, 
#endif
               &transproxy, 
#ifndef NO_SHAPER
               &bufsize, &max_bandwidth, &random_wait,
               &wait_in_out,
#endif
                   &connect_str);

      /* Set up target */
      target.sin_family = AF_INET;
      target.sin_port = htons(target_port);
      if (target_addr != NULL) {
            struct hostent *hp;

            debug1("target is %s\n", target_addr);
            if ((hp = gethostbyname(target_addr)) == NULL) {
                  fprintf(stderr, "%s: host unknown.\n", target_addr);
                  exit(1);
            }
            memcpy(&target.sin_addr, hp->h_addr, hp->h_length);
      } else {
            debug("target is default\n");
            target.sin_addr.s_addr = htonl(inet_addr("0.0.0.0"));
      }

      target_ip = strdup(inet_ntoa(target.sin_addr));
      debug1("target IP address is %s\n", target_ip);
      debug1("target port is %d\n", target_port);

      /* Set up outgoing IP addr (optional);
       * we have to wait for bind until targetsock = socket() is done
       */
      if (bind_addr && !transproxy) {
            struct hostent *hp;

            fprintf(stderr, "bind_addr is %s\n", bind_addr);
            addr_out.sin_family = AF_INET;
            addr_out.sin_port = 0;
            if ((hp = gethostbyname(bind_addr)) == NULL) {
                  fprintf(stderr, "%s: cannot resolve forced outgoing IP address.\n", bind_addr);
                  exit(1);
            }
            memcpy(&addr_out.sin_addr, hp->h_addr, hp->h_length);

            ip_to_target = strdup(inet_ntoa(addr_out.sin_addr));
            debug1("IP address for target is %s\n", ip_to_target);
      }
           

      if (inetd) {
            int targetsock;
            struct sockaddr_in client;
            int client_size = sizeof(client);

#ifdef USE_TCP_WRAPPERS
            request_init(&request, RQ_DAEMON, ident, RQ_FILE, 0, 0);
            sock_host(&request);
            sock_hostname(&request);
            sock_hostaddr(&request);
      
            if (!hosts_access(&request))
                  refuse(&request);
#endif /* USE_TCP_WRAPPERS */

            if (!getpeername(0, (struct sockaddr *) &client, &client_size)) {
                  debug1("peer IP is %s\n", inet_ntoa(client.sin_addr));
                  debug1("peer socket is %d\n", client.sin_port);
            }
            if ((targetsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
                  perror("target: socket");

                  if (dosyslog)
                        syslog(LOG_ERR, "targetsock failed: %m");

                  exit(1);
            }

            if(transproxy) {
                  memcpy(&addr_out, &client, sizeof(struct sockaddr_in));
                  addr_out.sin_port = 0;
            }

            if (bind_addr || transproxy) {
                  /* this only makes sense if an outgoing IP addr has been forced;
                   * at this point, we have a valid targetsock to bind() to.. */
                  if (bind(targetsock, (struct  sockaddr  *) &addr_out, 
                         sizeof(addr_out)) < 0) {
                        perror("bind_addr: cannot bind to forcerd outgoing addr");
                         
                        if (dosyslog)
                              syslog(LOG_ERR, "bind failed: %m");
                         
                        exit(1);
                  }
                  debug1("outgoing IP is %s\n", inet_ntoa(addr_out.sin_addr));
            }

            if (connect(targetsock, (struct sockaddr *) &target, 
                      sizeof(target)) < 0) {
                  perror("target: connect");

                  if (dosyslog)
                        syslog(LOG_ERR, "connect failed: %m");

                  exit(1);
            }

            if (dosyslog) {
                  syslog(LOG_NOTICE, "connecting %s/%d to %s/%d",
                         inet_ntoa(client.sin_addr), client.sin_port,
                         target_ip, target.sin_port);
            }

            /* Just start copying - one side of the loop is stdin - 0 */
            copyloop(0, targetsock, timeout);
      } else {
            int servsock;
      
            if(local_addr)
                  servsock = bindsock(local_addr, local_port, 0);
            else
                  servsock = bindsock(NULL, local_port, 0);

            /*
             * Accept connections.  When we accept one, ns
             * will be connected to the client.  client will
             * contain the address of the client.
             */

            while (1) 
                  do_accept(servsock, &target);
      }

      /* this should really never be reached */

      exit(0);

}



Generated by  Doxygen 1.6.0   Back to index