/* * 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 #endif #include // memmove() #include // LONG_MAX #include #include // struct timeval (unused, but needed for select) #if HAVE_SYS_SELECT_H # include #endif #include // close() #ifdef HAVE_SYS_SOCKET_H # include // 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 . * * 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; }