/*
* 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;
}
syntax highlighted by Code2HTML, v. 0.9.1