Reputation: 965
I am working on an application in C for an embedded Linux system on a serial device server. I need to access the system's own shell that you can telnet to and has a bunch of custom command line interface commands that can be executed at the shell prompt.
An engineer working for the manufacturer of the device said the only way to programatically issue these command line interface commands is via a telnet client that logs into the shell.
So, within my application, I want to have the device server telnet to itself so that it can issue some commands to itself via its shell. Before I do the C implementation, I figured I'd just try to open a socket via IRB and try to get the commands working that way first. The actual telnet session looks like this:
login: admin
password: password
prompt# set watchdog off
prompt# save
Save config to flash ROM y/n
y
Below is the simple script I'm running in IRB to see if I can emulate the above telnet session via a TCP socket since that's how I'll implement in C on Linux:
require 'socket'
@ip = 'xxx.xxx.xxx.xxx'
@enter = "\n"
@sock = TCPSocket.open(@ip,23)
@ret = @sock.write("admin" + @enter)
puts @ret.to_s
sleep(1)
@ret = @sock.write("password" + @enter)
puts @ret.to_s
puts @sock.read(1)
sleep(2)
@ret = @sock.write("set watchdog off" + @enter)
puts @ret.to_s
puts @sock.read(1)
sleep(1)
@ret = @sock.write("save" + @enter)
puts @ret.to_s
puts @sock.read(1)
sleep(1)
@ret = @sock.write("y")
puts @ret.to_s
puts @sock.read(1)
sleep(1)
@sock.close
Opening, writing to and reading from sockets in C are no problem for me and I do it a lot already in my application so I already have easy functions to do those things ready to go. My question is what strings should I be writing to the sockets to emulate let's say pressing the enter key after I type something at a prompt? "\n" or "\x0a" were not working. OR could I be using a linux C library that'll make this way easier for me?
Upvotes: 0
Views: 10978
Reputation: 965
You could definitely go read the telnet protocol, but check out the answer at the bottom of the link below that involves using WireShark to actually see what's flying back and forth.
http://www.codeproject.com/Questions/147555/Telnet-client-using-TCP
If you've never used a packet sniffer, it's very enlightening (as it was for me). Here are the steps I followed to telnet over a TCP socket connection.
(1) Follow the directions at the above link to capture the traffic on your network while you execute exactly the commands you want to eventually execute with your desired application/script beginning with creating the telnet connection and ending with closing it
(2) After you stop capturing your session, you might see in your capture a lot of other traffic that was happening on your network at the same time, so filter that out with ip.addr == xxx.xxx.xxx.xxx where you of course use the ip address of the remote computer
(3) Now as you look at all of the stuff going back and forth between your computer and the remote computer, you can see how the application separates out the different layers of communication for each message. You want to pay special attention to the messages with protocol "Telnet", which just means that's the highest layer. When you highlight a message on the topmost pane of the WireShark window, you see in the second pane down how it separates that message into its different layers. In that second pane, if you highlight let's say the Telnet layer, you can see how in the third pane down, the bytes of the message that implement the telnet protocol become highlighted. Take some time to analyze the messages that are going back forth. You can see in a much more immediate way, the negotiation process involved in the beginning of a telnet communication.
(4) Now you just want to basically emulate the communication you can see in Wireshark in your application. Remember since you are opening a TCP socket in your application, your application is taking care of all the TCP and down communication stuff behind the scenes, so you only have to worry about reading/writing the telnet bytes/data/commands to your TCP socket.
This method doesn't take very long at all and doesn't require any extra stuff on your system which is great if you are dealing with a device with limited system resources/a very simple os/no os at all. For example the mini Linux system on my device doesn't have Expect already installed, nor does it have an easy way of installing stuff in general. I mean it can be done, but we're talking really annoying stuff here. Also even if there was a not too crazy way of installing stuff on the box, I already have 50 of these in the field which i already have set to autoupdate to whatever new binary I put on my server sure, but they are not expecting to have to also download a script from my server. And again, they don't have an OS installed that would allow them to run that script. So we are talking relatively low level stuff here where keeping it simple is not an option but a requirement. My final C function that let my serial device server telnet to itself (the only way to access its own command line interface--that's the manufacturer's choice) and issue itself the necessary commands looks like this:
turnoff_watchdog
{
// telnetsock, TCP socket to 127.0.0.1 at port 23, has already been set previously
char negotiation_1[] = {0xFF,0xFC,0X18,0xFF,0xFC,0x20,0xFF,0xFC,0x23,0xFF,0xFC,0x27,0xFF,0xFD,0x03};
char negotiation_2[] = {0xFF,0xFC,0x01,0xFF,0xFC,0x1F,0xFF,0xFE,0x05,0xFF,0xFB,0x21};
char telnet_login_name[] = "admin\r\n";
char telnet_login_password[] = "password\r\n";
char set_command[] = "set watchdog off\r\n";
char save_command[] = "save\r\n";
char confirm[] = "y\n";
char escape[] = {0x5E,0x5D,0x0A}; // ^]
char quit[] = "quit\n";
sleep(2);
write(telnetsock,&negotiation_1[0],15);
sleep(2);
write(telnetsock,&negotiation_2[0],12);
sleep(2);
write(telnetsock,&telnet_login_name[0],7);
sleep(2);
write(telnetsock,&telnet_login_password[0],10);
sleep(2);
write(telnetsock,&set_command[0],18);
sleep(2);
write(telnetsock,&save_command[0],6);
sleep(2);
write(telnetsock,&confirm[0],2);
sleep(2);
write(telnetsock,&escape[0],3);
sleep(2);
write(telnetsock,&quit[0],5);
sleep(2);
return(0);
}
Simple as that. The most difficult part of this was that the original (what I captured using Wireshark) telnet negotiations were quite a bit longer, but after looking at the options that were being negotiated, I realized I didn't need most of it, so I chopped that down considerably. By the way, if you are intimidated by looking at bytes, don't be. It's not a big deal. You see how in negotiation_1
and negotiation_2
above, 0xFF keeps popping up every 3 bytes? Well all those two negotiations are are strings of commands/negotiations each 3 bytes in length starting with 0xFF, trying to communicate what it will and won't do during the telnet session. And the second byte in each chunk of 3 bytes (either 0xFB, 0xFC, 0xFD or 0xFE) is just communicating whether it WILL or WON'T do something or whether it wants the other guy to DO or DON'T something. The last byte is just the option each is willing or not willing to do or going to do or not do. You'll see it in Wireshark how it all gets broken down. It's actually pretty funny the language of it. I didn't include the lines I originally had where I was reading the socket to see the responses to the negotiations (which I had done all that testing and stuff in ruby since I could just run it in irb super easy), but you must do that and basically just negotiate as little as you can until you get sent the login prompt and then as you can see it's super straightforward. Make sure in testing to determine the first negotiation you will write to the socket, that when you open the socket, you read in whatever telnet control sequences are sent FIRST. Then you only want to respond to those sequences--mostly in the negative to reduce the amount of negotiation. Have fun!
Upvotes: 3
Reputation: 23906
You ask a C question yet you post script, if you want to know how telnet works, read the RFC. In this case 854
https://www.rfc-editor.org/rfc/rfc854
Upvotes: 0