#!/usr/bin/env perl package INF::ILO5; use HTTP::Daemon::SSL; use HTTP::Server::Brick; use HTTP::Status; use IO::Socket::SSL qw(); use HTML::TreeBuilder; use HTTP::Request::Common; use LWP::UserAgent; use URI::Escape; use File::Temp qw/ tempfile tempdir /; use XML::Simple; use Data::Dumper; use JSON::PP; #IO::Socket::SSL::set_ctx_defaults( SSL_verify_mode => SSL_VERIFY_NONE ); IO::Socket::SSL::set_ctx_defaults( SSL_verify_mode => Net::SSLeay::VERIFY_NONE() ); sub read_file($) { my ($name) = @_; my $fh = new IO::File "<" . $name; local $/; my $guts = $fh->getline; $fh->close; undef $fh; return $guts; } #sub setup_port_proxy($$$) { # my ( $local_port, $remote_host, $remote_port ) = @_; # # my $child = fork(); # # print STDERR "balance ", # join( # ' ', # ( # "balance", "-d", "-f", "127.0.0.1", $local_port, # $remote_host . ":" . $remote_port # ) # ), # "\n"; # # if ( $child == 0 ) { # # exec( "balance", "-d", "-f", "-b", "127.0.0.1", $local_port, # $remote_host . ":" . $remote_port ); # print STDERR "failed to start port proxy"; # sleep(10000); # } # # print "Setup proxy $local_port -> $remote_host:$remote_port\n"; # # return $child; #} # sub setup_port_proxy($$$$) { my ( $self, $local_port, $remote_host, $remote_port ) = @_; my $child = fork(); my $cmd = [ "balance", "-d", "-f", "-b", "127.0.0.1", $local_port, $remote_host . ":" . $remote_port ]; print STDERR "PROXY CMD: " . join( ' ', @$cmd ) . "\n"; if ( $child == 0 ) { if ( defined $self->{proxy_host} ) { my $tmp = File::Temp->new( UNLINK => 0, SUFFIX => '.cnf' ); select( ( select($tmp), $| = 1 )[0] ); print $tmp "server = 127.0.0.1\n"; print $tmp "server_port = " . $self->{proxy_port} . "\n"; print $tmp "local = 127.0.0.0/255.0.0.0\n"; $ENV{'LD_PRELOAD'} = 'libtsocks.so'; $ENV{'TSOCKS_CONF_FILE'} = $tmp->filename; print "Filename is $tmp->filename\n"; } exec(@$cmd); print STDERR "failed to start port proxy"; sleep(10000); } sleep(4); print "Setup proxy $local_port -> $remote_host:$remote_port\n"; return $child; } sub proxy($$$) { my ( $self, $req, $res ) = @_; my $suri = $req->uri->as_string; print STDERR "proxying $suri\n"; if ( $req->uri->as_string =~ /^\/html\/java_irc.html/ ) { $res->header( 'Content-type' => 'text/html' ); $res->add_content( $self->{java_html} ); $res->code(200); return; } if ( $req->uri->as_string =~ /^\/html\/intgapp.*\.jar/ ) { $res->header( 'Content-type' => 'application/x-ms-application' ); $res->add_content( read_file('/usr/local/share/inf/ilo5/intgapp4_251.jar') ); $res->code(200); return; } # print STDERR "fish: ",Dumper($req->headers),"\n"; my $proxy_req = HTTP::Request->new( $req->method, $self->{ilo_url} . $req->uri->as_string, [], $req->content ); $proxy_req->header( 'cookie' => 'sessionKey=' . $self->{skey} ); $proxy_req->header( 'x-auth-token' => $self->{skey} ); # print STDERR "soup: ",Dumper($proxy_req->headers),"\n"; #sessionLang=en; externalIrc=ircLocation=/redfish/v1/SessionService/Sessions/administrator0000000060db1eb3cdd2f1a9/ #Location=/redfish/v1/SessionService/Sessions/uxen00000000612e3f404189374c/; #UserPref=html5_statbar=true%2Ckbd_type=en #irc=last=jrc my $proxy_res = $self->{ua}->request($proxy_req); unless ( $proxy_res->is_success ) { print STDERR "request failed - did not get 200\n"; } print "URI:", $req->uri->as_string, " code ", $proxy_res->code, " type ", $proxy_res->header('Content-type'), "\n"; $res->code( $proxy_res->code ); $res->header( 'Content-type' => $proxy_res->header('Content-type') ); my $content = $proxy_res->content; if ( $req->uri->as_string =~ /^\/redfish\/v1\/Managers\/1\/RcInfo/ ) { print "content: $content\n"; my $local_port = int( rand(30000) ) + 30000; $content =~ s/"RcPort":(\d+),/"RcPort":$local_port,/; push @{ $self->{to_kill} }, $self->setup_port_proxy( $local_port, $self->{host}, $1 ); print "FISH $1 -> $local_port\n"; $local_port = int( rand(30000) ) + 30000; $content =~ s/"VmPort":(\d+),/"VmPort":$local_port,/; push @{ $self->{to_kill} }, $self->setup_port_proxy( $local_port, $self->{host}, $1 ); print "SOUP $1 -> $local_port\n"; } $res->add_content($content); } sub login($) { my $self = shift; my $post = POST( $self->{ilo_url} . '/json/login_session' ); my $json = '{"method":"login","user_login":"' . $self->{user} . '","password":"' . $self->{password} . '"}'; $post->header( 'Content-Type' => 'application/json' ); $post->header( 'Content-Length' => length($json) ); $post->content($json); my $res = $self->{ua}->request($post); unless ( $res->is_success ) { print STDERR "Login failed - did not get 200\n"; print Dumper($res); $self->{skey} = undef; return -1; } my $json = decode_json( $res->content ); $self->{skey} = $json->{session_key}; # print "Session key ".$self->{skey}."\n"; return 0; } sub view($) { my $self = shift; $self->login() unless defined $self->{skey}; my $get = GET( $self->{ilo_url} . '/html/java_irc.html?lang=en', 'Cookie' => 'sessionKey=' . $self->{skey} ); my $res = $self->{ua}->request($get); unless ( $res->is_success ) { print STDERR "IRC frequest failed - did not get 200\n"; return -1; } my $content = $res->content; unless ( $content =~ /Netscape'\) \{(.*)\}[\s\n]*else if/s ) { print STDERR "returned html doesn't look right\n"; return -1; } $content = $1; #$content=~ s/document.writeln\("(.*)"\);$/\1/m; $content =~ s/^\s*document.writeln\("(.*)"\);\s*$/\1/mg; $content =~ s/\\//g; $content =~ s/RCINFO1=.*$/RCINFO1="$self->{skey}"/m; $content =~ s/RCINFO6=.*$/RCINFO6=""/m; $content =~ s/RCINFOLANG=.*$/RCINFOLANG="en"/m; $content =~ s%(archive=)(/.*)$%\1$self->{proxy_url}\2%m; $content = "" . $content . ""; $self->{java_html} = $content; print $content; my $webserver_pid = fork(); if ( $webserver_pid == 0 ) { $SIG{INT} = sub { kill 'KILL', ( @{ $self->{to_kill} } ); die; }; $SIG{TERM} = sub { kill 'KILL', ( @{ $self->{to_kill} } ); die; }; $self->{server}->start; print STDERR "failed to web server"; sleep(100000); } push @{ $self->{to_kill} }, $webserver_pid; $SIG{INT} = sub { kill 'INT', ( @{ $self->{to_kill} } ); die; }; $SIG{TERM} = sub { kill 'TERM', ( @{ $self->{to_kill} } ); die; }; system( "echo", "appletviewer", "-J-Djava.security.manager", # "-J-Djava.security.debug=access,failure,policy", "-J-Djava.security.policy=/usr/local/share/inf/ilo5/mypolicy", "-J-Djavax.net.ssl.trustStore=/usr/local/share/inf/ilo5/server.jks", $self->{proxy_url} . "/html/java_irc.html" ); system( "appletviewer", "-J-Djava.security.manager", # "-J-Djava.security.debug=access,failure,policy", "-J-Djava.security.policy=/usr/local/share/inf/ilo5/mypolicy", "-J-Djavax.net.ssl.trustStore=/usr/local/share/inf/ilo5/server.jks", $self->{proxy_url} . "/html/java_irc.html" ); kill 'TERM', ( @{ $self->{to_kill} } ); } sub get_host_power($) { my ($self) = @_; $self->login() unless defined $self->{skey}; my $get = GET( $self->{ilo_url} . '/json/host_power' ); $get->header( 'cookie' => 'sessionKey=' . $self->{skey} ); my $res = $self->{ua}->request($get); unless ( $res->is_success ) { print STDERR " get host power - did not get 200\n"; return undef; } my $state = decode_json $res->content; return $state->{'hostpwr_state'}; } sub set_host_power($$) { my ( $self, $what ) = @_; $self->login() unless defined $self->{skey}; my $post = POST( $self->{ilo_url} . '/json/host_power' ); my $json = '{"method":"' . $what . '","session_key":"' . $self->{skey} . '"}'; $post->header( 'Content-Type' => 'application/json' ); $post->header( 'Content-Length' => length($json) ); $post->content($json); $post->header( 'cookie' => 'sessionKey=' . $self->{skey} ); my $res = $self->{ua}->request($post); unless ( $res->is_success ) { print STDERR " $what - did not get 200\n"; return 0; } return 1; } sub cold_boot($) { my $self = shift; return $self->set_host_power('system_coldboot'); } sub reset($) { my $self = shift; return $self->set_host_power('system_reset'); } sub off($) { my $self = shift; if ( $self->get_host_power =~ /ON/i ) { return $self->set_host_power('hold_power_button'); } return 1; } sub on($) { my $self = shift; if ( $self->get_host_power =~ /OFF/i ) { return $self->set_host_power('press_power_button'); } return 1; } sub port_on($$) { my $self = shift; return $self->on(); } sub port_off($$) { my $self = shift; return $self->off(); } sub port_cycle($$) { my $self = shift; return $self->cold_boot(); } sub port_state_get_no_cache($$) { my $self = shift; return $self->get_host_power(); } sub port_state_get($$) { my $self = shift; return $self->get_host_power(); } sub port_name_get($$) { my $self = shift; return $self->{name}; } sub name_get($) { my $self = shift; return $self->{name}; } sub pdu_load_get($) { return "N/A"; } sub psu_status($) { return "N/A"; } sub port_count($) { return 1; } sub port_id_get_by_number($$) { return "K0"; } sub logout($) { my $self = shift; } sub new ($;$) { my ( $class, $parm ) = @_; my $self; $self->{ua} = my $ua = LWP::UserAgent->new; $self->{host} = $parm->{host} || "127.0.0.1"; $self->{user} = $parm->{user} || "Administrator"; $self->{password} = $parm->{password} || ""; $self->{name} = $parm->{name} || $self->{host}; $self->{ilo_url} = $parm->{ilo_url} || 'https://' . $self->{host}; $self->{userid} = undef; $self->{ua}->ssl_opts( # SSL_verify_mode => SSL_VERIFY_NONE, SSL_verify_mode => Net::SSLeay::VERIFY_NONE(), verify_hostname => 0, ); if ( defined $parm->{proxy_host} ) { $self->{ua}->proxy( [qw(http https)] => "socks://" . $parm->{proxy_host} . ":" . $parm->{proxy_port} ); $self->{proxy_host} = $parm->{proxy_host}; $self->{proxy_port} = $parm->{proxy_port}; } my $local_port = int( rand(30000) ) + 30000; $self->{proxy_url} = 'https://127.0.0.1:' . $local_port; $self->{server} = HTTP::Server::Brick->new( port => $local_port, daemon_class => 'HTTP::Daemon::SSL', daemon_args => [ LocalAddr => '127.0.0.1', SSL_key_file => '/usr/local/share/inf/ilo5/server.key', SSL_cert_file => '/usr/local/share/inf/ilo5/server.crt', SSL_verify_mode => Net::SSLeay::VERIFY_NONE(), ], ); $self->{server}->mount( '/' => { handler => sub { my ( $req, $res ) = @_; $self->proxy( $req, $res ); 1; }, wildcard => 1, } ); $self->{skey} = undef; $self->{to_kill} = []; return bless $self, $class; } 1;