ircu2.10.12 pk910 fork
[ircu2.10.12-pk.git] / tools / Bounce / Bounce.cpp
1 /*
2  * IRC - Internet Relay Chat, tools/Bounce/Bounce.cpp
3  * Copyright (C) 1990 Jarkko Oikarinen and
4  *                    University of Oulu, Computing Center
5  *
6  * See file AUTHORS in IRC package for additional names of
7  * the programmers.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 1, or (at your option)
12  * any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  *
23  * Port Bouncer.
24  *
25  * This tool is designed to set up a number of local listening ports, and
26  * then forward any data recived on those ports, to another host/port combo.
27  * Each listening port can bounce to a different host/port defined in the
28  * config file. --Gte 
29  *
30  * $Id: Bounce.cpp,v 1.3 2002/03/07 22:52:57 ghostwolf Exp $ 
31  *
32  */
33
34 #include "Bounce.h"
35  
36 int main() {
37   Bounce* application = new Bounce();
38
39   /*
40    *  Ignore SIGPIPE.
41    */
42
43   struct sigaction act; 
44   act.sa_handler = SIG_IGN;
45   act.sa_flags = 0;
46   sigemptyset(&act.sa_mask);
47   sigaction(SIGPIPE, &act, 0);
48  
49 #ifndef DEBUG
50   /*
51    *  If we aren't debugging, we might as well
52    *  detach from the console.
53    */
54
55   pid_t forkResult = fork() ;
56   if(forkResult < 0)
57   { 
58     printf("Unable to fork new process.\n");
59     return -1 ;
60   } 
61   else if(forkResult != 0)
62   {
63     printf("Successfully Forked, New process ID is %i.\n", forkResult);
64     return 0;
65   } 
66 #endif
67
68   /*
69    *  Create new application object, bind listeners and begin
70    *  polling them.
71    */
72   application->bindListeners();
73
74   while (1) {
75     application->checkSockets();
76   } 
77 }
78
79 /*
80  ****************************************
81  *                                      *
82  *     Bounce class implementation.     *
83  *                                      *
84  ****************************************
85  */
86  
87 void Bounce::bindListeners() { 
88 /*
89  *  bindListeners.
90  *  Inputs: Nothing.
91  *  Outputs: Nothing.
92  *  Process: 1. Reads the config file, and..
93  *           2. Creates a new listener for each 'P' line found.
94  *
95  */
96
97   FILE* configFd;
98   char tempBuf[256];
99   int localPort = 0;
100   int remotePort = 0;
101   char* remoteServer;
102   char* vHost; 
103  
104   /*
105    *  Open config File.
106    */
107   
108   if(!(configFd = fopen("bounce.conf", "r")))
109   {
110     printf("Error, unable to open config file!\n");
111     exit(0);
112   } 
113
114   while (fgets(tempBuf, 256, configFd) != NULL) { 
115     if((tempBuf[0] != '#') && (tempBuf[0] != '\r')) {
116     switch(tempBuf[0])
117     {
118       case 'P': { /* Add new port listener */ 
119         strtok(tempBuf, ":");
120         vHost = strtok(NULL, ":");
121         localPort = atoi(strtok(NULL, ":"));
122         remoteServer = strtok(NULL, ":");
123         remotePort = atoi(strtok(NULL, ":")); 
124
125         Listener* newListener = new Listener();
126         strcpy(newListener->myVhost, vHost); 
127         strcpy(newListener->remoteServer, remoteServer);
128         newListener->remotePort = remotePort;
129         newListener->localPort = localPort;
130 #ifdef DEBUG
131         printf("Adding new Listener: Local: %s:%i, Remote: %s:%i\n", vHost, localPort, remoteServer, remotePort);
132 #endif
133
134         newListener->beginListening();
135         listenerList.insert(listenerList.begin(), newListener); 
136         break;
137       }
138     }
139     } 
140   } 
141 }
142
143 void Bounce::checkSockets() { 
144 /*
145  *  checkSockets.
146  *  Inputs: Nothing.
147  *  Outputs: Nothing.
148  *  Process: 1. Builds up a FD_SET of all sockets we wish to check.
149  *              (Including all listeners & all open connections).
150  *           2. SELECT(2) the set, and forward/accept as needed.
151  *
152  */ 
153   typedef std::list<Listener*> listenerContainer;
154   typedef listenerContainer::iterator listIter;
155
156   typedef std::list<Connection*> connectionContainer;
157   typedef connectionContainer::iterator connIter; 
158
159   struct timeval tv;
160   fd_set readfds; 
161   tv.tv_sec = 0;
162   tv.tv_usec = 1000;
163   int tempFd = 0;
164   int tempFd2 = 0;
165   int highestFd = 0;
166   int delCheck = 0;
167   char* tempBuf;
168
169   FD_ZERO(&readfds);
170  
171   /*
172    *  Add all Listeners to the set.
173    */
174
175   listIter a = listenerList.begin();
176   while(a != listenerList.end())
177   { 
178     tempFd = (*a)->fd; 
179     FD_SET(tempFd, &readfds);
180     if (highestFd < tempFd) highestFd = tempFd;
181     a++;
182   }
183
184   /*
185    *  Add Local & Remote connections from each
186    *  connection object to the set.
187    */
188
189   connIter b = connectionsList.begin();
190   while(b != connectionsList.end())
191   { 
192     tempFd = (*b)->localSocket->fd;
193     tempFd2 = (*b)->remoteSocket->fd;
194     FD_SET(tempFd, &readfds);
195     if (highestFd < tempFd) highestFd = tempFd;
196     FD_SET(tempFd2, &readfds);
197     if (highestFd < tempFd2) highestFd = tempFd2;
198     b++;
199   }
200
201   select(highestFd+1, &readfds, NULL, NULL, &tv); 
202
203   /*
204    *  Check all connections for readability.
205    *  First check Local FD's.
206    *  If the connection is closed on either side,
207    *  shutdown both sockets, and clean up.
208    *  Otherwise, send the data from local->remote, or
209    *  remote->local.
210    */
211
212   b = connectionsList.begin();
213   while(b != connectionsList.end())
214   { 
215     tempFd = (*b)->localSocket->fd;
216  
217     if (FD_ISSET(tempFd, &readfds))
218     { 
219       tempBuf = (*b)->localSocket->read();
220       if ((tempBuf[0] == 0)) // Connection closed.
221       {
222         close((*b)->localSocket->fd);
223         close((*b)->remoteSocket->fd); 
224 #ifdef DEBUG
225         printf("Closing FD: %i\n", (*b)->localSocket->fd);
226         printf("Closing FD: %i\n", (*b)->remoteSocket->fd); 
227 #endif
228         delete(*b);
229         delCheck = 1;
230         b = connectionsList.erase(b); 
231       } else {
232         (*b)->remoteSocket->write(tempBuf, (*b)->localSocket->lastReadSize); 
233       }
234     } 
235  
236   if (!delCheck) b++;
237   delCheck = 0;
238   } 
239
240   /*
241    *  Now check Remote FD's..
242    */
243   b = connectionsList.begin();
244   while(b != connectionsList.end())
245   { 
246     tempFd = (*b)->remoteSocket->fd;
247     if (FD_ISSET(tempFd, &readfds))
248     {
249       tempBuf = (*b)->remoteSocket->read();
250       if ((tempBuf[0] == 0)) // Connection closed.
251       {
252         close((*b)->localSocket->fd);
253         close((*b)->remoteSocket->fd); 
254 #ifdef DEBUG
255         printf("Closing FD: %i\n", (*b)->localSocket->fd);
256         printf("Closing FD: %i\n", (*b)->remoteSocket->fd);
257 #endif
258         delete(*b);
259         delCheck = 1;
260         b = connectionsList.erase(b); 
261       } else {
262         (*b)->localSocket->write(tempBuf, (*b)->remoteSocket->lastReadSize);
263       }
264     }
265   if (!delCheck) b++;
266   delCheck = 0;
267   } 
268  
269   /*
270    *  Check all listeners for new connections.
271    */
272
273   a = listenerList.begin();
274   while(a != listenerList.end())
275   { 
276     tempFd = (*a)->fd; 
277     if (FD_ISSET(tempFd, &readfds))
278     { 
279       recieveNewConnection(*a);
280     }
281     a++;
282   } 
283
284 }
285
286 void Bounce::recieveNewConnection(Listener* listener) {
287 /*
288  *  recieveNewConnection.
289  *  Inputs: A Listener Object.
290  *  Outputs: Nothing.
291  *  Process: 1. Recieves a new connection on a local port,
292  *              and creates a connection object for it.
293  *           2. Accepts the incomming connection.
294  *           3. Creates a new Socket object for the remote
295  *              end of the connection.
296  *           4. Connects up the remote Socket.
297  *           5. Adds the new Connection object to the
298  *              connections list.
299  *
300  */
301
302   Connection* newConnection = new Connection(); 
303   newConnection->localSocket = listener->handleAccept();
304
305   Socket* remoteSocket = new Socket();
306   newConnection->remoteSocket = remoteSocket; 
307   if(remoteSocket->connectTo(listener->remoteServer, listener->remotePort)) { 
308     connectionsList.insert(connectionsList.begin(), newConnection);
309   } else {
310 #ifdef DEBUG
311     newConnection->localSocket->write("Unable to connect to remote host.\n");
312 #endif
313     close(newConnection->localSocket->fd);
314     delete(newConnection);
315     delete(remoteSocket);
316   } 
317 }
318  
319
320 /*
321  ****************************************
322  *                                      *
323  *    Listener class implementation.    *
324  *                                      *
325  ****************************************
326  */
327
328  
329 Socket* Listener::handleAccept() {
330 /*
331  *  handleAccept.
332  *  Inputs: Nothing.
333  *  Outputs: A Socket Object.
334  *  Process: 1. Accept's an incomming connection,
335  *              and returns a new socket object. 
336  */
337
338   int new_fd = 0;
339   int sin_size = sizeof(struct sockaddr_in);
340
341   Socket* newSocket = new Socket();
342   new_fd = accept(fd, (struct sockaddr*)&newSocket->address, (socklen_t*)&sin_size);
343   newSocket->fd = new_fd; 
344   return newSocket;
345 }
346  
347 void Listener::beginListening() {
348 /*
349  *  beginListening.
350  *  Inputs: Nothing.
351  *  Outputs: Nothing.
352  *  Process: 1. Binds the local ports for all the
353  *              Listener objects.
354  *
355  */
356
357   struct sockaddr_in my_addr;
358   int bindRes;
359   int optval;
360   optval = 1;
361
362   fd = socket(AF_INET, SOCK_STREAM, 0); /* Check for no FD's left?! */
363
364   my_addr.sin_family = AF_INET;
365   my_addr.sin_port = htons(localPort);
366   my_addr.sin_addr.s_addr = inet_addr(myVhost);
367   bzero(&(my_addr.sin_zero), 8);
368
369   setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
370
371   bindRes = bind(fd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr));
372   if(bindRes == 0)
373   {
374     listen(fd, 10);
375   } else { 
376      /*
377       *  If we can't bind a listening port, we might aswell drop out.
378       */
379      printf("Unable to bind to %s:%i!\n", myVhost, localPort);
380      exit(0);
381    } 
382 }
383
384 /*
385  ****************************************
386  *                                      *
387  *     Socket class implementation.     *
388  *                                      *
389  ****************************************
390  */
391
392
393 Socket::Socket() {
394 /*
395  *  Socket Constructor.
396  *  Inputs: Nothing.
397  *  Outputs: Nothing.
398  *  Process: Initialises member variables.
399  *
400  */
401
402   fd = -1;
403   lastReadSize = 0;
404 }
405
406 int Socket::write(char *message, int len) { 
407 /*
408  *  write.
409  *  Inputs: Message string, and lenght.
410  *  Outputs: Amount written, or 0 on error.
411  *  Process: 1. Writes out 'len' amount of 'message'.
412  *              to this socket.
413  *
414  */
415
416    if (fd == -1) return 0; 
417  
418    int amount = ::write(fd, message, len); 
419 #ifdef DEBUG
420    printf("Wrote %i Bytes.\n", amount);
421 #endif
422    return amount; 
423 }
424
425 int Socket::write(char *message) { 
426 /*
427  *  write(2).
428  *  Inputs: Message string.
429  *  Outputs: Amount writte, or 0 on error.
430  *  Process: Writes out the whole of 'message'.
431  *
432  */
433
434    if (fd == -1) return 0; 
435  
436    int amount = ::write(fd, message, strlen(message)); 
437 #ifdef DEBUG
438    printf("Wrote %i Bytes.\n", amount);
439 #endif
440    return amount; 
441 }
442
443
444 int Socket::connectTo(char *hostname, unsigned short portnum) { 
445 /*
446  *  connectTo.
447  *  Inputs: Hostname and port.
448  *  Outputs: +ve on success, 0 on failure.
449  *  Process: 1. Connects this socket to remote 'hostname' on
450  *              port 'port'.
451  *
452  */
453
454   struct hostent     *hp;
455  
456   if ((hp = gethostbyname(hostname)) == NULL) { 
457      return 0; 
458   }          
459
460   memset(&address,0,sizeof(address));
461   memcpy((char *)&address.sin_addr,hp->h_addr,hp->h_length);
462   address.sin_family= hp->h_addrtype;
463   address.sin_port= htons((u_short)portnum);
464
465   if ((fd = socket(hp->h_addrtype,SOCK_STREAM,0)) < 0)
466     return 0; 
467  
468   if (connect(fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
469     close(fd);
470     fd = -1; 
471     return 0;
472   } 
473   return(1);
474 }
475
476 char* Socket::read() { 
477 /*
478  *  read.
479  *  Inputs: Nothing.
480  *  Outputs: char* to static buffer containing data.
481  *  Process: 1. Reads as much as possible from this socket, up to
482  *              4k.
483  *
484  */
485
486   int amountRead = 0;
487   static char buffer[4096];
488
489   amountRead = ::read(fd, &buffer, 4096);
490
491   if ((amountRead == -1)) buffer[0] = '\0';
492   buffer[amountRead] = '\0';
493
494 #ifdef DEBUG
495   printf("Read %i Bytes.\n", amountRead);
496 #endif
497  
498   /* 
499    * Record this just incase we're dealing with binary data with 0's in it.
500    */
501   lastReadSize = amountRead;
502   return (char *)&buffer;
503 }
504