#!/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); my ($host, $help, $usage, $capabilities, $login, $password, $kidpid, $hello_message); GetOptions ( "h|help" =>\$help, "C|capabilities=s"=>\$capabilities ); $usage = " USAGE: netconf_terminal.pl [-h|--help] [-C|--capabilities ] <[login[:password]@]host[:port]> [login] [password] Simple netconf terminal client that can be used as an alternative to 'openssh [-p port] <[login@]host> -s netconf'. The main difference is the built-in handshake phase with hello capabilties that can be loaded from an external file. This is particularly useful to avoid timeouts. OPTIONS : -C or --capabilities use the given file to advertise a hello message with customized capabilities -h or --help print this help "; 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'; } 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 ($capabilities)) { open(CAPABILITIES,'<',$capabilities) or die ("can not open $capabilities") ; while () { $hello_message .= $_; } chop $hello_message; # removing EOF $hello_message.="\n]]>]]>\n"; close(CAPABILITIES); } #otherwise using a basic hello message #EXI extension is not advertised by default since difficult to handle manually else{ $hello_message=' urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?module=ietf-netconf-monitoring&revision=2010-10-04 urn:ietf:params:xml:ns:yang:ietf-netconf-notifications?module=ietf-netconf-notifications&revision=2012-02-06 urn:ietf:params:xml:ns:netconf:base:1.0?module=ietf-netconf&revision=2011-06-01 urn:ietf:params:xml:ns:yang:ietf-yang-types?module=ietf-yang-types&revision=2013-07-15 urn:ietf:params:netconf:capability:candidate:1.0 urn:ietf:params:xml:ns:netconf:notification:1.0?module=notifications&revision=2008-07-14 urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring-extension?module=ietf-netconf-monitoring-extension&revision=2013-12-10 urn:ietf:params:netconf:base:1.0 urn:ietf:params:xml:ns:yang:iana-afn-safi?module=iana-afn-safi&revision=2013-07-04 urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2013-07-15 '; $hello_message.="\n]]>]]>\n"; } print STDERR "connecting to ".$connection_string."\n"; my $ssh_handle= Net::OpenSSH->new($connection_string, master_opts => [-o => 'StrictHostKeyChecking=no'], timeout => 500, kill_ssh_on_timeout => 500); #netconf requires a specific socket my ($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()); # the if{} block runs only in the parent process (terminal output) if (!$kidpid) { $|=1; # copy the socket to standard output my $buf; my $nread; #while (<$ssh_subsocket>) { #buffer seems not totally flushed when using the syntax above (nor when using autoflush) while ($nread = sysread($ssh_subsocket,$buf,150)) { print $buf; $ssh_subsocket->flush(); }; print; kill("TERM", $kidpid); # send SIGTERM to child } # the else{} block runs only in the child process (terminal input) else { $ssh_subsocket->autoflush(1); sleep 1; # wait needed for ensuring STDOUT buffer is not melt if (defined ($hello_message)) { print $ssh_subsocket $hello_message; sleep 1; } while (defined (my $line = )) { print $ssh_subsocket $line; } } sleep 2; kill("TERM", $kidpid); # send SIGTERM to child exit;