CONCEPT Telnet Negotiations DESCRIPTION Telnetnegotiations are being used for transmitting status and general informations between telnet-client und server. They are specified in their respective RFCs (Request For Comments) which define the standard-way-to-do-it. Check following for the options mentioned herein: RFC 0857 for ECHO /* no, not mentioned here, but used */ RFC 1073 for NAWS /* in Amylaar's windowsize */ RFC 1091 for TERMINAL TYPE RFC 1096 for XDISPLAY LOCATION RFC 1408 for ENVIRON RFC 1184 for LINEMODE 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 negotiating 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). Thats 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 much more funnier as it looks here, it for example 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 Amylaar's 3.2.1 (@67 and up)? 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. Sent 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 telnetnegotiations: Tinyfugue and some other mudclients to not support negotiations at all. They won't even reply with IAC DONT option or IAC WONT option. 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.uni-erlangen.de /pub/doc/rfc/rfc*.txt.Z (warning: these are approx 2000 files) 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, <email@example.com>).