SLMT / telnet-rs

A simple implementation of Telnet in Rust.
MIT License
45 stars 20 forks source link

Example code does not work out of the box. There is no comment on why that my be. #22

Open ZiCog opened 2 years ago

ZiCog commented 2 years ago

I have tried the example codes, as shown on crates.io. Sadly I never get any data returned from my telnet server. My telnet server is telnet running on Ubuntu.

After a few hours of research into the telnet protocol and experimentation I finally get something that receives a login prompt with code lie this:

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::str::FromStr;
use std::time::Duration;
use telnet::{Action, Event, Telnet, TelnetOption};
//use telnet::{Event, Telnet};

fn main() {
    let address: String = "192.168.0.107".to_string();
    let port: u16 = 23;

    let address = SocketAddr::new(
        IpAddr::V4(Ipv4Addr::from_str(&address).expect("Invalid address")),
        port,
    );

    println!("Openning telnet connection to: {}", address);

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

    loop {
        println!("Waiting for data on telnet connection");
        //        let event = telnet.read().expect("Read error");

        let event = telnet
            .read_timeout(Duration::new(1, 0))
            .expect("Read Error");

        println!("Got telnet event: {:?}", event);
        match event {
            Event::Data(data) => {
                println!(
                    "1 ********************: {:?}",
                    String::from_utf8_lossy(&data)
                );
            }
            Event::UnknownIAC(_iac) => {
                println!("2: ");
            }
            Event::TimedOut => {
                println!("5: ");
            }
            Event::NoData => {
                println!("6: ");
            }
            Event::Error(_telnet_error) => {
                println!("7: ");
            }

            // REPLY TO THE NEGOTIATION EVENTS WE DO GET!
            Event::Negotiation(Action::Do, TelnetOption::TTYPE) => {
                println!("EVENT: DO TTYPE");
                telnet
                    .negotiate(&Action::Wont, TelnetOption::TTYPE)
                    .expect("TTYPE option failed");
            }
            Event::Negotiation(Action::Do, TelnetOption::TSPEED) => {
                println!("EVENT: DO TSPEED");
                telnet
                    .negotiate(&Action::Wont, TelnetOption::TSPEED)
                    .expect("TSPEED option failed");
            }
            Event::Negotiation(Action::Do, TelnetOption::XDISPLOC) => {
                println!("EVENT: DO XDISPLOC");
                telnet
                    .negotiate(&Action::Wont, TelnetOption::XDISPLOC)
                    .expect("XDISPLOC option failed");
            }
            Event::Negotiation(Action::Do, TelnetOption::NewEnvironment) => {
                println!("EVENT: DO NewEnvironment");
                telnet
                    .negotiate(&Action::Wont, TelnetOption::NewEnvironment)
                    .expect("NewEnvironment option failed");
            }
            Event::Negotiation(Action::Will, TelnetOption::SuppressGoAhead) => {
                println!("EVENT: WILL SuppressGoAhead");
                telnet
                    .negotiate(&Action::Dont, TelnetOption::SuppressGoAhead)
                    .expect("SuppressGoAhead option failed");
            }
            Event::Negotiation(Action::Do, TelnetOption::Echo) => {
                println!("EVENT: DO Echo");
                telnet
                    .negotiate(&Action::Wont, TelnetOption::Echo)
                    .expect("Echo option failed");
            }
            // It seems to be essential to reply to Negotiate About Windows Size when connecting to telnetd on Ubuntu.
            // "Will" and "wont" both work.
            Event::Negotiation(Action::Do, TelnetOption::NAWS) => {
                println!("EVENT: DO NAWS");
                telnet
                    .negotiate(&Action::Wont, TelnetOption::NAWS)
                    .expect("NAWS option failed");
            }
            Event::Negotiation(Action::Will, TelnetOption::Status) => {
                println!("EVENT: WILL Status");
                telnet
                    .negotiate(&Action::Dont, TelnetOption::Status)
                    .expect("Status option failed");
            }
            Event::Negotiation(Action::Do, TelnetOption::LFLOW) => {
                println!("EVENT: DO LFLOW");
                telnet
                    .negotiate(&Action::Will, TelnetOption::LFLOW)
                    .expect("LFLOW option failed");
            }
            // Compression events
            Event::Negotiation(Action::Will, TelnetOption::Compress2) => {
                telnet
                    .negotiate(&Action::Do, TelnetOption::Compress2)
                    .expect("Compress2 option failed");
            }
            Event::Subnegotiation(TelnetOption::Compress2, _) => {
                telnet.begin_zlib();
            }
            Event::Negotiation(_action, _telnet_option) => {
                println!("8: ");
            }
            Event::Subnegotiation(_telnet_option, _buffer) => {
                println!("9: ");
            }
        }
    }
}

Well, that all starts to make sense after such a long time hacking around. Replying to the NAWS option seems to be essential in my setup.

It would be great if there were some mention of these things in the opening documentation in the crate. Save others many hours of head scratching.

SLMT commented 2 years ago

Hi, thanks for your sharing! Sorry, I missed this issue since there were too many notifications in my inbox. Our documentation is indeed too simple and unclear. I will enhance the documentation recently and take your concern into my consideration.