33

I need to automate logging into a TELNET session using expect, but I need to take care of multiple passwords for the same username.

Here's the flow I need to create:

  1. Open TELNET session to an IP
  2. Send user-name
  3. Send password
  4. Wrong password? Send the same user-name again, then a different password
  5. Should have successfully logged-in at this point...

For what it's worth, here's what I've got so far:

#!/usr/bin/expect
spawn telnet 192.168.40.100
expect "login:"
send "spongebob\r"
expect "password:"
send "squarepants\r"
expect "login incorrect" {
  expect "login:"
  send "spongebob\r"
  expect "password:"
  send "rhombuspants\r"
}
expect "prompt\>" {
  send_user "success!\r"
}
send "blah...blah...blah\r"

Needless to say this doesn't work, and nor does it look very pretty. From my adventures with Google expect seems to be something of a dark-art. Thanks in advance to anyone for assistance in the matter!

shuckster
  • 5,279
  • 2
  • 23
  • 22

3 Answers3

46

Have to recomment the Exploring Expect book for all expect programmers -- invaluable.

I've rewritten your code: (untested)

proc login {user pass} {
    expect "login:"
    send "$user\r"
    expect "password:"
    send "$pass\r"
}

set username spongebob 
set passwords {squarepants rhombuspants}
set index 0

spawn telnet 192.168.40.100
login $username [lindex $passwords $index]
expect {
    "login incorrect" {
        send_user "failed with $username:[lindex $passwords $index]\n"
        incr index
        if {$index == [llength $passwords]} {
            error "ran out of possible passwords"
        }
        login $username [lindex $passwords $index]
        exp_continue
    }
    "prompt>" 
}
send_user "success!\n"
# ...

exp_continue loops back to the beginning of the expect block -- it's like a "redo" statement.

Note that send_user ends with \n not \r

You don't have to escape the > character in your prompt: it's not special for Tcl.

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
13

With a bit of bashing I found a solution. Turns out that expect uses a TCL syntax that I'm not at all familiar with:

#!/usr/bin/expect
set pass(0) "squarepants"
set pass(1) "rhombuspants"
set pass(2) "trapezoidpants"
set count 0
set prompt "> "
spawn telnet 192.168.40.100
expect {
  "$prompt" {
    send_user "successfully logged in!\r"
  }
  "password:" {
    send "$pass($count)\r"
    exp_continue
  }
  "login incorrect" {
    incr count
    exp_continue
  }
  "username:" {
    send "spongebob\r"
    exp_continue
  }
}
send "command1\r"
expect "$prompt"
send "command2\r"
expect "$prompt"
send "exit\r"
expect eof
exit

Hopefully this will be useful to others.

shuckster
  • 5,279
  • 2
  • 23
  • 22
  • 1
    I used a similar solution for SSH but read the passwords from a passwords file '~/.ssh/ssh-passwords'. The hostname and username is read from my '~/.ssh/config' file. So running: '[cssh](https://github.com/DavidGamba/bin/blob/master/cssh) server1' will expand to ubuntu@server1.mycompany.com – DavidGamba Jul 30 '13 at 16:59
  • I am new to Expect, the expect { "case1"{...} "case2"{...} "case3"{...}}, is this some kind of switch case statement like C++? thanks – B.Mr.W. Oct 10 '13 at 21:35
  • @B.Mr.W. yes, using it in the outside ```{}``` basically makes it a fall through instead of individual ```if``` statements – eulerworks May 02 '17 at 00:09
-1

If you know the user ids and passwords, then you ought also to know which userid/password pairs are aligned with which systems. I think you'd be better off maintaining a map of which userid/password pair goes with which system then extracting that information and simply use the correct one.

So -- since you obviously don't like my advice, then I suggest you look at the wikipedia page and implement a procedure that returns 0 if successful and 1 if the expectation times out. That will allow you to detect when the password supplied failed -- the prompt expectation times out -- and retry. If this is helpful, you can remove your downvote now that I've edited it.

In retrospect, you'd probably want to do this in conjunction with the map anyway since you'd want to detect a failed login if the password was changed.

tvanfosson
  • 524,688
  • 99
  • 697
  • 795
  • Sorry for the downvote. I was on a long call and wanted a quick way of showing that I didn't have an answer yet without typing up a post to explain why. Anyway, I certainly wouldn't be using the conditional method if I had a reliable way of documenting the passwords being used on certain devices. It can change across firmware revisions outside of my control, so all I can do is have a list of passwords ready to try out. I understand what I need to do -- use a conditional statement and act on the answer -- I just don't know how to do it. :) – shuckster Oct 08 '09 at 16:24
  • You can remove the vote now that I've edited -- that is if the additional information is helpful or at least doesn't make the post unhelpful. As long as you don't accept an answer, people are likely to continue adding answers -- though you may want to update your question with the information on why keeping track of the passwords is hard. – tvanfosson Oct 08 '09 at 16:42