# #!/usr/bin/expect -f (include this file, don't run it) # Login and logout expect procedures for Cisco SSH or telnet access # # written by Scott L. Miller # # Last modified: # 6/15/2013 By Scott L. Miller # ####################################################################### # Handle the login & logout procedures # # login will return the prompt string if successful # # The calling script expects return codes of 2 or less to mean the # login was unsuccessful, and 3 or more if at least the login was # successful but there were other errors. ####################################################################### proc connect_login { hostaddr user } { global logfile procid expect_out spawn_id set try_telnet 0 spawn /usr/bin/ssh -l $user $hostaddr expect { -gl "timeout" { puts $logfile "Connection timed out"; exit 1 } "timed out" { puts $logfile "Connection timed out"; exit 1 } "Connection refused" { puts $logfile "Connection refused ssh"; set try_telnet 1 } "uthentication failed" { puts $logfile "SSH Authentication failed"; exit 1 } -re "authenticity of host .* can't be established" { expect { "connecting (yes/no)" { send "yes\r" } } } "REMOTE HOST IDENTIFICATION HAS CHANGED" { expect { -re "(Offending key \[^\r]*)" { puts $logfile "Host SSH key has changed, $expect_out(1,string)"; } } exit 1 } -notransfer "assword:" { } default { puts $logfile "expect timed out ssh"; exit 2 } } if { $try_telnet > 0 } { set pstr [login_telnet $hostaddr $user] } else { set procid $spawn_id catch { login_ssh } pstr } return $pstr } proc login_ssh {} { global logfile procid ropwd rwpwd expect_out spawn_id #set spawn_id $procid expect { -gl "timeout" { puts $logfile "Connection timed out"; exit 1 } "timed out" { puts $logfile "Connection timed out"; exit 1 } "Connection refused" { puts $logfile "Connection refused"; exit 1 } "assword:" { send "$ropwd\r" } default { puts $logfile "expect timed out ssh login"; exit 2 } } expect { "Permission denied" { puts $logfile "Login incorrect"; exit 1 } "assword:" { puts $logfile "Login incorrect"; exit 1 } #attempt to match empty line between password prompt and cisco command prompt #"\r\n\r\n" { } "\r\n" { } "available commands.\r\n" { } default { puts $logfile "expect timed out ssh passwd"; exit 2 } } catch { get_prompt_and_enable } pstr return $pstr } proc login_telnet { hostaddr user } { global logfile ropwd rwpwd expect_out spawn_id spawn telnet $hostaddr expect { "timeout" { puts $logfile "Connection timed out"; exit 1 } "timed out" { puts $logfile "Connection timed out"; exit 1 } "Connection refused" { puts $logfile "Connection refused telnet"; exit 1 } "sername:" { send "$user\r" } default { puts $logfile "Connection timed out"; exit 2 } } set procid $spawn_id expect { "assword:" { send "$ropwd\r" } default { puts $logfile "expect timed out waiting for telnet password:"; exit 2 } } expect { "assword:" { puts $logfile "Login incorrect"; exit 1 } "Authentication failed" { puts $logfile "Login incorrect"; exit 1 } -notransfer -re "(>|#)" { } default { puts $logfile "expect timed out after telnet password"; exit 2 } } catch { get_prompt_and_enable } pstr return $pstr } proc get_prompt_and_enable {} { global logfile procid rwpwd expect_out spawn_id expect { "assword:" { puts $logfile "Login incorrect"; exit 1 } -re "(\[a-zA-Z0-9_@\\-: ]*)(>|#)" { #### grab the prompt string to simplify prompt recognition & help eliminate false positives set pstr $expect_out(1,string) set plvl $expect_out(2,string) #yeah, the bare '>' below is correct, but I don't like the way it looks either if { [string equal $plvl >] } { send "enable\r" expect { "assword:" { send "$rwpwd\r" } default { puts $logfile "expect timed out during enable"; exit 2 } } expect { "Access denied" { puts $logfile "Enable Login incorrect"; exit 1 } -notransfer "[set pstr]#" { } default { puts $logfile "expect timed out after enable passwd"; exit 2 } } } send "\r" } default { puts $logfile "expect timed out while identifying prompt string"; exit 2 } } expect { -notransfer "[set pstr]#" { } default { puts $logfile "expect timed out after enable mode"; exit 3 } } return $pstr } ######################################################################### # remember, all the expect pre- and post- tests are still active, # That's why we need the "global ... " statement ######################################################################### proc logout {} { global pstr logfile lastcmd errorcount expect_after { #The work is done, we're just trying to shutdown cleanly, If this #shutdown attempt is ALL that fails, there's no sense in saying the #operations failed. Unless of course the operations did fail... default { puts $logfile "expect timed out after \'$lastcmd\', exiting with $errorcount status" exit $errorcount } } expect "[set pstr]#" { send "exit\r" } expect { "\r\n" { close } } wait; } # # vim:ai:ts=8:sw=8