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

bridge.cpp

/*
 * bridge.cpp - Monitored socket bridge
 * $Id: bridge.cpp,v 1.10 2004/06/05 15:15:17 rdenisc Exp $
 */

/***********************************************************************
 *  Copyright (C) 2002-2004 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 <string.h> // memmove()
#include <limits.h> // LONG_MAX
#include <sys/types.h>
#include <sys/time.h> // struct timeval (unused, but needed for select)
#if HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif
#include <unistd.h> // close()
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h> // shutdown(), send(), recv()
#endif

#include "log.h"

#include "bridge.h"

#define BRIDGE_BUFFER_SIZE    16384 // bytes

typedef struct
{
      int infd, outfd;
      char buffer[BRIDGE_BUFFER_SIZE];
      size_t buflen;
      OnewayLogList *logs;
} bridge;


/*
 * Registers file descriptors in various fd_set for select().
 * Returns the biggest fd found.
 */
inline int
register_bridge (const bridge& b, fd_set *readset, fd_set *writeset,
                  fd_set *exceptset)
{
      if (b.infd == -1)
            return -1; // bridge was shut down

      int maxfd = b.infd;

      FD_SET (maxfd, exceptset);
      if (b.buflen)
      {
            /* Data pending in the buffer */
            FD_SET (b.outfd, writeset);
            if (b.outfd > b.infd)
                  maxfd = b.outfd;
      }
      else
      {
            /* Buffer empty */
            FD_SET (b.infd, readset);
      }

      return maxfd;
}


/*
 * Shutdowns both sides of a bridge.
 */
inline void
shutdown_bridge (bridge& b)
{
      b.logs->Shutdown ();

      shutdown (b.infd, SHUT_RD);
      shutdown (b.outfd, SHUT_WR);
      b.infd = (b.outfd = -1);
      b.buflen = 0;
}


/*
 * Operates a full-duplex bridge between fd[0] and fd[1] and display any
 * transmitted data to each streams in the NULL-terminated stream list <logs>.
 *
 * Note: no assumption is made about the transport protocol used,
 * but the use of shutdown() assumes we work with sockets
 * (shutdown() will otherwise silently fail -- not a big problem).
 *
 * fd should be closed upon return.
 * fd should probably be in non-blocking I/O mode so that the function operates
 * fine.
 */

int
monitor_bridge(const int *fds, DataLogList *logs, long limit)
{
      long totalcount = 0;
      bridge b[2];

      b[0].outfd = b[1].infd = fds[0];
      b[0].infd = b[1].outfd = fds[1];
      b[0].buflen = b[1].buflen = 0;
      b[0].logs = &logs->ServerSide ();
      b[1].logs = &logs->ClientSide ();

      do
      {
            fd_set rdset, wrset, exset;

            FD_ZERO (&rdset);
            FD_ZERO (&wrset);
            FD_ZERO (&exset);

            int val = -1;
            for (int i = 0; i < 2; i++)
            {
                  int f = register_bridge (b[i], &rdset, &wrset, &exset);
                  if (val < f)
                        val = f;
            }

            if (val == -1)
                  return 0; // no more active bridge -> exit nicely

            // What should we do?
            val = select (val + 1, &rdset, &wrset, &exset, NULL);
            if (val == -1)
                  return -1; // most likely EINTR -> die

            for (int i = 0; val > 0; i++)
            {
                  int fd = b[i].infd;

                  if (fd == -1)
                        continue; // bridge was shut down earlier

                  if (FD_ISSET (fd, &exset))
                  {
                        val--;
                        /* transmit OOB byte */
                        char oob;

                        if (recv (fd, &oob, 1, MSG_OOB) != 1)
                              return -1; // strange error
                        if ((b[i].logs->WriteData (&oob, 1, 1) != 1)
                         || (send (b[i].outfd, &oob, 1, MSG_OOB) != 1))
                        {
                              shutdown_bridge (b[i]);
                              shutdown_bridge (b[i ^ 1]);
                              return -1;
                        }
                        totalcount ++;
                  }

                  if (FD_ISSET (fd, &rdset))
                  {
                        val--;
                        /* receive data (b[i].buflen MUST be zero) */
                        int check = recv (fd, b[i].buffer,
                                          sizeof (b[i].buffer),
                                          0);
                        switch (check)
                        {
                              case -1:
                              case 0:
                                    if (FD_ISSET (b[i].outfd, &wrset))
                                          val --;
                                    shutdown_bridge (b[i]);
                                    continue;

                              default:
                                    b[i].buflen = check;
                        }
                  }

                  fd = b[i].outfd;
                  if (FD_ISSET (fd, &wrset))
                  {
                        val--;
                        /* send data (buflen MUST be non-zero) */
                        int check = send (b[i].outfd, b[i].buffer,
                                          b[i].buflen, 0);
                        switch (check)
                        {
                              case -1:
                              case 0:
                                    shutdown_bridge (b[i]);
                                    shutdown_bridge (b[i ^ 1]);
                                    return check;

                              default:
                                    b[i].logs->WriteData (
                                                b[i].buffer,
                                                check);
                                    b[i].buflen -= check;
                                    memmove (b[i].buffer,
                                           b[i].buffer + check,
                                           b[i].buflen);
                                    totalcount += check;
                        }
                  }
            }

            if (totalcount < 0)
                  totalcount = LONG_MAX;
      }
      while ((limit == -1) || (totalcount < limit));

      /* limit reached */
      return 0;
}

Generated by  Doxygen 1.6.0   Back to index