Proposal: MUD client identification
If a MUD server developer disregards client problems, then they have little use for what I describe below. But I believe it should be possible to fingerprint the client that is being used to connect to a MUD, and to work with all possible clients in a manner where the user gets the best possible experience.
The problem with negotiation
If a MUD server supports negotiation, it typically tries sends negotiation to any client that connects. If that client is Windows Telnet, then on response to negotiation it will stop echoing the users input and will expect the server to echo it back. This is not standard behaviour, and if the developer of the server wishes to support this client, then it rules out doing negotiation unless users of this client are expected to fend for themselves.
It is possible to identify whether Windows Telnet is being used, by requesting the value of the SYSTEMTYPE
variable through the NEW-ENVIRON negotiation option. But engaging in negotiation to know whether to avoid using it, defeats the point. Even if Windows Telnet is disregarded, negotiating may still result in junk characters being displayed to users of clients that do not support it or do not implement it correctly.
The problem with escape sequences
Outside of negotiation, there is another way to get feedback from the client, which can allow it to be identified. This is through the use of escape sequences which a client might support, if it has decent terminal support. Almost any client worth using, whether a telnet client or a MUD client, supports at least the basic set of sequences that allow display of text in different colours. So it should be safe to send them arbitrary sequences that will only result in a response in the clients with more advanced support. But unfortunately it isn't safe, escape sequence support is often haphazard with unsupported sequences resulting in junk characters being displayed to the user. But let's ignore that for now, and assume that we will work around it if possible.
Some clients answer to the ENQ character (5) with an "answerback" string. For Putty, this is "PuTTY". It is not widely supported, but for those clients that do support it, it is an alternative form of feedback to add to negotiation and escape sequences. However, trying it out against a connecting Putty client without any form of server-side negotiation reveals another problem. Putty works in line mode by default, any responses to special server-side characters like this one, or sequences, are included in that line. But they do not magically get sent with the line unbeknownst to the user, instead they are injected as visible text within whatever the user is typing. This is confusing and unacceptable, and it means that we are in a situation where we cannot do anything to identify Putty.
At this stage we know that if we negotiate we clobber Windows Telnet users, and if we send special output that triggers a response we clobber Putty users. Now, if we were able to identify Putty in another way which does not involve doing anything, then we could concentrate on identifying Windows Telnet without doing negotiation through escape sequences, and from there we have a lot more freedom.
Identifying Putty
While we cannot send anything at this point without clobbering the users of some clients, we still have another way to identify Putty. One thing that differentiates Putty from most other clients I have seen, is that it does active negotiation. On connection it will proactively send negotiation to the server, where every other client waits for the server to send negotiation to them.WILL NAWS
WILL TERMINAL-SPEED
WILL TERMINAL-TYPE
WILL NEW-ENVIRON
DO ECHO
WILL SGA
DO SGA
If we receive negotiation of these commands without doing anything, we know that the client is Putty. This means that while sending negotiation is still off limit due to Windows Telnet, we are now safe to try sending escape sequences in order to identify it.
Identifying Windows Telnet
Windows Telnet has excellent terminal support. It responds to at least two standard sequences.\x1b[0c
\x1b[0x
Now the most interesting sequence is \x1b[0x
. The response to this is supposed to increment the number by two, so it would start with \x1b[2
. But Windows Telnet actually sends a response starting with \x1b[3
, which gives us a way to specifically identify that it is the client in use. However, we still have to deal with clients that do not properly process this sequence. GMud for instance displays this sequence and does not seem to attempt to process it at all. This is unacceptable, and rules out the use of this sequence as the next line in identification.
The next thing to try is the remaining sequence \x1b[0c
. Strangely GMud does not display this and does not respond. Windows Telnet sends a standard response of \x1b[?1;0c
. So in theory we can send this sequence, and on receiving a response of \x1b[?1;0c
, we can proceed to send \x1b[0c
and through its response identify the client in use as Windows Telnet.
The client identification protocol
We now have a protocol for identifying what clients are in use without requiring work on the part of or degrading the experience of the user. It identifies the most problematic clients and knowing that they are not in use, widens the range of techniques that can be used to identify the remaining clients.
- If negotiation is received from the client, then the client is most likely Putty. The commands received can be compared to the known ones that Putty sends.
- The escape sequence
\x1b[?1;0c
should be sent to the client, if is a response then we know the client should be able to handle other escape sequences. - The escape sequence
\x1b[0x
should be sent to the client, if there is a response and it is\x1b[3;1;1;128;128;1;0x
then we know the client is Windows Telnet. - At this point negotiation can be used to identify what the connected client is. Windows telnet can be used without requiring negotiation.
Regarding identification of exactly what MUD client is in use past this point is somewhat easier, given the common approach to using the MUD client name as the terminal type that can be obtained through negotiation.
Dealing with broken telnet stacks is a pain. Unfortunately many clients work around broken server implementations by not negotiating at all until the server has demonstrated it can.
ReplyDeleteYou are right about TERMINAL-TYPE negotiation: I just looked at the source of 2 clients and how they handle TTYPE (tinyfugue and leanlyn). They both follow the RFC and report the client's name first followed by some defaults like "ANSI".
If you want to test how a client against a full featured server implementation check out Cryosphere (cryosphere.net 6665). They have a very good telnet self-test command that exercises all the common negotiated options and some of the uncommon ones too.
Bugger, posted this with Python tags, which it shouldn't have had.
ReplyDeleteIn any case, it looks like Cryosphere is really polished. I can tell it uses negotiation because Windows Telnet no longer echos my input, which is what this post was intended to show people how to avoid.
Interesting insight into handling client detection for MUDs.
ReplyDeleteAnother useful tip is to NOT capitalize or lower-case the Telnet Terminal Type. If you use it as-it-is, you will see that Windows telnet sends "ANSI" in upper case, as other clients send "ansi" in lower case.
Please also note that at least two major MUD protocols let the client identify itself, even by version: MXP and ATCP.
xtian
from over avalon.mud.de
I know this is an old post, but I bookmarked it ages ago and finally got around to using it - your solution works great, thanks. The only minor drawback is the slight delay if you want to negotiate prior to displaying the login screen (eg if you want it to include extended colour, inline graphics, clickable links, etc), as you have to wait a moment to see if the client responds to each of the escape codes.
ReplyDeleteWith this method I noticed that at least linux xterm+telnet does print the respose (^[[?1;2c and the second response also) directly to screen, which doesnt really look good. The response will be added to the next line the user sends, so I'm thinking linux telnet defaults to linemode at startup and doesnt send the response directly back to the host when its in linemode.
ReplyDeleteWorkarounds:
1.) if the response to the first sequence isn't the one wintelnet would send (\e[?1;0c), abort immediately and start sending normal telnet negotiations.
2.) cut ^[[?1;2c from the first line, the user sends.
xtian of avalon.mud.de
Thanks for the heads up, I assumed that it and Putty were equivalent when testing this. Kavir is the only person I know to put this into practice, so one of us should bring it up to him and see what he says - although I think he has only adopted a limited approach.
ReplyDeleteThe approach I use is as follows, and is basically taken from your blog:
ReplyDeletehttp://clanscw.brinkster.net/godwars/topic.asp?TOPIC_ID=2675
One of the staff of The 7th Plane mentioned the Linux telnet issue to me, although I'm not sure how serious the problem really is. Some of my players can ONLY use Windows telnet when connecting from work/school, but I imagine anyone connecting from Linux could fairly easily install TinTin++ or another Linux client.
However one of my players also had a problem with TinTin++ - I'm a bit fuzzy with the specifics, but apparently it depended on whether or not he was using wordwrap. It would prepend the escape code to his first line of input, which messed up the autologin. I asked him to report it as a TinTin++ bug, but I'm not sure if he ever did. This doesn't seem to happen with the default setup though, only when configuring wordwrap.
While auditing muds on TMC I came across one which didn't display the Diku credits. The owner of the mud contested the claim, so I investigated more thoroughly and discovered that he was displaying the credits in the "black" ANSI colour (which most clients seem to treat as the background colour, meaning it's effectively invisible regardless of your colour scheme). I wonder if this technique could be used to hide the escape codes when detecting client type?
This wouldn't help for clients that add the escape sequence to the input buffer, but perhaps xtian's workaround could deal with that.