Skip to main content
Topic: Fixing the Microchip telnet server (Read 9052 times) previous topic - next topic

Fixing the Microchip telnet server

To implement the telnet server for the web platform you need to define it ([tt:]#define STACK_USE_TELNET_SERVER[/tt:]) in [tt:]TCPIPConfig.h[/tt:] so that it's included in your firmware.

However, doing that results in an error about [tt:]AN0String[/tt:] being undeclared in [tt:]Telnet.c[/tt:]. How to deal with it? You need to realise that [tt:]AN0String[/tt:] is an internal variable used by the Microchip TCPIP stack to hold the potentiometer D/A value on the Microchip demo board. As the web platform does not include a potentiometer, the best course of action is simply to dispense with it by commenting it out, along with the other lines dealing with the non-existent pot readings.

Now we're cooking. It compiles and, once uploaded, telnetting to the web platform results in the telnet server banner and the ubiquitous login prompt. Typing in the default login name of admin results in the password prompt. Typing in the default password of microchip rather anti-climatically results in an [tt:]Access denied, Connection closed by foreign host.[/tt:] response. Huh? Ok, I must have botched the password because it isn't echoed to the screen and my typing sometimes sucks. Try again... same response. Hmmm.

The help file for the TCPIP stack is no help. But, lo, the [tt:]TCPIP Stack Version.txt[/tt:] file holds the clue:

Quote
Known Problems:
1.  Telnet server module does not implement a lot of Telnet functions.  As a
    result, it will likely not display correctly or work at all with some Telnet
    clients.  The server was tested with the Microsoft telnet.exe utility which
    is provided with Microsoft Windows.

It is only guaranteed to work with the MS telnet client, and... it does indeed. Not all that useful when my main machine runs FreeBSD and I was trying to test using the OS X telnet client from an xterm because I was running Windows 7 under OS X via Parallels on a Mac Mini. It must be easy enough to fix, but then again you'd have thought Microchip would have fixed it.

No matter, let's do some debugging and find out whether it falls at the first hurdle, the username. In the switch statement in [tt:]Telnet.c[/tt:] there's a very sensible precaution taken when an incorrect username is entered: the code moves on to ask for the password whether the username is correct or not so as not to leak any information which could assist an unauthorised individual in breaking in. So it's a simple matter of commenting the [tt:]SM_GET_PASSWORD_BAD_LOGIN:[/tt:] case statement and adding my own just above:

Code: [Select]
	case SM_GET_PASSWORD_BAD_LOGIN:
TCPPutROMString(MyTelnetSocket, "nrBad user: access deniednr");
TCPDisconnect(MyTelnetSocket);
break;

Sure enough, this shows that attempting to login using the OS X telnet client is failing at the first hurdle: the username. So, let's look at the username code in [tt:]Telnet.c[/tt:] located in the [tt:]case SM_GET_LOGIN[/tt:] code block. The code

1) checks that there's enough room in the socket tx buffer to output the password prompt (as we've already output the login prompt)
2) checks the rx buffer for a return character, if found saves the location to [tt:]w[/tt:] (why not named pos?)
3) checks if there's no room in the rx buffer, if none complains and then disconnects
4) checks the rx buffer for the username with a case insensitive [tt:]TCPFindROMArray[/tt:] (security alert!) and if found saves the starting location of the match to [tt:]w2[/tt:] (what is it with w?)
5) checks that [tt:]w2[/tt:] is equal to zero - so it expects that the very first character in the username will have been received from the client immediately after the login prompt is output... curious methinks and indeed it turns out to be significant as we shall see
6) checks that the [tt:]sizeof(TELNET_USERNAME)-1[/tt:] or [tt:]sizeof(TELNET_USERNAME)[/tt:] is equal to [tt:]w[/tt:] (ie that the username string is the right length).

So let's see what these values are when we telnet to the web platform with MS telnet and then with OS X telnet. Some new code is need to output this info:

Code: [Select]
char strTemp[40];
sprintf(strTemp, "nrw=%hi; w2=%hi; sizeof=%inr", w, w2, sizeof(TELNET_USERNAME));
TCPPutROMString(MyTelnetSocket, strTemp);

just after [tt:]w2[/tt:] has been stored. As expected, with MS telnet, the username is found in the rx buffer starting at position zero and the terminating return character is found at position 6. Now, with the OS X telnet client, the values turn out to be 30 (not zero) and 36 (not 6). What's going on? Did we type an extra 30 characters in our sleep? Let's do some digging with a BSD favourite [tt:]tcpdump[/tt:] while logging in to the telnet server with the OS X telnet:

Quote

dynamic1.sentry.org.61010 > 192.168.1.1.telnet: Flags [P.], cksum 0x83bc (incorrect -> 0x57cb), seq 1:31, ack 1, win 65535, length 30 [telnet WILL AUTHENTICATION, DO SUPPRESS GO AHEAD, WILL TERMINAL TYPE, WILL NAWS, WILL TSPEED, WILL LFLOW, WILL LINEMODE, WILL NEW-ENVIRON, DO STATUS, WILL XDISPLOC]

Ahhhhh. So when the OS X telnet client connects to the web platform telnet server it sends some do/will telnet options to our server (which simply ignores them as there's no code to parse them and respond/negotiate). And look at that, the length of the packet: 30! Each telnet option is 3 bytes: the IAC (Interpret As Command - xff), the command (do/don't - xfd/xfe; will/won't - xfb/xfc), the option (eg echo - x1). So 30 bytes is 10 options, and yes, there's ten in the packet (see above).

My first solution was to throw away these extra bytes by adding:

Code: [Select]
        // Check for a return in the rx buffer
w = TCPFind(MyTelnetSocket, 'n', 0, FALSE);
// If we do not have a return yet...
if(w == 0xFFFFu)
// ...throw this data (if any) away as it will be telnet protocol options if the client isn't MS telnet
TCPDiscard(MyTelnetSocket);

immediately after the [tt:]case SM_GET_LOGIN:[/tt:] statement. This fixes the username issue for all telnet clients which send protocol options (ie at least OS X and FreeBSD). However, it breaks MS telnet which was a surprise. The issue seems to be that MS telnet is in character mode, not lineinput mode. So, MS telnet sends each character to the server one-by-one. OS X and FreeBSD telnet sends the whole line when the user presses return. See the issue? MS telnet sends the username character by character so my test for a return is not triggered and those characters are thrown away. No username. I can live with this restriction for the time being, so we'll move on after noting that I could handle the IAC three-byte sequences in a more elegant fashion and that I couldn't coax MS telnet into lineinput mode which would have been too easy.

After a quick compile and firmware upgrade, we're ready to rock. Ooops. Look at that: the password is appearing in plain text as it's being typed! The telnet control sequence "DO suppress local echo" at the end of the [tt:]strPassword[/tt:] prompt is not being honoured by the non-MS telnet client. Ahh, but there's more than one way to skin this cat: let's try sending "WILL echo" to the client (ie [tt:]xffxfbx1[/tt:]) instead and then simply not echo back the characters from the client. Yes! No character echo, but what's this? Pressing return does not send the password... not sure why this behaviour has changed, but the solution was to look for "r" instead of "n", so change the line:

Code: [Select]
// See if the user pressed return
w = TCPFind(MyTelnetSocket, 'n', 0, FALSE);

in the [tt:]case SM_GET_PASSWORD:[/tt:] code block to:

Code: [Select]
// See if the user pressed return
w = TCPFind(MyTelnetSocket, 'r', 0, FALSE);

The next issue is that the code to clear the screen and print the telnet server banner and login prompt, while it works for MS telnet, does not work for OS X or FreeBSD. The simple fix for this issue is to add [tt:]x1b[1;1f[/tt:] to the [tt:]strTitle[/tt:] string after the ANSI cls comand and before the banner is printed. That extra ANSI code positions the cursor on line 1 at position 1 which is the top left of the screen. Now when [tt:]strDisplay[/tt:] is output, all the lines appear in the right place and overprint each other.

You might want to also enable the SNTP client to print the seconds by adding [tt:]#define STACK_USE_SNTP_CLIENT[/tt:] to [tt:]TCPIPConfig.h[/tt:] if you haven't already done so and, while you're there, add [tt:]#define MAX_TELNET_CONNECTIONS   (1u)[/tt:] to limit the number of simultaneous telnet connections to 1 otherwise the limit defaults to 3 and chews up a few more bytes.

Talking of chewing up bytes: you can save a few more by defining:

Code: [Select]
static ROM BYTE strTooMuchData[]	= "rnToo much data.rn";

at the top of [tt:]Telnet.c[/tt:] and then replacing those two hard coded error messages with the new string variable. While we're in the byte saving mood, we can save another 18 bytes by replacing:

Code: [Select]
// [Username|Password] verified, throw this line of data away
TCPGetArray(MyTelnetSocket, NULL, w + 1);

where twice occurring with:

Code: [Select]
TCPDiscard(MyTelnetSocket);

I should note that I renamed MySocket to MyTelnetSocket throughout [tt:]Telnet.c[/tt:] which made it more obvious which file I was editing and, anyway, I like descriptive variable names :)

I also made the username search case sensitive (change the TRUE parameter in TCPFindROMArray to False) because the default case insensitive one seems like a security issue and is not the way my OS X, FreeBSD or Solaris boxes (not to mention my routers) handle login usernames which are definitely case sensitive.

So there you have it, the Microchip telnet server working with real telnet clients!

Re: Fixing the Microchip telnet server

Reply #1
Nice sleuthing, but isn't the username supposed to be case insensitive? I thought that was the Unix standard.

A quick check under Mac(h) shows that I can log in under the same user whether all caps, all lower case, or mixed. I think this is one of the very few cases where Unix is genuinely case insensitive, and it's not a side-effect of the default Mac file system.

Password should certainly be case sensitive.

Anyway, that's obviously only one tiny detail and you uncovered a great many, so thanks for opening up the Microchip code to a wider support range than just Microsoft's broken telnet.

Re: Fixing the Microchip telnet server

Reply #2
Jups, a  username is supposed to be case insensative.

It is a pain in the ass for sysadmins to have different users with almost the same name (more room for errors and difficult to distinguish between users)

Re: Fixing the Microchip telnet server

Reply #3
[quote author="rsdio"]
Nice sleuthing, but isn't the username supposed to be case insensitive? I thought that was the Unix standard.

A quick check under Mac(h) shows that I can log in under the same user whether all caps, all lower case, or mixed. I think this is one of the very few cases where Unix is genuinely case insensitive, and it's not a side-effect of the default Mac file system.
[/quote]

Under Solaris and FreeBSD, the username is case sensitive (just verified it in case I'd been imagining it for the last 16 years). I'm confused about your statement that the username is not case sensitive under Mac OS X , because it definitely is when I try it on my three Mac Minis running Tiger, Leopard and Snow Leopard. I can login via ssh with "admin" but not "Admin".

Quote
Anyway, that's obviously only one tiny detail and you uncovered a great many, so thanks for opening up the Microchip code to a wider support range than just Microsoft's broken telnet.

I didn't realise it had actually been posted... so I've edited out the username test because it allowed any number of characters before the username string and was still valid. To be continued :)

Re: Fixing the Microchip telnet server

Reply #4
Hi together,

I impemented the TCP/IP Stack on my mikrocontroller and want to control a scope with it. I tried it with http-protokoll using the GenericTCPDemo.c in the Stack. But this is inconsistent. SOmetimes it works and sometimes not.

Now I saw, there is telnet server implemented. But I need telnet client. Can you give me any hint how to implement telnet client in tcp/IP Stack from microchip?

thank you and greetings
kikarin

Re: Fixing the Microchip telnet server

Reply #5
Check further up this post, I think TREV gives a step-by-step for getting the telnet server working. It is for an older version of the stack, but should probably still relate to the current code.
Got a question? Please ask in the forum for the fastest answers.