This project is read-only.

on cisco router no response after send Enable

Apr 8, 2014 at 1:55 PM
Edited Apr 8, 2014 at 2:02 PM
Hi there!
I've search everywhere, tried using expect, tried using other platforms like PowerShell, but I can't find a solution to this:
I can set up a connection with the Cisco router, create a shellstream and issue commands and receive the results up to the point where I issue the enable command. From that point all I write into the stream appears to be disappearing and there's no data available when I try to read from the stream.

I really hope someone can point me in the right direction!

Here's the test-code:
Imports System.IO
Imports Renci.SshNet
Imports Renci.SshNet.Common
Imports System.Threading
Imports System.Text
Module test
    Const ClientIP As String = "192.168.0.220"
    Const ClientUsername As String = "paul"
    Const ClientPassword As String = "apassword"
    Const ClientEnablepassword As String = "enablepassword"
    Dim ClientResponse As String

    Public Sub Test()

        Try
            Dim sshOutput As New StringBuilder
            Dim PasswordConnection = New PasswordAuthenticationMethod(ClientUsername, ClientPassword)
            Dim KeyboardInteractive = New KeyboardInteractiveAuthenticationMethod(ClientUsername)
            Dim ConnectionInfo = New ConnectionInfo(ClientIP, 22, ClientUsername, PasswordConnection, KeyboardInteractive)

            AddHandler KeyboardInteractive.AuthenticationPrompt, AddressOf handleAuthentication

            Dim sshClient = New SshClient(ConnectionInfo)
            sshClient.ConnectionInfo.Timeout = TimeSpan.FromSeconds(10)
            sshClient.ConnectionInfo.RetryAttempts = 3
            sshClient.Connect()

            If sshClient.IsConnected = False Then
                Throw New Exception("connection failed")
                Exit Sub
            End If
            Dim sshStream = sshClient.CreateShellStream("dumb", 80, 24, 800, 600, 1024)
            Dim reader = New StreamReader(sshStream)
            Dim writer = New StreamWriter(sshStream)
            writer.AutoFlush = True
            Thread.Sleep(1000)
            reader.ReadToEnd()
            sshStream.Write("terminal length 0" & vbCr)
            Thread.Sleep(100)
            sshOutput.Append(reader.ReadToEnd)
            sshStream.Write("show users" & vbCr)
            Thread.Sleep(100)
            sshOutput.Append(reader.ReadToEnd)
            sshStream.Write("enable" & vbCr)
            Thread.Sleep(100)
            sshOutput.Append(reader.ReadToEnd)
            sshStream.Write(ClientEnablepassword & vbCr)
            Thread.Sleep(100)
            sshOutput.Append(reader.ReadToEnd)
            sshStream.Write("show running-config" & vbCr)
            Thread.Sleep(1000)
            sshOutput.Append(reader.ReadToEnd)
            Thread.Sleep(100)
            sshStream.Write("exit" & vbCr)
            sshOutput.Append(reader.ReadToEnd)

            ClientResponse = sshOutput.ToString

            sshStream.Close()
            sshStream.Dispose()
            sshClient.Disconnect()
            sshClient.Dispose()

        Catch ex As Exception
            Debug.Print(ex.Message.ToString)
        End Try

    End Sub

    Sub handleAuthentication(sender As Object, e As AuthenticationPromptEventArgs)
        For Each prompt As AuthenticationPrompt In e.Prompts
            If prompt.Request.Equals("Password: ", StringComparison.InvariantCultureIgnoreCase) Then
                prompt.Response = ClientPassword
            End If
        Next
    End Sub

End Module

and this is the final result in ClientResponse:
terminal length 0
c3745>show users
    Line       User       Host(s)              Idle       Location
   0 con 0                idle                 01:33:52   
*162 vty 0     paul       idle                 00:00:00 192.168.0.102

  Interface    User               Mode         Idle     Peer Address

c3745>enable
Password: 
Any help will be greatly appreciated!
kind regards, Paul
Apr 8, 2014 at 11:18 PM
Paul,

I'm too lazy to debug your code, as you are taking a different approach to communication than I did/do. I try to modularize it a bit so I don't get lost! :) I imagine you are at the 'just make it work and I'll make it pretty later' stage... So to help get you out of a rut, this is a copy of an earlier post to someone doing the same things as you. My example is also in VB.NET:
In working with Cisco equipment, for example, I found that you had to be careful about when to issue CR, LF, CRLF, or even nothing! To help others extend renci, I wrote a short blog article on how to wrap commands/messages sent.

At first I added the necessary variations to the source (didn't I post that a while ago as a suggestion?), but when new source was coming out without the subtle inclusions, I had to wrap it to do the same thing.

Maybe it will help you...?

All around, best ssh app/library out there, IMHO... :)
Let me know if this gets you out of your rut & moving again!

pat
:)
Apr 9, 2014 at 9:52 AM
Hi Pat,

Thanks for your reply.

I've read your article before and it sure helped.
But, I'm equally sure it's not a 'when to send a CR, LF, CRLF character' problem.

After sending enable (sshStream.Write("enable" & vbCr)), this is what I've read from the stream:
c3745>
terminal length 0
c3745>
enable
Password: 
I then write the password:
sshStream.Write(ClientEnablepassword & vbCr)
and in the stream only an empty line is added
c3745>
terminal length 0
c3745>
enable
Password: 

It looks like I should write and read from an other stream after sending the enable password??

Paul
Apr 10, 2014 at 10:35 AM
Edited Apr 10, 2014 at 10:50 AM
I've done a couple of tests using the Cisco IOS debug function.

I turn on debugging on the router (debug ip ssh) using the console on the router just before I send the enablePassword with my test-program (shown above).
On the router console I see the debug-messages of ssh data being received, but in the shellstream only a newline is added.

I then send the exit command. On the router console I again see the ssh data being received including the message
SSH0: Session terminated normally
But the shellstream only shows one more added newline.

I've tried the same scenario with the SharpSSH test-program: everything works as expected. I'm able to send the enablePassword, see the "enabled-prompt" (#) and issue commands and retrieving its output.

This is driving me nuts!
Apr 10, 2014 at 10:38 AM
Edited Apr 10, 2014 at 10:38 AM
Please try the current beta version. There are changes at WriteLine() (and Write()?)
Apr 10, 2014 at 11:10 AM
Edited Apr 10, 2014 at 11:23 AM
Thanks for your response da_rinkes.

Unfortunately I have the same result: after sending enable only newlines can be found in the shellstream, one for each subsequent command sent.

Commands are being received on the Cisco router. I've send a reload command (an enabled mode command) and the router restarted.

So: I can confirm that all commands are being sent, and that I do not see the router's responses, once the enable command has been sent.


Paul
Apr 10, 2014 at 12:41 PM
Update:
I've tried my test program on another Cisco router (2621) and there it's all working perfectly!

The problem seems to be related to specific router(s), such as my test-router: 3745

Paul
Apr 10, 2014 at 3:00 PM
Paul(PE),

A change in symptoms is always a Good(tm) thing! :)

Since Cisco is just the ios, note the ios versions on known good, and known bad routers, along with model (s).

In the case where the sshd implementation subtly changed, this info is valuable in prod dev for things like release notes.

Glad you stuck with it!

pat
:)
Apr 10, 2014 at 4:46 PM
Thanks Pat,

Question remains: why can I send the Enable command and password using SharpSSH and also using terminal emulators like Putty and SecureCRT? Granted IOS versions are not all the same and even not all as reliable, but still if I can use a program using the SharpSSH library then why not SSH.NET?

Paul
Apr 10, 2014 at 5:36 PM
but still if I can use a program using the SharpSSH library then why not SSH.NET?
I doubt anyone here knows. There are no doubt subtleties as to how they both (and others) interpret and interface/interact with the SSH protocol. Also, we will never know why Cisco IOS is reacting differently to different approaches.

I would not assume that Cisco is not doing something to make connections "easier" for COTS TTY programs. They no doubt could care less about how GASP Open Source ssh libraries behave with their products.

It may be as simple as "If you find something that works, then use it". This project, like thousands of others, depends on people like you to find the issues and inconsistencies, sure, but also to determine work-arounds for source code.

And of course the obligatory caveat: If something in Cisco does/doesn't work today, it may be the opposite tomorrow for no reason whatsoever.

Happy Hunting!

pat
:)
Apr 12, 2014 at 10:31 AM
Edited Apr 12, 2014 at 10:43 AM
All right Pat, da_rinkes,

Perhaps you wonder what I'm working at. I have a system, in use for some 10 years now, by which people can perform settings on switches/routers.
Up to now I used external programs to do the actual communication to the switches. I want to have more control and not to depend on these programs.

I've created a class hierarchy switchSession and switchSshSession to handle sending a script containing commands and expected results to a switch/router.

Perhaps it's of some use to somebody else.

Base class switchSession
    ' Class switchSession is the base class for switchSshSession.
    '
    ' A switch session is the execution of a (text) script. The script contains alternating WAIT and SEND lines:
    '   WAIT "expected response"
    '   SEND "command"
    ' This format of SEND and WAIT commands has been in use for a long time and is constructed by the front-end
    ' application. The classes in this file implement the execution of such scripts.
    '
    ' example:
    '
    '192.168.88.238
    'Wait "Username:"
    'SEND "******@******\m"
    'WAIT "Password:"
    'SEND "*********\m"
    'wait ">"
    'SEND "enable\m"
    'WAIT "Password:"
    'SEND "********\m"
    'wait "#"
    'send "terminal length 0\m"
    'wait "#"
    'send "show mac-address-table vlan 1\m"
    'wait "#"
    'send "exit\m"

    Class switchSession
        ' base class switchSession
        Protected ClientIP As String = Nothing          ' ip address
        Protected ClientEndPort As IPEndPoint = Nothing
        Protected ClientResponseWait As Integer = 0     ' wait time (ms) to wait for response after sending a command
        Protected ClientResponseRetries As Integer = 0  ' nr of allowed retries when ScriptWait value is not (yet) received
        Protected ClientResponse As String = ""         ' responses received from the switch
        Protected ClientUsername As String = ""         ' username
        Protected ClientPassword As String = ""         ' password
        Protected ClientPort As Integer = 0             ' port (23: telnet, 22: ssh)
        Protected ClientDisplayscript As String = ""    ' password and username less version of the scriptText
        Protected sessionIOindex As Integer             ' index in send and wait
        Protected ScriptSend() As String                ' commands to send
        Protected ScriptWait() As String                ' expected responses
        Protected ClientValid As Boolean = False
        Protected sessionStatus As String = ""
        Public Sub New(scriptText As String, ResponseWait As Integer, ResponseRetries As Integer)
            ' Main purpose of base class switchSession New constructor is to convert the scriptText with SEND and WAIT lines
            ' into ScriptSend and ScriptWait string array's.
            ' Along with building ScriptSend (from SEND lines) and ScriptWait (from WAIT lines) ClientDisplayscript is filled,
            ' which will contain all SEND en WAIT lines but with username and password replaced by "********" (to allow an
            ' username and password less version of the script to present to the user)

            Dim qSend As New Queue(Of String)
            Dim qWait As New Queue(Of String)
            Try
                sessionStatus = "parse scripttext"
                scriptText = Regex.Replace(scriptText, "Wait " & Chr(34), "", RegexOptions.IgnoreCase)  ' remove all "wait"'s
                scriptText = Regex.Replace(scriptText, "Send " & Chr(34), "", RegexOptions.IgnoreCase)  ' remove all "send"'s
                scriptText = scriptText.Replace("\m" & Chr(34), "")                                     ' remove all command-end strings: \m"
                scriptText = scriptText.Replace("\M" & Chr(34), "")
                scriptText = scriptText.Replace(Chr(34), "")                                            ' remove all "

                For Each aIOstring As String In scriptText.Split(Environment.NewLine)
                    If sessionIOindex = 0 Then ClientIP = aIOstring.Trim
                    If sessionIOindex = 2 Then ClientUsername = aIOstring.Trim
                    If sessionIOindex = 4 Then ClientPassword = aIOstring.Trim
                    If sessionIOindex Mod 2 = 0 Or sessionIOindex = 0 Then
                        qSend.Enqueue(aIOstring.Trim)               ' even = send "..."
                        ClientDisplayscript += aIOstring & Environment.NewLine
                    Else
                        qWait.Enqueue(aIOstring.ToLower.Trim)       ' odd = wait "..."
                        If sessionIOindex <> 4 And sessionIOindex <> 8 Then
                            ClientDisplayscript += aIOstring & Environment.NewLine
                        Else
                            ClientDisplayscript += "********" & Environment.NewLine
                        End If
                    End If
                    sessionIOindex += 1
                Next
                sessionStatus = sessionIOindex & " lines in scripttext, " & qSend.Count & " script commands " & qWait.Count & " expected prompts"
                ReDim ScriptSend(qSend.Count - 1)
                ReDim ScriptWait(qWait.Count - 1)
                Dim i As Integer = 0
                While qSend.Count > 0
                    ScriptSend(i) = qSend.Dequeue
                    i += 1
                End While
                i = 0
                While qWait.Count > 0
                    ScriptWait(i) = qWait.Dequeue
                    i += 1
                End While

                qSend.Clear()
                qWait.Clear()

                ClientResponseWait = ResponseWait
                ClientResponseRetries = ResponseRetries

                ClientValid = True
                sessionIOindex = 0

            Catch ex As Exception
                ClientValid = False
                sessionStatus += Environment.NewLine & ex.Message
            End Try

        End Sub
        Public Function ExecuteScript() As Boolean
            Return False
        End Function

        Public ReadOnly Property ScriptDisplay As String
            Get
                Return ClientDisplayscript
            End Get
        End Property
        Public ReadOnly Property ScripLog As String
            Get
                Return ClientResponse
            End Get
        End Property
        Public ReadOnly Property isValid() As Boolean
            Get
                Return ClientValid
            End Get
        End Property
        Public ReadOnly Property status() As String
            Get
                Return sessionStatus
            End Get
        End Property
        Public Sub Dispose()
            ClientDisplayscript = ""
            ClientResponse = ""
            Array.Clear(ScriptSend, 0, ScriptSend.Length)
            Array.Clear(ScriptWait, 0, ScriptWait.Length)
            ClientIP = ""
            ClientUsername = ""
            ClientPassword = ""
        End Sub
    End Class
continued below...
Apr 12, 2014 at 10:33 AM
Edited Apr 12, 2014 at 6:22 PM
class switchSshSession
    Class switchSshSession
        Inherits switchSession
        Private SSHPasswordConnection As Renci.SshNet.PasswordAuthenticationMethod
        Private SSHKeyboardInteractive As Renci.SshNet.KeyboardInteractiveAuthenticationMethod
        Private SSHConnectionInfo As Renci.SshNet.ConnectionInfo
        Private ClientSsh As Renci.SshNet.SshClient = Nothing
        Private SSHShell As Renci.SshNet.Shell = Nothing
        Private SSHStream As Renci.SshNet.ShellStream
        Private SSHreader As StreamReader
        Private SSHwriter As StreamWriter

        Public Sub New(scriptText As String, ResponseWait As Integer, ResponseRetries As Integer)
            ' The switchSshSession New call's base class New to fill ScriptSend and ScriptWait
            ' and performs the SSH session setup

            MyBase.New(scriptText, ResponseWait, ResponseRetries)
            ClientPort = 22

            Try
                ' if keyboard password authentication is requested (demanded) by the switch, we can handle that
                SSHPasswordConnection = New Renci.SshNet.PasswordAuthenticationMethod(ClientUsername, ClientPassword)
                SSHKeyboardInteractive = New Renci.SshNet.KeyboardInteractiveAuthenticationMethod(ClientUsername)
                SSHConnectionInfo = New Renci.SshNet.ConnectionInfo(ClientIP, ClientPort, ClientUsername, SSHPasswordConnection, SSHKeyboardInteractive)

                AddHandler SSHKeyboardInteractive.AuthenticationPrompt, AddressOf SSHhandleKeyboardAuthentication

                ClientSsh = New Renci.SshNet.SshClient(SSHConnectionInfo)
                ClientSsh.ConnectionInfo.Timeout = TimeSpan.FromSeconds(10)
                ClientSsh.ConnectionInfo.RetryAttempts = 3
                ClientSsh.Connect()

                If ClientSsh.IsConnected = False Then
                    Throw New Exception("connection failed")
                    Exit Sub
                End If

                SSHStream = ClientSsh.CreateShellStream("xterm", 80, 24, 800, 600, 1024)
                SSHreader = New StreamReader(SSHStream)
                SSHwriter = New StreamWriter(SSHStream)
                SSHwriter.AutoFlush = True
                Thread.Sleep(ClientResponseWait)
                SSHreader.ReadToEnd()

            Catch ex As Exception
                sessionStatus = ex.Message & " connecting " & ClientIP & " port " & ClientPort & " (SSH)"
                ClientValid = False
            End Try

        End Sub
        Public Overloads Function ExecuteScript() As Boolean
            ' perform SSH i/o
            '
            ' In the While loop the commands in ScriptSend are send and responses in ScriptWait are 
            ' (substring) matched to the received response.
            ' SSHReadResult does the actual reading.
            ' The ClientResponse is build up as we go along, replacing passwords with *******

            ExecuteScript = False

            Dim sendString As String = ""
            Dim waitString As String = ""
            Dim receivedString As String = ""
            Dim PasswordSkip As Boolean = False
            sessionIOindex = 3 ' ssh offset 3: 0 = ip, 1 = username, 2 = password
            Try

                While sessionIOindex < ScriptSend.Length
                    sendString = ScriptSend(sessionIOindex)
                    waitString = ScriptWait(sessionIOindex)

                    SSHStream.Write(sendString & vbCr)          ' send command
                    receivedString = SSHReadResult(waitString)  ' read result

                    If receivedString.IndexOf(waitString, 0, receivedString.Length, StringComparison.CurrentCultureIgnoreCase) < 0 Then
                        Throw New Exception("received: '" & receivedString & "' waiting for: '" & waitString & "'")
                    End If

                    If PasswordSkip Then
                        ClientResponse += "*******"
                    Else
                        ClientResponse += receivedString
                    End If
                    ClientResponse += Environment.NewLine

                    PasswordSkip = False
                    If receivedString.IndexOf("password", 0, receivedString.Length, StringComparison.CurrentCultureIgnoreCase) >= 0 Then
                        PasswordSkip = True
                    End If
                    sessionIOindex += 1
                End While
                ExecuteScript = True

            Catch ex As Exception
                sessionStatus = ex.Message & Environment.NewLine & "send [ " & sendString & _
                    " ] to " & ClientIP & " (SSH), expecting response to contain [ " & waitString & " ] (script-index " & sessionIOindex * 2 & ")"
                ExecuteScript = False
            End Try
        End Function
        Private Sub SSHhandleKeyboardAuthentication(sender As Object, e As Renci.SshNet.Common.AuthenticationPromptEventArgs)
            For Each prompt As Renci.SshNet.Common.AuthenticationPrompt In e.Prompts
                If prompt.Request.Equals("Password: ", StringComparison.InvariantCultureIgnoreCase) Then
                    prompt.Response = ClientPassword
                End If
            Next
        End Sub
        Private Function SSHReadResult(Expected As String) As String
            ' Read the result from SSHreader until Expected is read.
            ' Commands issued to the switch may require some processing. Not all data may be available at once, so give em some slack.
            ' We asume the command send to allways be received by the switch
            Dim strRead As String = ""
            Dim retries As Integer = 0
            Try
                Thread.Sleep(ClientResponseWait)        ' give IOS a first change to form the response
                strRead = SSHreader.ReadToEnd

                While retries < ClientResponseRetries
                    If strRead.IndexOf(Expected, 0, strRead.Length, StringComparison.CurrentCultureIgnoreCase) < 0 Then
                        retries += 1
                        Thread.Sleep(ClientResponseWait)
                        If SSHStream.DataAvailable Then
                            strRead += SSHreader.ReadToEnd   ' append to data already received
                        End If
                    Else
                        Exit While
                    End If
                End While

                Return (strRead)

            Catch ex As Exception
                Return (Nothing)
            End Try
        End Function
        Public Overloads Sub Dispose()
            MyBase.Dispose()
            On Error Resume Next
            SSHreader.Close()
            SSHreader.Dispose()
            SSHwriter.Close()
            SSHwriter.Dispose()
            SSHShell.Dispose()
            Err.Clear()
        End Sub
    End Class
performing a session:
Session = New switchSshSession(scripttextString, 250, 20)
If Session.isValid Then
      ' session setup is ok. Now execute the script
      If Session.ExecuteScript() Then
             MsgBox(Session.ScripLog, MsgBoxStyle.OkOnly, "Script executed successfully")
      Else
            MsgBox(Session.status, MsgBoxStyle.OkOnly + MsgBoxStyle.Critical, "Script execution failed")
      End If
Else
      MsgBox(Session.status, MsgBoxStyle.OkOnly + MsgBoxStyle.Critical, "Session setup failed")
End If
Many thanks to the Renci SSH.NET developers!

Regards, Paul