Anyone remember telnet?

I have need to connect to an ancient piece of equipment using telnet. So I grab the one and only telnet crate https://crates.io/crates/telnet and try out the example code:

    let mut telnet = Telnet::connect(("192.168.0.109", 23), 256)
            .expect("Couldn't connect to the server...");

    println!("Connected");
    loop {
        let event = telnet.read_nonblocking().expect("Read error");

        match event {
            TelnetEvent::Data(buffer) => {
                // Debug: print the data buffer
                println!("{:?}", String::from_utf8_lossy(&buffer));
                // process the data buffer
            },
            _ => {}
        }
    }

And run it against a Raspberry Pi running telnetd.

Nothing happens.

Well, it connects and that is it. What I'm expecting is the Pi login prompt as I get when I telnet to it manually:

$ telnet 192.168.0.109
Trying 192.168.0.109...
Connected to 192.168.0.109.
Escape character is '^]'.
Raspbian GNU/Linux 10
raspberrypi login:

Tried the blocking read as well. Same result.

As a random experiment I reduce the buffer size from 256 to 1. And then I get this:

Connected
"�"
"\u{18}"
"�"
" "
"�"
"#"
"�"
"\'"```

Which does not look like what I want. Not in hex either:

[fd]
[18]
[fd]
[20]
[fd]
[23]
[fd]
[27]

Any suggestions?

Habe you tried the blocking example?
Maybe connecting to something other people can reproduce?

Wasn't there somewhere a telnet starwars server?

Edit: found it towel.blinkenlights.nl

1 Like

I think you're seeing option negotiation. FF FD 18 is the byte sequence for IAC DO TERMINAL-TYPE. The crate probably understands FF is a control code but not the specific DO codes that follow.

2 Likes

I wrote this project, which you may find useful:

3 Likes

BINGO! With that clue, a sniff around the RFCs and another look at the telnet crate docs I came up with the following, which just replies "WONT" to all negotiations from the server:

    let mut telnet = Telnet::connect(("192.168.0.109", 23), 256)
            .expect("Couldn't connect to the server...");
    let mut line = 0;

    println!("Connected");

    loop {
        let event = telnet.read().expect("Read error");

        match event {
            TelnetEvent::Data(buffer) => {
                println!("Line {}: {}", line, String::from_utf8_lossy(&buffer));
                line += 1;
            },
            TelnetEvent::UnknownIAC(iac) => {
                println!("Unknown  IAC: {}", iac);
            },
            TelnetEvent::Negotiation(action, option) => {
                println!("Action {:?}, Option: {:?}", action, option);
                telnet.negotiate(telnet::NegotiationAction::Wont, option);
            },
            TelnetEvent::Subnegotiation(option, buf) => {
                println!("Option: {:?}, box: {:?}", option, buf);
            },
            TelnetEvent::TimedOut => {
                println!("Read timed out.");
            },
            TelnetEvent::NoData => {
                println!("No data.");
            },
            TelnetEvent::Error(err) => {
                println!("Error: {}", err);
            }
        }
    }

Which connects and produces the following:

Connected
Action Do, Option: TTYPE
Action Do, Option: TSPEED
Action Do, Option: XDISPLOC
Action Do, Option: NewEnvironment
Action Will, Option: SuppressGoAhead
Action Do, Option: Echo
Action Do, Option: NAWS
Action Will, Option: Status
Action Do, Option: LFLOW
Action Will, Option: Echo
Line 0: Raspbian GNU/Linux 10

Line 1: raspberrypi login:
Line 2:
Login timed out after 60 seconds.

Who knew telnet was so sophisticated? :slight_smile:

Pretty sure I have a clear road to getting this little job done now.

Thanks.

7 Likes

Can the readme of this project add more content? I'm not very good at using this demo. :innocent:

(In addition: Happy Birthday to you~ :birthday:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.