1 package ServerPackages;
3 use Exporter;
4 @ISA = ("Exporter");
6 # Each module has to have a function 'process_incoming_msg'. This function works as a interface to gosa-sd and recieves the msg hash from gosa-sd. 'process_incoming_function checks, wether it has a function to process the incoming msg and forward the msg to it.
9 use strict;
10 use warnings;
11 use GosaSupportDaemon;
12 use IO::Socket::INET;
13 use XML::Simple;
15 BEGIN{}
16 END {}
19 my ($server_activ, $server_port, $server_passwd, $max_clients);
20 my ($bus_activ, $bus_passwd, $bus_ip, $bus_port);
21 my $server;
22 my $no_bus;
24 my %cfg_defaults =
25 ("server" =>
26 {"server_activ" => [\$server_activ, "on"],
27 "server_port" => [\$server_port, "20081"],
28 "server_passwd" => [\$server_passwd, ""],
29 "max_clients" => [\$max_clients, 100],
30 },
31 "bus" =>
32 {"bus_activ" => [\$bus_activ, "on"],
33 "bus_passwd" => [\$bus_passwd, ""],
34 "bus_ip" => [\$bus_ip, ""],
35 "bus_port" => [\$bus_port, "20080"],
36 },
37 );
39 ### START #####################################################################
42 # read configfile and import variables
43 &read_configfile();
45 # detect own ip and mac address
46 my ($server_ip, $server_mac_address) = &get_ip_and_mac();
47 if (not defined $server_ip) {
48 die "EXIT: ip address of $0 could not be detected";
49 }
50 &main::daemon_log("server ip address detected: $server_ip", 1);
51 &main::daemon_log("server mac address detected: $server_mac_address", 1);
53 # complete addresses
54 my $server_address = "$server_ip:$server_port";
55 my $bus_address = "$bus_ip:$bus_port";
57 # create general settings for this module
58 my $xml = new XML::Simple();
60 # open server socket
61 if($server_activ eq "on"){
62 &main::daemon_log(" ", 1);
63 $server = IO::Socket::INET->new(LocalPort => $server_port,
64 Type => SOCK_STREAM,
65 Reuse => 1,
66 Listen => 20,
67 );
68 if(not defined $server){
69 &main::daemon_log("cannot be a tcp server at $server_port : $@");
70 } else {
71 &main::daemon_log("start server: $server_address", 1);
72 }
73 }
75 # register at bus
76 if ($main::no_bus > 0) {
77 $bus_activ = "off"
78 }
79 if($bus_activ eq "on") {
80 &main::daemon_log(" ", 1);
81 ®ister_at_bus();
82 }
84 ### functions #################################################################
86 #sub get_module_tags {
87 #
88 # # lese config file aus dort gibt es eine section Basic
89 # # dort stehen drei packettypen, für die sich das modul anmelden kann, gosa-admin-packages,
90 # # server-packages, client-packages
91 # my %tag_hash = (gosa_admin_packages => "yes",
92 # server_packages => "yes",
93 # client_packages => "yes",
94 # );
95 # return \%tag_hash;
96 #}
99 sub get_module_info {
100 my @info = ($server_address,
101 $server_passwd,
102 $server,
103 $server_activ,
104 "socket",
105 );
106 return \@info;
107 }
110 #=== FUNCTION ================================================================
111 # NAME: read_configfile
112 # PARAMETERS: cfg_file - string -
113 # RETURNS: nothing
114 # DESCRIPTION: read cfg_file and set variables
115 #===============================================================================
116 sub read_configfile {
117 my $cfg;
118 if( defined( $main::cfg_file) && ( length($main::cfg_file) > 0 )) {
119 if( -r $main::cfg_file ) {
120 $cfg = Config::IniFiles->new( -file => $main::cfg_file );
121 } else {
122 print STDERR "Couldn't read config file!";
123 }
124 } else {
125 $cfg = Config::IniFiles->new() ;
126 }
127 foreach my $section (keys %cfg_defaults) {
128 foreach my $param (keys %{$cfg_defaults{ $section }}) {
129 my $pinfo = $cfg_defaults{ $section }{ $param };
130 ${@$pinfo[0]} = $cfg->val( $section, $param, @$pinfo[1] );
131 }
132 }
133 }
136 #=== FUNCTION ================================================================
137 # NAME: get_ip_and_mac
138 # PARAMETERS: nothing
139 # RETURNS: (ip, mac)
140 # DESCRIPTION: executes /sbin/ifconfig and parses the output, the first occurence
141 # of a inet address is returned as well as the mac address in the line
142 # above the inet address
143 #===============================================================================
144 sub get_ip_and_mac {
145 my $ip = "0.0.0.0.0"; # Defualt-IP
146 my $mac = "00:00:00:00:00:00"; # Default-MAC
147 my @ifconfig = qx(/sbin/ifconfig);
148 foreach(@ifconfig) {
149 if (/Hardware Adresse (\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2}):(\S{2})/) {
150 $mac = "$1:$2:$3:$4:$5:$6";
151 next;
152 }
153 if (/inet Adresse:(\d+).(\d+).(\d+).(\d+)/) {
154 $ip = "$1.$2.$3.$4";
155 last;
156 }
157 }
158 return ($ip, $mac);
159 }
162 #=== FUNCTION ================================================================
163 # NAME: open_socket
164 # PARAMETERS: PeerAddr string something like 192.168.1.1 or 192.168.1.1:10000
165 # [PeerPort] string necessary if port not appended by PeerAddr
166 # RETURNS: socket IO::Socket::INET
167 # DESCRIPTION: open a socket to PeerAddr
168 #===============================================================================
169 sub open_socket {
170 my ($PeerAddr, $PeerPort) = @_ ;
171 if(defined($PeerPort)){
172 $PeerAddr = $PeerAddr.":".$PeerPort;
173 }
174 my $socket;
175 $socket = new IO::Socket::INET(PeerAddr => $PeerAddr ,
176 Porto => "tcp" ,
177 Type => SOCK_STREAM,
178 Timeout => 5,
179 );
180 if(not defined $socket) {
181 return;
182 }
183 &main::daemon_log("open_socket to: $PeerAddr", 7);
184 return $socket;
185 }
187 #=== FUNCTION ================================================================
188 # NAME: register_at_bus
189 # PARAMETERS: nothing
190 # RETURNS: nothing
191 # DESCRIPTION: creates an entry in known_daemons and send a 'here_i_am' msg to bus
192 #===============================================================================
193 sub register_at_bus {
195 # create known_daemons entry
196 &main::create_known_daemon($bus_address);
197 &main::add_content2known_daemons(hostname=>$bus_address, status=>"register_at_bus", passwd=>$bus_passwd);
199 my $msg_hash = &create_xml_hash("here_i_am", $server_address, $bus_address);
200 my $answer = "";
201 $answer = &send_msg_hash2address($msg_hash, $bus_address);
202 if ($answer == 0) {
203 &main::daemon_log("register at bus: $bus_address", 1);
204 } else {
205 &main::daemon_log("unable to send 'register'-msg to bus: $bus_address", 1);
206 }
207 return;
208 }
210 #=== FUNCTION ================================================================
211 # NAME: process_incoming_msg
212 # PARAMETERS: crypted_msg - string - incoming crypted message
213 # RETURNS: nothing
214 # DESCRIPTION: handels the proceeded distribution to the appropriated functions
215 #===============================================================================
216 sub process_incoming_msg {
217 my ($crypted_msg) = @_ ;
218 if(not defined $crypted_msg) {
219 &main::daemon_log("function 'process_incoming_msg': got no msg", 7);
220 }
222 $crypted_msg =~ /^([\s\S]*?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)\.(\d{1,3}?)$/;
223 $crypted_msg = $1;
224 my $host = sprintf("%s.%s.%s.%s", $2, $3, $4, $5);
226 # collect addresses from possible incoming clients
227 my @valid_keys;
228 my @host_keys = keys %$main::known_daemons;
229 foreach my $host_key (@host_keys) {
230 if($host_key =~ "^$host") {
231 push(@valid_keys, $host_key);
232 }
233 }
234 my @client_keys = keys %$main::known_clients;
235 foreach my $client_key (@client_keys) {
236 if($client_key =~ "^$host"){
237 push(@valid_keys, $client_key);
238 }
239 }
240 push(@valid_keys, $server_address);
242 my $l = @valid_keys;
243 my $msg_hash;
244 my $msg_flag = 0;
245 my $msg = "";
247 # determine the correct passwd for deciphering of the incoming msgs
248 foreach my $host_key (@valid_keys) {
249 eval{
250 &main::daemon_log("ServerPackage: host_key: $host_key", 7);
251 my $key_passwd;
252 if (exists $main::known_daemons->{$host_key}) {
253 $key_passwd = $main::known_daemons->{$host_key}->{passwd};
254 } elsif (exists $main::known_clients->{$host_key}) {
255 $key_passwd = $main::known_clients->{$host_key}->{passwd};
256 } elsif ($host_key eq $server_address) {
257 $key_passwd = $server_passwd;
258 }
259 &main::daemon_log("ServerPackage: key_passwd: $key_passwd", 7);
260 my $key_cipher = &create_ciphering($key_passwd);
261 $msg = &decrypt_msg($crypted_msg, $key_cipher);
262 &main::daemon_log("ServerPackages: decrypted msg: $msg", 7);
263 $msg_hash = $xml->XMLin($msg, ForceArray=>1);
264 #my $tmp = printf Dumper $msg_hash;
265 #&main::daemon_log("DEBUG: ServerPackages: xml hash: $tmp", 7);
266 };
267 if($@) {
268 &main::daemon_log("ServerPackage: key raise error: $@", 7);
269 $msg_flag += 1;
270 } else {
271 last;
272 }
273 }
275 if($msg_flag >= $l) {
276 &main::daemon_log("WARNING: ServerPackage do not understand the message:", 5);
277 &main::daemon_log("$@", 7);
278 return;
279 }
281 # process incoming msg
282 my $header = @{$msg_hash->{header}}[0];
283 my $source = @{$msg_hash->{source}}[0];
285 &main::daemon_log("recieve '$header' at ServerPackages from $host", 1);
286 # &main::daemon_log("ServerPackages: msg from host:", 5);
287 # &main::daemon_log("\t$host", 5);
288 # &main::daemon_log("ServerPackages: header from msg:", 5);
289 # &main::daemon_log("\t$header", 5);
290 &main::daemon_log("ServerPackages: msg to process:", 5);
291 &main::daemon_log("\t$msg", 5);
293 my @targets = @{$msg_hash->{target}};
294 my $len_targets = @targets;
295 if ($len_targets == 0){
296 &main::daemon_log("ERROR: ServerPackages: no target specified for msg $header", 1);
298 } elsif ($len_targets == 1){
299 # we have only one target symbol
301 my $target = $targets[0];
302 &main::daemon_log("SeverPackages: msg is for:", 7);
303 &main::daemon_log("\t$target", 7);
305 if ($target eq $server_address) {
306 # msg is for server
307 if ($header eq 'new_passwd'){ &new_passwd($msg_hash)}
308 elsif ($header eq 'here_i_am') { &here_i_am($msg_hash)}
309 elsif ($header eq 'who_has') { &who_has($msg_hash) }
310 elsif ($header eq 'who_has_i_do') { &who_has_i_do($msg_hash)}
311 elsif ($header eq 'update_status') { &update_status($msg_hash) }
312 elsif ($header eq 'got_ping') { &got_ping($msg_hash)}
313 elsif ($header eq 'get_load') { &execute_actions($msg_hash)}
314 else { &main::daemon_log("ERROR: ServerPackages: no function assigned to this msg", 5) }
317 } elsif ($target eq "*") {
318 # msg is for all clients
320 my @target_addresses = keys(%$main::known_clients);
321 foreach my $target_address (@target_addresses) {
322 if ($target_address eq $source) { next; }
323 $msg_hash->{target} = [$target_address];
324 &send_msg_hash2address($msg_hash, $target_address);
325 }
326 } else {
327 # msg is for one host
329 if (exists $main::known_clients->{$target}) {
330 &send_msg_hash2address($msg_hash, $target);
331 } elsif (exists $main::known_daemons->{$target}) {
332 # target is known
333 &send_msg_hash2address($msg_hash, $target);
334 } else {
335 # target is not known
336 &main::daemon_log("ERROR: ServerPackages: target $target is not known neither in known_clients nor in known_daemons", 1);
337 }
338 }
339 }
341 return ;
342 }
345 #=== FUNCTION ================================================================
346 # NAME: got_ping
347 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
348 # RETURNS: nothing
349 # DESCRIPTION: process this incoming message
350 #===============================================================================
351 sub got_ping {
352 my ($msg_hash) = @_;
354 my $source = @{$msg_hash->{source}}[0];
355 my $target = @{$msg_hash->{target}}[0];
356 my $header = @{$msg_hash->{header}}[0];
358 if(exists $main::known_daemons->{$source}) {
359 &main::add_content2known_daemons(hostname=>$source, status=>$header);
360 } else {
361 &main::add_content2known_clients(hostname=>$source, status=>$header);
362 }
364 return;
365 }
368 #=== FUNCTION ================================================================
369 # NAME: new_passwd
370 # PARAMETERS: msg_hash - ref - hash from function create_xml_hash
371 # RETURNS: nothing
372 # DESCRIPTION: process this incoming message
373 #===============================================================================
374 sub new_passwd {
375 my ($msg_hash) = @_;
377 my $source = @{$msg_hash->{source}}[0];
378 my $passwd = @{$msg_hash->{new_passwd}}[0];
380 if (exists $main::known_daemons->{$source}) {
381 &main::add_content2known_daemons(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
382 my $hash = &create_xml_hash("confirm_new_passwd", $server_address, $source);
383 &send_msg_hash2address($hash, $source);
385 } elsif (exists $main::known_clients->{$source}) {
386 &main::add_content2known_clients(hostname=>$source, status=>"new_passwd", passwd=>$passwd);
388 } else {
389 &main::daemon_log("ERROR: $source not known, neither in known_daemons nor in known_clients", 1)
390 }
392 return;
393 }
396 #=== FUNCTION ================================================================
397 # NAME: here_i_am
398 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
399 # RETURNS: nothing
400 # DESCRIPTION: process this incoming message
401 #===============================================================================
402 sub here_i_am {
403 my ($msg_hash) = @_;
405 my $source = @{$msg_hash->{source}}[0];
406 my $mac_address = @{$msg_hash->{mac_address}}[0];
407 my $out_hash;
409 # number of known clients
410 my $nu_clients = keys %$main::known_clients;
412 # check wether client address or mac address is already known
413 if (exists $main::known_clients->{$source}) {
414 &main::daemon_log("WARNING: $source is already known as a client", 1);
415 &main::daemon_log("WARNING: values for $source are being overwritten", 1);
416 $nu_clients --;
417 }
419 # number of actual activ clients
420 my $act_nu_clients = $nu_clients;
422 &main::daemon_log("number of actual activ clients: $act_nu_clients", 5);
423 &main::daemon_log("number of maximal allowed clients: $max_clients", 5);
425 if($max_clients <= $act_nu_clients) {
426 my $out_hash = &create_xml_hash("denied", $server_address, $source);
427 &add_content2xml_hash($out_hash, "denied", "I_cannot_take_any_more_clients!");
428 my $passwd = @{$msg_hash->{new_passwd}}[0];
429 &send_msg_hash2address($out_hash, $source, $passwd);
430 return;
431 }
433 # new client accepted
434 my $new_passwd = @{$msg_hash->{new_passwd}}[0];
436 # create known_daemons entry
437 my $events = @{$msg_hash->{events}}[0];
438 &main::create_known_client($source);
439 &main::add_content2known_clients(hostname=>$source, events=>$events, mac_address=>$mac_address,
440 status=>"registered", passwd=>$new_passwd);
442 # return acknowledgement to client
443 $out_hash = &create_xml_hash("registered", $server_address, $source);
444 &send_msg_hash2address($out_hash, $source);
446 # notify registered client to bus
447 $out_hash = &create_xml_hash("new_client", $server_address, $bus_address, $source);
448 #&main::send_msg_hash2bus($out_hash);
449 &send_msg_hash2address($out_hash, $bus_address);
451 # give the new client his ldap config
452 &new_ldap_config($source);
454 return;
455 }
458 #=== FUNCTION ================================================================
459 # NAME: who_has
460 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
461 # RETURNS: nothing
462 # DESCRIPTION: process this incoming message
463 #===============================================================================
464 sub who_has {
465 my ($msg_hash) = @_ ;
467 # what is your search pattern
468 my $search_pattern = @{$msg_hash->{who_has}}[0];
469 my $search_element = @{$msg_hash->{$search_pattern}}[0];
470 &main::daemon_log("who_has-msg looking for $search_pattern $search_element", 7);
472 # scanning known_clients for search_pattern
473 my @host_addresses = keys %$main::known_clients;
474 my $known_clients_entries = length @host_addresses;
475 my $host_address;
476 foreach my $host (@host_addresses) {
477 my $client_element = $main::known_clients->{$host}->{$search_pattern};
478 if ($search_element eq $client_element) {
479 $host_address = $host;
480 last;
481 }
482 }
484 # search was successful
485 if (defined $host_address) {
486 my $source = @{$msg_hash->{source}}[0];
487 my $out_msg = &create_xml_hash("who_has_i_do", $server_address, $source, "mac_address");
488 &add_content2xml_hash($out_msg, "mac_address", $search_element);
489 &send_msg_hash2address($out_msg, $bus_address);
490 }
491 return;
492 }
495 sub who_has_i_do {
496 my ($msg_hash) = @_ ;
497 my $header = @{$msg_hash->{header}}[0];
498 my $source = @{$msg_hash->{source}}[0];
499 my $search_param = @{$msg_hash->{$header}}[0];
500 my $search_value = @{$msg_hash->{$search_param}}[0];
501 print "\ngot msg $header:\nserver $source has client with $search_param $search_value\n";
502 }
505 #=== FUNCTION ================================================================
506 # NAME: new_ldap_config
507 # PARAMETERS: address - string - ip address and port of a host
508 # RETURNS: nothing
509 # DESCRIPTION: send to address the ldap configuration found for dn gotoLdapServer
510 #===============================================================================
511 sub new_ldap_config {
512 my ($address) = @_ ;
514 if (not exists $main::known_clients->{$address}) {
515 &main::daemon_log("ERROR: $address does not exist in known_clients, cannot send him his ldap config", 1);
516 return;
517 }
519 my $mac_address = $main::known_clients->{$address}->{"mac_address"};
520 if (not defined $mac_address) {
521 &main::daemon_log("ERROR: no mac address found for client $address", 1);
522 return;
523 }
525 # fetch dn
526 my $goHard_cmd = "ldapsearch -x '(&(objectClass=goHard)(macAddress=00:11:22:33:44:57))' dn gotoLdapServer";
527 my $dn;
528 my @gotoLdapServer;
529 open (PIPE, "$goHard_cmd 2>&1 |");
530 # my $rbits = "";
531 # vec($rbits, fileno PIPE, 1) = 1;
532 # my $rout;
533 # my $nf = select($rout=$rbits, undef, undef, $ldap_timeout);
534 while(<PIPE>) {
535 chomp $_;
536 # If it's a comment, goto next
537 if ($_ =~ m/^[#]/) { next;}
538 if ($_ =~ m/^dn: ([\S]+?)$/) {
539 $dn = $1;
540 } elsif ($_ =~ m/^gotoLdapServer: ([\S]+?)$/) {
541 push(@gotoLdapServer, $1);
542 }
543 }
544 close(PIPE);
546 # no dn found
547 if (not defined $dn) {
548 &main::daemon_log("ERROR: no dn arose from command: $goHard_cmd", 1);
549 return;
550 }
552 # no gotoLdapServer found
553 my $gosaGroupOfNames_cmd = "ldapsearch -x '(&(objectClass=gosaGroupOfNames)(member=$dn))' gotoLdapServer";
554 if (@gotoLdapServer == 0) {
555 open (PIPE, "$gosaGroupOfNames_cmd 2>&1 |");
556 while(<PIPE>) {
557 chomp $_;
558 if ($_ =~ m/^[#]/) { next; }
559 if ($_ =~ m/^gotoLdapServer: ([\S]+?)$/) {
560 push(@gotoLdapServer, $1);
561 }
562 }
563 close(PIPE);
564 }
566 # still no gotoLdapServer found
567 if (@gotoLdapServer == 0) {
568 &main::daemon_log("ERROR: cannot find gotoLdapServer entry in command: $gosaGroupOfNames_cmd", 1);
569 return;
570 }
572 # sort @gotoLdapServer and then split of ranking
573 my @sorted_gotoLdapServer = sort(@gotoLdapServer);
574 @gotoLdapServer = reverse(@sorted_gotoLdapServer);
575 foreach (@gotoLdapServer) {
576 $_ =~ s/^\d://;
577 }
579 my $t = join(" ", @gotoLdapServer);
581 my $out_hash = &create_xml_hash("new_ldap_config", $server_address, $address);
582 map(&add_content2xml_hash($out_hash, "new_ldap_config", $_), @gotoLdapServer);
583 &send_msg_hash2address($out_hash, $address);
585 return;
586 }
589 #=== FUNCTION ================================================================
590 # NAME: execute_actions
591 # PARAMETERS: msg_hash - hash - hash from function create_xml_hash
592 # RETURNS: nothing
593 # DESCRIPTION: invokes the script specified in msg_hash which is located under
594 # /etc/gosad/actions
595 #===============================================================================
596 sub execute_actions {
597 my ($msg_hash) = @_ ;
598 my $configdir= '/etc/gosad/actions/';
599 my $result;
601 my $header = @{$msg_hash->{header}}[0];
602 my $source = @{$msg_hash->{source}}[0];
603 my $target = @{$msg_hash->{target}}[0];
605 if((not defined $source)
606 && (not defined $target)
607 && (not defined $header)) {
608 &main::daemon_log("ERROR: Entries missing in XML msg for gosad actions under /etc/gosad/actions");
609 } else {
610 my $parameters="";
611 my @params = @{$msg_hash->{$header}};
612 my $params = join(", ", @params);
613 &main::daemon_log("execute_actions: got parameters: $params", 5);
615 if (@params) {
616 foreach my $param (@params) {
617 my $param_value = (&get_content_from_xml_hash($msg_hash, $param))[0];
618 &main::daemon_log("execute_actions: parameter -> value: $param -> $param_value", 7);
619 $parameters.= " ".$param_value;
620 }
621 }
623 my $cmd= $configdir.$header."$parameters";
624 &main::daemon_log("execute_actions: executing cmd: $cmd", 7);
625 $result= "";
626 open(PIPE, "$cmd 2>&1 |");
627 while(<PIPE>) {
628 $result.=$_;
629 }
630 close(PIPE);
631 }
633 # process the event result
636 return;
637 }
640 1;