#!/usr/bin/perl # Copyright (c) 2020, Mirko Ludeke # Copyright (c) 2020, Carsten Rosenberg # Copyright (c) 2020, Andreas Boesen # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. use strict; use warnings; use IO::Socket::IP; use IO::Select; use threads; use Data::Dumper; use POSIX qw(setlocale strftime); use Razor2::Client::Agent; # set to 1 to enable debug logging my $debug = defined($ENV{'RAZORFY_DEBUG'}) ? $ENV{'RAZORFY_DEBUG'} : 0; # max number of threa to use my $maxthreads = defined($ENV{'RAZORFY_MAXTHREADS'}) ? $ENV{'RAZORFY_MAXTHREADS'} : 200; # bind razorfy to default to local ip address # use :: for all (dual stack), 0.0.0.0 (all ipv4), ::1 localhost v6only, 127.0.0.1 localhost ipv4 my $bindaddress = defined($ENV{'RAZORFY_BINDADDRESS'}) ? $ENV{'RAZORFY_BINDADDRESS'} : '127.0.0.1'; # tcp port to use my $bindport = defined($ENV{'RAZORFY_BINDPORT'}) ? $ENV{'RAZORFY_BINDPORT'} : '11342'; my $agent = new Razor2::Client::Agent('razor-check') or die ; $agent->read_options() or die $agent->errstr ."\n"; $agent->do_conf() or die $agent->errstr ."\n"; my %logret = ( 0 => 'spam', 1 => 'ham'); sub Main { # flush after every write $| = 1; my ( $socket, $client_socket ); # Bind to listening address and port $socket = new IO::Socket::IP ( LocalHost => $bindaddress, LocalPort => $bindport, Proto => 'tcp', Listen => 10, ReuseAddr => 1 ) or die "Could not open socket: ".$!."\n"; ErrorLog( "RAZORFY started, PID: $$ Waiting for client connections..."); my @clients = (); # start infinity loop while(1) { # Limit threads my @threads = threads->list(threads::running); if( $#threads < $maxthreads ) { # Waiting for new client connection. $client_socket = $socket->accept(); # Push new client connection to it's own thread push ( @clients, threads->create( \&clientHandler, $client_socket ) ); ErrorLog( "active threads: $#threads") if $debug ; ErrorLog( "client array length: " . scalar @clients) if $debug ; my $counter = 0; foreach ( @clients ) { if( $_->is_joinable() ) { $_->join(); } if( not $_->is_running() ) { splice(@clients,$counter,1); } $counter++; } } } $socket->close(); return 1; } sub clientHandler { # Socket is passed to thread as first (and only) argument. my ($client_socket) = @_; # Create hash for user connection/session information and set initial connection information. my %user = (); $user{peer_address} = $client_socket->peerhost(); $user{peer_port} = $client_socket->peerport(); ErrorLog( "Accepted New Client Connection From:".$user{peer_address}.":".$user{peer_port}) if $debug; my %hashr; $hashr{'fh'} = $client_socket; my $ret = $agent->checkit(\%hashr); print $client_socket ( $ret == 0) ? "spam" : "ham"; ErrorLog( "return value: ". $logret{$ret} ) if $debug; $client_socket->shutdown(2); threads->exit(); } sub ErrorLog { setlocale(&POSIX::LC_ALL, "en_US"); my $msg = shift; my $datestring = strftime "%b %e %H:%M:%S", localtime; print STDERR $msg."\n"; } # Start the Main loop Main();