- Timestamp:
- 2015-02-20T22:50:54Z (10 years ago)
- Branches:
- master
- Children:
- 0b9daac, 3d45471, 7733b8c
- Parents:
- af359b4
- git-author:
- Indent <please@…> (19-02-15 05:47:20)
- git-committer:
- dequis <dx@…> (20-02-15 22:50:54)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
utils/bitlbeed.c
raf359b4 r5ebff60 13 13 \****************************************************************/ 14 14 15 /* 15 /* 16 16 ChangeLog: 17 17 18 18 2004-06-27: Added support for AF_LOCAL (UNIX domain) sockets 19 19 Renamed log to do_log to fix conflict warning … … 50 50 #include <arpa/inet.h> 51 51 52 typedef struct settings 53 { 52 typedef struct settings { 54 53 char local; 55 54 char debug; 56 55 char *interface; 57 56 signed int port; 58 57 59 58 unsigned char max_conn; 60 59 int seconds; 61 60 62 61 int rate_seconds; 63 62 int rate_times; 64 63 int rate_ignore; 65 64 66 65 char **call; 67 66 } settings_t; 68 67 69 typedef struct ipstats 70 { 68 typedef struct ipstats { 71 69 unsigned int ip; 72 70 73 71 time_t rate_start; 74 72 int rate_times; 75 73 time_t rate_ignore; 76 74 77 75 struct ipstats *next; 78 76 } ipstats_t; … … 81 79 ipstats_t *ipstats; 82 80 83 settings_t *set_load( int argc, char *argv[]);84 void do_log( char *fmt, ...);85 ipstats_t *ip_get( char *ip_txt);86 87 int main( int argc, char *argv[])81 settings_t *set_load(int argc, char *argv[]); 82 void do_log(char *fmt, ...); 83 ipstats_t *ip_get(char *ip_txt); 84 85 int main(int argc, char *argv[]) 88 86 { 89 87 const int rebind_on = 1; 90 88 settings_t *set; 91 89 92 90 int serv_fd, serv_len; 93 91 struct sockaddr_in serv_addr; 94 92 struct sockaddr_un local_addr; 95 93 96 94 pid_t st; 97 98 if( !( set = set_load( argc, argv ) ) ) 99 return( 1 ); 100 101 if( !logfile ) 102 if( !( logfile = fopen( "/dev/null", "w" ) ) ) 103 { 104 perror( "fopen" ); 105 return( 1 ); 106 } 107 108 fcntl( fileno( logfile ), F_SETFD, FD_CLOEXEC ); 109 110 if( set->local ) 111 serv_fd = socket( PF_LOCAL, SOCK_STREAM, 0 ); 112 else 113 serv_fd = socket( PF_INET, SOCK_STREAM, 0 ); 114 if( serv_fd < 0 ) 115 { 116 perror( "socket" ); 117 return( 1 ); 118 } 119 setsockopt( serv_fd, SOL_SOCKET, SO_REUSEADDR, &rebind_on, sizeof( rebind_on ) ); 120 fcntl( serv_fd, F_SETFD, FD_CLOEXEC ); 95 96 if (!(set = set_load(argc, argv))) { 97 return(1); 98 } 99 100 if (!logfile) { 101 if (!(logfile = fopen("/dev/null", "w"))) { 102 perror("fopen"); 103 return(1); 104 } 105 } 106 107 fcntl(fileno(logfile), F_SETFD, FD_CLOEXEC); 108 109 if (set->local) { 110 serv_fd = socket(PF_LOCAL, SOCK_STREAM, 0); 111 } else { 112 serv_fd = socket(PF_INET, SOCK_STREAM, 0); 113 } 114 if (serv_fd < 0) { 115 perror("socket"); 116 return(1); 117 } 118 setsockopt(serv_fd, SOL_SOCKET, SO_REUSEADDR, &rebind_on, sizeof(rebind_on)); 119 fcntl(serv_fd, F_SETFD, FD_CLOEXEC); 121 120 if (set->local) { 122 121 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';125 122 strncpy(local_addr.sun_path, set->interface, sizeof(local_addr.sun_path) - 1); 123 local_addr.sun_path[sizeof(local_addr.sun_path) - 1] = '\0'; 124 126 125 /* warning - don't let untrusted users run this program if it 127 126 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 ) 130 { 131 perror( "bind" ); 132 return( 1 ); 133 } 134 chmod( set->interface, S_IRWXO|S_IRWXG|S_IRWXU ); 127 unlink(set->interface); 128 if (bind(serv_fd, (struct sockaddr *) &local_addr, SUN_LEN(&local_addr)) != 0) { 129 perror("bind"); 130 return(1); 131 } 132 chmod(set->interface, S_IRWXO | S_IRWXG | S_IRWXU); 135 133 136 134 } else { 137 135 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 ); 141 142 if( bind( serv_fd, (struct sockaddr *) &serv_addr, serv_len ) != 0 ) 143 { 144 perror( "bind" ); 145 return( 1 ); 146 } 147 } 148 149 if( listen( serv_fd, set->max_conn ) != 0 ) 150 { 151 perror( "listen" ); 152 return( 1 ); 153 } 154 155 if ( ! set->debug ) { 136 serv_addr.sin_addr.s_addr = inet_addr(set->interface); 137 serv_addr.sin_port = htons(set->port); 138 serv_len = sizeof(serv_addr); 139 140 if (bind(serv_fd, (struct sockaddr *) &serv_addr, serv_len) != 0) { 141 perror("bind"); 142 return(1); 143 } 144 } 145 146 if (listen(serv_fd, set->max_conn) != 0) { 147 perror("listen"); 148 return(1); 149 } 150 151 if (!set->debug) { 156 152 st = fork(); 157 if( st < 0 ) 158 { 159 perror( "fork" ); 160 return( 1 ); 161 } 162 else if( st > 0 ) 163 { 164 return( 0 ); 165 } 166 153 if (st < 0) { 154 perror("fork"); 155 return(1); 156 } else if (st > 0) { 157 return(0); 158 } 159 167 160 setsid(); 168 close( 0);169 close( 1);170 close( 2);171 } 172 173 do_log( "bitlbeed running");174 161 close(0); 162 close(1); 163 close(2); 164 } 165 166 do_log("bitlbeed running"); 167 175 168 /* The Daemon */ 176 while( 1 ) 177 { 169 while (1) { 178 170 int cli_fd, cli_len, i, st; 179 171 struct sockaddr_in cli_addr; … … 182 174 char *cli_txt; 183 175 pid_t child; 184 176 185 177 static int running = 0; 186 178 187 179 fd_set rd; 188 180 struct timeval tm; 189 181 190 182 /* accept() only returns after someone connects. To clean up old 191 183 processes (by running waitpid()) it's better to use select() 192 184 with a timeout. */ 193 FD_ZERO( &rd);194 FD_SET( serv_fd, &rd);185 FD_ZERO(&rd); 186 FD_SET(serv_fd, &rd); 195 187 tm.tv_sec = SELECT_TIMEOUT; 196 188 tm.tv_usec = 0; 197 if( select( serv_fd + 1, &rd, NULL, NULL, &tm ) > 0 ) 198 { 189 if (select(serv_fd + 1, &rd, NULL, NULL, &tm) > 0) { 199 190 if (set->local) { 200 cli_len = SUN_LEN( &cli_local);201 cli_fd = accept( serv_fd, (struct sockaddr *) &cli_local, &cli_len);191 cli_len = SUN_LEN(&cli_local); 192 cli_fd = accept(serv_fd, (struct sockaddr *) &cli_local, &cli_len); 202 193 cli_txt = "127.0.0.1"; 203 194 } else { 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 ); 207 } 208 209 ip = ip_get( cli_txt ); 210 211 if( set->rate_times == 0 || time( NULL ) > ip->rate_ignore ) 212 { 195 cli_len = sizeof(cli_addr); 196 cli_fd = accept(serv_fd, (struct sockaddr *) &cli_addr, &cli_len); 197 cli_txt = inet_ntoa(cli_addr.sin_addr); 198 } 199 200 ip = ip_get(cli_txt); 201 202 if (set->rate_times == 0 || time(NULL) > ip->rate_ignore) { 213 203 /* We want this socket on stdout and stderr too! */ 214 dup( cli_fd ); dup( cli_fd ); 215 216 if( ( child = fork() ) == 0 ) 217 { 218 if( set->seconds ) 219 { 204 dup(cli_fd); dup(cli_fd); 205 206 if ((child = fork()) == 0) { 207 if (set->seconds) { 220 208 struct rlimit li; 221 209 222 210 li.rlim_cur = (rlim_t) set->seconds; 223 211 li.rlim_max = (rlim_t) set->seconds + 1; 224 setrlimit( RLIMIT_CPU, &li);212 setrlimit(RLIMIT_CPU, &li); 225 213 } 226 execv( set->call[0], set->call);227 do_log( "Error while executing %s!", set->call[0]);228 return( 1);214 execv(set->call[0], set->call); 215 do_log("Error while executing %s!", set->call[0]); 216 return(1); 229 217 } 230 231 running 232 close( 0);233 close( 1);234 close( 2);235 236 do_log( "Started child process for client %s (PID=%d), got %d clients now", cli_txt, child, running );237 238 if( time( NULL ) < ( ip->rate_start + set->rate_seconds ) ) 239 {240 ip->rate_times 241 if ( ip->rate_times >= set->rate_times )242 {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;218 219 running++; 220 close(0); 221 close(1); 222 close(2); 223 224 do_log("Started child process for client %s (PID=%d), got %d clients now", cli_txt, 225 child, running); 226 227 if (time(NULL) < (ip->rate_start + set->rate_seconds)) { 228 ip->rate_times++; 229 if (ip->rate_times >= set->rate_times) { 230 do_log("Client %s crossed the limit; ignoring for the next %d seconds", 231 cli_txt, set->rate_ignore); 232 ip->rate_ignore = time(NULL) + set->rate_ignore; 245 233 ip->rate_start = 0; 246 234 } 247 } 248 else 249 { 250 ip->rate_start = time( NULL ); 235 } else { 236 ip->rate_start = time(NULL); 251 237 ip->rate_times = 1; 252 238 } 253 } 254 else 255 { 256 do_log( "Ignoring connection from %s", cli_txt ); 257 close( cli_fd ); 258 } 259 } 260 239 } else { 240 do_log("Ignoring connection from %s", cli_txt); 241 close(cli_fd); 242 } 243 } 244 261 245 /* If the max. number of connection is reached, don't accept 262 246 new connections until one expires -> Not always WNOHANG 263 247 264 248 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 ) 266 { 267 running --; 268 if( WIFEXITED( st ) ) 269 { 270 do_log( "Child process (PID=%d) exited normally with status %d. %d Clients left now", 271 i, WEXITSTATUS( st ), running ); 272 } 273 else if( WIFSIGNALED( st ) ) 274 { 275 do_log( "Child process (PID=%d) killed by signal %d. %d Clients left now", 276 i, WTERMSIG( st ), running ); 277 } 278 else 279 { 249 while ((i = waitpid(0, &st, ((running < set->max_conn) || (set->max_conn == 0)) ? WNOHANG : 0)) > 0) { 250 running--; 251 if (WIFEXITED(st)) { 252 do_log("Child process (PID=%d) exited normally with status %d. %d Clients left now", 253 i, WEXITSTATUS(st), running); 254 } else if (WIFSIGNALED(st)) { 255 do_log("Child process (PID=%d) killed by signal %d. %d Clients left now", 256 i, WTERMSIG(st), running); 257 } else { 280 258 /* Should not happen AFAIK... */ 281 do_log( 282 i, running);283 } 284 } 285 } 286 287 return( 0);259 do_log("Child process (PID=%d) stopped for unknown reason, %d clients left now", 260 i, running); 261 } 262 } 263 } 264 265 return(0); 288 266 } 289 267 290 settings_t *set_load( int argc, char *argv[])268 settings_t *set_load(int argc, char *argv[]) 291 269 { 292 270 settings_t *set; 293 271 int opt, i; 294 295 set = malloc( sizeof( settings_t ));296 memset( set, 0, sizeof( settings_t ));297 set->interface = NULL; 272 273 set = malloc(sizeof(settings_t)); 274 memset(set, 0, sizeof(settings_t)); 275 set->interface = NULL; /* will be filled in later */ 298 276 set->port = 6667; 299 277 set->local = 0; 300 278 set->debug = 0; 301 279 302 280 set->rate_seconds = 600; 303 281 set->rate_times = 5; 304 282 set->rate_ignore = 900; 305 306 while( ( opt = getopt( argc, argv, "i:p:n:t:l:r:hud" ) ) >= 0 ) 307 { 308 if( opt == 'i' ) 309 { 310 set->interface = strdup( optarg ); 311 } 312 else if( opt == 'p' ) 313 { 314 if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i <= 0 ) || ( i > 65535 ) ) 315 { 316 fprintf( stderr, "Invalid port number: %s\n", optarg ); 317 return( NULL ); 283 284 while ((opt = getopt(argc, argv, "i:p:n:t:l:r:hud")) >= 0) { 285 if (opt == 'i') { 286 set->interface = strdup(optarg); 287 } else if (opt == 'p') { 288 if ((sscanf(optarg, "%d", &i) != 1) || (i <= 0) || (i > 65535)) { 289 fprintf(stderr, "Invalid port number: %s\n", optarg); 290 return(NULL); 318 291 } 319 292 set->port = i; 320 } 321 else if( opt == 'n' ) 322 { 323 if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i < 0 ) ) 324 { 325 fprintf( stderr, "Invalid number of connections: %s\n", optarg ); 326 return( NULL ); 293 } else if (opt == 'n') { 294 if ((sscanf(optarg, "%d", &i) != 1) || (i < 0)) { 295 fprintf(stderr, "Invalid number of connections: %s\n", optarg); 296 return(NULL); 327 297 } 328 298 set->max_conn = i; 329 } 330 else if( opt == 't' ) 331 { 332 if( ( sscanf( optarg, "%d", &i ) != 1 ) || ( i < 0 ) || ( i > 600 ) ) 333 { 334 fprintf( stderr, "Invalid number of seconds: %s\n", optarg ); 335 return( NULL ); 299 } else if (opt == 't') { 300 if ((sscanf(optarg, "%d", &i) != 1) || (i < 0) || (i > 600)) { 301 fprintf(stderr, "Invalid number of seconds: %s\n", optarg); 302 return(NULL); 336 303 } 337 304 set->seconds = i; 338 } 339 else if( opt == 'l' ) 340 { 341 if( !( logfile = fopen( optarg, "a" ) ) ) 342 { 343 perror( "fopen" ); 344 fprintf( stderr, "Error opening logfile, giving up.\n" ); 345 return( NULL ); 346 } 347 setbuf( logfile, NULL ); 348 } 349 else if( opt == 'r' ) 350 { 351 if( sscanf( optarg, "%d,%d,%d", &set->rate_seconds, &set->rate_times, &set->rate_ignore ) != 3 ) 352 { 353 fprintf( stderr, "Invalid argument to -r.\n" ); 354 return( NULL ); 355 } 356 } 357 else if( opt == 'u' ) 305 } else if (opt == 'l') { 306 if (!(logfile = fopen(optarg, "a"))) { 307 perror("fopen"); 308 fprintf(stderr, "Error opening logfile, giving up.\n"); 309 return(NULL); 310 } 311 setbuf(logfile, NULL); 312 } else if (opt == 'r') { 313 if (sscanf(optarg, "%d,%d,%d", &set->rate_seconds, &set->rate_times, &set->rate_ignore) != 3) { 314 fprintf(stderr, "Invalid argument to -r.\n"); 315 return(NULL); 316 } 317 } else if (opt == 'u') { 358 318 set->local = 1; 359 else if( opt == 'd' )319 } else if (opt == 'd') { 360 320 set->debug = 1; 361 else if( opt == 'h' ) 362 { 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" 367 "\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] ); 380 return( NULL ); 381 } 382 } 383 384 if( set->interface == NULL ) 321 } else if (opt == 'h') { 322 printf("Usage: %s [-i <interface>] [-p <port>] [-n <num>] [-r x,y,z] ...\n" 323 " ... <command> <args...>\n" 324 "A simple inetd-like daemon to have a program listening on a TCP socket without\n" 325 "needing root access to the machine\n" 326 "\n" 327 " -i Specify the interface (by IP address) to listen on.\n" 328 " (Default: 0.0.0.0 (any interface))\n" 329 " -p Port number to listen on. (Default: 6667)\n" 330 " -n Maximum number of connections. (Default: 0 (unlimited))\n" 331 " -t Specify the maximum number of CPU seconds per process.\n" 332 " (Default: 0 (unlimited))\n" 333 " -l Specify a logfile. (Default: none)\n" 334 " -r Rate limiting: Ignore a host for z seconds when it connects for more\n" 335 " than y times in x seconds. (Default: 600,5,900. Disable: 0,0,0)\n" 336 " -u Use a local socket, by default /tmp/bitlbee (override with -i <filename>)\n" 337 " -d Don't fork for listening (for debugging purposes)\n" 338 " -h This information\n", argv[0]); 339 return(NULL); 340 } 341 } 342 343 if (set->interface == NULL) { 385 344 set->interface = (set->local) ? "/tmp/bitlbee" : "0.0.0.0"; 386 387 if( optind == argc ) 388 {389 fprintf( stderr, "Missing program parameter!\n");390 return( NULL);391 } 392 345 } 346 347 if (optind == argc) { 348 fprintf(stderr, "Missing program parameter!\n"); 349 return(NULL); 350 } 351 393 352 /* 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;397 398 return( set);353 set->call = malloc((argc - optind + 1) * sizeof(char*)); 354 memcpy(set->call, argv + optind, sizeof(char*) * (argc - optind)); 355 set->call[argc - optind] = NULL; 356 357 return(set); 399 358 } 400 359 401 void do_log( char *fmt, ...)360 void do_log(char *fmt, ...) 402 361 { 403 362 va_list params; … … 405 364 time_t tm; 406 365 int l; 407 408 memset( line, 0, MAX_LOG_LEN);409 410 tm = time( NULL);411 strcpy( line, ctime( &tm ));412 l = strlen( line);413 line[l -1] = ' ';414 415 va_start( params, fmt);416 vsnprintf( line + l, MAX_LOG_LEN - l - 2, fmt, params);417 va_end( params);418 strcat( line, "\n");419 420 fprintf( logfile, "%s", line);366 367 memset(line, 0, MAX_LOG_LEN); 368 369 tm = time(NULL); 370 strcpy(line, ctime(&tm)); 371 l = strlen(line); 372 line[l - 1] = ' '; 373 374 va_start(params, fmt); 375 vsnprintf(line + l, MAX_LOG_LEN - l - 2, fmt, params); 376 va_end(params); 377 strcat(line, "\n"); 378 379 fprintf(logfile, "%s", line); 421 380 } 422 381 423 ipstats_t *ip_get( char *ip_txt)382 ipstats_t *ip_get(char *ip_txt) 424 383 { 425 384 unsigned int ip; 426 385 ipstats_t *l; 427 386 int p[4]; 428 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] ); 431 432 for( l = ipstats; l; l = l->next ) 433 { 434 if( l->ip == ip ) 435 return( l ); 436 } 437 438 if( ipstats ) 439 { 440 for( l = ipstats; l->next; l = l->next ); 441 442 l->next = malloc( sizeof( ipstats_t ) ); 387 388 sscanf(ip_txt, "%d.%d.%d.%d", p + 0, p + 1, p + 2, p + 3); 389 ip = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]); 390 391 for (l = ipstats; l; l = l->next) { 392 if (l->ip == ip) { 393 return(l); 394 } 395 } 396 397 if (ipstats) { 398 for (l = ipstats; l->next; l = l->next) { 399 ; 400 } 401 402 l->next = malloc(sizeof(ipstats_t)); 443 403 l = l->next; 444 } 445 else 446 { 447 l = malloc( sizeof( ipstats_t ) ); 404 } else { 405 l = malloc(sizeof(ipstats_t)); 448 406 ipstats = l; 449 407 } 450 memset( l, 0, sizeof( ipstats_t ));451 408 memset(l, 0, sizeof(ipstats_t)); 409 452 410 l->ip = ip; 453 454 return( l);411 412 return(l); 455 413 }
Note: See TracChangeset
for help on using the changeset viewer.