X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=debug_tools%2Fnetconf_TCP_SSH_hijackingproxy.pl;fp=debug_tools%2Fnetconf_TCP_SSH_hijackingproxy.pl;h=4efab85ae32ae9ab1aa61567c7fb2ebe89241307;hb=4e8296c45199b41c02e11c350490115941dda13b;hp=0000000000000000000000000000000000000000;hpb=64e60614b5c266be7d9d435096ae8d4eb48aa60b;p=transportpce.git diff --git a/debug_tools/netconf_TCP_SSH_hijackingproxy.pl b/debug_tools/netconf_TCP_SSH_hijackingproxy.pl new file mode 100755 index 000000000..4efab85ae --- /dev/null +++ b/debug_tools/netconf_TCP_SSH_hijackingproxy.pl @@ -0,0 +1,205 @@ +#!/usr/bin/env perl +############################################################################## +#Copyright (c) 2017 Orange, Inc. and others. All rights reserved. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License v1.0 which accompanies this distribution, +# and is available at http://www.eclipse.org/legal/epl-v10.html +############################################################################## +# +# debian dependecies: apt-get install libnet-openssh-perl libio-pty-perl +# + +use strict; +use warnings; +#use diagnostics; #uncomment this line for more details when encountering warnings +use Net::OpenSSH; +use FileHandle; +use Getopt::Long qw(:config no_ignore_case bundling); + +use IO::Socket; +use Net::hostent; + +my ($host, $help, $usage, $proxy_port, $login, $password, $kidpid, $ssh_subsocket, $simpleproxy, + $pid, $ssh_handle, $client, $server, $capabilities, $hello_message, $verbose); + +GetOptions ( + "h|help" =>\$help, + "p|port=i"=>\$proxy_port, + "s|simpleproxy" =>\$simpleproxy, + "v|verbose" =>\$verbose, + "C|capabilities=s"=>\$capabilities +); +$usage = " +USAGE: netconf_TCP_SSH_hijackproxy.pl [-h|--help] [-p|--port ] [-s|--simpleproxy] [-v|--verbose] [-C|--capabilities ] <[login[:password]@]host[:port]> [login] [password] + +Netconf SSH to TCP proxy to debug netconf exchanges. +It listens to connections in clear TCP to the given port. When a TCP connection demand is received, +it establishes a netconf SSH encrypted connection to the host in argument. Netconf rpcs and replies +are then proxified between both ends. +By default, exchanges are altered according to the rules specified inside this script and easily +modifiable. This behaviour can be disabled with the '-s' option. +For more convenience, the server hello handshake can also alternatively be replaced by the content +of an external file rather instead of writing specific rules. + +OPTIONS : + + -h or --help print this help + -p or --port use the given port number for listening TCP clients, default=9000 + -s or --simpleproxy simple proxy mode, do not alter any exchanges + -v or --verbose display exchanges to STDOUT + -C or --capabilities do not relay the real server hello message to the client + but replace it by the one provided in the following file + + +"; + +if ($help) { + print $usage; + exit(0); +} + +unless (@ARGV >= 1) { + print $usage; + exit(0); +} + +($host, $login, $password) = @ARGV; + +#netconf default port is no 22 but 830 +if ($host !~ /:[0-9]+$/) { $host.=':830'; } + +if (!defined($proxy_port)) { $proxy_port = 9000; } + +my $connection_string=$host; +if ($password) { + $connection_string=$login.":".$password."@".$connection_string; +} elsif ($login) { + $connection_string=$login."@".$connection_string; +} + +#retrieving hello custom file if any +if ((!defined ($simpleproxy))&&(defined ($capabilities))) { + open(CAPABILITIES,'<',$capabilities) or die ("can not open $capabilities") ; + while () { + $hello_message .= $_; + } + chop $hello_message; # removing EOF + $hello_message.="]]>]]>"; + close(CAPABILITIES); +} + +# the following regex are used to modify some part of the server messages relayed to the client +# you can adapt it to your needs, some examples have been commented. +my %regex_hash=( +# replace oo-device v1.2 by v1.2.1 +# 'module=org-openroadm-device&revision=2016-10-14.*<\/capability>'=>'s/&revision=2016-10-14/&revision=2017-02-06/', +# 'org-openroadm-device<\/identifier>2016-10-14'=>'s@org-openroadm-device2016-10-14@org-openroadm-device2017-02-06@', +# remove all deviations found +# '&deviations=.*<\/capability>'=>'s@&deviations=.*@@', +# add the ietf-netconf capability to the hello handshake - without it, ODL netconf mountpoints can not work +# '<\/capabilities>'=>'s@@\nurn:ietf:params:xml:ns:yang:ietf-netconf?module=ietf-netconf&revision=2011-06-01\n@', +# add the right notifications capabilities to the hello handshake + provide another solution for the ietf-netconf capability + '<\/capabilities>'=>'s@@\nurn:ietf:params:xml:ns:netmod:notification?module=nc-notifications&revision=2008-07-14\nurn:ietf:params:xml:ns:netconf:notification:1.0?module=notifications&revision=2008-07-14\nurn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01\n@' +); + +if (defined ($simpleproxy)) { %regex_hash=(); } + +my %compiled_regex_hash; +foreach my $keyword (keys %regex_hash){ + eval ('$compiled_regex_hash{$keyword}= qr/'.$keyword.'/;'); +} + +$server = IO::Socket::INET->new( Proto => 'tcp', + LocalPort => $proxy_port, + Listen => SOMAXCONN, + Reuse => 1); +die "can't setup server" unless $server; +print STDERR "[Proxy server $0 accepting clients: Ctrl-C to stop]\n"; + + +while ($client = $server->accept()) { + $client->autoflush(1); + my $hostinfo = gethostbyaddr($client->peeraddr); + printf STDERR "[Incoming connection from %s]\n", $hostinfo->name || $client->peerhost; + + +print STDERR "[relaying to ".$connection_string."]\n"; + +$ssh_handle = Net::OpenSSH->new($connection_string, + master_opts => [-o => 'StrictHostKeyChecking=no'], + timeout => 500, kill_ssh_on_timeout => 500); + +#netconf requires a specific socket +($ssh_subsocket, $pid) = $ssh_handle->open2socket({ssh_opts => '-s'}, 'netconf'); +die "can't establish connection: exiting\n" unless defined($ssh_subsocket); + +print STDERR "[Connected]\n"; + +# split the program into two processes, identical twins +die "can't fork: $!" unless defined($kidpid = fork()); + +$|=1; + +# the if{} block runs only in the parent process (server output relayed to the client) +if (!$kidpid) { + + # copy the socket to standard output + my $buf; + + if (defined ($hello_message)) { + #retrieve the server hello but do not relay it + while (my $nread = sysread($ssh_subsocket,$buf,400)) { + $ssh_subsocket->flush(); + if ($buf =~ /]]>]]>/) { last }; + }; + #send a custom hello message instead + print $client $hello_message; + if (defined($verbose)) { print STDOUT $hello_message; } + } + + #while (<$ssh_subsocket>) { + #buffer seems not totally flushed when using the usual syntax above (nor when using autoflush) + while (my $nread = sysread($ssh_subsocket,$buf,400)) { + foreach my $keyword (keys %regex_hash){ + if($buf =~ $compiled_regex_hash{$keyword}){ + print STDERR 'found regex '.$keyword.": replacing '\n".$buf."\n' by '\n"; + eval ('$buf =~ '.$regex_hash{$keyword}.';'); + print STDERR $buf."\n'\n"; + } + } + print $client $buf; + $ssh_subsocket->flush(); + if (defined($verbose)) { print STDOUT $buf; } + + }; + + kill("TERM", $kidpid); # send SIGTERM to child +} +# the else{} block runs only in the child process (client input relayed to the server) +else { + + $ssh_subsocket->autoflush(1); + sleep 1; # wait needed for ensuring STDOUT buffer is not melt + my $buf; + + #while (defined (my $buf = <$client>)) { + #usual syntax above used in verbose mode results into flush problems + while (my $nread = sysread($client,$buf,400)) { + print $ssh_subsocket $buf; + $client->flush(); + if (defined($verbose)) { print STDOUT $buf; } + }continue {} + + close $client; + +} + +$|=0; + +sleep 2; +kill("TERM", $kidpid); # send SIGTERM to child + +} + +exit;