1 /****************************************************************\
5 * A tiny daemon to allow you to run The Bee as a non-root user *
6 * (without access to /etc/inetd.conf or whatever) *
8 * Copyright 2002-2004 Wilmer van der Gaast <wilmer@gaast.net> *
10 * Licensed under the GNU General Public License *
12 * Modified by M. Dennis, 20040627 *
13 \****************************************************************/
18 2004-06-27: Added support for AF_LOCAL (UNIX domain) sockets
19 Renamed log to do_log to fix conflict warning
20 Changed protocol to 0 (6 is not supported?)
21 Added error check for socket()
22 Added a no-fork (debug) mode
23 2004-05-15: Added rate limiting
24 2003-12-26: Added the SO_REUSEADDR sockopt, logging and CPU-time limiting
25 for clients using setrlimit(), fixed the execv() call
26 2002-11-29: Added the timeout so old child processes clean up faster
27 2002-11-28: First version
30 #define SELECT_TIMEOUT 2
31 #define MAX_LOG_LEN 128
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <sys/resource.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
52 typedef struct settings
59 unsigned char max_conn
;
69 typedef struct ipstats
83 settings_t
*set_load( int argc
, char *argv
[] );
84 void do_log( char *fmt
, ... );
85 ipstats_t
*ip_get( char *ip_txt
);
87 int main( int argc
, char *argv
[] )
89 const int rebind_on
= 1;
92 int serv_fd
, serv_len
;
93 struct sockaddr_in serv_addr
;
94 struct sockaddr_un local_addr
;
98 if( !( set
= set_load( argc
, argv
) ) )
102 if( !( logfile
= fopen( "/dev/null", "w" ) ) )
108 fcntl( fileno( logfile
), F_SETFD
, FD_CLOEXEC
);
111 serv_fd
= socket( PF_LOCAL
, SOCK_STREAM
, 0 );
113 serv_fd
= socket( PF_INET
, SOCK_STREAM
, 0 );
119 setsockopt( serv_fd
, SOL_SOCKET
, SO_REUSEADDR
, &rebind_on
, sizeof( rebind_on
) );
120 fcntl( serv_fd
, F_SETFD
, FD_CLOEXEC
);
122 local_addr
.sun_family
= AF_LOCAL
;
123 strncpy( local_addr
.sun_path
, set
->interface
, sizeof( local_addr
.sun_path
) - 1 );
124 local_addr
.sun_path
[sizeof( local_addr
.sun_path
) - 1] = '\0';
126 /* warning - don't let untrusted users run this program if it
127 is setuid/setgid! Arbitrary file deletion risk! */
128 unlink( set
->interface
);
129 if( bind( serv_fd
, (struct sockaddr
*) &local_addr
, SUN_LEN( &local_addr
) ) != 0 )
134 chmod( set
->interface
, S_IRWXO
|S_IRWXG
|S_IRWXU
);
137 serv_addr
.sin_family
= AF_INET
;
138 serv_addr
.sin_addr
.s_addr
= inet_addr( set
->interface
);
139 serv_addr
.sin_port
= htons( set
->port
);
140 serv_len
= sizeof( serv_addr
);
142 if( bind( serv_fd
, (struct sockaddr
*) &serv_addr
, serv_len
) != 0 )
149 if( listen( serv_fd
, set
->max_conn
) != 0 )
155 if ( ! set
->debug
) {
173 do_log( "bitlbeed running" );
178 int cli_fd
, cli_len
, i
, st
;
179 struct sockaddr_in cli_addr
;
180 struct sockaddr_un cli_local
;
185 static int running
= 0;
190 /* accept() only returns after someone connects. To clean up old
191 processes (by running waitpid()) it's better to use select()
194 FD_SET( serv_fd
, &rd
);
195 tm
.tv_sec
= SELECT_TIMEOUT
;
197 if( select( serv_fd
+ 1, &rd
, NULL
, NULL
, &tm
) > 0 )
200 cli_len
= SUN_LEN( &cli_local
);
201 cli_fd
= accept( serv_fd
, (struct sockaddr
*) &cli_local
, &cli_len
);
202 cli_txt
= "127.0.0.1";
204 cli_len
= sizeof( cli_addr
);
205 cli_fd
= accept( serv_fd
, (struct sockaddr
*) &cli_addr
, &cli_len
);
206 cli_txt
= inet_ntoa( cli_addr
.sin_addr
);
209 ip
= ip_get( cli_txt
);
211 if( set
->rate_times
== 0 || time( NULL
) > ip
->rate_ignore
)
213 /* We want this socket on stdout and stderr too! */
214 dup( cli_fd
); dup( cli_fd
);
216 if( ( child
= fork() ) == 0 )
222 li
.rlim_cur
= (rlim_t
) set
->seconds
;
223 li
.rlim_max
= (rlim_t
) set
->seconds
+ 1;
224 setrlimit( RLIMIT_CPU
, &li
);
226 execv( set
->call
[0], set
->call
);
227 do_log( "Error while executing %s!", set
->call
[0] );
236 do_log( "Started child process for client %s (PID=%d), got %d clients now", cli_txt
, child
, running
);
238 if( time( NULL
) < ( ip
->rate_start
+ set
->rate_seconds
) )
241 if( ip
->rate_times
>= set
->rate_times
)
243 do_log( "Client %s crossed the limit; ignoring for the next %d seconds", cli_txt
, set
->rate_ignore
);
244 ip
->rate_ignore
= time( NULL
) + set
->rate_ignore
;
250 ip
->rate_start
= time( NULL
);
256 do_log( "Ignoring connection from %s", cli_txt
);
261 /* If the max. number of connection is reached, don't accept
262 new connections until one expires -> Not always WNOHANG
264 Cleaning up child processes is a good idea anyway... :-) */
265 while( ( i
= waitpid( 0, &st
, ( ( running
< set
->max_conn
) || ( set
->max_conn
== 0 ) ) ? WNOHANG
: 0 ) ) > 0 )
268 if( WIFEXITED( st
) )
270 do_log( "Child process (PID=%d) exited normally with status %d. %d Clients left now",
271 i
, WEXITSTATUS( st
), running
);
273 else if( WIFSIGNALED( st
) )
275 do_log( "Child process (PID=%d) killed by signal %d. %d Clients left now",
276 i
, WTERMSIG( st
), running
);
280 /* Should not happen AFAIK... */
281 do_log( "Child process (PID=%d) stopped for unknown reason, %d clients left now",
290 settings_t
*set_load( int argc
, char *argv
[] )
295 set
= malloc( sizeof( settings_t
) );
296 memset( set
, 0, sizeof( settings_t
) );
297 set
->interface
= NULL
; /* will be filled in later */
302 set
->rate_seconds
= 600;
304 set
->rate_ignore
= 900;
306 while( ( opt
= getopt( argc
, argv
, "i:p:n:t:l:r:hud" ) ) >= 0 )
310 set
->interface
= strdup( optarg
);
312 else if( opt
== 'p' )
314 if( ( sscanf( optarg
, "%d", &i
) != 1 ) || ( i
<= 0 ) || ( i
> 65535 ) )
316 fprintf( stderr
, "Invalid port number: %s\n", optarg
);
321 else if( opt
== 'n' )
323 if( ( sscanf( optarg
, "%d", &i
) != 1 ) || ( i
< 0 ) )
325 fprintf( stderr
, "Invalid number of connections: %s\n", optarg
);
330 else if( opt
== 't' )
332 if( ( sscanf( optarg
, "%d", &i
) != 1 ) || ( i
< 0 ) || ( i
> 600 ) )
334 fprintf( stderr
, "Invalid number of seconds: %s\n", optarg
);
339 else if( opt
== 'l' )
341 if( !( logfile
= fopen( optarg
, "a" ) ) )
344 fprintf( stderr
, "Error opening logfile, giving up.\n" );
347 setbuf( logfile
, NULL
);
349 else if( opt
== 'r' )
351 if( sscanf( optarg
, "%d,%d,%d", &set
->rate_seconds
, &set
->rate_times
, &set
->rate_ignore
) != 3 )
353 fprintf( stderr
, "Invalid argument to -r.\n" );
357 else if( opt
== 'u' )
359 else if( opt
== 'd' )
361 else if( opt
== 'h' )
363 printf( "Usage: %s [-i <interface>] [-p <port>] [-n <num>] [-r x,y,z] ...\n"
364 " ... <command> <args...>\n"
365 "A simple inetd-like daemon to have a program listening on a TCP socket without\n"
366 "needing root access to the machine\n"
368 " -i Specify the interface (by IP address) to listen on.\n"
369 " (Default: 0.0.0.0 (any interface))\n"
370 " -p Port number to listen on. (Default: 6667)\n"
371 " -n Maximum number of connections. (Default: 0 (unlimited))\n"
372 " -t Specify the maximum number of CPU seconds per process.\n"
373 " (Default: 0 (unlimited))\n"
374 " -l Specify a logfile. (Default: none)\n"
375 " -r Rate limiting: Ignore a host for z seconds when it connects for more\n"
376 " than y times in x seconds. (Default: 600,5,900. Disable: 0,0,0)\n"
377 " -u Use a local socket, by default /tmp/bitlbee (override with -i <filename>)\n"
378 " -d Don't fork for listening (for debugging purposes)\n"
379 " -h This information\n", argv
[0] );
384 if( set
->interface
== NULL
)
385 set
->interface
= (set
->local
) ? "/tmp/bitlbee" : "0.0.0.0";
389 fprintf( stderr
, "Missing program parameter!\n" );
393 /* The remaining arguments are the executable and its arguments */
394 set
->call
= malloc( ( argc
- optind
+ 1 ) * sizeof( char* ) );
395 memcpy( set
->call
, argv
+ optind
, sizeof( char* ) * ( argc
- optind
) );
396 set
->call
[argc
-optind
] = NULL
;
401 void do_log( char *fmt
, ... )
404 char line
[MAX_LOG_LEN
];
408 memset( line
, 0, MAX_LOG_LEN
);
411 strcpy( line
, ctime( &tm
) );
415 va_start( params
, fmt
);
416 vsnprintf( line
+ l
, MAX_LOG_LEN
- l
- 2, fmt
, params
);
418 strcat( line
, "\n" );
420 fprintf( logfile
, "%s", line
);
423 ipstats_t
*ip_get( char *ip_txt
)
429 sscanf( ip_txt
, "%d.%d.%d.%d", p
+ 0, p
+ 1, p
+ 2, p
+ 3 );
430 ip
= ( p
[0] << 24 ) | ( p
[1] << 16 ) | ( p
[2] << 8 ) | ( p
[3] );
432 for( l
= ipstats
; l
; l
= l
->next
)
440 for( l
= ipstats
; l
->next
; l
= l
->next
);
442 l
->next
= malloc( sizeof( ipstats_t
) );
447 l
= malloc( sizeof( ipstats_t
) );
450 memset( l
, 0, sizeof( ipstats_t
) );