#!/usr/bin/perl use strict; my $version = "0.0.2"; &log("whoisd $version for xs26 by yanek startup..."); # whoisd for XS26.net # yanek (c) 2002 # GPL # Config #print " - Loading config\n"; use vars qw (%config); &log("- Setting config defaults"); do 'whoisd-config' or die "Cannot load whoisd-config, buddy. $!"; &log(" - Setting variables"); # Sets variables # Load perl's libraries &log(" - Loading perl's libraries"); use Data::Dumper; # debug use IO::Select; #use IO::Socket; use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK); # non-block IO use Socket; use Socket6; use DBI; # Set autoflush $| = 1; # Bind SIGs &log(" - Setting SIGnals"); $SIG{TERM} = \&botdie; $SIG{ALRM} = \&botdie; $SIG{INT} = \&botdie; # Create socket list &log(" - Create socket list"); my $select = new IO::Select( ); # Bind to listen port if (!$config{'ipv6notbind'}) { &log(" - Binding listen port ipv6"); my $proto = getprotobyname('tcp'); socket(Server, AF_INET6, SOCK_STREAM, $proto) || die "socket: $!"; setsockopt(Server, SOL_SOCKET, SO_REUSEADDR, pack("l", $config{'connectmax'})) || die "setsockopt: $!"; bind(Server, sockaddr_in6($config{'port'}, inet_pton(PF_INET6, "::"))) || die "bind: $!"; listen(Server,SOMAXCONN) || die "listen: $!"; { my $oldfh = select(Server); $| = 1; select($oldfh); } $select->add( \*Server ); } if ($config{'ipv4extrabind'}) { &log(" - Binding listen port ipv4"); my $proto = getprotobyname('tcp'); socket(Server2, AF_INET, SOCK_STREAM, $proto) || die "socket: $!"; setsockopt(Server2, SOL_SOCKET, SO_REUSEADDR, pack("l", $config{'connectmax'})) || die "setsockopt: $!"; bind(Server2, sockaddr_in($config{'port'}, inet_aton("0.0.0.0"))) || die "bind: $!"; listen(Server2,SOMAXCONN) || die "listen: $!"; { my $oldfh = select(Server2); $| = 1; select($oldfh); } $select->add( \*Server2 ); } # MySQL connection &log(" - Establishing connection to MySQL"); my $dbpath = sprintf("DBI:mysql:database=%s;host=%s",$config{'mysql-database'},$config{'mysql-host'}); my $sql = DBI->connect($dbpath,$config{'mysql-user'},$config{'mysql-pass'}); #, {'RaiseError' => 1}); my ($dcc,$input,$idxi,$ridx,$idx); # core loop &log(" - Going to loop, wait for incoming connection"); &demonize_me if $config{daemonize}; my $mainloop = 1; while ($mainloop) { my @ready = $select->can_read(1); foreach my $fh (@ready) { if (!$config{'ipv6notbind'} && (\*$fh == \*Server)) { # Create a new incoming socket my $new; accept($new,*$fh) or (warn "accept(...): $!\n" and next); $select->add( $new); fcntl($new, F_SETFL, O_NONBLOCK) or die "Can't set flags for the socket: $!\n"; { my $old = select($new); $| = 1; select($old); } print $new "$config{'motd'}\n"; $dcc->{$new}->{'state'} = 1; $dcc->{$new}->{'idx'} = &getfreeidx($new); $dcc->{$new}->{'type'} = 6; my $rso = getpeername($new); my ($peerport,$peeraddr) = unpack_sockaddr_in6($rso); $peeraddr = &inet_ntop(AF_INET6, $peeraddr); &log(" - - [%s] New connection (AF_INET6) ([%s]:%s)", $dcc->{$new}->{'idx'}, $peeraddr, $peerport); } elsif ($config{'ipv4extrabind'} && (\*$fh == \*Server2)) { # Create a new incoming socket my $new; accept($new,*$fh) or (warn "accept(...): $!\n" and next); $select->add( $new); fcntl($new, F_SETFL, O_NONBLOCK) or die "Can't set flags for the socket: $!\n"; { my $old = select($new); $| = 1; select($old); } print $new "$config{'motd'}\n"; $dcc->{$new}->{'state'} = 1; $dcc->{$new}->{'idx'} = &getfreeidx($new); $dcc->{$new}->{'type'} = 4; my $rso = getpeername($new); my ($peerport,$peeraddr) = sockaddr_in($rso); $peeraddr = inet_ntoa($peeraddr); &log(" - - [%s] New connection (AF_INET) ([%s]:%s)", $dcc->{$new}->{'idx'}, $peeraddr, $peerport); } else { # Process socket my $r = read($fh,my $data,2048); my @a = getpeername($fh) || warn "a: $!"; if (defined $r and $a[0]) { $input->{$fh} .= $data; my $idx = $dcc->{$fh}->{idx}; if ($idx) { $idxi->{$idx}->{idle} = scalar time; my @d = split(/\n/,$input->{$fh}); my $next = pop(@d); if ($input->{$fh} =~ /(\n|\r)$/) { push(@d,$next); $next = undef; } foreach (@d) { $data = &strip($_); &log(" * * C0re[$idx]: '$data'") if $config{debug}; &parse_it($fh,$idx,$data); } $input->{$fh} = ($next ? $next : undef); } else { warn "Whau!\n"; print Dumper($dcc,$fh); } } else { &socket_die($fh); } } } # System timers # foreach my $a (@stimers) { # } # User timers # foreach my $a (@timers) { } sub botdie { &log("Received die, cleaning..."); foreach $a ($select->handles) { close($a); } close(Server); &log("OK. Exit"); exit; } sub socket_die { my $fh = shift; &log(" - - [$ridx->{$fh}] Socket disconnected"); $idx->{$ridx->{$fh}} = undef; $ridx->{$fh} = undef; $select->remove($fh); $dcc->{$fh} = undef; close($fh); return 0; } sub getfreeidx { # return first free idx my $socket = shift; for(my $i = 1; $i < $config{'connectmax'}; ++$i) { if (!$idx->{$i}) { $idx->{$i} = $socket; $idxi->{$i}->{io} = $socket; $ridx->{$socket} = $i; return $i; } } die "Max. connections reached"; } #sub putidx { # my ($toidx,$data) = @_; # my $out = $idx->{$toidx}; # print $out "$data\n"; #} sub putidx { my ($toidx,$form,@values) = (@_,'','',''); &putidxf($toidx,$form."\r\n",@values); } sub putidxf { my ($toidx,$form,@values) = @_; if ($toidx == 0) { warn "!!! IDX can be zero value !!! ($toidx,$form,(".&strip(join(", ",@values))."))"; } my $data = (@values ? sprintf($form, @values) : $form); my $out = $idxi->{$toidx}->{io}; # print 'PUTIDX ',$toidx," )$out( ",&strip($data); # print Dumper($out); my @a = getpeername($out); if ($out and defined $a[0]) { syswrite($out,$data,length $data); return 1; } if (defined $out) { &socket_die($out); } return 0; } sub strip { my $a = shift; $a =~ s/\r//g; $a =~ s/\n//g; return $a; } sub reply { my $a = shift; my $b = shift; &putidx($a,"%-14s%s",$b.':', @_); } sub log { my ($form,@values) = (@_,'',''); &logf($form."\r\n",@values); } sub logf { my ($form,@values) = @_; my $data = '['.(scalar localtime).'] '.(@values ? sprintf($form, @values) : $form); print STDERR $data; } sub reformatzone { my $zone = shift; $zone =~ s/(....)/$1:/g; my @zone1; foreach (split(':',$zone)) { s/^0+//g; push(@zone1,$_); } my $zone2 = join(":",@zone1); $zone2 =~ s/\:{2,}/::/g; return $zone2; } sub reformatzone2 { my $zone = shift; my @zone1; foreach (split(/:/,$zone)) { push(@zone1,('0' x (4 - length($_))).$_); } my $zone2 = join('',@zone1); return $zone2.('0' x (32 - length($zone2))); } sub reformatzone3 { my ($zone,undef,$prefix) = $_[0] =~ /([^\/]*)(\/(.*))?/; my ($s) = 0; if (!$prefix) { #compute prefix $prefix ||= 128; $s = 1; } return (unpack("H*", pack("B*", ('1' x $prefix) . ('0' x (128 - $prefix)) & unpack("B*", pack("H*", &reformatzone2($zone)))))) . (!$s ? '/' . $prefix : ''); } sub demonize_me ($) { my $pidfile = shift; # &log("Daemonizing..."); defined (my $pid = fork) or die "Can't fork: $!"; if ($pid) { exit; } else { require 'POSIX.pm'; &POSIX::setsid or die "Can't start a new session: $!"; open STDOUT,'>/dev/null' or die "ERROR: Redirecting STDOUT to /dev/null: $!"; open STDERR,'>/dev/null' or die "ERROR: Redirecting STDERR to /dev/null: $!"; open STDIN, '