/* * IRC - Internet Relay Chat * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * See file AUTHORS in IRC package for additional names of * the programmers. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * 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, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Port Bouncer. * * This tool is designed to set up a number of local listening ports, and * then forward any data recived on those ports, to another host/port combo. * Each listening port can bounce to a different host/port defined in the * config file. --Gte * * $Id: Bounce.cpp 839 2004-10-17 22:10:58Z r33d $ * */ #include "Bounce.h" int main() { Bounce* application = new Bounce(); /* * Ignore SIGPIPE. */ struct sigaction act; act.sa_handler = SIG_IGN; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGPIPE, &act, 0); #ifndef DEBUG /* * If we aren't debugging, we might as well * detach from the console. */ pid_t forkResult = fork() ; if(forkResult < 0) { printf("Unable to fork new process.\n"); return -1 ; } else if(forkResult != 0) { printf("Successfully Forked, New process ID is %i.\n", forkResult); return 0; } #endif /* * Create new application object, bind listeners and begin * polling them. */ application->bindListeners(); while (1) { application->checkSockets(); } } /* **************************************** * * * Bounce class implementation. * * * **************************************** */ void Bounce::bindListeners() { /* * bindListeners. * Inputs: Nothing. * Outputs: Nothing. * Process: 1. Reads the config file, and.. * 2. Creates a new listener for each 'P' line found. * */ FILE* configFd; char tempBuf[256]; int localPort = 0; int remotePort = 0; char* remoteServer; char* vHost; /* * Open config File. */ if(!(configFd = fopen("bounce.conf", "r"))) { printf("Error, unable to open config file!\n"); exit(0); } while (fgets(tempBuf, 256, configFd) != NULL) { if((tempBuf[0] != '#') && (tempBuf[0] != '\r')) { switch(tempBuf[0]) { case 'P': { /* Add new port listener */ strtok(tempBuf, ":"); vHost = strtok(NULL, ":"); localPort = atoi(strtok(NULL, ":")); remoteServer = strtok(NULL, ":"); remotePort = atoi(strtok(NULL, ":")); Listener* newListener = new Listener(); strcpy(newListener->myVhost, vHost); strcpy(newListener->remoteServer, remoteServer); newListener->remotePort = remotePort; newListener->localPort = localPort; #ifdef DEBUG printf("Adding new Listener: Local: %s:%i, Remote: %s:%i\n", vHost, localPort, remoteServer, remotePort); #endif newListener->beginListening(); listenerList.insert(listenerList.begin(), newListener); break; } } } } } void Bounce::checkSockets() { /* * checkSockets. * Inputs: Nothing. * Outputs: Nothing. * Process: 1. Builds up a FD_SET of all sockets we wish to check. * (Including all listeners & all open connections). * 2. SELECT(2) the set, and forward/accept as needed. * */ typedef std::list listenerContainer; typedef listenerContainer::iterator listIter; typedef std::list connectionContainer; typedef connectionContainer::iterator connIter; struct timeval tv; fd_set readfds; tv.tv_sec = 0; tv.tv_usec = 1000; int tempFd = 0; int tempFd2 = 0; int highestFd = 0; int delCheck = 0; char* tempBuf; FD_ZERO(&readfds); /* * Add all Listeners to the set. */ listIter a = listenerList.begin(); while(a != listenerList.end()) { tempFd = (*a)->fd; FD_SET(tempFd, &readfds); if (highestFd < tempFd) highestFd = tempFd; a++; } /* * Add Local & Remote connections from each * connection object to the set. */ connIter b = connectionsList.begin(); while(b != connectionsList.end()) { tempFd = (*b)->localSocket->fd; tempFd2 = (*b)->remoteSocket->fd; FD_SET(tempFd, &readfds); if (highestFd < tempFd) highestFd = tempFd; FD_SET(tempFd2, &readfds); if (highestFd < tempFd2) highestFd = tempFd2; b++; } select(highestFd+1, &readfds, NULL, NULL, &tv); /* * Check all connections for readability. * First check Local FD's. * If the connection is closed on either side, * shutdown both sockets, and clean up. * Otherwise, send the data from local->remote, or * remote->local. */ b = connectionsList.begin(); while(b != connectionsList.end()) { tempFd = (*b)->localSocket->fd; if (FD_ISSET(tempFd, &readfds)) { tempBuf = (*b)->localSocket->read(); if ((tempBuf[0] == 0)) // Connection closed. { close((*b)->localSocket->fd); close((*b)->remoteSocket->fd); #ifdef DEBUG printf("Closing FD: %i\n", (*b)->localSocket->fd); printf("Closing FD: %i\n", (*b)->remoteSocket->fd); #endif delete(*b); delCheck = 1; b = connectionsList.erase(b); } else { (*b)->remoteSocket->write(tempBuf, (*b)->localSocket->lastReadSize); } } if (!delCheck) b++; delCheck = 0; } /* * Now check Remote FD's.. */ b = connectionsList.begin(); while(b != connectionsList.end()) { tempFd = (*b)->remoteSocket->fd; if (FD_ISSET(tempFd, &readfds)) { tempBuf = (*b)->remoteSocket->read(); if ((tempBuf[0] == 0)) // Connection closed. { close((*b)->localSocket->fd); close((*b)->remoteSocket->fd); #ifdef DEBUG printf("Closing FD: %i\n", (*b)->localSocket->fd); printf("Closing FD: %i\n", (*b)->remoteSocket->fd); #endif delete(*b); delCheck = 1; b = connectionsList.erase(b); } else { (*b)->localSocket->write(tempBuf, (*b)->remoteSocket->lastReadSize); } } if (!delCheck) b++; delCheck = 0; } /* * Check all listeners for new connections. */ a = listenerList.begin(); while(a != listenerList.end()) { tempFd = (*a)->fd; if (FD_ISSET(tempFd, &readfds)) { recieveNewConnection(*a); } a++; } } void Bounce::recieveNewConnection(Listener* listener) { /* * recieveNewConnection. * Inputs: A Listener Object. * Outputs: Nothing. * Process: 1. Recieves a new connection on a local port, * and creates a connection object for it. * 2. Accepts the incomming connection. * 3. Creates a new Socket object for the remote * end of the connection. * 4. Connects up the remote Socket. * 5. Adds the new Connection object to the * connections list. * */ Connection* newConnection = new Connection(); newConnection->localSocket = listener->handleAccept(); Socket* remoteSocket = new Socket(); newConnection->remoteSocket = remoteSocket; if(remoteSocket->connectTo(listener->remoteServer, listener->remotePort)) { connectionsList.insert(connectionsList.begin(), newConnection); } else { #ifdef DEBUG newConnection->localSocket->write("Unable to connect to remote host.\n"); #endif close(newConnection->localSocket->fd); delete(newConnection); delete(remoteSocket); } } /* **************************************** * * * Listener class implementation. * * * **************************************** */ Socket* Listener::handleAccept() { /* * handleAccept. * Inputs: Nothing. * Outputs: A Socket Object. * Process: 1. Accept's an incomming connection, * and returns a new socket object. */ int new_fd = 0; int sin_size = sizeof(struct sockaddr_in); Socket* newSocket = new Socket(); new_fd = accept(fd, (struct sockaddr*)&newSocket->address, (socklen_t*)&sin_size); newSocket->fd = new_fd; return newSocket; } void Listener::beginListening() { /* * beginListening. * Inputs: Nothing. * Outputs: Nothing. * Process: 1. Binds the local ports for all the * Listener objects. * */ struct sockaddr_in my_addr; int bindRes; int optval; optval = 1; fd = socket(AF_INET, SOCK_STREAM, 0); /* Check for no FD's left?! */ my_addr.sin_family = AF_INET; my_addr.sin_port = htons(localPort); my_addr.sin_addr.s_addr = inet_addr(myVhost); bzero(&(my_addr.sin_zero), 8); setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); bindRes = bind(fd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); if(bindRes == 0) { listen(fd, 10); } else { /* * If we can't bind a listening port, we might aswell drop out. */ printf("Unable to bind to %s:%i!\n", myVhost, localPort); exit(0); } } /* **************************************** * * * Socket class implementation. * * * **************************************** */ Socket::Socket() { /* * Socket Constructor. * Inputs: Nothing. * Outputs: Nothing. * Process: Initialises member variables. * */ fd = -1; lastReadSize = 0; } int Socket::write(char *message, int len) { /* * write. * Inputs: Message string, and lenght. * Outputs: Amount written, or 0 on error. * Process: 1. Writes out 'len' amount of 'message'. * to this socket. * */ if (fd == -1) return 0; int amount = ::write(fd, message, len); #ifdef DEBUG printf("Wrote %i Bytes.\n", amount); #endif return amount; } int Socket::write(char *message) { /* * write(2). * Inputs: Message string. * Outputs: Amount writte, or 0 on error. * Process: Writes out the whole of 'message'. * */ if (fd == -1) return 0; int amount = ::write(fd, message, strlen(message)); #ifdef DEBUG printf("Wrote %i Bytes.\n", amount); #endif return amount; } int Socket::connectTo(char *hostname, unsigned short portnum) { /* * connectTo. * Inputs: Hostname and port. * Outputs: +ve on success, 0 on failure. * Process: 1. Connects this socket to remote 'hostname' on * port 'port'. * */ struct hostent *hp; if ((hp = gethostbyname(hostname)) == NULL) { return 0; } memset(&address,0,sizeof(address)); memcpy((char *)&address.sin_addr,hp->h_addr,hp->h_length); address.sin_family= hp->h_addrtype; address.sin_port= htons((u_short)portnum); if ((fd = socket(hp->h_addrtype,SOCK_STREAM,0)) < 0) return 0; if (connect(fd, (struct sockaddr*)&address, sizeof(address)) < 0) { close(fd); fd = -1; return 0; } return(1); } char* Socket::read() { /* * read. * Inputs: Nothing. * Outputs: char* to static buffer containing data. * Process: 1. Reads as much as possible from this socket, up to * 4k. * */ int amountRead = 0; static char buffer[4096]; amountRead = ::read(fd, &buffer, 4096); if ((amountRead == -1)) buffer[0] = '\0'; buffer[amountRead] = '\0'; #ifdef DEBUG printf("Read %i Bytes.\n", amountRead); #endif /* * Record this just incase we're dealing with binary data with 0's in it. */ lastReadSize = amountRead; return (char *)&buffer; }