| CONCEPT |
| Telnet Negotiations |
| |
| DESCRIPTION |
| The telnet protocol is used to control textbased connections |
| between a client (the 'telnet' program or a mud client) and a |
| server (the game driver). Most of the options offered by the |
| protocol are optional and need to be negotiated between the |
| client and the server. Consequently, and due to their |
| specialized nature, mud clients don't have to support the full |
| telnet option feature set. |
| |
| For the server to find out if a client supports the telnet |
| protocol at all, one good approach is to issue a simple, |
| commonly used telnet command to the client. If the client reaction |
| conforms to the protocol (or sends telnet commands itself), the |
| mud can continue to negotiate further options. If the client |
| does not react, the mud can safely refrain from further |
| negotiations. |
| |
| The following list is a more or less comprehensive overview of |
| the telnet related RFCs (available for example on |
| http://www.faqs.org/rfcs): |
| |
| RFC Title rel. Code |
| |
| 495 TELNET Protocol Specification |
| 513 Comments on the new TELNET specifications |
| 559 Comments on the new TELNET Protocol and its Implem |
| 595 Some Thoughts in Defense of the TELNET Go-Ahead |
| 596 Second Thoughts on Telnet Go-Ahead |
| 652 Telnet Output Carriage-Return Disposition Option NAOCRD 10 |
| 653 Telnet Output Horizontal Tabstops Option NAOHTS 11 |
| 654 Telnet Output Horizontal Tab Disposition Option NAOHTD 12 |
| 655 Telnet Output Formfeed Disposition Option NAOFFD 13 |
| 656 Telnet Output Vertical Tabstops Option NAOVTS 14 |
| 657 Telnet Output Vertical Tab Disposition Option NAOVTD 15 |
| 658 Telnet Output Linefeed Disposition NAOLFD 16 |
| 698 Telnet Extended Ascii Option X-ASCII 17 |
| 727 Telnet Logout Option LOGOUT 18 |
| 728 A Minor Pitfall in the Telnet Protocol |
| 735 Revised TELNET Byte Macro Option BM 19 |
| 749 Telnet SUPDUP-OUTPUT Option SUPDUP 22 |
| 764 Telnet Protocol Specification |
| 779 Telnet SEND-LOCATION Option SENDLOC 23 |
| 818 The Remote User Telnet Service |
| 854 Telnet Protocol Specification |
| 855 Telnet Option Specifications |
| 856 Telnet Binary Transmission BINARY 0 |
| 857 Telnet Echo Option ECHO 1 |
| 858 Telnet Suppress Go Ahead Option SGA 3 |
| 859 Telnet Status Option STATUS 5 |
| 860 Telnet Timing Mark Option TM 6 |
| 861 Telnet Extended Options - List Option EXOPL 255 |
| 884 Telnet Terminal Type Option TTYPE 24 |
| 885 Telnet End of Record Option EOR 25 |
| 930 Telnet Terminal Type Option TTYPE 24 |
| 933 Output Marking Telnet Option OUTMRK 27 |
| 946 Telnet Terminal Location Number Option TTYLOC 28 |
| 1043 Telnet Data Entry Terminal Option DODIIS Implement DET 20 |
| 1053 Telnet X.3 PAD Option X.3-PAD 30 |
| 1073 Telnet Window Size Option NAWS 31 |
| 1079 Telnet Terminal Speed Option TSPEED 32 |
| 1080 Telnet Remote Flow Control Option FLOWCTRL 33 |
| 1091 Telnet Terminal-Type Option TTYPE 24 |
| 1096 Telnet X Display Location Option XDISPLOC 35 |
| 1116 Telnet Linemode Option LINEMODE 34 |
| 1143 The Q Method of Implementing TELNET Option Negotia |
| 1184 Telnet Linemode Option LINEMODE 34 |
| 1372 Telnet Remote Flow Control Option FLOWCTRL 33 |
| 1408 Telnet Environment Option ENVIRON 36 |
| 1571 Telnet Environment Option Interoperability Issues |
| 1572 Telnet Environment Option NEWENV 39 |
| 2066 Telnet Charset Option CHARSET 42 |
| 2217 Telnet Com Port Control Option COMPORT 44 |
| 2877 5250 Telnet Enhancements |
| |
| All negotiations start with the special character IAC which is |
| defined in /usr/include/arpa/telnet.h (or in |
| src/driver/telnet.h for 3.2(.1)) and has the decimal value of |
| 255. Negotiations are based on different telnetoptions (their |
| values are defined in telnet.h too). Before a negotiation can |
| start the client and the server have to agree that they |
| support the option. This works in the following way: |
| |
| If a client wants to send something to the server it has to |
| send 'IAC WILL option' (For terminaltype negotation this would |
| be the 3 bytes 255,251,24; again, check telnet.h) to confirm |
| that it is able to do that. If the server is supporting that |
| option and wants to receive something it sends 'IAC DO option' |
| (255,253,option) |
| |
| If one side is receiving an 'IAC WILL option' and has not yet |
| sent with DO or DONT it has to respond with either 'IAC DO |
| option' if it will support this negotiation or 'IAC DONT |
| option' if it won't. |
| |
| If one side is receiving an 'IAC DO option' and has not yet |
| sent a WILL or WONT it has to reply with either 'IAC WILL |
| option' if it supports the option or 'IAC WONT option' if not. |
| |
| A small example: Lets assume we want to negotiate |
| terminaltype. (TELOPT_TTYPE with value 24). client is the |
| telnet executable on the playerside, the server is the |
| gamedriver. |
| |
| client server |
| IAC WILL TTYPE |
| IAC DO TTYPE |
| |
| Or: |
| IAC DO TTYPE |
| IAC WILL TTYPE |
| |
| After this we are ready to transfer the terminaltype from the |
| client to the server as explained below. |
| |
| Now we are ready to start the real negotiations. I explain the |
| 3 options I have currently implemented. |
| |
| First TerminalType aka TTYPE aka 24 aka TELOPT_TTYPE assuming |
| the client and the server have exchanged WILL/DO. |
| |
| The server is now free to send 'IAC SB TELOPT_TTYPE |
| TELQUAL_SEND IAC SE' which will be replied with 'IAC SB |
| TELOPT_TTYPE TELQUAL_IS terminaltype IAC SE' where |
| terminaltype is a non-zero terminated string (it's terminated |
| by the IAC) (For values look up telnet.h) AND switch the |
| client's terminalemulation to 'terminaltype'. terminaltype is |
| case-insensitive. terminal-type may be UNKNOWN. The server may |
| repeat the SEND request and the client will respond with the |
| next preferred terminaltype. If this is the same as the |
| previous received, it marks the end of the list of |
| terminaltypes. The next SEND request will start the |
| terminaltypes from the beginning. |
| |
| Example: (we have exchanged WILL/DO already) |
| client server |
| IAC SB TTYPE SEND IAC SE |
| IAC SB TTYPE IS VT200 IAC SE |
| IAC SB TTYPE SEND IAC SE |
| IAC SB TTYPE IS VT100 IAC SE |
| IAC SB TTYPE SEND IAC SE |
| IAC SB TTYPE IS VT52 IAC SE |
| IAC SB TTYPE SEND IAC SE |
| IAC SB TTYPE IS VT52 IAC SE |
| /* this marks that we have all terminaltypes. We decide to use the |
| * vt200 mode so we have to skip to VT200 |
| */ |
| IAC SB TTYPE SEND IAC SE |
| IAC SB TTYPE IS VT200 IAC SE |
| |
| |
| Next important option is NAWS (31) or WindowSizeNegotiation. |
| |
| This one is a bit easier than terminaltype. After having |
| received a IAC DO NAWS from the server, the client will reply |
| with IAC WILL NAWS and immediately after that send IAC SB NAWS |
| columns_high columns_low lines_high lines_low IAC SE where |
| xx_low refers to the lowbyte of xx and xx_high refers to the |
| highbyte of xx. This will be automagically resent at every |
| windowresize (when the client gets a SIGWINCH for example) or |
| at your request with 'IAC SB NAWS SEND IAC SE'. |
| |
| Example: (WILL/DO exchanged) |
| client server |
| IAC SB NAWS 0 80 0 24 IAC SE /* the standard vt100 windowsize */ |
| /* no reply */ |
| |
| And, a bit less important but most complex, the LINEMODE (34) |
| option. It was implemented it due to the fact, that |
| some weird DOS telnets would not work otherwise. Implemented |
| are only the absolute basic feature, which is the actual |
| switching the telnet to linemode. After exchanging WILL/DO the |
| server sends a modechange request to the client using IAC SB |
| LINEMODE LM_MODE MODE_EDIT IAC SE, which should turn on local |
| commandline-editing for the client. If a client supports |
| LINEMODE it HAS to support this modechange. The client will |
| reply with IAC SB LINEMODE LM_MODE MODE_EDIT|MODE_ACK IAC SE |
| (x|y is bitwise or). That's it for linemode. (You will perhaps |
| receive other IAC SB LINEMODEs with other LM_xxx ... you may |
| ignore them. (At least IRIX 5.x sends IAC SB LINEMODE LM_SLC |
| .... IAC SE which declares the local characterset.)). |
| |
| Example: (WILL/DO negotiated) |
| |
| client server |
| IAC SB LINEMODE LM_MODE |
| MODE_EDIT IAC SE |
| IAC SB LINEMODE LM_MODE |
| MODE_EDIT|MODE_ACK IAC SE |
| |
| Note: The option is more interesting than it looks here. For |
| example it supports a mixed mode between linemode and |
| charactermode, flushing the input at certain characters (at |
| ESC or TAB for shell-like commandline completition). We suggest |
| reading RFC 1184. |
| |
| You might be interested in TELOPT_XDISPLAYLOC and TELOPT_ENVIRON too. |
| |
| Now, how to implement this using LDMud? |
| |
| 0. Patch src/driver/comm1.c, function init_telopts() to include |
| telopts_do[TELOPT_XXX] = reply_h_telnet_neg; |
| telopts_dont[TELOPT_XXX] = reply_h_telnet_neg; |
| telopts_will[TELOPT_XXX] = reply_h_telnet_neg; |
| telopts_wont[TELOPT_XXX] = reply_h_telnet_neg; |
| for every telnet negotiation you want to use. |
| Do not overwrite the TELOPT_ECHO and TELOPT_SGA hooks. |
| |
| Alternatively, set the driver hook H_NOECHO in master.c: |
| this diverts _all_ telnet data into the mudlib. |
| |
| 1. Add a new driver hook to master.c just below the others. |
| set_driver_hook(H_TELNET_NEG,"telnet_neg"), |
| 2. Make a telnet.h for your mudlib... just change the arrays in |
| src/driver/telnet.h. |
| 3. define a function |
| |
| void telnet_neg(int cmd, int option, int * optargs) |
| |
| in your interactive objects (login.c , shells, player.c or |
| whereever). And note, in ALL objects, through which a |
| player is handed through (in TAPPMud these are login.c and |
| player.c). [Ok, master.c is interactive for a very short |
| time too, but it won't accept input, will it?] |
| 'cmd' will be TELCMD_xxxx (see telnet.h), 'option' one of |
| TELOPT_xxxx and 'optargs' will be an array of ints (bytes in |
| fact) when 'cmd' is SB. |
| Parse 'cmd'/'option' and reply with appropiate answers |
| using binary_message() (appropiate meaning sending the |
| right DO/DONT/WILL/WONT if not sent before and using the SB |
| return values). |
| 3.1. Send IAC DO TTYPE IAC DO NAWS IAC DO LINEMODE at the |
| first time you can do it (before cat()ing /WELCOME perhaps). |
| 3.2. Note all sent and received WILL/WONT/DO/DONT options for |
| conforming to the standard, avoiding endless loops and for |
| easy debugging :) |
| 3.3. Pass those recevied/sent data and other data when the |
| interactive object is changed (from login.c to player.c or |
| at other bodychanges). Clear the data when the player goes |
| linkdead or quits. You won't need to save this data. |
| 3.4. Lower_case() terminaltypes... ;) |
| 3.5. Use reasonable defaultvalues if the client does not |
| support one of the options. (columns 80, lines 24 if not |
| NAWS, unknown or vt100 for no terminaltype) |
| |
| The WILL/WONT/DO/DONT data is best saved in a mapping looking |
| like this: |
| ([ "received": ([ option1: DO_DONT_OR_0;WILL_WONT_OR_0, ... ]) |
| , "sent" : ([ option1: DO_DONT_OR_0;WILL_WONT_OR_0, ... ]) |
| ]) |
| |
| (Ok, it can be done better. But not without confusing *me* |
| more.) |
| |
| Before sending anything check |
| TN["sent"][option,0_if_do_dont_or_1_if_will_wont] |
| so you don't enter endless loops, save network traffic and the |
| like. |
| |
| The windowsize is best saved in the players environment |
| variables so that he can modify them later on. (Or in two |
| integers in the player object...). Use for these values is |
| clear I think. |
| |
| The terminaltypes received using above mentioned method are |
| best stored in an array. The actual set terminaltype is best |
| stored in an environment variable where the player can modify |
| it. Upon modifying it the IAC SB TTYPE SEND IAC SE cycle |
| should be started to match the emulation to the entered new |
| terminaltype. You then may use data retrieved from |
| /etc/termcap (man 5 termcap) or /usr/lib/terminfo/*/* (SysVID, |
| man 5 terminfo) to implement terminalcontrol codes dependend |
| on the terminaltype. /etc/termcap may prove to be the easiest |
| way tough /usr/lib/terminfo/*/* is the newer (and better) SysV |
| way of doing it. |
| |
| [Anyone got a description of the internal terminfo format for |
| me? -Marcus] |
| |
| LINEMODE replies may be left alone if only using the mode |
| change to MODE_EDIT |
| |
| Some statistics about what clients support telnet negotiations: |
| |
| Tinyfugue and some other mudclients usually do not support |
| negotiations. |
| |
| Except for TF, which supports the Telnet End-Of-Record option |
| as marker for the end of the prompt. So if you send IAC EOR |
| after every prompt, it will print the prompt always in the |
| input window. (Do not forget to negotiate that. First IAC WILL |
| TELOPT_EOR/wait for IAC DO TELOPT_EOR). Newer versions of |
| TF will support NAWS and there will be a patch for TTYPE |
| negotiation available soon. |
| |
| All telnets able to do negotiations I've encountered support |
| the TTYPE option. |
| HP9.x,Irix5.x,Linux,EP/IX,CUTELNET/NCSATELNET (Novell) and |
| perhaps more support NAWS. |
| At least Irix5.x,Linux,CU/NCSATELNET support LINEMODE. |
| SUN does not support NAWS and LINEMODE neither in SunOS 4.1.3 |
| nor in Solaris 2.3. |
| |
| For getting RFCs you can for example use |
| ftp://ftp.uni-erlangen.de/pub/doc/rfc/ |
| |
| BUGS |
| Not all aspects of the options are mentioned to keep this doc |
| at a reasonable size. Refer to the RFCs to get more confused. |
| |
| CREDITS |
| Provided by Marcus@TAPPMud (Marcus Meissner, |
| <msmeissn@cip.informatik.uni-erlangen.de>). |