]> jfr.im git - irc/quakenet/snircd.git/blob - tools/Bounce/Bounce.cpp
Initial import of 2.10.12.01
[irc/quakenet/snircd.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