:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / apps / utils / net / telnet / vm.cpp
1 /* $Id$
2  *
3  * FILE       : vm.cpp
4  * AUTHOR     : unknown (sources found on www.telnet.org)
5  * PROJECT    : ReactOS Operating System
6  * DESCRIPTION: telnet client for the W32 subsystem
7  * DATE       : 2001-01-21
8  * REVISIONS
9  *      2001-02-21 ea   Modified to compile under 0.0.16 src tree
10  */
11 #include <winsock.h>
12 #include <windows.h>
13
14 #include "telnet.h"
15
16 //      NAME          CODE   MEANING
17 // NVT codes
18 #define NUL     0    // No Operation
19 #define BEL     7    // BELL 
20 #define BS      8    // Back Space 
21 #define HT      9    // Horizontal Tab 
22 #define LF     10    // Line Feed 
23 #define VT     11    // Vertical Tab 
24 #define FF     12    // Form Feed 
25 #define CR     13    // Carriage Return 
26   
27 // telnet command codes
28 #define SE    240    // End of subnegotiation parameters.
29 #define NOP   241    // No operation.
30 #define DM    242    // Data Mark
31 #define BRK   243    // Break
32 #define IP    244    // Interrupt Process
33 #define AO    245    // Abort output
34 #define AYT   246    // Are You There
35 #define EC    247    // Erase character
36 #define EL    248    // Erase Line          
37 #define GA    249    // Go ahead
38 #define SB    250    // SuBnegotiate
39 #define WILL  251    // 
40 #define WONT  252    // 
41 #define DO    253    // 
42 #define DONT  254    // 
43 #define IAC   255    // Interpret As Command
44
45 //Telnet options: 
46 //    0x00 - binary mode
47 //    0x01 - Local Echo
48 //    0x03 - Suppress GA (char at a time)
49 //    0x05 - status
50 //    0x06 - Timing Mark   
51 //
52 // do 0x25 - zub?
53 //
54 //    0xff - Extended Options List
55
56 enum _option
57 {
58   TOPT_BIN = 0,   // Binary Transmission
59   TOPT_ECHO = 1,  // Echo
60   TOPT_RECN = 2,  // Reconnection
61   TOPT_SUPP = 3,  // Suppress Go Ahead
62   TOPT_APRX = 4,  // Approx Message Size Negotiation
63   TOPT_STAT = 5,  // Status
64   TOPT_TIM = 6,   // Timing Mark
65   TOPT_REM = 7,   // Remote Controlled Trans and Echo
66   TOPT_OLW = 8,   // Output Line Width
67   TOPT_OPS = 9,   // Output Page Size
68   TOPT_OCRD = 10, // Output Carriage-Return Disposition
69   TOPT_OHT = 11,  // Output Horizontal Tabstops
70   TOPT_OHTD = 12, // Output Horizontal Tab Disposition
71   TOPT_OFD = 13,  // Output Formfeed Disposition
72   TOPT_OVT = 14,  // Output Vertical Tabstops
73   TOPT_OVTD = 15, // Output Vertical Tab Disposition
74   TOPT_OLD = 16,  // Output Linefeed Disposition
75   TOPT_EXT = 17,  // Extended ASCII
76   TOPT_LOGO = 18, // Logout
77   TOPT_BYTE = 19, // Byte Macro
78   TOPT_DATA = 20, // Data Entry Terminal
79   TOPT_SUP = 21,  // SUPDUP
80   TOPT_SUPO = 22, // SUPDUP Output
81   TOPT_SNDL = 23, // Send Location
82   TOPT_TERM = 24, // Terminal Type
83   TOPT_EOR = 25,  // End of Record
84   TOPT_TACACS = 26, // TACACS User Identification
85   TOPT_OM = 27,   // Output Marking
86   TOPT_TLN = 28,  // Terminal Location Number
87   TOPT_3270 = 29, // Telnet 3270 Regime
88   TOPT_X3 = 30,  // X.3 PAD
89   TOPT_NAWS = 31, // Negotiate About Window Size
90   TOPT_TS = 32,   // Terminal Speed
91   TOPT_RFC = 33,  // Remote Flow Control
92   TOPT_LINE = 34, // Linemode
93   TOPT_XDL = 35,  // X Display Location
94   TOPT_ENVIR = 36,// Telnet Environment Option
95   TOPT_AUTH = 37, // Telnet Authentication Option
96   TOPT_NENVIR = 39,// Telnet Environment Option
97   TOPT_EXTOP = 255, // Extended-Options-List
98   TOPT_ERROR = 256  // Magic number
99 };
100
101 // Wanted by linux box:
102 // 37 - TOPT_AUTH
103 // 24 - TOPT_TERM
104
105 enum _verb
106 {
107   verb_sb   = 250,
108   verb_will = 251,
109   verb_wont = 252,
110   verb_do   = 253, 
111   verb_dont = 254
112 };
113
114 enum _state
115 {
116   state_data,   //we expect a data byte
117   state_code,   //we expect a code
118   state_option  //we expect an option
119 };
120
121 int option_error(_verb,_option,int,SOCKET);
122
123 typedef void(*LPOPTIONPROC)(SOCKET,_verb,_option);
124 typedef void(*LPDATAPROC)(SOCKET,unsigned char data);
125
126 ///////////////////////////////////////////////////////////////////////////////
127
128 inline void yesreply(SOCKET server, _verb verb,_option option)
129 {
130   unsigned char buf[3];
131   buf[0] = IAC;
132   buf[1] = (verb==verb_do)?WILL:(verb==verb_dont)?WONT:(verb==verb_will)?DO:DONT;
133   buf[2] = (unsigned char)option;
134   send(server,(char*)buf,3,0);
135 }
136
137 inline void noreply(SOCKET server, _verb verb,_option option)
138 {
139   unsigned char buf[3];
140   buf[0] = IAC;
141   buf[1] = (verb==verb_do)?WONT:(verb==verb_dont)?WILL:(verb==verb_will)?DONT:DO;
142   buf[2] = (unsigned char)option;
143   send(server,(char*)buf,3,0);
144 }
145
146 inline void askfor(SOCKET server, _verb verb,_option option)
147 {
148   unsigned char buf[3];
149   buf[0] = IAC;
150   buf[1] = (unsigned char)verb;
151   buf[2] = (unsigned char)option;
152   send(server,(char*)buf,3,0);
153 }
154
155
156 void ddww_error(SOCKET server,_verb verb,_option option)
157 {
158 #ifdef _DEBUG
159   char tmp[256];
160   wsprintf(tmp,"Unknown Option Code: %s, %i\n",(verb==verb_do)?"DO":(verb==verb_dont)?"DON'T":(verb==verb_will)?"WILL":"WONT",(int)option);
161   OutputDebugString(tmp);
162 #endif
163
164   switch(verb)
165   {
166   case verb_will: // server wants to support something
167     noreply(server,verb,option); // I don't want that.
168     break;
169   case verb_wont: // server waants to disable support
170     return;       // don't confirm - already disabled.
171   case verb_do:   // server wants me to support something
172     noreply(server,verb,option); //I won't do that
173     break;
174   case verb_dont: // server wants me to disable something
175     return;       // don't worry, we don't do that anyway (I hope :)
176   }
177 }
178
179 ///////////////////////////////////////////////////////////////////////////////
180 // Option ECHO & SUPPRESS GA
181 //
182 // These options are curiously intertwined...
183 // The Win32 console doesn't support ECHO_INPUT (echo) if
184 // LINE_INPUT (==GA) isn't set.
185 // I can't see how to code this negotiation without using
186 // some form of Lock-Step algorythm
187 // ie: if("WILL ECHO")
188 //       Send "DO SUPP"
189 //       if("WILL SUPP")
190 //         Send "DO ECHO"
191 //       else 
192 //         Send "DONT ECHO"
193
194
195 void ddww_echo(SOCKET server,_verb verb, _option option)
196 {
197   DWORD mode;
198   GetConsoleMode(StandardInput, & mode); // ENABLE_ECHO_INPUT
199   
200   int set = !(mode & ENABLE_ECHO_INPUT);
201
202   switch(verb)
203   {
204   case verb_will: // server wants to echo stuff
205     if(set) return; //don't confirm - already set.
206     SetConsoleMode(StandardInput,mode & (~ENABLE_ECHO_INPUT));
207     break;
208   case verb_wont: // server don't want to echo
209     if(!set) return; //don't confirm - already unset.
210     SetConsoleMode(StandardInput,mode | ENABLE_ECHO_INPUT);
211     break;
212   case verb_do:   // server wants me to loopback
213     noreply(server,verb,option);
214     return;
215   case verb_dont: // server doesn't want me to echo
216     break;        // don't bother to reply - I don't
217   }
218   yesreply(server,verb,option);
219 }
220
221
222 void ddww_supp(SOCKET server,_verb verb,_option option) //Suppress GA
223 {
224   DWORD mode;
225   GetConsoleMode(StandardInput,&mode); // ENABLE_LINE_INPUT
226   
227   int set = !(mode & ENABLE_LINE_INPUT);
228
229   switch(verb)
230   {
231   case verb_will: // server wants to suppress GA's
232     if(set) break; //don't confirm - already set.
233     SetConsoleMode(StandardInput,mode & (~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT)));
234     askfor(server,verb_do,TOPT_SUPP);
235     askfor(server,verb_will,TOPT_SUPP);
236     askfor(server,verb_do,TOPT_ECHO);
237     break;
238   case verb_wont: // server wants to send GA's 
239     if(!set) break; //don't confirm - already unset.
240     SetConsoleMode(StandardInput,mode | ENABLE_LINE_INPUT);
241     askfor(server,verb_dont,TOPT_SUPP);
242     askfor(server,verb_wont,TOPT_SUPP);
243     break;
244   case verb_do:   // server wants me to suppress GA's
245     if(set) break;
246     askfor(server,verb_do,TOPT_SUPP);
247     break;
248   case verb_dont: // server wants me to send GA's
249     if(!set) break;
250     askfor(server,verb_dont,TOPT_SUPP);
251     break;
252   }
253 }
254
255 ///////////////////////////////////////////////////////////////////////////////
256 // Option TERMINAL-TYPE
257
258 void ddww_term(SOCKET server,_verb verb,_option option) //Subnegotiate terminal type
259 {
260   switch(verb)
261   {
262   case verb_will:
263     noreply(server,verb,option); // I don't want terminal info
264     break;
265   case verb_wont:
266     //dat be cool - its not going to send. no need to confirm
267     break;
268   case verb_do:
269     yesreply(server,verb,option); //I'll send it when asked
270     break;
271   case verb_dont://Ok - I won't
272     break;
273   }
274 }
275
276 // TERMINAL TYPE subnegotation
277 enum
278 {
279   SB_TERM_IS = 0,
280   SB_TERM_SEND = 1
281 };
282
283 #define NUM_TERMINALS 2
284
285 struct
286 {
287   char* name;
288   LPDATAPROC termproc;
289   //pre requsites.
290 } terminal[NUM_TERMINALS] = {
291   { "NVT", nvt }, 
292   { "ANSI", ansi }
293 };
294
295 int term_index = 0;
296
297 void sbproc_term(SOCKET server,unsigned char data)
298 {
299
300   if(data == SB_TERM_SEND)
301   {
302     if(term_index == NUM_TERMINALS)
303       term_index = 0;
304     else
305       term_index++;
306     char buf[16]; //pls limit 
307     buf[0] = IAC;
308     buf[1] = SB;
309     buf[2] = TOPT_TERM;
310     buf[3] = SB_TERM_IS;
311     lstrcpy(&buf[4],terminal[(term_index==NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].name);
312     int nlen = lstrlen(&buf[4]);
313     buf[4+nlen] = IAC;
314     buf[5+nlen] = SE;
315     send(server,buf,4+nlen+2,0);
316   }
317 }
318
319 ///////////////////////////////////////////////////////////////////////////////
320
321 struct
322 {
323   _option option;
324   LPOPTIONPROC OptionProc;
325   LPDATAPROC DataProc;
326 }  ol[] = {
327   {TOPT_ECHO,   ddww_echo,  NULL},
328   {TOPT_SUPP,   ddww_supp,  NULL},
329   {TOPT_TERM,   ddww_term,  sbproc_term},
330   {TOPT_ERROR,  ddww_error, NULL}
331 };
332
333
334 void vm(SOCKET server,unsigned char code)
335 {
336 //These vars are the finite state
337   static int state = state_data;
338   static _verb verb = verb_sb;
339   static LPDATAPROC DataProc = terminal[(term_index==NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].termproc;
340 // for index
341   int i=0;
342
343 //Decide what to do (state based)
344   switch(state)
345   {
346   case state_data:
347     switch(code)
348     {
349     case IAC: state = state_code; break;
350     default: DataProc(server,code);
351     }
352     break;
353   case state_code:
354     state = state_data;
355     switch(code)
356     {
357     // State transition back to data
358     case IAC: 
359       DataProc(server,code);
360       break;
361     // Code state transitions back to data
362     case SE:
363       DataProc = terminal[(term_index==NUM_TERMINALS)?(NUM_TERMINALS-1):term_index].termproc;
364       break;
365     case NOP:
366       break;
367     case DM:
368       break;
369     case BRK:
370       break;
371     case IP:
372       break;
373     case AO:
374       break;
375     case AYT:
376       break;
377     case EC:
378       break;
379     case EL:
380       break;
381     case GA:
382       break;
383     // Transitions to option state
384     case SB:
385       verb = verb_sb;
386       state = state_option;
387       break;
388     case WILL:
389       verb = verb_will;
390       state = state_option;
391       break;
392     case WONT:
393       verb = verb_wont;
394       state = state_option;
395       break;
396     case DO:
397       verb = verb_do;
398       state = state_option;
399       break;
400     case DONT:
401       verb = verb_dont;
402       state = state_option;
403       break;
404     }
405     break;
406   case state_option:
407     state = state_data;
408
409     //Find the option entry
410     for(
411       i = 0;
412       ol[i].option != TOPT_ERROR && ol[i].option != code;
413       i++);
414
415     //Do some verb specific stuff
416     if(verb == verb_sb)
417       DataProc = ol[i].DataProc;
418     else
419       ol[i].OptionProc(server,verb,(_option)code);
420     break;
421   }
422 }
423
424 /* EOF */