Lpc Manpages

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,
	<msmeissn@cip.informatik.uni-erlangen.de>).