505 lines
12 KiB
C++
505 lines
12 KiB
C++
/*
|
|
* IRC - Internet Relay Chat, tools/Bounce/Bounce.cpp
|
|
* 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,v 1.3 2002-03-07 22:52:57 ghostwolf Exp $
|
|
*
|
|
*/
|
|
|
|
#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<Listener*> listenerContainer;
|
|
typedef listenerContainer::iterator listIter;
|
|
|
|
typedef std::list<Connection*> 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;
|
|
}
|
|
|