1 /*
2  * tcpreen.cpp - TCP connection reverse engineering tool.
3  * $Id: tcpreen.cpp 195 2006-03-25 10:50:36Z remi $
4  */
5 
6 /***********************************************************************
7  *  Copyright (C) 2002-2005 Remi Denis-Courmont.                       *
8  *  This program is free software; you can redistribute and/or modify  *
9  *  it under the terms of the GNU General Public License as published  *
10  *  by the Free Software Foundation; version 2 of the license.         *
11  *                                                                     *
12  *  This program is distributed in the hope that it will be useful,    *
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of     *
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               *
15  *  See the GNU General Public License for more details.               *
16  *                                                                     *
17  *  You should have received a copy of the GNU General Public License  *
18  *  along with this program; if not, you can get it from:              *
19  *  http://www.gnu.org/copyleft/gpl.html                               *
20  ***********************************************************************/
21 
22 #ifdef HAVE_CONFIG_H
23 # include <config.h>
24 #endif
25 
26 #include <stdio.h>
27 #include <stdlib.h> // exit()
28 #include <string.h> // strerror(), strsignal()
29 #include <stdarg.h>
30 #include <signal.h> // signal()
31 #include <limits.h> // INT_MAX
32 
33 #include <sys/types.h> // uid_t, pid_t
34 #include <sys/time.h>
35 #include <unistd.h> // close(), fork(), sleep()
36 #if HAVE_SYS_WAIT_H
37 # include <sys/wait.h> // wait()
38 #endif
39 #if HAVE_SYS_SOCKET_H
40 # include <sys/socket.h>
41 #endif
42 #include <errno.h> // errno, EINTR
43 #include <fcntl.h> // fcntl()
44 #if HAVE_SYSLOG_H
45 # include <syslog.h> // FIXME: move
46 #endif
47 #include "gettext.h"
48 
49 #include "tcpreen.h"
50 #include "bridge.h"
51 #include "log.h"
52 #include "libsolve/sockprot.h"
53 
54 static int mode;
55 static uid_t user;
56 static pid_t mainpid;
57 static int niflags;
58 
59 static void
bridge_printf(int level,const char * fmt,...)60 bridge_printf (int level, const char *fmt, ...)
61 {
62 #if HAVE_VSYSLOG
63 	if (mode & tcpreen_daemon)
64 	{
65 		va_list ap;
66 
67 		va_start (ap, fmt);
68 		vsyslog (level, fmt, ap);
69 		va_end (ap);
70 	}
71 #endif
72 
73 	if (mode & tcpreen_verbose)
74 	{
75 		va_list ap;
76 
77 		va_start (ap, fmt);
78 		vfprintf (stderr, fmt, ap);
79 		va_end (ap);
80 	}
81 }
82 
83 
84 static void
bridge_perror(const char * str,int level=LOG_ERR)85 bridge_perror (const char *str, int level = LOG_ERR)
86 {
87 	bridge_printf (level, "%s: %s\n", str, strerror (errno));
88 }
89 
90 
91 static inline void
bridge_fatal(void)92 bridge_fatal (void)
93 {
94 	bridge_perror (_("Fatal error"), LOG_CRIT);
95 }
96 
97 
98 static void
bridge_hosterror(const SocketAddress & host,int l=LOG_ERR)99 bridge_hosterror (const SocketAddress& host, int l = LOG_ERR)
100 {
101 	if (*(host.Node ()))
102 		bridge_printf (l, _("%s port %s: %s\n"), host.Node (),
103 				host.Service (), host.StrError ());
104 	else
105 		bridge_printf (l, _("port %s: %s\n"), host.Service (),
106 				host.StrError ());
107 }
108 
109 
110 /*
111  * Accepts a socket passive connection.
112  */
113 static int
bridge_accept(int listenfd)114 bridge_accept (int listenfd)
115 {
116 	int fd = accept (listenfd, NULL, 0);
117 	if (fd != -1)
118 	{
119 		const int val = 1;
120 
121 		setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof (val));
122 #ifdef O_NONBLOCK
123 		int flags = fcntl (fd, F_GETFL);
124 		fcntl (fd, F_SETFL, flags | O_NONBLOCK);
125 #endif
126 	}
127 
128 	return fd;
129 }
130 
131 
132 static int
bridge_listen(const SocketAddress & iface)133 bridge_listen (const SocketAddress& iface)
134 {
135 	int fd;
136 	SocketAddress real = iface;
137 
138 	seteuid (0);
139 	fd = real.Bind ();
140 	seteuid (user);
141 
142 	if (fd == -1)
143 	{
144 		bridge_hosterror (real);
145 		return -1;
146 	}
147 
148 	if (real.GetSockName (fd, niflags)) // very strange error
149 	{
150 		bridge_hosterror (real);
151 		close (fd);
152 		return -1;
153 	}
154 
155 	if (listen (fd, INT_MAX) == 0)
156 	{
157 		bridge_printf (LOG_NOTICE, _("Listening on: %s port %s\n"),
158 				real.Node (), real.Service ());
159 		return fd;
160 	}
161 
162 	real.SetError ();
163 	close (fd);
164 	bridge_hosterror (real);
165 	return -1;
166 }
167 
168 
169 /*
170  * SIGNAL HANDLING
171  */
172 static void
bridge_child_signal_handler(int signum)173 bridge_child_signal_handler (int signum)
174 {
175 	/* This is not like SIG_IGN: it still interrupts select() & sleep()
176 	 * which is what matters */
177 	signal (signum, bridge_child_signal_handler);
178 }
179 
180 static volatile int bridge_signaled = 0;
181 
182 static void
bridge_parent_signal_handler(int signum)183 bridge_parent_signal_handler (int signum)
184 {
185 	if (!bridge_signaled)
186 		bridge_signaled = signum;
187 
188 	if (getpid () == mainpid)
189 		signal (signum, bridge_parent_signal_handler);
190 }
191 
192 static void
bridge_wait(int & numclients)193 bridge_wait (int& numclients)
194 {
195 #ifndef WINSOCK
196 	sleep (30000);
197 
198 	if (bridge_signaled)
199 		return;
200 
201 	int val;
202 	pid_t child = wait (&val);
203 
204 	if (child != (pid_t)(-1))
205 	{
206 		if (WIFEXITED (val))
207 		{
208 			val = WEXITSTATUS (val);
209 			if (val)
210 				bridge_printf (LOG_WARNING, _(
211 					"Child %d returned an error (%d)\n"),
212 						(int)child, val);
213 		}
214 		else
215 		if (WIFSIGNALED (val))
216 		{
217 			val = WTERMSIG (val);
218 
219 			bridge_printf (LOG_WARNING, _(
220 					"Child %d killed by signal %d (%s)\n"),
221 					(int)child, val,
222 #if HAVE_STRSIGNAL
223 					strsignal(val)
224 #else
225 					""
226 #endif
227 					);
228 		}
229 		numclients--;
230 	}
231 #endif
232 }
233 
234 
235 
236 /*
237  * Establishes a bridge between a TCP listener and a TCP active
238  * connection.
239  * The listening socket is dropped as soon as the first incoming
240  * connection is established.
241  *
242  * Returns 0 on success, true on error.
243  */
244 #ifdef WINSOCK
245 # define signal( num, hd ) (0)
246 # define fork( ) (0)
247 # define exit( val ) continue
248 # define kill( pid, sig ) (0)
249 # define SIGHUP SIGBREAK
250 #endif
251 int
bridge_main(const struct bridgeconf * conf)252 bridge_main (const struct bridgeconf *conf)
253 {
254 	/* explicitly bufferized settings */
255 	mode = conf->mode; // static global variable
256 	user = conf->user;
257 	mainpid = getpid ();
258 	niflags = (mode & tcpreen_numeric) ? NI_NUMERICHOST|NI_NUMERICSERV : 0;
259 
260 	// Socket addresses
261 	SocketAddress svrif, cltif;
262 	if (svrif.SetByName (conf->servername, conf->serverservice,
263 			(mode & tcpreen_listen_server) ? AI_PASSIVE : 0,
264 			conf->serveraf, SOCK_STREAM))
265 	{
266 		bridge_hosterror (svrif);
267 		return -1;
268 	}
269 
270 	if (cltif.SetByName (conf->bridgename, conf->bridgeservice,
271 			(mode & tcpreen_listen_client) ? AI_PASSIVE : 0,
272 			conf->bridgeaf, SOCK_STREAM))
273 	{
274 		bridge_hosterror (cltif);
275 		return -1;
276 	}
277 
278 	// Captures deadly signal
279 	// (done after name resolution, which is blocking)
280 	signal (SIGHUP, bridge_parent_signal_handler);
281 	signal (SIGINT, bridge_parent_signal_handler);
282 	signal (SIGTERM, bridge_parent_signal_handler);
283 #ifndef WINSOCK
284 	signal (SIGQUIT, bridge_parent_signal_handler);
285 	signal (SIGUSR1, SIG_IGN);
286 	signal (SIGUSR2, SIG_IGN);
287 	signal (SIGCHLD, bridge_child_signal_handler);
288 #endif
289 
290 
291 	// Things that must not be goto-bypassed
292 	int in = -1, out = -1, numclients = 0, retval = -1,
293 		maxclients = conf->maxclients;
294 	long countdown = conf->totalclients;
295 
296 	// Sets up server socket(s)
297 	if (mode & tcpreen_listen_client)
298 	{
299 		in = bridge_listen (cltif);
300 		if (in == -1)
301 			goto bridge_abort;
302 	}
303 
304 	if (mode & tcpreen_listen_server)
305 	{
306 		out = bridge_listen (svrif);
307 		if (out == -1)
308 			goto bridge_abort;
309 	}
310 
311 	/* Definitely drops priviledges */
312 	if (seteuid (user) || setuid (user))
313 	{
314 		bridge_perror (_("UID setting"), LOG_CRIT);
315 		goto bridge_abort;
316 	}
317 
318 #if HAVE_DAEMON
319 	// Goes in the background
320 	if ((mode & tcpreen_daemon) && daemon (0, 0))
321 	{
322 		bridge_perror (_("Background mode"), LOG_CRIT);
323 		goto bridge_abort;
324 	}
325 #endif
326 
327 	/*
328 	 * SERVER LOOP
329 	 */
330 	retval = 0;
331 
332 	while ((!bridge_signaled) && countdown)
333 	{
334 		/*
335 		 * Simple pre-forking server model: there are always a fixed
336 		 * predefined number of processes running. This will not work
337 		 * fine with a broken OS.
338 		 */
339 		if (numclients >= maxclients)
340 		{
341 			bridge_wait (numclients);
342 			continue;
343 		}
344 
345 		if (countdown > 0)
346 			countdown--; /* one connection attempt */
347 
348 		pid_t pid = fork ();
349 		if (pid == (pid_t)(-1))
350 		{
351 			bridge_perror (_("Process creation"));
352 			sleep (5); // prevent CPU usage from bursting
353 			continue;
354 		}
355 		else
356 		if (pid != 0)
357 		{
358 			numclients++;
359 			continue; // back to server loop
360 		}
361 
362 		/*
363 		 * CHILD PROCESS
364 		 */
365 		// some blocking calls follow, so die upon signal
366 		signal (SIGHUP, SIG_DFL);
367 		signal (SIGINT, SIG_DFL);
368 		signal (SIGTERM, SIG_DFL);
369 		signal (SIGQUIT, SIG_DFL);
370 		signal (SIGUSR1, SIG_IGN);
371 		signal (SIGUSR2, SIG_IGN);
372 		if (bridge_signaled)
373 			exit (EXIT_SUCCESS);
374 
375 		int fd[2];
376 		/* settinp up client socket... */
377 
378 		if (in != -1)
379 		{
380 			fd[0] = bridge_accept (in);
381 #ifndef WINSOCK
382 			close (in);
383 #endif
384 		}
385 		else
386 			fd[0] = cltif.Connect ();
387 
388 		if (fd[0] == -1)
389 		{
390 			#ifdef EINTR
391 			if (errno == EINTR)
392 				exit (EXIT_SUCCESS);
393 			#endif
394 			bridge_hosterror (cltif);
395 			exit (1);
396 		}
397 
398 		/* Computes client address */
399 		SocketAddress clt;
400 		clt.GetPeerName (fd[0], niflags);
401 
402 		if (!clt)
403 			bridge_hosterror (clt);
404 		if (mode & tcpreen_daemon)
405 			syslog (LOG_INFO, _("Connection from: %s port %s\n"),
406 				clt.Node (), clt.Service ());
407 
408 		/* setting up server socket... */
409 		if (out != -1)
410 		{
411 			fd[1] = bridge_accept (out);
412 #ifndef WINSOCK
413 			close (out);
414 #endif
415 		}
416 		else
417 			fd[1] = svrif.Connect ();
418 
419 		if (fd[1] == -1)
420 		{
421 			#ifdef EINTR
422 			if (errno == EINTR)
423 			{
424 				close (fd[0]);
425 				exit (0);
426 			}
427 			#endif
428 
429 			bridge_hosterror (svrif);
430 			close (fd[0]);
431 			exit (2);
432 		}
433 
434 		/* Computes server address */
435 		SocketAddress svr;
436 		svr.GetPeerName (fd[1], niflags);
437 
438 		if (!svr)
439 			bridge_hosterror (svr);
440 		if (mode & tcpreen_daemon)
441 			syslog (LOG_INFO, _("Connection to: %s port %s\n"),
442 				svr.Node (), svr.Service ());
443 
444 		/* Both sides of the bridge are now properly connected! */
445 		// from then on, do not exit when receiving a signal
446 		signal (SIGHUP, bridge_child_signal_handler);
447 		signal (SIGINT, bridge_child_signal_handler);
448 		signal (SIGTERM, bridge_child_signal_handler);
449 		signal (SIGQUIT, bridge_child_signal_handler);
450 
451 		DataLogList *logs = conf->logsmaker->MakeLogList (clt.Node (),
452 							clt.Service ());
453 		if (logs == NULL)
454 		{
455 			bridge_perror (_("Log file error"));
456 			close (fd[1]);
457 			close (fd[0]);
458 			exit (3);
459 		}
460 
461 		if (!!(*conf->logsmaker))
462 			logs->Connect (svr.Node (), svr.Service(),
463 					clt.Node (), clt.Service ());
464 		retval = monitor_bridge (fd, logs, conf->bytelimit);
465 
466 		close (fd[1]);
467 		close (fd[0]);
468 		delete logs;
469 		exit (retval ? EXIT_FAILURE : EXIT_SUCCESS);
470 		/* END OF CHILD PROCESS */
471 	}
472 
473 	/* Waits for pending clients */
474 	while (numclients && !bridge_signaled)
475 		bridge_wait (numclients);
476 
477 bridge_abort:
478 	if (bridge_signaled)
479 	{
480 		kill (-getpid (), bridge_signaled); // kill children
481 
482 #if HAVE_STRSIGNAL
483 		bridge_printf (LOG_NOTICE, "%s: %s\n", _("Caught signal"),
484 				strsignal (bridge_signaled));
485 #else
486 		bridge_printf (LOG_NOTICE, "%s %d\n", _("Caught signal"),
487 				bridge_signaled);
488 #endif
489 	}
490 
491 	if (mode & tcpreen_listen_client)
492 		cltif.CleanUp ();
493 	if (mode & tcpreen_listen_server)
494 		svrif.CleanUp ();
495 
496 	if (out != -1)
497 		close (out);
498 	if (in != -1)
499 		close (in);
500 
501 	return retval;
502 }
503