d45e64bc37c9cb44fce0e0ae66ec4b0a70966eda
1 #!/usr/bin/perl
2 #===============================================================================
3 #
4 # FILE: gosa-server
5 #
6 # USAGE: ./gosasc
7 #
8 # DESCRIPTION:
9 #
10 # OPTIONS: ---
11 # REQUIREMENTS: ---
12 # BUGS: ---
13 # NOTES:
14 # AUTHOR: (Andreas Rettenberger), <rettenberger@gonicus.de>
15 # COMPANY:
16 # VERSION: 1.0
17 # CREATED: 12.09.2007 08:54:41 CEST
18 # REVISION: ---
19 #===============================================================================
21 use strict;
22 use warnings;
23 use Getopt::Long;
24 use Config::IniFiles;
25 use POSIX;
26 use Time::HiRes qw( gettimeofday );
28 use Fcntl;
29 use IO::Socket::INET;
30 use Crypt::Rijndael;
31 use MIME::Base64;
32 use Digest::MD5 qw(md5 md5_hex md5_base64);
33 use XML::Simple;
34 use Data::Dumper;
35 use Sys::Syslog qw( :DEFAULT setlogsock);
36 use File::Spec;
37 use Cwd;
38 use GosaSupportDaemon;
41 my ($cfg_file, %cfg_defaults, $foreground, $verbose, $pid_file, $procid, $pid, $log_file);
42 my ($server_address, $server_ip, $server_port, $server_domain, $server_passwd, $server_cipher, $server_timeout);
43 my ($client_address, $client_ip, $client_port, $client_mac_address);
44 my ($input_socket, $rbits, $wbits, $ebits, $xml, $known_hosts);
45 my (@events);
47 # default variables
48 my $event_dir = "/etc/gosac/events";
49 $known_hosts = {};
50 $foreground = 0 ;
51 %cfg_defaults =
52 ("general" =>
53 {"log_file" => [\$log_file, "/var/run/".$0.".log"],
54 "pid_file" => [\$pid_file, "/var/run/".$0.".pid"],
55 },
56 "client" =>
57 {"client_port" => [\$client_port, "20083"],
58 },
59 "server" =>
60 {"server_ip" => [\$server_ip, ""],
61 "server_port" => [\$server_port, "20081"],
62 "server_passwd" => [\$server_passwd, ""],
63 "server_timeout" => [\$server_timeout, 10],
64 "server_domain" => [\$server_domain, ""],
65 },
66 );
69 #=== FUNCTION ================================================================
70 # NAME: read_configfile
71 # PARAMETERS: cfg_file - string -
72 # RETURNS:
73 # DESCRIPTION:
74 #===============================================================================
75 sub read_configfile {
76 my $cfg;
77 if( defined( $cfg_file) && ( length($cfg_file) > 0 )) {
78 if( -r $cfg_file ) {
79 $cfg = Config::IniFiles->new( -file => $cfg_file );
80 } else {
81 print STDERR "Couldn't read config file!";
82 }
83 } else {
84 $cfg = Config::IniFiles->new() ;
85 }
86 foreach my $section (keys %cfg_defaults) {
87 foreach my $param (keys %{$cfg_defaults{ $section }}) {
88 my $pinfo = $cfg_defaults{ $section }{ $param };
89 ${@$pinfo[ 0 ]} = $cfg->val( $section, $param, @$pinfo[ 1 ] );
90 }
91 }
92 }
95 #=== FUNCTION ================================================================
96 # NAME: logging
97 # PARAMETERS: level - string - default 'info'
98 # msg - string -
99 # facility - string - default 'LOG_DAEMON'
100 # RETURNS:
101 # DESCRIPTION:
102 #===============================================================================
103 sub daemon_log {
104 my( $msg, $level ) = @_;
105 if(not defined $msg) { return }
106 if(not defined $level) { $level = 1 }
107 if(defined $log_file){
108 open(LOG_HANDLE, ">>$log_file");
109 if(not defined open( LOG_HANDLE, ">>$log_file" )) {
110 print STDERR "cannot open $log_file: $!";
111 return }
112 chomp($msg);
113 if($level <= $verbose){
114 print LOG_HANDLE $msg."\n";
115 if(defined $foreground) { print $msg."\n" }
116 }
117 }
118 close( LOG_HANDLE );
119 # my ($msg, $level, $facility) = @_;
120 # if(not defined $msg) {return}
121 # if(not defined $level) {$level = "info"}
122 # if(not defined $facility) {$facility = "LOG_DAEMON"}
123 # openlog($0, "pid,cons,", $facility);
124 # syslog($level, $msg);
125 # closelog;
126 # return;
127 }
130 #=== FUNCTION ================================================================
131 # NAME: check_cmdline_param
132 # PARAMETERS:
133 # RETURNS:
134 # DESCRIPTION:
135 #===============================================================================
136 sub check_cmdline_param () {
137 my $err_config;
138 my $err_counter = 0;
139 if( not defined( $cfg_file)) {
140 #$err_config = "please specify a config file";
141 #$err_counter += 1;
142 my $cwd = getcwd;
143 my $name = "/etc/gosa-si/client.conf";
144 $cfg_file = File::Spec->catfile( $cwd, $name );
145 print STDERR "no conf file specified\n try to use default: $cfg_file\n";
146 }
147 if( $err_counter > 0 ) {
148 &usage( "", 1 );
149 if( defined( $err_config)) { print STDERR "$err_config\n"}
150 print STDERR "\n";
151 exit( -1 );
152 }
153 }
156 #=== FUNCTION ================================================================
157 # NAME: check_pid
158 # PARAMETERS:
159 # RETURNS:
160 # DESCRIPTION:
161 #===============================================================================
162 sub check_pid {
163 $pid = -1;
164 # Check, if we are already running
165 if( open(LOCK_FILE, "<$pid_file") ) {
166 $pid = <LOCK_FILE>;
167 if( defined $pid ) {
168 chomp( $pid );
169 if( -f "/proc/$pid/stat" ) {
170 my($stat) = `cat /proc/$pid/stat` =~ m/$pid \((.+)\).*/;
171 if( $0 eq $stat ) {
172 close( LOCK_FILE );
173 exit -1;
174 }
175 }
176 }
177 close( LOCK_FILE );
178 unlink( $pid_file );
179 }
181 # create a syslog msg if it is not to possible to open PID file
182 if (not sysopen(LOCK_FILE, $pid_file, O_WRONLY|O_CREAT|O_EXCL, 0644)) {
183 my($msg) = "Couldn't obtain lockfile '$pid_file' ";
184 if (open(LOCK_FILE, '<', $pid_file)
185 && ($pid = <LOCK_FILE>))
186 {
187 chomp($pid);
188 $msg .= "(PID $pid)\n";
189 } else {
190 $msg .= "(unable to read PID)\n";
191 }
192 if( ! ($foreground) ) {
193 openlog( $0, "cons,pid", "daemon" );
194 syslog( "warning", $msg );
195 closelog();
196 }
197 else {
198 print( STDERR " $msg " );
199 }
200 exit( -1 );
201 }
202 }
205 #=== FUNCTION ================================================================
206 # NAME: get_ip_and_mac
207 # PARAMETERS: nothing
208 # RETURNS: (ip, mac)
209 # DESCRIPTION: executes /sbin/ifconfig and parses the output, the first occurence
210 # of a inet address is returned as well as the mac address in the line
211 # above the inet address
212 #===============================================================================
213 sub get_ip_and_mac {
214 my $ip = "0.0.0.0.0"; # Defualt-IP
215 my $mac = "00:00:00:00:00:00"; # Default-MAC
216 my @ifconfig = qx(/sbin/ifconfig);
217 foreach(@ifconfig) {
218 if (/Hardware Adresse (\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/) {
219 $mac = "$1:$2:$3:$4:$5:$6";
220 next;
221 }
222 if (/inet Adresse:(\d+).(\d+).(\d+).(\d+)/) {
223 $ip = "$1.$2.$3.$4";
224 last;
225 }
226 }
227 return ($ip, $mac);
228 }
231 #=== FUNCTION ================================================================
232 # NAME: usage
233 # PARAMETERS:
234 # RETURNS:
235 # DESCRIPTION:
236 #===============================================================================
237 sub usage {
238 my( $text, $help ) = @_;
239 $text = undef if( "h" eq $text );
240 (defined $text) && print STDERR "\n$text\n";
241 if( (defined $help && $help) || (!defined $help && !defined $text) ) {
242 print STDERR << "EOF" ;
243 usage: $0 [-hvf] [-c config]
245 -h : this (help) message
246 -c <file> : config file
247 -f : foreground, process will not be forked to background
248 -v : be verbose (multiple to increase verbosity)
249 EOF
250 }
251 print "\n" ;
252 }
254 #=== FUNCTION ================================================================
255 # NAME: get_server_addresses
256 # PARAMETERS:
257 # RETURNS:
258 # DESCRIPTION:
259 #===============================================================================
260 sub get_server_addresses {
261 my $domain= shift;
262 my @result;
263 my $dig_cmd= 'dig +nocomments srv _gosad._tcp.'.$domain;
265 my $output= `$dig_cmd 2>&1`;
266 open (PIPE, "$dig_cmd 2>&1 |");
267 while(<PIPE>) {
268 chomp $_;
269 # If it's not a comment
270 if($_ =~ m/^[^;]/) {
271 my @matches= split /\s+/;
273 # Push hostname with port
274 if($matches[3] eq 'SRV') {
275 push @result, $matches[7].':'.$matches[6];
276 } elsif ($matches[3] eq 'A') {
277 my $i=0;
279 # Substitute the hostname with the ip address of the matching A record
280 foreach my $host (@result) {
281 if ((split /\:/, $host)[0] eq $matches[0]) {
282 $result[$i]= $matches[4].':'.(split /\:/, $host)[1];
283 }
284 $i++;
285 }
286 }
287 }
288 }
289 close(PIPE);
290 return @result;
291 }
294 #=== FUNCTION ================================================================
295 # NAME: register_at_server
296 # PARAMETERS:
297 # RETURNS:
298 # DESCRIPTION:
299 #===============================================================================
300 sub register_at_server {
301 my ($tmp) = @_;
303 # create new passwd and ciphering object for client-server communication
304 my $new_server_passwd = &create_passwd();
305 my $new_server_cipher;
307 # detect all client accepted events
308 opendir(DIR, $event_dir)
309 or daemon_log("cannot find directory $event_dir!\ngosac starts without any accepting events!", 1);
310 my $file_name;
311 @events = ();
312 while(defined($file_name = readdir(DIR))){
313 if ($file_name eq "." || $file_name eq "..") {
314 next;
315 }
316 push(@events, $file_name);
317 }
318 my $events = join(",", @events);
319 daemon_log("found events: $events", 1);
321 # fill in all possible servers
322 my @servers;
323 if (defined $server_domain) {
324 my @tmp_servers = &get_server_addresses($server_domain);
325 foreach my $server (@tmp_servers) { unshift(@servers, $server); }
326 }
327 # add server address from config file at first position of server list
328 if (defined $server_address) {
329 unshift(@servers, $server_address);
330 }
331 daemon_log("found servers in configuration file and via DNS:", 5);
332 foreach my $server (@servers) {
333 daemon_log("\t$server", 5);
334 }
336 my ($rout, $wout, $reg_server);
337 foreach my $server (@servers) {
338 # create msg hash
339 my $register_hash = &create_xml_hash("here_i_am", $client_address, $server);
340 &add_content2xml_hash($register_hash, "new_passwd", $new_server_passwd);
341 &add_content2xml_hash($register_hash, "mac_address", $client_mac_address);
342 &add_content2xml_hash($register_hash, "events", $events);
344 # send xml hash to server with general server passwd
345 my $answer = &send_msg_hash2address($register_hash, $server, $server_passwd);
347 if ($answer != 0) { next; }
349 # waiting for response
350 daemon_log("waiting for response...\n", 5);
351 my $nf = select($rout=$rbits, $wout=$wbits, undef, $server_timeout);
353 # something is coming in
354 if(vec $rout, fileno $input_socket, 1) {
355 my $crypted_msg;
356 my $client = $input_socket->accept();
357 my $other_end = getpeername($client);
358 if(not defined $other_end) {
359 daemon_log("client cannot be identified: $!\n");
360 } else {
361 my ($port, $iaddr) = unpack_sockaddr_in($other_end);
362 my $actual_ip = inet_ntoa($iaddr);
363 daemon_log("\naccept client from $actual_ip\n", 5);
364 my $in_msg = &read_from_socket($client);
365 if(defined $in_msg){
366 chomp($in_msg);
367 $crypted_msg = $in_msg;
368 } else {
369 daemon_log("cannot read from $actual_ip\n", 5);
370 }
371 }
372 close($client);
374 # validate acknowledge msg from server
375 $new_server_cipher = &create_ciphering($new_server_passwd);
376 my $msg_hash;
377 eval {
378 my $decrypted_msg = &decrypt_msg($crypted_msg, $new_server_cipher);
379 daemon_log("decrypted register msg: $decrypted_msg", 5);
380 $msg_hash = $xml->XMLin($decrypted_msg, ForceArray=>1);
381 };
382 if($@) {
383 daemon_log("ERROR: do not understand the incoming message:" , 5);
384 daemon_log("$@", 7);
385 } else {
386 my $header = @{$msg_hash->{header}}[0];
387 if($header eq "registered") {
388 $reg_server = $server;
389 last;
390 } elsif($header eq "denied") {
391 my $reason = (&get_content_from_xml_hash($msg_hash, "denied"))[0];
392 daemon_log("registration at $server denied: $reason", 1);
393 } else {
394 daemon_log("cannot register at $server", 1);
395 }
396 }
397 }
398 # if no answer arrive, try next server in list
400 }
402 if(defined $reg_server) {
403 daemon_log("registered at $reg_server", 1);
404 } else {
405 daemon_log("cannot register at any server", 1);
406 daemon_log("exiting!!!", 1);
407 exit(1);
408 }
410 # update the global available variables
411 $server_address = $reg_server;
412 $server_passwd = $new_server_passwd;
413 $server_cipher = $new_server_cipher;
414 return;
415 }
418 #=== FUNCTION ================================================================
419 # NAME: create_xml_hash
420 # PARAMETERS:
421 # RETURNS:
422 # DESCRIPTION:
423 #===============================================================================
424 #sub create_xml_hash {
425 # my ($header, $source, $target, $header_value) = @_;
426 # my $hash = {
427 # header => [$header],
428 # source => [$source],
429 # target => [$target],
430 # $header => [$header_value],
431 # };
432 # daemon_log("create_xml_hash:", 7),
433 # chomp(my $tmp = Dumper $hash);
434 # daemon_log("\t$tmp\n", 7);
435 # return $hash
436 #}
439 #=== FUNCTION ================================================================
440 # NAME: create_xml_string
441 # PARAMETERS:
442 # RETURNS:
443 # DESCRIPTION:
444 #===============================================================================
445 #sub create_xml_string {
446 # my ($xml_hash) = @_ ;
447 # my $xml_string = $xml->XMLout($xml_hash, RootName => 'xml');
448 # $xml_string =~ s/[\n]+//g;
449 # daemon_log("create_xml_string:\n\t$xml_string\n", 7);
450 # return $xml_string;
451 #}
454 #=== FUNCTION ================================================================
455 # NAME: add_content2xml_hash
456 # PARAMETERS:
457 # RETURNS:
458 # DESCRIPTION:
459 #===============================================================================
460 #sub add_content2xml_hash {
461 # my ($xml_ref, $element, $content) = @_;
462 # if(not exists $$xml_ref{$element} ) {
463 # $$xml_ref{$element} = [];
464 # }
465 # my $tmp = $$xml_ref{$element};
466 # push(@$tmp, $content);
467 # return;
468 #}
471 #=== FUNCTION ================================================================
472 # NAME: get_content_from_xml_hash
473 # PARAMETERS: ref : reference to the xml hash
474 # string: key of the value you want
475 # RETURNS: STRING AND ARRAY
476 # DESCRIPTION: if key of the hash is either 'header', 'target' or 'source' the
477 # function returns a string cause it is expected that these keys
478 # do just have one value, all other keys returns an array!!!
479 #===============================================================================
480 #sub get_content_from_xml_hash {
481 # my ($xml_ref, $element) = @_;
482 # my $result = $xml_ref->{$element};
483 # if( $element eq "header" || $element eq "target" || $element eq "source") {
484 # return @$result[0];
485 # }
486 # return @$result;
487 #}
489 # my ($xml_ref, $element) = @_;
490 # if (exists $xml_ref->{$element}) {
491 # my $result = $xml_ref->{$element};
492 # if( $element eq "header" || $element eq "target" || $element eq "source") {
493 # return @$result[0];
494 # } else {
495 # return @$result;
496 # }
497 #
498 # } else {
499 # my $result = ();
500 # return @$result;
501 # }
502 #}
505 #=== FUNCTION ================================================================
506 # NAME: encrypt_msg
507 # PARAMETERS:
508 # RETURNS:
509 # DESCRIPTION:
510 #===============================================================================
511 #sub encrypt_msg {
512 # my ($msg, $my_cipher) = @_;
513 # if(not defined $my_cipher) { print "no cipher object\n"; }
514 # $msg = "\0"x(16-length($msg)%16).$msg;
515 # my $crypted_msg = $my_cipher->encrypt($msg);
516 # chomp($crypted_msg = &encode_base64($crypted_msg));
517 # return $crypted_msg;
518 #}
521 #=== FUNCTION ================================================================
522 # NAME: decrypt_msg
523 # PARAMETERS:
524 # RETURNS:
525 # DESCRIPTION:
526 #===============================================================================
527 #sub decrypt_msg {
528 # my ($crypted_msg, $my_cipher) = @_ ;
529 # $crypted_msg = &decode_base64($crypted_msg);
530 # my $msg = $my_cipher->decrypt($crypted_msg);
531 # $msg =~ s/\0*//g;
532 # return $msg;
533 #}
536 #=== FUNCTION ================================================================
537 # NAME: create_ciphering
538 # PARAMETERS:
539 # RETURNS: cipher object
540 # DESCRIPTION:
541 #===============================================================================
542 #sub create_ciphering {
543 # my ($passwd) = @_;
544 # $passwd = substr(md5_hex("$passwd") x 32, 0, 32);
545 # my $iv = substr(md5_hex('GONICUS GmbH'),0, 16);
546 #
547 # #daemon_log("iv: $iv", 7);
548 # #daemon_log("key: $passwd", 7);
549 # my $my_cipher = Crypt::Rijndael->new($passwd , Crypt::Rijndael::MODE_CBC());
550 # $my_cipher->set_iv($iv);
551 # return $my_cipher;
552 #}
555 #=== FUNCTION ================================================================
556 # NAME: create_passwd
557 # PARAMETERS:
558 # RETURNS: cipher object
559 # DESCRIPTION:
560 #===============================================================================
561 sub create_passwd {
562 my $new_passwd = "";
563 for(my $i=0; $i<31; $i++) {
564 $new_passwd .= ("a".."z","A".."Z",0..9)[int(rand(62))]
565 }
567 return $new_passwd;
568 }
571 #=== FUNCTION ================================================================
572 # NAME: send_msg_hash2address
573 # PARAMETERS: msg string - xml message
574 # PeerAddr string - socket address to send msg
575 # PeerPort string - socket port, if not included in socket address
576 # RETURNS: nothing
577 # DESCRIPTION: ????
578 #===============================================================================
579 #sub send_msg_hash2address {
580 # my ($msg_hash, $address, $passwd) = @_ ;
581 #
582 # # fetch header for logging
583 # my $header = @{$msg_hash->{header}}[0];
584 #
585 # # generiere xml string
586 # my $msg_xml = &create_xml_string($msg_hash);
587 #
588 # # hole das entsprechende passwd aus dem hash
589 # if(not defined $passwd) {
590 # if(exists $known_hosts->{$address}) {
591 # $passwd = $known_hosts->{$address}->{passwd};
592 # } elsif ($address eq $server_address) {
593 # $passwd = $server_passwd;
594 # } else {
595 # daemon_log("$address not known, neither as server nor as client", 1);
596 # return "failed";
597 # }
598 # }
599 #
600 # # erzeuge ein ciphering object
601 # my $act_cipher = &create_ciphering($passwd);
602 #
603 # # encrypt xml msg
604 # my $crypted_msg = &encrypt_msg($msg_xml, $act_cipher);
605 #
606 # # öffne socket
607 # my $socket = &open_socket($address);
608 # if(not defined $socket){
609 # daemon_log("cannot open socket to $address, server not reachable", 1);
610 # daemon_log("cannot send '$header'-msg", 1);
611 # return "failed";
612 # }
613 #
614 # # versende xml msg
615 # print $socket $crypted_msg."\n";
616 #
617 # # schließe socket
618 # close $socket;
619 #
620 # daemon_log("send '$header'-msg to $address", 5);
621 # daemon_log("crypted_msg:\n\t$crypted_msg", 7);
622 #
623 # return "done";
624 #}
627 #=== FUNCTION ================================================================
628 # NAME: open_socket
629 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
630 # [PeerPort] string necessary if port not appended by PeerAddr
631 # RETURNS: socket IO::Socket::INET
632 # DESCRIPTION:
633 #===============================================================================
634 sub open_socket {
635 my ($PeerAddr, $PeerPort) = @_ ;
636 if(defined($PeerPort)){
637 $PeerAddr = $PeerAddr.":".$PeerPort;
638 }
639 my $socket;
640 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
641 Porto => "tcp" ,
642 Type => SOCK_STREAM,
643 Timeout => 5,
644 );
645 if(not defined $socket) {
646 #daemon_log("cannot connect to socket at $PeerAddr, $@\n");
647 return;
648 }
649 daemon_log("open_socket:\n\t$PeerAddr", 7);
650 return $socket;
651 }
654 #=== FUNCTION ================================================================
655 # NAME: read_from_socket
656 # PARAMETERS: socket fh -
657 # RETURNS: result string - readed characters from socket
658 # DESCRIPTION: reads data from socket in 16 byte steps
659 #===============================================================================
660 sub read_from_socket {
661 my ($socket) = @_;
662 my $result = "";
664 $socket->blocking(1);
665 $result = <$socket>;
667 $socket->blocking(0);
668 while ( my $char = <$socket> ) {
669 if (not defined $char) { last }
670 $result .= $char;
671 }
672 return $result;
676 # my ($socket) = @_;
677 # my $result = "";
678 # my $len = 16;
679 # while($len == 16){
680 # my $char;
681 # $len = sysread($socket, $char, 16);
682 # if($len != 16) { last }
683 # if($len != 16) { last }
684 # $result .= $char;
685 # }
686 # return $result;
687 }
690 #=== FUNCTION ================================================================
691 # NAME: print_known_hosts_hash
692 # PARAMETERS:
693 # RETURNS:
694 # DESCRIPTION:
695 #===============================================================================
696 sub print_known_hosts_hash {
697 my ($tmp) = @_;
698 print "####################################\n";
699 print "# status of known_hosts\n";
700 my $hosts;
701 my $host_hash;
702 my @hosts = keys %$known_hosts;
703 foreach my $host (@hosts) {
704 #my @elements = keys %$known_hosts->{$host};
705 my $status = $known_hosts->{$host}->{status} ;
706 my $passwd = $known_hosts->{$host}->{passwd};
707 my $timestamp = $known_hosts->{$host}->{timestamp};
708 print "$host\n";
709 print "\t$status\n";
710 print "\t$passwd\n";
711 print "\t$timestamp\n";
712 }
713 print "####################################\n";
714 return;
715 }
717 #=== FUNCTION ================================================================
718 # NAME:
719 # PARAMETERS:
720 # RETURNS:
721 # DESCRIPTION:
722 #===============================================================================
723 sub create_known_hosts_entry {
724 my ($hostname) = @_;
725 $known_hosts->{$hostname} = {};
726 $known_hosts->{$hostname}->{status} = "none";
727 $known_hosts->{$hostname}->{passwd} = "none";
728 $known_hosts->{$hostname}->{timestamp} = "none";
729 return;
730 }
733 #=== FUNCTION ================================================================
734 # NAME:
735 # PARAMETERS:
736 # RETURNS:
737 # DESCRIPTION:
738 #===============================================================================
739 sub update_known_hosts_entry {
740 my ($hostname, $status, $passwd, $timestamp) = @_;
741 my ($seconds, $minutes, $hours, $monthday, $month,
742 $year, $weekday, $yearday, $sommertime) = localtime(time);
743 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
744 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
745 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
746 $month+=1;
747 $month = $month < 10 ? $month = "0".$month : $month;
748 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
749 $year+=1900;
750 my $t = "$year$month$monthday$hours$minutes$seconds";
752 if($status) {
753 $known_hosts->{$hostname}->{status} = $status;
754 }
755 if($passwd) {
756 $known_hosts->{$hostname}->{passwd} = $passwd;
757 }
758 if($timestamp) {
759 $t = $timestamp;
760 }
761 $known_hosts->{$hostname}->{timestamp} = $t;
762 return;
763 }
766 #=== FUNCTION ================================================================
767 # NAME:
768 # PARAMETERS:
769 # RETURNS:
770 # DESCRIPTION:
771 #===============================================================================
772 sub add_content2known_hosts {
773 my ($hostname, $element, $content) = @_;
774 my ($seconds, $minutes, $hours, $monthday, $month,
775 $year, $weekday, $yearday, $sommertime) = localtime(time);
776 $hours = $hours < 10 ? $hours = "0".$hours : $hours;
777 $minutes = $minutes < 10 ? $minutes = "0".$minutes : $minutes;
778 $seconds = $seconds < 10 ? $seconds = "0".$seconds : $seconds;
779 $month+=1;
780 $month = $month < 10 ? $month = "0".$month : $month;
781 $monthday = $monthday < 10 ? $monthday = "0".$monthday : $monthday;
782 $year+=1900;
783 my $t = "$year$month$monthday$hours$minutes$seconds";
785 $known_hosts->{$hostname}->{$element} = $content;
786 $known_hosts->{$hostname}->{timestamp} = $t;
787 return;
788 }
791 #=== FUNCTION ================================================================
792 # NAME:
793 # PARAMETERS:
794 # RETURNS:
795 # DESCRIPTION:
796 #===============================================================================
797 sub process_incoming_msg {
798 my ($crypted_msg) = @_;
799 if(not defined $crypted_msg) {
800 daemon_log("function 'process_incoming_msg': got no msg", 7);
801 }
802 $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
803 $crypted_msg = $1;
804 my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
805 daemon_log("msg from host:", 1);
806 daemon_log("\t$host", 1);
807 daemon_log("crypted msg:", 7);
808 daemon_log("\t$crypted_msg", 7);
810 my $act_cipher = &create_ciphering($server_passwd);
812 # try to decrypt incoming msg
813 my ($msg, $msg_hash);
814 eval{
815 $msg = &decrypt_msg($crypted_msg, $act_cipher);
816 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
817 };
818 if($@) {
819 daemon_log("ERROR: incoming msg cannot be decrypted with server passwd", 1);
820 return;
821 }
823 my $header = @{$msg_hash->{header}}[0];
825 daemon_log("recieve '$header' from $host", 1);
826 # daemon_log("header from msg:", 1);
827 # daemon_log("\t$header", 1);
828 # daemon_log("msg to process:", 7);
829 # daemon_log("\t$msg", 7);
831 #check whether msg to process is a event
832 opendir(DIR, $event_dir)
833 or daemon_log("cannot find directory $event_dir, no events specified", 5);
834 my $file_name;
835 while(defined($file_name = readdir(DIR))){
836 if ($file_name eq "." || $file_name eq "..") {
837 next;
838 }
839 if ($file_name eq $header) {
840 my $cmd = "$event_dir/$file_name '$msg'";
841 my $result_xml = "";
842 open(PIPE, "$cmd 2>&1 |");
843 while(<PIPE>) {
844 $result_xml.=$_;
845 last;
846 }
847 close(PIPE);
848 my $res_hash = &transform_msg2hash($result_xml);
849 my $res_target = @{$res_hash->{target}}[0];
850 &send_msg_hash2address($res_hash, $server_address);
852 return;
853 }
854 }
855 close(DIR);
856 daemon_log("could not assign the msg $header to an event", 5);
860 if ($header eq 'new_ldap_config') { &new_ldap_config($msg_hash)}
861 elsif ($header eq 'ping') { &got_ping($msg_hash) }
862 elsif ($header eq 'wake_up') { &execute_event($msg_hash)}
863 elsif ($header eq 'new_passwd') { &new_passwd()}
864 else { daemon_log("ERROR: no function assigned to msg $header", 5) }
866 return;
867 }
870 #=== FUNCTION ================================================================
871 # NAME:
872 # PARAMETERS:
873 # RETURNS:
874 # DESCRIPTION:
875 #===============================================================================
876 sub update_status {
877 my ($new_status) = @_ ;
878 my $out_hash = &create_xml_hash("update_status", $client_address, $server_address);
879 &add_content2xml_hash($out_hash, "update_status", $new_status);
880 &send_msg_hash2address($out_hash, $server_address);
881 return;
882 }
885 #=== FUNCTION ================================================================
886 # NAME:
887 # PARAMETERS:
888 # RETURNS:
889 # DESCRIPTION:
890 #===============================================================================
891 sub server_leaving {
892 my ($msg_hash) = @_ ;
893 my $source = &get_content_from_xml_hash("source");
894 my $header = &get_content_from_xml_hash("header");
896 daemon_log("gosa daemon $source is going down, cause registration procedure", 1);
897 my $server_address = "none";
898 my $server_passwd = "none";
899 my $server_cipher = "none";
901 # reinitialization of default values in config file
902 &read_configfile;
904 # registrated at new daemon
905 ®ister_at_server();
907 return;
908 }
911 sub got_ping {
912 my ($msg_hash) = @_ ;
914 my $source = &get_content_from_xml_hash($msg_hash, 'source');
915 my $target = &get_content_from_xml_hash($msg_hash, 'target');
916 my $header = &get_content_from_xml_hash($msg_hash, 'header');
918 &add_content2known_hosts(hostname=>$target, status=>$header);
920 my $out_hash = &create_xml_hash("got_ping", $target, $source);
921 &send_msg_hash2address($out_hash, $source, $server_passwd);
923 return;
924 }
927 sub new_ldap_config {
928 my ($msg_hash) = @_ ;
930 my @gotoLdapServer = &get_content_from_xml_hash($msg_hash, "new_ldap_config");
931 print Dumper @gotoLdapServer;
934 return;
936 }
939 sub execute_event {
940 my ($msg_hash)= @_;
941 my $configdir= '/etc/gosac/events/';
942 my $result;
944 my $header = &get_content_from_xml_hash($msg_hash, 'header');
945 my $source = &get_content_from_xml_hash($msg_hash, 'source');
946 my $target = &get_content_from_xml_hash($msg_hash, 'target');
949 if((not defined $source)
950 && (not defined $target)
951 && (not defined $header)) {
952 daemon_log("ERROR: Entries missing in XML msg for gosa events under /etc/gosac/events");
953 } else {
954 my $parameters="";
955 my @params = &get_content_from_xml_hash($msg_hash, $header);
956 my $params = join(", ", @params);
957 daemon_log("execute_event: got parameters: $params", 5);
959 if (@params) {
960 foreach my $param (@params) {
961 my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
962 daemon_log("execute_event: parameter -> value: $param -> $param_value", 7);
963 $parameters.= " ".$param_value;
964 }
965 }
967 my $cmd= $configdir.$header."$parameters";
968 daemon_log("execute_event: executing cmd: $cmd", 7);
969 $result= "";
970 open(PIPE, "$cmd 2>&1 |");
971 while(<PIPE>) {
972 $result.=$_;
973 }
974 close(PIPE);
975 }
977 # process the event result
980 return;
981 }
984 sub new_passwd {
985 # my ($msg_hash) = @_ ;
986 my $new_server_passwd = &create_passwd();
987 my $new_server_cipher = &create_ciphering($new_server_passwd);
989 my $out_hash = &create_xml_hash("new_passwd", $client_address, $server_address, $new_server_passwd);
991 &send_msg_hash2address($out_hash, $server_address, $server_passwd);
993 $server_passwd = $new_server_passwd;
994 $server_cipher = $new_server_cipher;
995 return;
996 }
1001 #==== MAIN = main ==============================================================
1003 # parse commandline options
1004 Getopt::Long::Configure( "bundling" );
1005 GetOptions("h|help" => \&usage,
1006 "c|config=s" => \$cfg_file,
1007 "f|foreground" => \$foreground,
1008 "v|verbose+" => \$verbose,
1009 );
1011 # read and set config parameters
1012 &check_cmdline_param ;
1013 &read_configfile;
1014 &check_pid;
1016 # restart daemon log file
1017 if(-e $log_file ) { unlink $log_file }
1018 daemon_log(" ", 1);
1019 daemon_log("$0 started!", 1);
1021 # Just fork, if we"re not in foreground mode
1022 if( ! $foreground ) { $pid = fork(); }
1023 else { $pid = $$; }
1025 # Do something useful - put our PID into the pid_file
1026 if( 0 != $pid ) {
1027 open( LOCK_FILE, ">$pid_file" );
1028 print LOCK_FILE "$pid\n";
1029 close( LOCK_FILE );
1030 if( !$foreground ) { exit( 0 ) };
1031 }
1033 # detect own ip and mac address
1034 ($client_ip, $client_mac_address) = &get_ip_and_mac();
1035 if (not defined $client_ip) {
1036 die "EXIT: ip address of $0 could not be detected";
1037 }
1038 daemon_log("client ip address detected: $client_ip", 1);
1039 daemon_log("client mac address detected: $client_mac_address", 1);
1041 # prepare variables
1042 if (defined $server_ip && defined $server_port) {
1043 $server_address = $server_ip.":".$server_port;
1044 }
1045 $client_address = $client_ip.":".$client_port;
1047 # setup xml parser
1048 $xml = new XML::Simple();
1050 # create input socket
1051 daemon_log(" ", 1);
1052 $rbits = $wbits = $ebits = "";
1053 $input_socket = IO::Socket::INET->new(LocalPort => $client_port,
1054 Type => SOCK_STREAM,
1055 Reuse => 1,
1056 Listen => 20,
1057 );
1058 if(not defined $input_socket){
1059 daemon_log("cannot be a tcp server at $client_port : $@\n");
1060 } else {
1061 daemon_log("start client at $client_address",1) ;
1062 vec($rbits, fileno $input_socket, 1) = 1;
1063 vec($wbits, fileno $input_socket, 1) = 1;
1064 }
1066 # register at server
1067 daemon_log(" ", 1);
1068 ®ister_at_server();
1071 ##############
1072 # Debugging
1073 #############
1074 #sleep(2);
1075 #&update_status("ich_bin_ein_neuer_status");
1077 ###################################
1078 #everything ready, okay, lets start
1079 ###################################
1080 while(1) {
1081 my ($rout, $wout);
1082 my $nf = select($rout=$rbits, $wout=$wbits, undef, undef);
1084 # error handling
1085 if($nf < 0 ) {
1086 }
1088 # something is coming in
1089 if(vec $rout, fileno $input_socket, 1) {
1090 my $client = $input_socket->accept();
1091 my $other_end = getpeername($client);
1093 if(not defined $other_end) {
1094 daemon_log("client cannot be identified: $!");
1095 } else {
1096 my ($port, $iaddr) = unpack_sockaddr_in($other_end);
1097 my $actual_ip = inet_ntoa($iaddr);
1098 daemon_log("accept client from $actual_ip", 5);
1099 my $in_msg = &read_from_socket($client);
1100 if(defined $in_msg){
1101 chomp($in_msg);
1102 $in_msg = $in_msg.".".$actual_ip;
1103 &process_incoming_msg($in_msg);
1105 }
1106 }
1107 }
1108 }