Code

neue struktur
authorrettenbe <rettenbe@594d385d-05f5-0310-b6e9-bd551577e9d8>
Tue, 20 Nov 2007 07:50:39 +0000 (07:50 +0000)
committerrettenbe <rettenbe@594d385d-05f5-0310-b6e9-bd551577e9d8>
Tue, 20 Nov 2007 07:50:39 +0000 (07:50 +0000)
gosa daemon
gosa bus
gosa client
jeweils mit cfg file

git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@7818 594d385d-05f5-0310-b6e9-bd551577e9d8

contrib/daemon/gosa-sc [new file with mode: 0755]
contrib/daemon/gosa-sc.cfg [new file with mode: 0644]
contrib/daemon/gosa-sd [new file with mode: 0755]
contrib/daemon/gosa-sd-bus
contrib/daemon/gosa-sd-bus.cfg
contrib/daemon/gosa-sd.cfg [new file with mode: 0644]

diff --git a/contrib/daemon/gosa-sc b/contrib/daemon/gosa-sc
new file mode 100755 (executable)
index 0000000..03084d2
--- /dev/null
@@ -0,0 +1,695 @@
+#!/usr/bin/perl
+#===============================================================================
+#
+#         FILE:  gosa-server
+#
+#        USAGE:  ./gosasc
+#
+#  DESCRIPTION:
+#
+#      OPTIONS:  ---
+# REQUIREMENTS:  ---
+#         BUGS:  ---
+#        NOTES:
+#       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
+#      COMPANY:
+#      VERSION:  1.0
+#      CREATED:  12.09.2007 08:54:41 CEST
+#     REVISION:  ---
+#===============================================================================
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Config::IniFiles;
+use POSIX;
+use Time::HiRes qw( gettimeofday );
+
+use Fcntl;
+use IO::Socket::INET;
+use Crypt::Rijndael;
+use XML::Simple;
+use Data::Dumper;
+use Sys::Syslog qw( :DEFAULT setlogsock);
+
+
+my ($cfg_file, %cfg_defaults, $foreground, $verbose, $pid_file, $procid, $pid, $log_file);
+my ($server_address, $server_ip, $server_port, $server_passwd, $server_cipher, $server_timeout);
+my ($my_address, $my_ip, $my_port, $input_socket, $rbits, $wbits, $ebits, $xml, $known_hosts);
+
+# default variables
+$known_hosts = {};
+$foreground = 0 ;
+$my_ip = "10.89.1.155";
+$my_port = "10010";
+%cfg_defaults =
+("general" =>
+    {"log_file" => [\$log_file, "/var/run/".$0.".log"],
+    "pid_file" => [\$pid_file, "/var/run/".$0.".pid"],
+
+    },
+"server" =>
+    {"server_ip" => [\$server_ip, "10.89.1.155"],
+    "server_port" => [\$server_port, "10001"],
+    "server_passwd" => [\$server_passwd, "tester"],
+    "server_timeout" => [\$server_timeout, 10],
+    }
+    );
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  read_configfile
+#   PARAMETERS:  cfg_file - string - 
+#      RETURNS:  
+#  DESCRIPTION: 
+#===============================================================================
+sub read_configfile {
+    my $cfg;
+    if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
+        if( -r $cfg_file ) {
+            $cfg = Config::IniFiles->new( -file => $cfg_file );
+        } else {
+            print STDERR "Couldn't read config file!";
+        }
+    } else {
+        $cfg = Config::IniFiles->new() ;
+    }
+    foreach my $section (keys %cfg_defaults) {
+        foreach my $param (keys %{$cfg_defaults{ $section }}) {
+            my $pinfo = $cfg_defaults{ $section }{ $param };
+            ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
+        }
+    }
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  logging
+#   PARAMETERS:  level - string - default 'info' 
+#                msg - string - 
+#                facility - string - default 'LOG_DAEMON' 
+#      RETURNS:  
+#  DESCRIPTION: 
+#===============================================================================
+sub daemon_log {
+    my( $msg, $level ) = @_;
+    if(not defined $msg) { return }
+    if(not defined $level) { $level = 1 }
+    if(defined $log_file){
+        open(LOG_HANDLE, ">>$log_file");
+        if(not defined open( LOG_HANDLE, ">>$log_file" )) { 
+            print STDERR "cannot open $log_file: $!";
+            return }
+        chomp($msg);
+        if($level <= $verbose){
+            print LOG_HANDLE $msg."\n";
+            if(defined $foreground) { print $msg."\n" }
+        }
+    }
+    close( LOG_HANDLE );
+#    my ($msg, $level, $facility) = @_;
+#    if(not defined $msg) {return}
+#    if(not defined $level) {$level = "info"}
+#    if(not defined $facility) {$facility = "LOG_DAEMON"}
+#    openlog($0, "pid,cons,", $facility);
+#    syslog($level, $msg);
+#    closelog;
+#    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME: check_cmdline_param
+#   PARAMETERS: 
+#      RETURNS:  
+#  DESCRIPTION: 
+#===============================================================================
+sub check_cmdline_param () {
+    my $err_config;
+    my $err_counter = 0;
+    if( not defined( $cfg_file)) {
+        $err_config = "please specify a config file";
+        $err_counter += 1;
+    }
+    if( $err_counter > 0 ) {
+        &usage( "", 1 );
+        if( defined( $err_config)) { print STDERR "$err_config\n"}
+        print STDERR "\n";
+        exit( -1 );
+    }
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME: check_pid
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub check_pid {
+    $pid = -1;
+    # Check, if we are already running
+    if( open(LOCK_FILE, "<$pid_file") ) {
+        $pid = <LOCK_FILE>;
+        if( defined $pid ) {
+            chomp( $pid );
+            if( -f "/proc/$pid/stat" ) {
+                my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
+                if( $0 eq $stat ) {
+                    close( LOCK_FILE );
+                    exit -1;
+                }
+            }
+        }
+        close( LOCK_FILE );
+        unlink( $pid_file );
+    }
+
+    # create a syslog msg if it is not to possible to open PID file
+    if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
+        my($msg) = "Couldn't obtain lockfile '$pid_file' ";
+        if (open(LOCK_FILE, '<', $pid_file)
+                && ($pid = <LOCK_FILE>))
+        {
+            chomp($pid);
+            $msg .= "(PID $pid)\n";
+        } else {
+            $msg .= "(unable to read PID)\n";
+        }
+        if( ! ($foreground) ) {
+            openlog( $0, "cons,pid", "daemon" );
+            syslog( "warning", $msg );
+            closelog();
+        }
+        else {
+            print( STDERR " $msg " );
+        }
+        exit( -1 );
+    }
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  usage
+#   PARAMETERS: 
+#      RETURNS:  
+#  DESCRIPTION: 
+#===============================================================================
+sub usage {
+        my( $text, $help ) = @_;
+        $text = undef if( "h" eq $text );
+        (defined $text) && print STDERR "\n$text\n";
+        if( (defined $help && $help) || (!defined $help && !defined $text) ) {
+                print STDERR << "EOF" ;
+usage: $0 [-hvf] [-c config]
+
+    -h        : this (help) message
+    -c <file> : config file
+    -v        : be verbose (multiple to increase verbosity)
+EOF
+        }
+        print "\n" ;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  register_at_server
+#   PARAMETERS:  
+#      RETURNS:  
+#  DESCRIPTION:  
+#===============================================================================
+sub register_at_server {
+    my ($tmp) = @_;
+
+    # create new passwd and ciphering object for client-server communication
+    my $new_server_passwd = &create_passwd();
+    my $new_server_cipher;
+
+    # fill in all possible servers
+    my @servers = ($server_address);
+
+    my ($rout, $wout, $reg_server);
+    foreach my $server (@servers) {
+        #   sende xml anfrage
+        my $msg_hash = &create_xml_hash("here_i_am", $my_address, $server_address);
+        &add_content2xml_hash($msg_hash, "new_passwd", $new_server_passwd);
+
+        # fetch header for logging
+        my $header = &get_content_from_xml_hash($msg_hash, "header");
+        # generiere xml string
+        my $msg_xml = &create_xml_string($msg_hash);
+        # erzeuge ein ciphering object
+        $server_cipher = &create_ciphering($server_passwd);
+
+        # encrypt xml msg
+        my $crypted_msg = &encrypt_msg($msg_xml, $server_cipher);
+        # öffne socket
+        my $socket = &open_socket($server);
+        if(not defined $socket){
+            print "cannot send '$header'-msg to $server , server not reachable\n";
+            last;
+        }
+        # versende xml msg
+        print $socket $crypted_msg."\n";
+        # schließe socket
+        close $socket;
+
+        daemon_log("send '$header'-msg to $server", 5);
+        daemon_log("crypted_msg:\n\t$crypted_msg", 7);
+
+        # waiting for response
+        daemon_log("waiting for response...", 5);
+        my $nf = select($rout=$rbits, $wout=$wbits, undef, $server_timeout);
+        # error handling
+        if($nf < 0 ) {
+        }
+
+        # something is coming in
+        if(vec $rout, fileno $input_socket, 1) {
+            my $crypted_msg;
+            my $client = $input_socket->accept();
+            my $other_end = getpeername($client);
+            if(not defined $other_end) {
+                daemon_log("client cannot be identified: $!\n");
+            } else {
+                my ($port, $iaddr) = unpack_sockaddr_in($other_end);
+                my $actual_ip = inet_ntoa($iaddr);
+                daemon_log("\naccept client from $actual_ip\n", 5);
+                my $in_msg = &read_from_socket($client);
+                if(defined $in_msg){
+                    chomp($in_msg);
+                    $crypted_msg = $in_msg;
+                } else {
+                    daemon_log("cannot read from $actual_ip\n", 5);
+                }
+            }
+            close($client);
+            
+            # validate acknowledge msg from server
+            $new_server_cipher = &create_ciphering($new_server_passwd);
+            eval {
+                my $decrypted_msg = &decrypt_msg($crypted_msg, $new_server_cipher);
+                $msg_hash = $xml->XMLin($decrypted_msg, ForceArray=>1);
+            };
+            if($@) {
+                daemon_log("cannot register at $server", 1);
+                daemon_log("ERROR: do not understand the message:\n\t$crypted_msg" , 5);   
+            } else {
+                my $header = &get_content_from_xml_hash($msg_hash, "header");
+                if($header eq "registered") {
+                    $reg_server = $server;
+                    last;
+                } else {
+                    daemon_log("cannot register at $server", 1);
+                }
+            }
+        }
+        # kommt antwort nicht, dann probiere es mit dem nächsten in der liste
+
+    }
+    
+    if(defined $reg_server) {
+        daemon_log("registered at $reg_server", 1);
+    } else {
+        daemon_log("cannot register at any server", 1);
+        daemon_log("exiting!!!", 1);
+        exit(1);
+    }
+
+    # update the global available variables
+    $server_passwd = $new_server_passwd;
+    $server_cipher = $new_server_cipher;
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_xml_hash
+#   PARAMETERS:  
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub create_xml_hash {
+    my ($header, $source, $target) = @_;
+    my $hash = {
+            header => [$header],
+            source => [$source],
+            target => [$target],
+            $header => [],
+    };
+    daemon_log("create_xml_hash:", 7),
+    chomp(my $tmp = Dumper $hash);
+    daemon_log("\t$tmp\n", 7);
+    return $hash
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_xml_string
+#   PARAMETERS:  
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub create_xml_string {
+    my ($xml_hash) = @_ ;
+    my $xml_string = $xml->XMLout($xml_hash, RootName => 'xml');
+    $xml_string =~ s/[\s]+//g;
+    daemon_log("create_xml_string:\n\t$xml_string\n", 7);
+    return $xml_string;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  add_content2xml_hash
+#   PARAMETERS:  
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub add_content2xml_hash {
+    my ($xml_ref, $element, $content) = @_;
+    if(not exists $$xml_ref{$element} ) {
+        $$xml_ref{$element} = [];
+    }
+    my $tmp = $$xml_ref{$element};
+    push(@$tmp, $content);
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  get_content_from_xml_hash
+#   PARAMETERS:  
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub get_content_from_xml_hash {
+    my ($xml_ref, $element) = @_;
+    my $result = $xml_ref->{$element};
+    if( @$result == 1) {
+        $result = @$result[0];
+    }
+    return $result;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  encrypt_msg
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub encrypt_msg {
+    my ($msg, $my_cipher) = @_;
+    if(not defined $my_cipher) { print "no cipher object\n"; }
+    $msg =~ s/[\s]+//g;
+    my $msg_length = length($msg);
+    my $multiplier = int($msg_length / 16) + 1;
+    my $extension = 16*$multiplier - $msg_length;
+    $msg = "a"x$extension.$msg;
+    my $crypted_msg = $my_cipher->encrypt($msg);
+    #my $crypted_msg = $msg;
+    return $crypted_msg;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  decrypt_msg
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub decrypt_msg {
+    my ($crypted_msg, $my_cipher) =@_;
+    my $msg = $my_cipher->decrypt($crypted_msg);
+    #my $msg = $crypted_msg;
+    $msg =~ s/^a*//gi;
+    return $msg;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_ciphering
+#   PARAMETERS:  
+#      RETURNS:  cipher object
+#  DESCRIPTION:  
+#===============================================================================
+sub create_ciphering {
+    my ($passwd) = @_;
+    my $passwd_length = length($passwd);
+    my $multiplier = int($passwd_length / 32) + 1;
+    my $extension = 32*$multiplier - $passwd_length;
+    $passwd = "a"x$extension.$passwd;
+
+    my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC );
+    return $my_cipher;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_passwd
+#   PARAMETERS:
+#      RETURNS:  cipher object
+#  DESCRIPTION:
+#===============================================================================
+sub create_passwd {
+    my $new_passwd = "";
+    for(my $i=0; $i<31; $i++) {
+        $new_passwd .= ("a".."z","A".."Z",0..9)[int(rand(62))]
+    }
+
+    return $new_passwd;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  send_msg_hash2address
+#   PARAMETERS:  msg string - xml message
+#                PeerAddr string - socket address to send msg
+#                PeerPort string - socket port, if not included in socket address
+#      RETURNS:  nothing
+#  DESCRIPTION:  ????
+#===============================================================================
+sub send_msg_hash2address {
+    my ($msg_hash, $address) = @_ ;
+
+    # fetch header for logging
+    my $header = &get_content_from_xml_hash($msg_hash, "header");
+    # generiere xml string
+    my $msg_xml = &create_xml_string($msg_hash);
+    # hole das entsprechende passwd aus dem hash
+    my $passwd = $known_hosts->{$address}->{passwd};
+    # erzeuge ein ciphering object
+    my $act_cipher = &create_ciphering($passwd);
+    # encrypt xml msg
+    my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
+    # öffne socket
+    my $socket = &open_socket($address);
+    if(not defined $socket){
+        print "cannot send '$header'-msg to $address , server not reachable\n";
+        return;
+    }
+    # versende xml msg
+    print $socket $crypted_msg."\n";
+    # schließe socket
+    close $socket;
+
+    daemon_log("send '$header'-msg to $address", 5);
+    daemon_log("crypted_msg:\n\t$crypted_msg", 7);
+
+    return
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  open_socket
+#   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
+#                [PeerPort] string necessary if port not appended by PeerAddr
+#      RETURNS:  socket IO::Socket::INET
+#  DESCRIPTION:
+#===============================================================================
+sub open_socket {
+    my ($PeerAddr, $PeerPort) = @_ ;
+    if(defined($PeerPort)){
+        $PeerAddr = $PeerAddr.":".$PeerPort;
+    }
+    my $socket;
+    $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
+            Porto => "tcp" ,
+            Type => SOCK_STREAM,
+            Timeout => 5,
+            );
+    if(not defined $socket) {
+        #daemon_log("cannot connect to socket at $PeerAddr, $@\n");
+        return;
+    }
+    daemon_log("open_socket:\n\t$PeerAddr", 7);
+    return $socket;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  read_from_socket
+#   PARAMETERS:  socket fh - 
+#      RETURNS:  result string - readed characters from socket
+#  DESCRIPTION:  reads data from socket in 16 byte steps
+#===============================================================================
+sub read_from_socket {
+    my ($socket) = @_;
+    my $result = "";
+    my $len = 16;
+    while($len == 16){
+        my $char;
+        $len = sysread($socket, $char, 16);
+        if($len != 16) { last }
+        if($len != 16) { last }
+        $result .= $char;
+    }
+    return $result;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  print_known_hosts_hash
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub print_known_hosts_hash {
+    my ($tmp) = @_;
+    print "####################################\n";
+    print "# status of $known_hosts\n";
+    my $hosts;
+    my $host_hash;
+    my @hosts = keys %$known_hosts;
+    foreach my $host (@hosts) {
+        #my @elements = keys %$known_hosts->{$host};
+        my $status = $known_hosts->{$host}->{status} ;
+        my $passwd = $known_hosts->{$host}->{passwd};
+        my $timestamp = $known_hosts->{$host}->{timestamp};
+        print "$host\n";
+        print "\t$status\n";
+        print "\t$passwd\n";
+        print "\t$timestamp\n";
+    }
+    print "####################################\n";
+    return;
+}
+
+
+sub create_known_hosts_entry {
+    my ($hostname) = @_;
+    $known_hosts->{$hostname} = {};
+    $known_hosts->{$hostname}->{status} = "none";
+    $known_hosts->{$hostname}->{passwd} = "none";
+    $known_hosts->{$hostname}->{timestamp} = "none";
+    return;  
+}
+
+sub update_known_hosts_entry {
+    my ($hostname, $status, $passwd, $timestamp) = @_;
+    my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat,
+    $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time);
+    $Stunden = $Stunden < 10 ? $Stunden = "0".$Stunden : $Stunden;
+    $Minuten = $Minuten < 10 ? $Minuten = "0".$Minuten : $Minuten;
+    $Sekunden = $Sekunden < 10 ? $Sekunden = "0".$Sekunden : $Sekunden;
+    $Monat+=1;
+    $Monat = $Monat < 10 ? $Monat = "0".$Monat : $Monat;
+    $Monatstag = $Monatstag < 10 ? $Monatstag = "0".$Monatstag : $Monatstag;
+    $Jahr+=1900;
+    my $t = "$Jahr$Monat$Monatstag$Stunden$Minuten$Sekunden";
+
+    if($status) {
+        $known_hosts->{$hostname}->{status} = $status;
+    }
+    if($passwd) {
+        $known_hosts->{$hostname}->{passwd} = $passwd;
+    }
+    if($timestamp) {
+        $t = $timestamp;
+    }
+    $known_hosts->{$hostname}->{timestamp} = $t;
+    return;  
+}
+
+
+
+
+
+#==== MAIN = main ==============================================================
+
+#  parse commandline options
+Getopt::Long::Configure( "bundling" );
+GetOptions("h|help" => \&usage,
+           "c|config=s" => \$cfg_file,
+           "f|foreground" => \$foreground,
+           "v|verbose+" => \$verbose,
+           );
+
+#  read and set config parameters
+&read_configfile;
+&check_cmdline_param ;
+&check_pid;
+
+# restart daemon log file
+if(-e $log_file ) { unlink $log_file }
+daemon_log("started!");
+
+# Just fork, if we"re not in foreground mode
+if( ! $foreground ) { $pid = fork(); }
+else { $pid = $$; }
+
+# Do something useful - put our PID into the pid_file
+if( 0 != $pid ) {
+    open( LOCK_FILE, ">$pid_file" );
+    print LOCK_FILE "$pid\n";
+    close( LOCK_FILE );
+    if( !$foreground ) { exit( 0 ) };
+}
+
+# prepare variables
+$my_address = $my_ip.":".$my_port;
+$server_address = $server_ip.":".$server_port;
+
+# setup xml parser
+$xml = new XML::Simple();
+
+# create input socket
+$rbits = $wbits = $ebits = "";
+$input_socket = IO::Socket::INET->new(LocalPort => $my_port,
+        Type => SOCK_STREAM,
+        Reuse => 1,
+        Listen => 20,
+        ); 
+if(not defined $input_socket){
+    daemon_log("cannot be a tcp server at $my_port : $@\n");
+} else {
+    daemon_log("start server:\n\t$server_ip:$my_port",1) ;
+    vec($rbits, fileno $input_socket, 1) = 1;
+    vec($wbits, fileno $input_socket, 1) = 1;
+}
+
+# register at server
+&register_at_server();
+
+
+###################################
+#everything ready, okay, lets start
+###################################
+while(1) {
+    my ($rout, $wout);
+    my $nf = select($rout=$rbits, $wout=$wbits, undef, undef);
+
+    # error handling
+    if($nf < 0 ) {
+    }
+
+    # something is coming in
+    if(vec $rout, fileno $input_socket, 1) {
+        print "debug: es ist was rein gekommen\n";
+    }
+
+}
+
+
+
diff --git a/contrib/daemon/gosa-sc.cfg b/contrib/daemon/gosa-sc.cfg
new file mode 100644 (file)
index 0000000..66f4cee
--- /dev/null
@@ -0,0 +1,9 @@
+[general]
+log_file = /var/log/gosa-sc.log
+pid_file = /var/run/gosa-sc.pid
+
+[server]
+server_ip = 10.89.1.155
+server_port = 10001
+server_passwd = tester
+
diff --git a/contrib/daemon/gosa-sd b/contrib/daemon/gosa-sd
new file mode 100755 (executable)
index 0000000..16c2c2f
--- /dev/null
@@ -0,0 +1,1170 @@
+#!/usr/bin/perl
+#===============================================================================
+#
+#         FILE:  gosa-sd
+#
+#        USAGE:  ./gosa-sd
+#
+#  DESCRIPTION:
+#
+#      OPTIONS:  ---
+# REQUIREMENTS:  ---
+#         BUGS:  ---
+#        NOTES:
+#       AUTHOR:   (Andreas Rettenberger), <rettenberger@gonicus.de>
+#      COMPANY:
+#      VERSION:  1.0
+#      CREATED:  12.09.2007 08:54:41 CEST
+#     REVISION:  ---
+#===============================================================================
+
+
+# TODO 2007-11-12: nachrichten senden an buss kann so nicht funktionieren, kind setzt $msg_to_bus, der elter testet aber, ob in $msg_to_bus was enthalten ist um es dorthin zu senden, so kann das nicht gehen, alternativ müsste das kind dem elter über die pipe die msg schicken und der elter müsste dann die variabel $msg_to_bus setzten, dann würde es so gehen
+# wird als zu aufwändig vorerst verworfen, es wäre nötig global ein dic/2 arrays bereitzustellen, maximal child_max groß in dem dann die erzeugten pipe-enden beim forken zugewiesen werden. die namen müssten aber vorher festgelegt sein, damit der elter die bereits existierenden und die noch nicht oder wieder nicht existierenden pipes checken kann ob dort was anliegt. kurzum, das pipe-handling wäre sehr aufwändig zu programmieren, wenn also kein zeitlicher bedarf besteht, dann eher hinten anstellen.
+
+# TODO 2007-11-19: ich hab zwei hashes, known_hosts und known_clients. 1. known_hosts in known_server umändern, 2. lösung suchen für print known_hosts_hash usw. ich kann zwar den hash als parameter übergeben, aber nicht nicht zb. die shm variablen zum sperren des shm, evtl. anstatt hash nur einen key übergeben, bei dem dann hash und shm variablen abgelegt sind, allerdings müsste dieser hash dann auch wieder von überall aus erreichbar sein, evtl. zuviel speicher overhead, alternative bedeutet nur mehr tipparbeit
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Config::IniFiles;
+use POSIX;
+use Time::HiRes qw( gettimeofday );
+
+use Fcntl;
+use IO::Socket::INET;
+use Crypt::Rijndael;
+use XML::Simple;
+use Data::Dumper;
+use Sys::Syslog qw( :DEFAULT setlogsock);
+use IPC::Shareable qw( :lock);
+
+my ($cfg_file, %cfg_defaults, $foreground, $verbose);
+my ($bus_activ, $bus_passwd, $bus_ip, $bus_port, $bus, $bus_address, $msg_to_bus, $bus_cipher);
+my ($server_address, $server_activ, $server_port, $server, $server_ip, $server_passwd);
+my ($known_hosts, $shmkh, $known_clients, $shmcl);
+my ($clientmax);
+my ($pid_file, $procid, $pid, $log_file);
+my (%free_child, %busy_child, $child_max, $child_min, %child_alive_time, $child_timeout);
+my ($xml);
+my ($arp_activ, $arp_fifo, $arp_fifo_path);
+
+$verbose = 0 ;
+$foreground = 0 ;
+$known_hosts = {};
+$shmkh = tie($known_hosts, 'IPC::Shareable', undef, {create => 1, 
+                                                            exclusive => 1, 
+                                                            mode => 0666, 
+                                                            destroy => 1,
+                                                            });
+$known_clients = {};
+$shmcl = tie($known_clients, 'IPC::Shareable', undef, {create => 1, 
+                                                            exclusive => 1, 
+                                                            mode => 0666, 
+                                                            destroy => 1,
+                                                            });
+
+%cfg_defaults =
+("general" =>
+    {"log_file" => [\$log_file, "/var/run/".$0.".log"],
+    "pid_file" => [\$pid_file, "/var/run/".$0.".pid"],
+    },
+"bus" =>
+    {"bus_activ" => [\$bus_activ, "on"],
+    "bus_passwd" => [\$bus_passwd, "tester"],
+    "bus_ip" => [\$bus_ip, "10.89.1.155"],
+    "bus_port" => [\$bus_port, "10001"],
+    "child_max" => [\$child_max, 10],
+    "child_min" => [\$child_min, 3],
+    "child_timeout" => [\$child_timeout, 180],
+
+    },
+"server" =>
+    {"server_activ" => [\$server_activ, "on"],
+    "server_ip" => [\$server_ip, ""],
+    "server_port" => [\$server_port, ""],
+    "server_passwd" => [\$server_passwd, ""],
+    },
+
+"arp" =>
+    {"arp_activ" => [\$arp_activ, "on"],
+    "arp_fifo_path" => [\$arp_fifo_path, "/tmp/arp_fifo"],
+
+    }
+    );
+
+#===  FUNCTION  ================================================================
+#         NAME:  usage
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:  nomen est omen
+#===============================================================================
+sub usage {
+        my( $text, $help ) = @_;
+        $text = undef if( "h" eq $text );
+        (defined $text) && print STDERR "\n$text\n";
+        if( (defined $help && $help) || (!defined $help && !defined $text) ) {
+                print STDERR << "EOF" ;
+usage: $0 [-hvf] [-c config]
+
+    -h        : this (help) message
+    -c <file> : config file
+    -v        : be verbose (multiple to increase verbosity)
+EOF
+        }
+        print "\n" ;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  read_configfile
+#   PARAMETERS:  cfg_file - string -
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub read_configfile {
+    my $cfg;
+    if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
+        if( -r $cfg_file ) {
+            $cfg = Config::IniFiles->new( -file => $cfg_file );
+        } else {
+            print STDERR "Couldn't read config file!";
+        }
+    } else {
+        $cfg = Config::IniFiles->new() ;
+    }
+    foreach my $section (keys %cfg_defaults) {
+        foreach my $param (keys %{$cfg_defaults{ $section }}) {
+            my $pinfo = $cfg_defaults{ $section }{ $param };
+            ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
+        }
+    }
+}
+
+#===  FUNCTION  ================================================================
+#         NAME:  logging
+#   PARAMETERS:  level - string - default 'info'
+#                msg - string -
+#                facility - string - default 'LOG_DAEMON'
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub daemon_log {
+# log into log_file
+    my( $msg, $level ) = @_;
+    if(not defined $msg) { return }
+    if(not defined $level) { $level = 1 }
+    if(defined $log_file){
+        open(LOG_HANDLE, ">>$log_file");
+        if(not defined open( LOG_HANDLE, ">>$log_file" )) {
+            print STDERR "cannot open $log_file: $!";
+            return }
+            chomp($msg);
+            if($level <= $verbose){
+                print LOG_HANDLE $msg."\n";
+                if(defined $foreground) { print $msg."\n" }
+            }
+    }
+    close( LOG_HANDLE );
+#log into syslog
+#    my ($msg, $level, $facility) = @_;
+#    if(not defined $msg) {return}
+#    if(not defined $level) {$level = "info"}
+#    if(not defined $facility) {$facility = "LOG_DAEMON"}
+#    openlog($0, "pid,cons,", $facility);
+#    syslog($level, $msg);
+#    closelog;
+#    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME: check_cmdline_param
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub check_cmdline_param () {
+    my $err_config;
+    my $err_counter = 0;
+    if( not defined( $cfg_file)) {
+        $err_config = "please specify a config file";
+        $err_counter += 1;
+    }
+    if( $err_counter > 0 ) {
+        &usage( "", 1 );
+        if( defined( $err_config)) { print STDERR "$err_config\n"}
+        print STDERR "\n";
+        exit( -1 );
+    }
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME: check_pid
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub check_pid {
+    $pid = -1;
+    # Check, if we are already running
+    if( open(LOCK_FILE, "<$pid_file") ) {
+        $pid = <LOCK_FILE>;
+        if( defined $pid ) {
+            chomp( $pid );
+            if( -f "/proc/$pid/stat" ) {
+                my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
+                if( $0 eq $stat ) {
+                    close( LOCK_FILE );
+                    exit -1;
+                }
+            }
+        }
+        close( LOCK_FILE );
+        unlink( $pid_file );
+    }
+
+    # create a syslog msg if it is not to possible to open PID file
+    if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
+        my($msg) = "Couldn't obtain lockfile '$pid_file' ";
+        if (open(LOCK_FILE, '<', $pid_file)
+                && ($pid = <LOCK_FILE>))
+        {
+            chomp($pid);
+            $msg .= "(PID $pid)\n";
+        } else {
+            $msg .= "(unable to read PID)\n";
+        }
+        if( ! ($foreground) ) {
+            openlog( $0, "cons,pid", "daemon" );
+            syslog( "warning", $msg );
+            closelog();
+        }
+        else {
+            print( STDERR " $msg " );
+        }
+        exit( -1 );
+    }
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  sig_int_handler
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:  nomen est omen
+#===============================================================================
+sub sig_int_handler {
+    my ($signal) = @_;
+    if($server){
+        close($server);
+        daemon_log("child closed\n", 1);
+    }
+    if( -p $arp_fifo_path ) {
+        close $arp_fifo  ;
+        unlink($arp_fifo_path) ;
+        daemon_log("FIFO closed\n", 1) ;
+    }
+
+    print "$signal\n";
+    
+    exit(1);
+}
+$SIG{INT} = \&sig_int_handler;
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  activating_child
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub activating_child {
+    my ($msg, $host) = @_;
+    my $child = &get_processing_child();
+    my $pipe_wr = $$child{'pipe_wr'};
+
+    daemon_log("activating: childpid:$$child{'pid'}", 5);
+
+    my $msg2write = $msg.".".$host."\n";
+
+    print $pipe_wr $msg.".".$host."\n";
+    return;
+
+}
+
+#===  FUNCTION  ================================================================
+#         NAME:  get_processing_child
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub get_processing_child {
+    my $child;
+    # schaue durch alle %busy_child{pipe_wr} ob irgendwo 'done' drinsteht, wenn ja, dann setze das kind von busy auf free um
+    while(my ($key, $val) = each(%busy_child)) {
+        # test ob prozess noch existiert
+        my $exitus_pid = waitpid($key, WNOHANG);
+        if($exitus_pid != 0) {
+            delete $busy_child{$key};
+            print "prozess:$key wurde aus busy_child entfernt\n";
+            next;
+        }
+
+        # test ob prozess noch arbeitet
+        my $fh = $$val{'pipe_rd'};
+        $fh->blocking(0);
+        my $child_answer;
+        if(not $child_answer = <$fh>) { next }
+        chomp($child_answer);
+        if($child_answer eq "done") {
+            delete $busy_child{$key};
+            $free_child{$key} = $val;
+        }
+    }
+
+    while(my ($key, $val) = each(%free_child)) {
+        my $exitus_pid = waitpid($key, WNOHANG);
+        if($exitus_pid != 0) {
+            delete $free_child{$key};
+            print "prozess:$key wurde aus free_child entfernt\n";
+        }
+        daemon_log( "free child:$key\n", 5);
+    }
+    # teste @free_child und @busy_child
+    my $free_len = scalar(keys(%free_child));
+    my $busy_len = scalar(keys(%busy_child));
+    daemon_log("free children $free_len, busy children $busy_len", 5);
+
+    # gibt es bereits ein freies kind, dann lass es arbeiten
+    if($free_len > 0){
+        my @keys = keys(%free_child);
+        $child = $free_child{$keys[0]};
+        if(defined $child) {
+            $busy_child{$$child{'pid'}} = $child ;
+            delete $free_child{$$child{'pid'}};
+        }
+        return $child;
+    }
+
+    # no free child, try to fork another one
+    if($free_len + $busy_len < $child_max) {
+
+        daemon_log("not enough children, create a new one", 5);
+
+        # New pipes for communication
+        my( $PARENT_wr, $PARENT_rd );
+        my( $CHILD_wr, $CHILD_rd );
+        pipe( $CHILD_rd,  $PARENT_wr );
+        pipe( $PARENT_rd, $CHILD_wr  );
+        $PARENT_wr->autoflush(1);
+        $CHILD_wr->autoflush(1);
+
+        ############
+        # fork child
+        ############
+        my $child_pid = fork();
+        
+        #CHILD
+        if($child_pid == 0) {
+            # Close unused pipes
+            close( $CHILD_rd );
+            close( $CHILD_wr );
+            while( 1 ) {
+                my $rbits = "";
+                vec( $rbits, fileno $PARENT_rd , 1 ) = 1;
+                my $nf = select($rbits, undef, undef, $child_timeout);
+                if($nf < 0 ) {
+                    # wenn $nf < 1, error handling
+                    die "select(): $!\n";
+                } elsif (! $nf) {
+                    # ist dieses kind nicht eines der letzenen $child_min, dann springe aus while schleife raus
+                    $free_len = scalar(keys(%free_child));
+                    $busy_len = scalar(keys(%busy_child));
+                    if($free_len + $busy_len >= $child_min) {
+                        last;
+                    } else {
+                        redo;
+                    }
+                }
+
+                # ansonsten
+                if ( vec $rbits, fileno $PARENT_rd, 1 ) {
+                    # hole alles was in der pipe ist    
+                    my $msg = "";
+                    $PARENT_rd->blocking(0);
+                    while(1) {
+                        my $read = <$PARENT_rd>;
+                        if(not defined $read) { last}
+                        $msg .= $read;
+                    }
+                    &process_incoming_msg($msg);
+                    daemon_log("processing of msg finished", 5);
+                    daemon_log(" \n", 5);
+                    print $PARENT_wr "done"."\n";
+                    redo;
+                }
+            }
+            # kinder die die while-schleife verlassen haben sterben
+            exit(0);
+
+
+        #PARENT
+        } else {
+            # Close unused pipes
+            close( $PARENT_rd );
+            close( $PARENT_wr );
+
+            # add child to child alive hash
+            my %child_hash = (
+                    'pid' => $child_pid,
+                    'pipe_wr' => $CHILD_wr,
+                    'pipe_rd' => $CHILD_rd,
+                    );
+
+            $child = \%child_hash;
+            $busy_child{$$child{'pid'}} = $child;
+            return $child;
+        }
+    }
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  process_incoming_msg
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub process_incoming_msg {
+    my ($crypted_msg) = @_;
+    if(not defined $crypted_msg) {
+        daemon_log("function 'process_incoming_msg': got no msg", 7);
+    }
+    $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
+    $crypted_msg = $1;
+    my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
+    daemon_log("msg from host:\n\t$host", 1);
+    daemon_log("crypted msg:\n\t$crypted_msg", 7);
+
+
+    my @valid_keys;
+    my @host_keys = keys %$known_hosts;
+    foreach my $host_key (@host_keys) {    
+        if($host_key =~ "^$host") {
+            push(@valid_keys, $host_key);
+        }
+    }
+    my @client_keys = keys %$known_clients;
+    foreach my $client_key (@client_keys) {
+        if($client_key =~ "^$host"){
+            push(@valid_keys, $client_key);
+        }
+    }
+    
+    my $l = @valid_keys;
+    my ($msg, $msg_hash);
+    my $msg_flag = 0;    
+
+    # ermittle alle in frage kommenden known_hosts einträge
+    foreach my $host_key (@valid_keys) {
+        eval{
+            daemon_log( "key: $host_key\n", 7);
+            my $key_passwd;
+            if(exists $known_hosts->{$host_key}) {
+                $key_passwd = $known_hosts->{$host_key}->{passwd};
+            } else {
+                $key_passwd = $known_clients->{$host_key}->{passwd};
+            }
+            daemon_log("key_passwd: $key_passwd\n", 7);
+            my $key_cipher = &create_ciphering($key_passwd);
+            $msg = &decrypt_msg($crypted_msg, $key_cipher);
+            $msg_hash = $xml->XMLin($msg, ForceArray=>1);
+        };
+        if($@) {
+            daemon_log("key raise error", 7);
+            $msg_flag += 1;
+        } else {
+            last;
+        }
+    } 
+    
+    if($msg_flag >= $l)  {
+        daemon_log("\nERROR: do not understand the message:\n\t$msg" , 1);
+        return;
+    }
+
+    my $header = &get_content_from_xml_hash($msg_hash, "header");
+    
+    daemon_log("header from msg:\n\t$header", 1);
+    daemon_log("msg to process:\n\t$msg", 7);
+
+    if($header eq 'new_passwd'){ &new_passwd($msg_hash)}
+    elsif($header eq 'ping'){ &got_ping($msg_hash)}
+    elsif($header eq 'here_i_am') { &here_i_am($msg_hash)}
+
+    &print_known_hosts_hash();
+    &print_known_clients();
+    daemon_log(" \n\n", 1);
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  open_socket
+#   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
+#                [PeerPort] string necessary if port not appended by PeerAddr
+#      RETURNS:  socket IO::Socket::INET
+#  DESCRIPTION:
+#===============================================================================
+sub open_socket {
+    my ($PeerAddr, $PeerPort) = @_ ;
+    if(defined($PeerPort)){
+        $PeerAddr = $PeerAddr.":".$PeerPort;
+    }
+    my $socket;
+    $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
+            Porto => "tcp" ,
+            Type => SOCK_STREAM,
+            Timeout => 5,
+            );
+    if(not defined $socket) {
+        #daemon_log("cannot connect to socket at $PeerAddr, $@\n");
+        return;
+    }
+    daemon_log("open_socket:\n\t$PeerAddr", 7);
+    return $socket;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  read_from_socket
+#   PARAMETERS:  socket fh - 
+#      RETURNS:  result string - readed characters from socket
+#  DESCRIPTION:  reads data from socket in 16 byte steps
+#===============================================================================
+sub read_from_socket {
+    my ($socket) = @_;
+    my $result = "";
+    my $len = 16;
+    while($len == 16){
+        my $char;
+        $len = sysread($socket, $char, 16);
+        if($len != 16) { last }
+        #print "$char\n";
+        #print "              $len\n";
+        if($len != 16) { last }
+        $result .= $char;
+    }
+    return $result;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_xml_hash
+#   PARAMETERS:  
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub create_xml_hash {
+    my ($header, $source, $target) = @_;
+    my $hash = {
+            header => [$header],
+            source => [$source],
+            target => [$target],
+            $header => [],
+    };
+    daemon_log("create_xml_hash:", 7),
+    chomp(my $tmp = Dumper $hash);
+    daemon_log("\t$tmp\n", 7);
+    return $hash
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_xml_string
+#   PARAMETERS:  
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub create_xml_string {
+    my ($xml_hash) = @_ ;
+    my $xml_string = $xml->XMLout($xml_hash, RootName => 'xml');
+    $xml_string =~ s/[\s]+//g;
+    daemon_log("create_xml_string:\n\t$xml_string\n", 7);
+    return $xml_string;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  add_content2xml_hash
+#   PARAMETERS:  
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub add_content2xml_hash {
+    my ($xml_ref, $element, $content) = @_;
+    if(not exists $$xml_ref{$element} ) {
+        $$xml_ref{$element} = [];
+    }
+    my $tmp = $$xml_ref{$element};
+    push(@$tmp, $content);
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  get_content_from_xml_hash
+#   PARAMETERS:  
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub get_content_from_xml_hash {
+    my ($xml_ref, $element) = @_;
+    my $result = $xml_ref->{$element};
+    if( @$result == 1) {
+        $result = @$result[0];
+    }
+    return $result;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  encrypt_msg
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub encrypt_msg {
+    my ($msg, $my_cipher) = @_;
+    if(not defined $my_cipher) { print "no cipher object\n"; }
+    $msg =~ s/[\s]+//g;
+    my $msg_length = length($msg);
+    my $multiplier = int($msg_length / 16) + 1;
+    my $extension = 16*$multiplier - $msg_length;
+    $msg = "a"x$extension.$msg;
+    my $crypted_msg = $my_cipher->encrypt($msg);
+    #my $crypted_msg = $msg;
+    return $crypted_msg;
+}
+
+#===  FUNCTION  ================================================================
+#         NAME:  decrypt_msg
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub decrypt_msg {
+    my ($crypted_msg, $my_cipher) =@_;
+    my $msg = $my_cipher->decrypt($crypted_msg);
+    #my $msg = $crypted_msg;
+    $msg =~ s/^a*//gi;
+    return $msg;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_ciphering
+#   PARAMETERS:  
+#      RETURNS:  cipher object
+#  DESCRIPTION:  
+#===============================================================================
+sub create_ciphering {
+    my ($passwd) = @_;
+    my $passwd_length = length($passwd);
+    my $multiplier = int($passwd_length / 32) + 1;
+    my $extension = 32*$multiplier - $passwd_length;
+    $passwd = "a"x$extension.$passwd;
+
+    my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC );
+    return $my_cipher;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  send_msg_hash2address
+#   PARAMETERS:  msg string - xml message
+#                PeerAddr string - socket address to send msg
+#                PeerPort string - socket port, if not included in socket address
+#      RETURNS:  nothing
+#  DESCRIPTION:  ????
+#===============================================================================
+sub send_msg_hash2address {
+    my ($msg_hash, $address) = @_ ;
+
+# fetch header for logging
+    my $header = &get_content_from_xml_hash($msg_hash, "header");
+# generiere xml string
+    my $msg_xml = &create_xml_string($msg_hash);
+# hole das entsprechende passwd aus dem hash
+    my $passwd;
+    if(exists $known_hosts->{$address}) {
+        $passwd = $known_hosts->{$address}->{passwd};
+    } elsif(exists $known_clients->{$address}) {
+        $passwd = $known_clients->{$address}->{passwd};
+    } else {
+        daemon_log("$address not known, neither as server nor as client", 1);
+        return;
+    }
+# erzeuge ein ciphering object
+    my $act_cipher = &create_ciphering($passwd);
+# encrypt xml msg
+    my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
+# öffne socket
+    my $socket = &open_socket($address);
+    if(not defined $socket){
+        daemon_log( "cannot send '$header'-msg to $address , server not reachable\n", 5);
+        return;
+    }
+# versende xml msg
+    print $socket $crypted_msg."\n";
+# schließe socket
+    close $socket;
+
+    daemon_log("send '$header'-msg to $address", 5);
+    daemon_log("crypted_msg:\n\t$crypted_msg", 7);
+
+    return
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  send_msg_hash2bus {
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION:
+#===============================================================================
+sub send_msg_hash2bus {
+    my($msg_hash) = @_;
+
+# fetch header for logging
+    my $header = &get_content_from_xml_hash($msg_hash, "header");    
+# generiere xml string
+    my $msg_xml = $xml->XMLout($msg_hash, RootName => 'xml');
+# encrypt xml msg 
+    my $crypted_msg = &encrypt_msg($msg_xml, $bus_cipher);
+# setze msg_to_bus
+    #$msg_to_bus = $crypted_msg;
+
+    daemon_log("send '$header'-msg to bus", 1);
+    daemon_log("crypted msg:\n\t$crypted_msg", 7);
+
+    return;
+}
+
+
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  register_at_bus
+#   PARAMETERS:  
+#      RETURNS:  
+#  DESCRIPTION:  
+#===============================================================================
+sub register_at_bus {
+    my ($tmp) = @_;
+
+    # create known_hosts entry
+    &create_known_hosts_entry($bus_address);
+    &update_known_hosts_entry($bus_address, "register_at_bus", $bus_passwd);
+
+    my $msg_hash = &create_xml_hash("here_i_am", "$server_ip:$server_port", $bus_address);
+    &send_msg_hash2address($msg_hash, $bus_address);
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  new_passwd
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub new_passwd {
+    my ($msg_hash) = @_;
+    
+    my $source = &get_content_from_xml_hash($msg_hash, "source");
+    my $passwd = &get_content_from_xml_hash($msg_hash, "new_passwd");
+
+    &update_known_hosts_entry($source, "new_passwd", $passwd);
+
+    $bus_cipher = &create_ciphering($passwd);
+    my $hash = &create_xml_hash("confirm_new_passwd", "$server_ip:$server_port", "$source");
+    &send_msg_hash2address($hash, $source);
+    return
+}
+
+#===  FUNCTION  ================================================================
+#         NAME:  make_ping
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub make_ping {
+    my ($msg_hash) = @_;
+
+    my $source = &get_content_from_xml_hash($msg_hash, "source");
+    my $target = &get_content_from_xml_hash($msg_hash, "target");
+    
+    print "make_ping:$source\n";
+    my $out_hash = &create_xml_hash("ping", $target, $source);
+    &send_msg_hash2address($out_hash, $source);
+    return;
+}
+
+#===  FUNCTION  ================================================================
+#         NAME:  make_ping
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub got_ping {
+    my ($msg_hash) = @_;
+    
+    my $source = &get_content_from_xml_hash($msg_hash, 'source');
+    my $target = &get_content_from_xml_hash($msg_hash, 'target');
+    my $header = &get_content_from_xml_hash($msg_hash, 'header');    
+    
+    if(exists $known_hosts->{$target}) {
+        &update_known_hosts_entry($target, $header);
+    } else {
+       &update_known_clients($target, $header);
+    }
+    
+    my $out_hash = &create_xml_hash("got_ping", $target, $source);
+    &send_msg_hash2address($out_hash, $source);
+    
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  here_i_am
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION:
+#===============================================================================
+sub here_i_am {
+    my ($msg_hash) = @_;
+
+    my $source = &get_content_from_xml_hash($msg_hash, "source");
+    my $new_passwd = &get_content_from_xml_hash($msg_hash, "new_passwd");
+
+    # create known_hosts entry
+    &create_known_client($source);
+    &update_known_clients($source, "registered", $new_passwd);
+
+    # return acknowledgement to client
+    my $out_hash = &create_xml_hash("registered", $server_address, $source);
+    &send_msg_hash2address($out_hash, $source);
+
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  print_known_hosts_hash
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub print_known_hosts_hash {
+    my ($hash) = @_;
+    print "####################################\n";
+    print "# status of known_hosts\n";
+    my $hosts;
+    my $host_hash;
+    $shmkh->shlock(LOCK_EX);
+    my @hosts = keys %$known_hosts;
+    foreach my $host (@hosts) {
+        #my @elements = keys %$known_hosts->{$host};
+        my $status = $known_hosts->{$host}->{status} ;
+        my $passwd = $known_hosts->{$host}->{passwd};
+        my $timestamp = $known_hosts->{$host}->{timestamp};
+        print "$host\n";
+        print "\t$status\n";
+        print "\t$passwd\n";
+        print "\t$timestamp\n";
+    }
+    $shmkh->shunlock(LOCK_EX);
+    print "####################################\n";
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  print_known_clients 
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub print_known_clients {
+    my ($hash) = @_;
+    print "####################################\n";
+    print "# status of known_clients\n";
+    my $hosts;
+    my $host_hash;
+    $shmcl->shlock(LOCK_EX);
+    my @hosts = keys %$known_clients;
+    foreach my $host (@hosts) {
+        #my @elements = keys %$known_hosts->{$host};
+        my $status = $known_clients->{$host}->{status} ;
+        my $passwd = $known_clients->{$host}->{passwd};
+        my $timestamp = $known_clients->{$host}->{timestamp};
+        print "$host\n";
+        print "\t$status\n";
+        print "\t$passwd\n";
+        print "\t$timestamp\n";
+    }
+    $shmcl->shunlock(LOCK_EX);
+    print "####################################\n";
+    return;
+}
+
+
+sub create_known_hosts_entry {
+    my ($hostname) = @_;
+    $shmkh->shlock(LOCK_EX);
+    $known_hosts->{$hostname} = {};
+    $known_hosts->{$hostname}->{status} = "none";
+    $known_hosts->{$hostname}->{passwd} = "none";
+    $known_hosts->{$hostname}->{timestamp} = "none";
+    $shmkh->shunlock(LOCK_EX); 
+    return;  
+}
+
+
+sub create_known_client {
+    my ($hostname) = @_;
+    $shmcl->shlock(LOCK_EX);
+    $known_clients->{$hostname} = {};
+    $known_clients->{$hostname}->{status} = "none";
+    $known_clients->{$hostname}->{passwd} = "none";
+    $known_clients->{$hostname}->{timestamp} = "none";
+    $shmcl->shunlock(LOCK_EX); 
+    return;  
+}
+
+
+sub update_known_hosts_entry {
+    my ($hostname, $status, $passwd, $timestamp) = @_;
+    my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat,
+    $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time);
+    $Stunden = $Stunden < 10 ? $Stunden = "0".$Stunden : $Stunden;
+    $Minuten = $Minuten < 10 ? $Minuten = "0".$Minuten : $Minuten;
+    $Sekunden = $Sekunden < 10 ? $Sekunden = "0".$Sekunden : $Sekunden;
+    $Monat+=1;
+    $Monat = $Monat < 10 ? $Monat = "0".$Monat : $Monat;
+    $Monatstag = $Monatstag < 10 ? $Monatstag = "0".$Monatstag : $Monatstag;
+    $Jahr+=1900;
+    my $t = "$Jahr$Monat$Monatstag$Stunden$Minuten$Sekunden";
+
+    $shmkh->shlock(LOCK_EX);
+    if($status) {
+        $known_hosts->{$hostname}->{status} = $status;
+    }
+    if($passwd) {
+        $known_hosts->{$hostname}->{passwd} = $passwd;
+    }
+    if($timestamp) {
+        $t = $timestamp;
+    }
+    $known_hosts->{$hostname}->{timestamp} = $t;
+    $shmkh->shunlock(LOCK_EX); 
+    return;  
+}
+
+sub update_known_clients {
+    my ($hostname, $status, $passwd, $timestamp) = @_;
+    my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat,
+    $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time);
+    $Stunden = $Stunden < 10 ? $Stunden = "0".$Stunden : $Stunden;
+    $Minuten = $Minuten < 10 ? $Minuten = "0".$Minuten : $Minuten;
+    $Sekunden = $Sekunden < 10 ? $Sekunden = "0".$Sekunden : $Sekunden;
+    $Monat+=1;
+    $Monat = $Monat < 10 ? $Monat = "0".$Monat : $Monat;
+    $Monatstag = $Monatstag < 10 ? $Monatstag = "0".$Monatstag : $Monatstag;
+    $Jahr+=1900;
+    my $t = "$Jahr$Monat$Monatstag$Stunden$Minuten$Sekunden";
+
+    $shmcl->shlock(LOCK_EX);
+    if($status) {
+        $known_clients->{$hostname}->{status} = $status;
+    }
+    if($passwd) {
+        $known_clients->{$hostname}->{passwd} = $passwd;
+    }
+    if($timestamp) {
+        $t = $timestamp;
+    }
+    $known_clients->{$hostname}->{timestamp} = $t;
+    $shmcl->shunlock(LOCK_EX); 
+    return;  
+}
+
+      
+
+#===  FUNCTION  ================================================================
+#         NAME:  open_fifo
+#   PARAMETERS:  $fifo_path
+#      RETURNS:  0: FIFO couldn"t be setup, 1: FIFO setup correctly
+#  DESCRIPTION:  creates a FIFO at $fifo_path
+#===============================================================================
+sub open_fifo {
+    my ($fifo_path) = @_ ;
+    if( -p $fifo_path ) {
+        daemon_log("FIFO at $fifo_path already exists\n", 1);
+        return 0;
+    }
+    POSIX::mkfifo($fifo_path, 0666) or die "can't mkfifo $fifo_path: $!";
+    daemon_log( "\nFIFO started at $fifo_path\n", 1) ;
+    return 1;
+}
+
+
+
+
+
+
+#==== MAIN = main ==============================================================
+
+#  parse commandline options
+Getopt::Long::Configure( "bundling" );
+GetOptions("h|help" => \&usage,
+           "c|config=s" => \$cfg_file,
+           "f|foreground" => \$foreground,
+           "v|verbose+" => \$verbose,
+           );
+
+#  read and set config parameters
+&read_configfile;
+&check_cmdline_param ;
+&check_pid;
+
+$SIG{CHLD} = 'IGNORE';
+
+# restart daemon log file
+if(-e $log_file ) { unlink $log_file }
+daemon_log("started!");
+
+# Just fork, if we"re not in foreground mode
+if( ! $foreground ) { $pid = fork(); }
+else { $pid = $$; }
+
+# Do something useful - put our PID into the pid_file
+if( 0 != $pid ) {
+    open( LOCK_FILE, ">$pid_file" );
+    print LOCK_FILE "$pid\n";
+    close( LOCK_FILE );
+    if( !$foreground ) { exit( 0 ) };
+}
+
+# setup xml parser
+$xml = new XML::Simple();
+
+# create cipher object
+$bus_cipher = &create_ciphering($bus_passwd);
+$bus_address = "$bus_ip:$bus_port";
+
+# create reading and writing vectors
+my $rbits = my $wbits = my $ebits = "";
+
+# open server socket
+$server_address = "$server_ip:$server_port";
+if($server_activ eq "on"){
+    $server = IO::Socket::INET->new(LocalPort => $server_port,
+            Type => SOCK_STREAM,
+            Reuse => 1,
+            Listen => 20,
+            ); 
+    if(not defined $server){
+        daemon_log("cannot be a tcp server at $server_port : $@\n");
+    } else {
+        daemon_log("start server:\n\t$server_ip:$server_port",1) ;
+        vec($rbits, fileno $server, 1) = 1;
+        vec($wbits, fileno $server, 1) = 1;
+    }
+
+    &create_known_client($server_address);
+    &update_known_clients($server_address, "server", $server_passwd);
+    &print_known_clients()
+}
+
+# register at bus
+if($bus_activ eq "on") {
+    &register_at_bus();
+}
+
+# start arp fifo
+my $my_fifo;
+if($arp_activ eq "on") {
+    $my_fifo = &open_fifo($arp_fifo_path);
+    if($my_fifo == 0) { die "fifo file disappeared\n" }
+    sysopen($arp_fifo, $arp_fifo_path, O_RDWR) or die "can't read from $arp_fifo: $!" ;
+    
+    vec($rbits, fileno $arp_fifo, 1) = 1;
+}
+
+
+###################################
+#everything ready, okay, lets start
+###################################
+while(1) {
+    my ($rout, $wout);
+    my $nf = select($rout=$rbits, $wout=$wbits, undef, undef);
+
+# error handling
+    if($nf < 0 ) {
+    }
+
+# something is coming in
+    if(vec $rout, fileno $server, 1) {
+        my $client = $server->accept();
+        my $other_end = getpeername($client);
+        if(not defined $other_end) {
+            daemon_log("client cannot be identified: $!\n");
+        } else {
+            my ($port, $iaddr) = unpack_sockaddr_in($other_end);
+            my $actual_ip = inet_ntoa($iaddr);
+            daemon_log("\naccept client from $actual_ip\n", 5);
+            #my $in_msg = <$client>;
+            my $in_msg = &read_from_socket($client);
+            if(defined $in_msg){
+                chomp($in_msg);
+                &activating_child($in_msg, $actual_ip);
+            } else {
+                daemon_log("cannot read from $actual_ip\n", 5);
+            }
+        }
+        close($client);
+    }
+
+    if($arp_activ eq "on" && vec($rout, fileno $arp_fifo, 1)) {
+        my $in_msg = <$arp_fifo>;
+        print "arp_activ: msg: $in_msg\n";
+        my $act_passwd = $known_hosts->{$bus_address}->{passwd};
+        print "arp_activ: arp_passwd: $act_passwd\n";
+        my $arp_cipher = &create_ciphering($act_passwd);
+        my $crypted_msg = &encrypt_msg($in_msg, $arp_cipher);
+        print "arp_activ: server_ip:$server_ip\n";
+        &activating_child($crypted_msg, $server_ip);
+        
+    }
+
+
+
+# something is going out
+    #if(vec $wbits, fileno $bus, 1){ 
+    #    print "msg leaving server to bus:$msg_to_bus\n";
+    #    print $bus $msg_to_bus."\n";
+    #    $msg_to_bus = "";
+
+    #}
+    
+}
+
+
+
+
index 230002dd9ba3c51a1e5ee5c170385d6b31f7f289..a6f1ba0370c1f8429efa91c7904f3cd2428d05cc 100755 (executable)
@@ -30,21 +30,33 @@ use Crypt::Rijndael;
 use XML::Simple;
 use Data::Dumper;
 use Sys::Syslog qw( :DEFAULT setlogsock);
+use IPC::Shareable qw( :lock);
+IPC::Shareable->clean_up_all;
 
-my ($cfg_file, %cfg_defaults, $foreground);
-my ($bus_passwd, $bus_ip, $bus_port, $bus);
-my ($pid_file, $procid, $pid, $log_file);
+my ($cfg_file, %cfg_defaults, $foreground, $verbose);
+my ($bus_activ, $bus_passwd, $bus_ip, $bus_port, $bus_address, $bus);
+my ($pid_file, $procid, $pid, $log_file, $my_own_address);
 my (%free_child, %busy_child, $child_max, $child_min, %child_alive_time, $child_timeout);
+my ($xml, $bus_cipher, $known_hosts, $shmkh);
 
 $foreground = 0 ;
+$known_hosts = {};
+$shmkh = tie($known_hosts, 'IPC::Shareable', undef, {create => 1, 
+                                                            exclusive => 1, 
+                                                            mode => 0666, 
+                                                            destroy => 1,
+                                                            });
+
+
 %cfg_defaults =
 ("general" =>
-    {"log_file" => [\$log_file, "/var/run/gosa-server-bus.log"],
-    "pid_file" => [\$pid_file, "/var/run/gosa-server-bus.pid"],
+    {"log_file" => [\$log_file, "/var/run/".$0.".log"],
+    "pid_file" => [\$pid_file, "/var/run/".$0.".pid"],
 
     },
 "bus" =>
-    {"bus_passwd" => [\$bus_passwd, "tester78901234567890123456789012"],
+    {"bus_activ" => [\$bus_activ, "on"],
+    "bus_passwd" => [\$bus_passwd, "tester78901234567890123456789012"],
     "bus_ip" => [\$bus_ip, "10.89.1.155"],
     "bus_port" => [\$bus_port, "10001"],
     "child_max" => [\$child_max, 10],
@@ -96,8 +108,10 @@ sub daemon_log {
             print STDERR "cannot open $log_file: $!";
             return }
         chomp($msg);
-        print LOG_HANDLE $msg."\n";
-        if(defined $foreground) { print $msg."\n" }
+        if($level <= $verbose){
+            print LOG_HANDLE $msg."\n";
+            if(defined $foreground) { print $msg."\n" }
+        }
     }
     close( LOG_HANDLE );
 #    my ($msg, $level, $facility) = @_;
@@ -146,7 +160,7 @@ sub check_pid {
             chomp( $pid );
             if( -f "/proc/$pid/stat" ) {
                 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
-                if( "faimond" eq $stat ) {
+                if( $0 eq $stat ) {
                     close( LOCK_FILE );
                     exit -1;
                 }
@@ -155,10 +169,10 @@ sub check_pid {
         close( LOCK_FILE );
         unlink( $pid_file );
     }
-    # Try to open PID file
-    if (!sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
-        my($msg) = "Couldn't obtain lockfile '$pid_file' ";
 
+    # create a syslog msg if it is not to possible to open PID file
+    if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
+        my($msg) = "Couldn't obtain lockfile '$pid_file' ";
         if (open(LOCK_FILE, '<', $pid_file)
                 && ($pid = <LOCK_FILE>))
         {
@@ -168,7 +182,7 @@ sub check_pid {
             $msg .= "(unable to read PID)\n";
         }
         if( ! ($foreground) ) {
-            openlog( "gosa-server-bus", "cons,pid", "daemon" );
+            openlog( $0, "cons,pid", "daemon" );
             syslog( "warning", $msg );
             closelog();
         }
@@ -179,6 +193,7 @@ sub check_pid {
     }
 }
 
+
 #===  FUNCTION  ================================================================
 #         NAME:  usage
 #   PARAMETERS: 
@@ -201,6 +216,13 @@ EOF
         print "\n" ;
 }
 
+
+#===  FUNCTION  ================================================================
+#         NAME:  sig_int_handler
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
 sub sig_int_handler {
     my ($signal) = @_;
     if($bus){
@@ -208,10 +230,12 @@ sub sig_int_handler {
         print "$bus closed\n";
     }
     print "$signal\n";
+    IPC::Shareable->clean_up;
     exit(1);
 }
 $SIG{INT} = \&sig_int_handler;
 
+
 #===  FUNCTION  ================================================================
 #         NAME:  activating_child
 #   PARAMETERS:
@@ -219,13 +243,13 @@ $SIG{INT} = \&sig_int_handler;
 #  DESCRIPTION:
 #===============================================================================
 sub activating_child {
-    my ($msg) = @_;
+    my ($msg, $host) = @_;
     my $child = &get_processing_child();
     my $pipe_wr = $$child{'pipe_wr'};
-    print "activating: childpid:$$child{'pid'}\n";
-    #print "activating: pipe_wr:$pipe_wr\n";
-    print $pipe_wr $msg."\n";
-    #print "activating: done\n";
+
+    daemon_log("activating: childpid: $$child{'pid'}", 5);
+
+    print $pipe_wr $msg.".".$host."\n";
     return;
 
 }
@@ -245,7 +269,7 @@ sub get_processing_child {
         my $exitus_pid = waitpid($key, WNOHANG);
         if($exitus_pid != 0) {
             delete $busy_child{$key};
-            print "prozess:$key wurde aus busy_child entfernt\n";
+            daemon_log( "prozess:$key wurde aus busy_child entfernt\n", 5);
             next;
         }
 
@@ -265,16 +289,14 @@ sub get_processing_child {
         my $exitus_pid = waitpid($key, WNOHANG);
         if($exitus_pid != 0) {
             delete $free_child{$key};
-            print "prozess:$key wurde aus free_child entfernt\n";
+            daemon_log( "prozess:$key wurde aus free_child entfernt\n", 5);
         }
-        print "free child:$key\n";
+        daemon_log("free child:$key\n", 5);
     }
-
-
     # teste @free_child und @busy_child
     my $free_len = scalar(keys(%free_child));
     my $busy_len = scalar(keys(%busy_child));
-    print "free children $free_len, busy children $busy_len\n";
+    daemon_log("free children $free_len, busy children $busy_len\n",5);
     
     # gibt es bereits ein freies kind, dann lass es arbeiten
     if($free_len > 0){
@@ -290,7 +312,7 @@ sub get_processing_child {
     # no free child, try to fork another one 
     if($free_len + $busy_len < $child_max) {
                 
-        print "not enough children, create a new one\n";
+        daemon_log("not enough children, create a new one\n",5);
 
         # New pipes for communication
         my( $PARENT_wr, $PARENT_rd );
@@ -318,9 +340,6 @@ sub get_processing_child {
                     # wenn $nf < 1, error handling
                     die "select(): $!\n";
                 } elsif (! $nf) {
-                    # seit timeout ist nichts mehr passiert,
-                    print "timeout!!\n";
-
                     # ist dieses kind nicht eines der letzenen $child_min, dann springe aus while schleife raus
                     $free_len = scalar(keys(%free_child));
                     $busy_len = scalar(keys(%busy_child));
@@ -333,16 +352,23 @@ sub get_processing_child {
 
                 # ansonsten
                 if ( vec $rbits, fileno $PARENT_rd, 1 ) {
-                    my $msg = <$PARENT_rd>;
+                    # hole alles was in der pipe ist
+                    my $msg = "";
+                    $PARENT_rd->blocking(0);
+                    while(1) {
+                        my $read = <$PARENT_rd>;
+                        if(not defined $read) { last}
+                        $msg .= $read;
+                    }                   
                     &process_incoming_msg($msg);
-                    
-                    # schreibe an den parent_wr zurück 'done' 
-                    print $PARENT_wr "done"."\n";
-                    
+                    daemon_log("processing of msg finished", 5);
+
+                    # wichtig!!! erst mit dem done wird das child nach free_child geschoben
+                    print $PARENT_wr "done";
                     redo;
                 }
             }
-            # child die die while-schleife verlassen haben, "stirbt!"
+            # kinder die die while-schleife verlassen haben sterben
             exit(0);
 
         #PARENT
@@ -350,7 +376,6 @@ sub get_processing_child {
             # Close unused pipes
             close( $PARENT_rd );
             close( $PARENT_wr );
-            
             # add child to child alive hash
             my %child_hash = (
                     'pid' => $child_pid,
@@ -373,12 +398,501 @@ sub get_processing_child {
 #  DESCRIPTION:
 #===============================================================================
 sub process_incoming_msg {
-    my ($msg) = @_;
-    print "msg to process:$msg\n";
-    sleep(10);
+    my ($crypted_msg) = @_;
+    if(not defined $crypted_msg) {
+        daemon_log("function 'process_incoming_msg': got no msg", 7);
+        return;
+    }
+    $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
+    $crypted_msg = $1;
+    my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
+
+    daemon_log("msg from host:\n\t$host", 1);
+    daemon_log("crypted_msg:\n\t$crypted_msg", 7);
+
+    my @valid_keys;
+    my @host_keys = keys %$known_hosts;
+    my $l = @host_keys;
+    daemon_log("number of host_keys: $l\n", 7);
+
+    foreach my $host_key (@host_keys) {    
+        if($host_key =~ "^$host") {
+            push(@valid_keys, $host_key);
+        }
+    }
+    
+    my ($msg, $msg_hash);
+    my $msg_flag = 0;    
+
+    # ermittle alle in frage kommenden known_hosts einträge
+    foreach my $host_key (@valid_keys) {
+        eval{
+            daemon_log( "key: $host_key\n", 7);
+            my $key_passwd = $known_hosts->{$host_key}->{passwd};
+            daemon_log("key_passwd: $key_passwd\n", 7);
+            my $key_cipher = &create_ciphering($key_passwd);
+            $msg = &decrypt_msg($crypted_msg, $key_cipher);
+            $msg_hash = $xml->XMLin($msg, ForceArray=>1);
+        };
+        if($@) {
+            daemon_log("key raise error", 7);
+            $msg_flag += 1;
+        } else {
+            last;
+        }
+    } 
+    
+    if($msg_flag >= $l)  {
+        daemon_log("\nERROR: do not understand the message:\n\t$msg" , 1);
+        return;
+    }
+
+    my $header = &get_content_from_xml_hash($msg_hash, "header");
+    my $target = &get_content_from_xml_hash($msg_hash, "target");
+
+    daemon_log("header from msg:\n\t$header", 1);
+    daemon_log("msg to process:\n\t$msg", 7);
+
+    if($target eq "$bus_ip:$bus_port") {
+        if($header eq 'here_i_am'){ &here_i_am($msg_hash)}
+        elsif($header eq 'confirm_new_passwd'){ &confirm_new_passwd($msg_hash)}
+        elsif($header eq 'got_ping') { &got_ping($msg_hash)} 
+        elsif($header eq 'ping') { &ping($msg_hash)}
+    } else {
+        print "bus leitet $header an $target weiter\n";
+        # bus ist nicht der ziel rechner
+        # leite msg an ziel rechner weiter
+
+    }
+
+    &print_known_hosts_hash();
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  get_content_of_known_hosts
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub get_content_of_known_hosts {
+    my ($host, $content) = @_;
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  encrypt_msg
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub encrypt_msg {
+    my ($msg, $my_cipher) = @_;
+    $msg =~ s/[\s]+//g;
+    my $msg_length = length($msg);
+    my $multiplier = int($msg_length / 16) + 1;
+    my $extension = 16*$multiplier - $msg_length;
+    $msg = "a"x$extension.$msg;
+    my $crypted_msg = $my_cipher->encrypt($msg);
+    #my $crypted_msg = $msg;
+    return $crypted_msg;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  decrypt_msg
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub decrypt_msg {
+    my ($crypted_msg, $my_cipher) =@_;
+    my $len = length $crypted_msg;
+    my $msg = $my_cipher->decrypt($crypted_msg);
+    #my $msg = $crypted_msg;
+    $msg =~ s/^a*//gi;
+    return $msg;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_xml_hash
+#   PARAMETERS:  
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub create_xml_hash {
+    my ($header, $source, $target) = @_;
+    my $hash = {
+            header => [$header],
+            source => [$source],
+            target => [$target],
+            $header => [],
+    };
+    daemon_log("create_xml_hash:", 7),
+    chomp(my $tmp = Dumper $hash);
+    daemon_log("\t$tmp\n", 7);
+    return $hash
+}
+
+
+sub create_xml_string {
+    my ($xml_hash) = @_ ;
+    my $xml_string = $xml->XMLout($xml_hash, RootName => 'xml');
+    $xml_string =~ s/[\s]+//g;
+    return $xml_string;
+}
+
+
+sub add_content2xml_hash {
+    my ($xml_ref, $element, $content) = @_;
+    if(not exists $$xml_ref{$element} ) {
+        $$xml_ref{$element} = [];
+    }
+    my $tmp = $$xml_ref{$element};
+    push(@$tmp, $content);
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  get_content_from_xml_hash
+#   PARAMETERS:
+#      RETURNS:
+#  DESCRIPTION:
+#===============================================================================
+sub get_content_from_xml_hash {
+    my ($xml_ref, $element) = @_;
+    my $result = $xml_ref->{$element};
+    if( $element eq "header" || $element eq "target" || $element eq "source") {
+        $result = @$result[0];
+    }
+    return $result;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  open_socket
+#   PARAMETERS:  PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
+#                [PeerPort] string necessary if port not appended by PeerAddr
+#      RETURNS:  socket IO::Socket::INET
+#  DESCRIPTION:
+#===============================================================================
+sub open_socket {
+    my ($PeerAddr, $PeerPort) = @_ ;
+    if(defined($PeerPort)){
+        $PeerAddr = $PeerAddr.":".$PeerPort;
+    }
+    my $socket;
+    $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
+            Porto => "tcp" ,
+            Type => SOCK_STREAM,
+            Reuse => 1,
+            Timeout => 5,
+            );
+    if(not defined $socket) {
+        #daemon_log("cannot connect to socket at $PeerAddr, $@\n");
+        return;
+    }
+    return $socket;
+}
+
+#===  FUNCTION  ================================================================
+#         NAME:  read_from_socket
+#   PARAMETERS:  socket fh - 
+#      RETURNS:  result string - readed characters from socket
+#  DESCRIPTION:  reads data from socket in 16 byte steps
+#===============================================================================
+sub read_from_socket {
+    my ($socket) = @_;
+    my $result = "";
+    my $len = 16;
+    while($len == 16){
+        my $char;
+        $len = sysread($socket, $char, 16);
+        if($len != 16) { last }
+        #print "$char\n";
+        #print "              $len\n";
+        if($len != 16) { last }
+        $result .= $char;
+    }
+    return $result;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  send_msg_hash2address
+#   PARAMETERS:  msg string - xml message
+#                PeerAddr string - socket address to send msg
+#                PeerPort string - socket port, if not included in socket address
+#      RETURNS:  nothing
+#  DESCRIPTION:  ????
+#===============================================================================
+sub send_msg_hash2address {
+    my ($msg_hash, $address) = @_ ;
+
+    # fetch header for logging
+    my $header = &get_content_from_xml_hash($msg_hash, "header");
+    # generiere xml string
+    my $msg_xml = &create_xml_string($msg_hash);
+    # hole das entsprechende passwd aus dem hash
+    my $passwd = $known_hosts->{$address}->{passwd};
+    # erzeuge ein ciphering object
+    my $act_cipher = &create_ciphering($passwd);
+    # encrypt xml msg
+    my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
+    # öffne socket
+    my $socket = &open_socket($address);
+    if(not defined $socket){
+        print "cannot send '$header'-msg to $address , server not reachable\n";
+        return;
+    }
+    # versende xml msg
+    print $socket $crypted_msg."\n";
+    # schließe socket
+    close $socket;
+    daemon_log("send '$header'-msg to $address", 5);
+    daemon_log("crypted_msg:\n\t$crypted_msg", 7);
+    return
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  
+#   PARAMETERS:
+#      RETURNS:  
+#  DESCRIPTION:
+#===============================================================================
+sub send_msg_hash2all {
+    my ($msg_hash) = @_;
+    # fetch header for logging
+    my $header = &get_content_from_xml_hash($msg_hash, "header");
+    # generiere xml string
+    my $msg_xml = &create_xml_string($msg_hash);
+    # hole die liste von target adressen
+    my @targets = keys(%$known_hosts);
+    # itteriere durch liste und schicke an jeden msg_xml
+    foreach my $target (@targets) {
+        if($target eq $bus_address) {next};   # schicke die nachricht nicht an den bus
+        # hole das entsprechende passwd aus dem hash
+        my $passwd = $known_hosts->{$target}->{passwd};
+        # erzeuge ein ciphering object
+        my $act_cipher = &create_ciphering($passwd);
+        # encrypt xml msg
+        my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
+        # öffne socket
+        my $socket = &open_socket($target);
+        if(not defined $socket){
+            # hole status für den server aus hash
+            my $status = $known_hosts->{$target}->{status};
+            if(not $status eq "down") {
+                daemon_log("cannot open socket to $target , server not reachable", 1);
+                # update status
+                &update_known_hosts_entry($target, "down");
+            }
+            next;
+        }
+        # versende xml msg
+        print $socket $crypted_msg."\n";
+        # schließe socket
+        close $socket;
+        daemon_log("send '$header'-msg to $target", 5);
+        daemon_log("crypted_msg:\n\t$crypted_msg", 7);
+    }
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_ciphering_object
+#   PARAMETERS:
+#      RETURNS:  cipher object
+#  DESCRIPTION:
+#===============================================================================
+sub create_ciphering {
+    my ($passwd) = @_;
+    my $passwd_length = length $passwd;
+    my $multiplier = int($passwd_length / 32) + 1;
+    my $extension = 32*$multiplier - $passwd_length;
+    $passwd = "a"x$extension.$passwd;
+
+    my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC );
+    return $my_cipher;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  create_passwd
+#   PARAMETERS:
+#      RETURNS:  cipher object
+#  DESCRIPTION:
+#===============================================================================
+sub create_passwd {
+    my $new_passwd = "";
+    for(my $i=0; $i<31; $i++) {
+        $new_passwd .= ("a".."z","A".."Z",0..9)[int(rand(62))]
+    }
+
+    return $new_passwd;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  here_i_am
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION:
+#===============================================================================
+sub here_i_am {
+    my ($msg_hash) = @_;
+    my $source = &get_content_from_xml_hash($msg_hash, "source");
+    #my $timestamp = time;
+
+    my $new_passwd = &create_passwd();
+
+    # create known_hosts entry
+    &create_known_hosts_entry($source);
+    &update_known_hosts_entry($source, "registered", $bus_passwd);
+
+    # create outgoing msg
+    my $out_hash = &create_xml_hash("new_passwd", "$bus_ip:$bus_port", $source);
+    &add_content2xml_hash($out_hash, "new_passwd", $new_passwd);
+    &send_msg_hash2address($out_hash, $source);
+
+    # change passwd, reason
+    # &send_msg_hash2address takes $known_hosts->{"$source"}->{passwd} to cipher msg
+    &update_known_hosts_entry($source, "new_passwd", $new_passwd);
+
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  confirm_new_passwd
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub confirm_new_passwd {
+    my ($msg_hash) = @_;
+    my $source = &get_content_from_xml_hash($msg_hash, "source");
+    &update_known_hosts_entry($source, "confirmed_new_passwd");
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  ping
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub ping {
+    my ($msg_hash) = @_;
+    my $source = &get_content_from_xml_hash($msg_hash, "source");   
+    my $out_hash = &create_xml_hash("got_ping", $bus_address, $source);
+    &send_msg_hash2address($out_hash, $source);
+    return;
+}
+
+
+#===  FUNCTION  ================================================================
+#         NAME:  make ping
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub make_ping {
+    my ($address) = @_;
+    daemon_log("ping:$address\n", 1);
+    my $out_hash = &create_xml_hash("ping", "$bus_ip:$bus_port", $address);
+    &send_msg_hash2address($out_hash, $address);
     return;
 }
 
+
+#===  FUNCTION  ================================================================
+#         NAME:  got_ping
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub got_ping {
+    my ($msg_hash) = @_;
+    my $source = &get_content_from_xml_hash($msg_hash, "source");
+    &update_known_hosts_entry($source, "got_ping");
+    return;
+}
+
+#===  FUNCTION  ================================================================
+#         NAME:  print_known_hosts_hash
+#   PARAMETERS:
+#      RETURNS: 
+#  DESCRIPTION: 
+#===============================================================================
+sub print_known_hosts_hash {
+    my ($tmp) = @_;
+    print "####################################\n";
+    print "# status of $known_hosts\n";
+    my $hosts;
+    my $host_hash;
+    $shmkh->shlock(LOCK_EX);
+    my @hosts = keys %$known_hosts;
+    foreach my $host (@hosts) {
+        #my @elements = keys %$known_hosts->{$host};
+        my $status = $known_hosts->{$host}->{status} ;
+        my $passwd = $known_hosts->{$host}->{passwd};
+        my $timestamp = $known_hosts->{$host}->{timestamp};
+        print "$host\n";
+        print "\t$status\n";
+        print "\t$passwd\n";
+        print "\t$timestamp\n";
+    }
+    $shmkh->shunlock(LOCK_EX);
+    print "####################################\n\n";
+    return;
+}
+
+sub create_known_hosts_entry {
+    my ($hostname) = @_;
+    $shmkh->shlock(LOCK_EX);
+    $known_hosts->{$hostname} = {};
+    $known_hosts->{$hostname}->{status} = "none";
+    $known_hosts->{$hostname}->{passwd} = "none";
+    $known_hosts->{$hostname}->{timestamp} = "none";
+    $shmkh->shunlock(LOCK_EX); 
+    return;  
+}
+
+sub update_known_hosts_entry {
+    my ($hostname, $status, $passwd, $timestamp) = @_;
+    my ($Sekunden, $Minuten, $Stunden, $Monatstag, $Monat,
+    $Jahr, $Wochentag, $Jahrestag, $Sommerzeit) = localtime(time);
+    $Stunden = $Stunden < 10 ? $Stunden = "0".$Stunden : $Stunden;
+    $Minuten = $Minuten < 10 ? $Minuten = "0".$Minuten : $Minuten;
+    $Sekunden = $Sekunden < 10 ? $Sekunden = "0".$Sekunden : $Sekunden;
+    $Monat+=1;
+    $Monat = $Monat < 10 ? $Monat = "0".$Monat : $Monat;
+    $Monatstag = $Monatstag < 10 ? $Monatstag = "0".$Monatstag : $Monatstag;
+    $Jahr+=1900;
+    my $t = "$Jahr$Monat$Monatstag$Stunden$Minuten$Sekunden";
+
+    $shmkh->shlock(LOCK_EX);
+    if($status) {
+        $known_hosts->{$hostname}->{status} = $status;
+    }
+    if($passwd) {
+        $known_hosts->{$hostname}->{passwd} = $passwd;
+    }
+    $known_hosts->{$hostname}->{timestamp} = $t;
+    $shmkh->shunlock(LOCK_EX); 
+    return;  
+}
+
+
+
 #==== MAIN = main ==============================================================
 
 #  parse commandline options
@@ -386,6 +900,7 @@ Getopt::Long::Configure( "bundling" );
 GetOptions("h|help" => \&usage,
            "c|config=s" => \$cfg_file,
            "f|foreground" => \$foreground,
+           "v|verbose+" => \$verbose,
            );
 
 #  read and set config parameters
@@ -411,49 +926,68 @@ if( 0 != $pid ) {
     if( !$foreground ) { exit( 0 ) };
 }
 
-# open the bus socket
-$bus = IO::Socket::INET->new(LocalPort => $bus_port,
-                            Type => SOCK_STREAM,
-                            Reuse => 1,
-                            Listen => 20,
-        ) or die "kann kein TCP-Server an Port $bus_port sein: $@\n";
-
-# waiting for incoming msg
-my $client;
-while($client = $bus->accept()){ 
-    my $other_end = getpeername($client)
-        or die "Gegenstelle konnte nicht identifiziert werden: $!\n";
-    my ($port, $iaddr) = unpack_sockaddr_in($other_end);
-    my $actual_ip = inet_ntoa($iaddr);
-    my $claimed_hostname = gethostbyaddr($iaddr, AF_INET);
-    daemon_log("accept client from $actual_ip\n");
-    
-    my $in_msg;
-    chomp( $in_msg = <$client> );
-    
-    &activating_child($in_msg);
-    close($client);
-}
+# detect own ip, port and address(= ip:port)
 
 
+# setup xml parser
+$xml = new XML::Simple();
 
-#Struktur Parent:
-# syslog(Parent gestartet)
+# create cipher object
+$bus_cipher = &create_ciphering($bus_passwd);
+$bus_address = "$bus_ip:$bus_port";
 
-# forken von min_child kindern
-#   liste mit child refs
-#   
-# aufbau des sockets
-# warten auf eingang
-# kinder die älter als child_timeout sind werden beendet, aber nicht mehr als child_min
-# wenn eingang da, abfrage, ist ein kind frei?
-#   kind frei: kind nimmt eingang entgegen und arbeitet ihn ab
-#   kind nicht frei: existieren bereits max_child kinder?
-#       max ereicht: tue nichts, warte bis ein kind fertig ist
-#       max nicht erreicht: forke neues kind
+# create reading and writing vectors
+my $rbits = my $wbits = my $ebits = "";
 
+# open the bus socket
+if($bus_activ eq "on") {
+    $bus = IO::Socket::INET->new(LocalPort => $bus_port,
+            Type => SOCK_STREAM,
+            Reuse => 1,
+            Listen => 20,
+            ) or die "kann kein TCP-Server an Port $bus_port sein: $@\n";
+    vec($rbits, fileno $bus, 1) = 1;
+    vec($wbits, fileno $bus, 1) = 1;
+    print "start bus at $bus_ip:$bus_port\n";        
+}
 
+# füge den bus zu known_hosts hinzu
+&create_known_hosts_entry($bus_address);
+&update_known_hosts_entry($bus_address, "bus", $bus_passwd);
 
 
+while(1) {
+    my $nf = select($rbits, $wbits, undef, undef);
+    # error handling
+    if($nf < 0 ) { 
+    }
+
+    # something is coming in 
+    if(vec $rbits, fileno $bus, 1 ) {
+        my $client = $bus->accept();
+        my $other_end = getpeername($client);
+        if(not defined $other_end) {
+            daemon_log("Gegenstelle konnte nicht identifiziert werden: $!\n");
+        } else {
+            my ($port, $iaddr) = unpack_sockaddr_in($other_end);
+            my $actual_ip = inet_ntoa($iaddr);
+            daemon_log("\naccept client from $actual_ip\n", 5);
+            #my $in_msg = <$client>;
+            my $in_msg = &read_from_socket($client);
+            if(defined $in_msg){
+                &activating_child($in_msg, $actual_ip);
+            } else {
+                daemon_log("cannot read from $actual_ip\n",1);
+            }
+        }
+        close($client);        
+    }
+
+    # something is going out
+    if(vec $wbits, fileno $bus, 1){
+        
+    }
+}
 
 
index 8294e6e913216aa8ac579621d1e1b5733e3fdb41..1fd58e18b8ac8ab3cf33b096ba690d64d8c24e25 100644 (file)
@@ -1,10 +1,13 @@
 [general]
-log_file = /var/log/gosa-server-bus.log
-pid_file = /var/run/gosa-server-bus.pid
+log_file = /var/log/gosa-sd-bus.log
+pid_file = /var/run/gosa-sd-bus.pid
+
 [bus]
-buspasswd = tester78901234567890123456789012
-bus_ip = 10.89.1.155
-bus_port = 10001
+bus_activ = on
+bus_passwd = tester
+bus_port = 10000
 child_max = 10
 child_min = 2
 child_timeout = 10
+
+
diff --git a/contrib/daemon/gosa-sd.cfg b/contrib/daemon/gosa-sd.cfg
new file mode 100644 (file)
index 0000000..7aa61ae
--- /dev/null
@@ -0,0 +1,27 @@
+[general]
+log_file = /var/log/gosa-sd.log
+pid_file = /var/run/gosa-sd.pid
+
+[bus]
+bus_activ = on
+bus_passwd = tester
+bus_ip = 10.89.1.155
+bus_port = 10000
+child_max = 10
+child_min = 2
+child_timeout = 10
+
+[server]
+server_activ = on
+server_ip = 10.89.1.155
+server_port = 10001
+server_passwd = tester
+
+
+[arp]
+arp_activ = on
+arp_fifo_path = /home/rettenbe/gonicus/projekte/gosa-trunk/contrib/daemon/fifo
+
+
+
+