]>
Commit | Line | Data |
---|---|---|
189935b1 | 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 |