KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > kawa > Telnet


1 package kawa; // For now
2

3 /** Encapsulates the state of a telnet connection.
4  * When run as an application, is a a minimal telnet client. */

5
6 // Some ideas from: Flanagan: "Java Examples in a Nutshell" (O'Reilly, 1997),
7
// example 9-8.
8

9 public class Telnet implements Runnable JavaDoc
10 {
11   boolean isServer;
12   final static int SE = 240; // End of subnegotiation parameters.
13
final static int NOP = 241; // No operation.
14
/*
15       Data Mark 242 The data stream portion of a Synch.
16                                  This should always be accompanied
17                                  by a TCP Urgent notification.
18  Break 243 NVT character BRK.*/

19   final static int IP = 244; // Interrupt Process.
20
final static int EOF = 236; // End of file.
21

22   /** Indicates that what follows is subnegotiation of the indicated option. */
23   final static int SB = 250;
24
25   /** Indicates the desire to begin performing, or confirmation that
26       you are now performing, the indicated option. */

27   public static final int WILL = 251;
28   /** Indicates the refusal to perform,or continue performing, the
29       indicated option. */

30   public static final int WONT = 252;
31
32   /** Indicates the request that the other party perform, or
33       confirmation that you are expecting the other party to perform, the
34       indicated option. */

35   public static final int DO = 253;
36
37   public static final int DONT = 254;
38   /** Indicates the demand that the other party stop performing,
39       or confirmation that you are no longer expecting the other party
40       to perform, the indicated option. */

41
42   /** The "Interpret As Command" prefix code. */
43   final static int IAC = 255;
44
45   // Various options.
46
public static final int ECHO = 1;
47   public static final int SUPPRESS_GO_AHEAD = 3;
48   final static int TM = 6; /* timing mark */
49   final static int TTYPE = 24; /* terminal type */
50   final static int NAWS = 31; /* window size */
51   final static int LINEMODE = 34;
52
53   /* DEBUGGING:
54   public static String toString(int code)
55   {
56     switch (code)
57       {
58       case DO: return "DO";
59       case DONT: return "DONT";
60       case WILL: return "WILL";
61       case WONT: return "WONT";
62       case ECHO: return "ECHO";
63       case LINEMODE: return "LINEMODE";
64       case TTYPE: return "TTYPE";
65       case NAWS: return "NAWS";
66       case SUPPRESS_GO_AHEAD: return "SUPPRESS_GO_AHEAD";
67       default: return Integer.toString(code);
68       }
69   }
70   */

71
72   public short windowHeight, windowWidth;
73   public byte[] terminalType;
74   final byte preferredLineMode = 3; // EDIT+TRAPSIG
75

76   java.io.InputStream JavaDoc sin;
77   java.io.OutputStream JavaDoc sout;
78
79   TelnetInputStream in;
80   TelnetOutputStream out;
81
82   public TelnetInputStream getInputStream()
83   {
84     return in;
85   }
86
87   public TelnetOutputStream getOutputStream()
88   {
89     return out;
90   }
91
92   /** Used to store the the current state of negotiation of telnet options.
93    * For example, for option LINEMODE (34), (telnet_options_state[34] & 7)
94    * is the state of the option on this side, and
95    * ((telnet_options_state[34] >> 3) & 7) is the state on the other side.
96    * The 3 bits for each side can be any of OPTION_NO though OPTION_YES.
97    * The option is only enabled if the value is OPTION_YES.
98    * See RFC 1143. */

99   final byte[] optionsState = new byte[256];
100
101   /** The option is disabled, and no negotiating is in progress. */
102   final static int OPTION_NO = 0;
103
104   /** We sent out DONT/WONT and are waiting for confirming WONT/DONT. */
105   final static int OPTION_WANTNO = 1;
106
107   /** Like WANTNO, but we changed our mind. */
108   final static int OPTION_WANTNO_OPPOSITE = 2;
109
110   /** We sent out DO/WILL and are waiting for confirming WILL/DO. */
111   final static int OPTION_WANTYES = 3;
112
113   /** Like WANTYES, but we changed our mind. */
114   final static int OPTION_WANTYES_OPPOSITE = 4;
115
116   /** The option is enabled, and no negotiating is in progress. */
117   final static int OPTION_YES = 5;
118
119   /** Actually (try to) change the state for an option.
120    * Return false is we don't know how or don't want to.
121    * command is DO if we're enabling on this side;
122    * DONT if we're disabling on this side;
123    * WILL if we're enabling for the other side;
124    * WONT if we're disabling for the other side.
125    *
126    * You should not call this function directly.
127    * Instead, call request to send a request to the other side
128    * (but with DO/WILL and DONT/WONT switched). Then, when
129    * confirmation comes back, it is handled by the handle method, which
130    * calls change.
131    * The optionsState array may not have been updated yet.
132    */

133   boolean change (int command, int option)
134   {
135     if (option == TM)
136       return true;
137     if (isServer && option == NAWS)
138       return true;
139     if (isServer && command == WILL && option == LINEMODE)
140       {
141     byte[] buf = new byte[2];
142     buf[0] = 1; // MODE
143
buf[1] = preferredLineMode;
144     try
145       {
146         out.writeSubCommand(LINEMODE, buf);
147       }
148     catch (java.io.IOException JavaDoc ex)
149       {
150         // Ignore it - I guess we'll do without.
151
}
152         return true;
153       }
154     if (isServer && command == WILL && option == TTYPE)
155       {
156     byte[] buf = new byte[1];
157     buf[0] = 1; // Request SEND terminal-type.
158
try
159       {
160         out.writeSubCommand(option, buf);
161       }
162     catch (java.io.IOException JavaDoc ex)
163       {
164         // Ignore it - I guess we'll do without.
165
}
166         return true;
167       }
168     if (! isServer && option == ECHO)
169       {
170     if (command == DO)
171       return false;
172     if (command == WILL)
173       return true;
174       }
175     return false;
176   }
177
178   /** Handle a sub-command (SB-sequence) that we received. */
179
180   public void subCommand (byte[] buf, int off, int len)
181   {
182     int command = buf[off];
183     switch (command)
184       {
185       case NAWS:
186     if (len == 5)
187       {
188         windowWidth = (short) ((buf[1] << 8) + (buf[2] & 0xFF));
189         windowHeight = (short) ((buf[3] << 8) + (buf[4] & 0xFF));
190         /*
191         System.err.println("Window size: w:"
192                    +windowWidth+"*h:"+windowHeight);
193         */

194         return;
195       }
196     break;
197       case TTYPE:
198     byte[] type = new byte[len-1];
199     System.arraycopy(buf, 1, type, 0, len-1);
200     terminalType = type;
201      System.err.println("terminal type: '"+new String JavaDoc(type)+"'");
202     return;
203       case LINEMODE:
204     ///*
205
System.err.println("SBCommand LINEMODE "+buf[1]+" len:"+len);
206     if (buf[1] == 3) // SLC
207
{
208         for (int i = 2; i+2 < len; i += 3)
209           {
210         System.err.println(" "+buf[i]+","+buf[i+1]+","+buf[i+2]);
211           }
212         return;
213       }
214     //*/
215
break;
216       }
217   }
218
219   /** Handle a request from the other side.
220    * Command is one of DO, DONT, WILL, WONT. */

221   void handle (int command, int option) throws java.io.IOException JavaDoc
222   {
223     // True if the other side wants to change itself I.e. we got WILL/WONT);
224
// false if it wants us to change (i.e. we got DO/DONT).
225
boolean otherSide = command < DO;
226
227     // True if DO or WILL; false if DONT or WONT.
228
boolean wantOn = (command & 1) != 0;
229     byte state = optionsState[option];
230     // System.err.println("telnet handle "+toString(command)+", "+toString(option));
231
if (otherSide)
232       state >>= 3;
233     switch ((state >> 3) & 7)
234       {
235       case OPTION_YES:
236     if (wantOn)
237       return; // Nothing to do.
238
// Got a DONT or WONT. Disable without arguing.
239
state = OPTION_NO;
240     change(command, option);
241     out.writeCommand(otherSide ? DONT : WONT, option);
242     break;
243       case OPTION_NO:
244     if (! wantOn)
245       return; // Nothing to do.
246
if (change (command, option))
247       {
248         state = OPTION_YES;
249         out.writeCommand(otherSide ? DO : WILL, option); // Confirm.
250
}
251     else
252       {
253         out.writeCommand(otherSide ? Telnet.DONT : Telnet.WONT,
254                  option);
255       }
256     break;
257       case OPTION_WANTNO:
258     state = OPTION_NO;
259     break;
260       case OPTION_WANTNO_OPPOSITE:
261     // if (goalState) Error: DONT/WONT answered by WILL/DO.
262
// Maybe some packets crossed in the mail.
263
// Presumably the other side will take our original
264
// request as de-conformation. In any case:
265
state = OPTION_WANTYES;
266     out.writeCommand(otherSide ? Telnet.DO : Telnet.WILL,
267              option);
268     break;
269       case OPTION_WANTYES:
270     if (wantOn)
271       {
272         state = OPTION_YES;
273         change (command, option);
274       }
275     else
276       state = OPTION_NO; // Declined.
277
break;
278       case OPTION_WANTYES_OPPOSITE:
279     if (wantOn)
280       {
281         state = OPTION_WANTNO;
282         out.writeCommand(otherSide ? DONT : WONT, option);
283       }
284     else
285       {
286         state = OPTION_NO;
287       }
288     break;
289       }
290     if (otherSide)
291       state = (byte) ((optionsState[option] & 0xC7) | (state << 3));
292     else
293       state = (byte) ((optionsState[option] & 0xF8) | state);
294     optionsState[option] = state;
295   }
296
297   /** Request (from this side) a new option state.
298    * Command is one of DO, DONT, WILL, WONT. */

299   public void request (int command, int option) throws java.io.IOException JavaDoc
300   {
301     // System.err.println("telnet request "+toString(command)+", "+toString(option));
302
// True if we want other side to change,
303
// false if we want this side to change.
304
boolean otherSide = command >= DO;
305
306     // True for DO, WILL; false for DONT or WONT.
307
boolean wantOn = (command & 1) != 0;
308
309     byte state = optionsState[option];
310     if (otherSide)
311       state >>= 3;
312
313     switch (state & 7)
314       {
315       case OPTION_NO:
316     if (wantOn)
317       {
318         state = OPTION_WANTYES;
319         out.writeCommand(command, option);
320       }
321     // else: Redundant - already disabled.
322
break;
323       case OPTION_YES:
324     if (! wantOn)
325       {
326         state = OPTION_WANTNO;
327         out.writeCommand(command, option);
328       }
329     // else: Redundant - already enabled.
330
break;
331       case OPTION_WANTNO:
332     if (wantOn)
333       state = OPTION_WANTNO_OPPOSITE;
334     // else: Error/redundant - already want to disable.
335
break;
336       case OPTION_WANTNO_OPPOSITE:
337     if (! wantOn)
338       state = OPTION_WANTNO;
339     // else: Error/redundant - already want to enable
340
break;
341       case OPTION_WANTYES:
342     if (! wantOn)
343       state = OPTION_WANTYES_OPPOSITE;
344     // else: Error/redundant - already want to disable.
345
case OPTION_WANTYES_OPPOSITE:
346     if (wantOn)
347       state = OPTION_WANTYES;
348     // else: Error/redundant - already want to enable
349
break;
350       }
351
352     if (otherSide)
353       state = (byte) ((optionsState[option] & 0xC7) | (state << 3));
354     else
355       state = (byte) ((optionsState[option] & 0xF8) | state);
356     optionsState[option] = state;
357   }
358
359   static void usage ()
360   {
361     System.err.println("Usage: [java] kawa.Telnet HOST [PORT#]");
362     System.exit(-1);
363   }
364
365   public static void main (String JavaDoc[] args)
366   {
367     if (args.length == 0)
368       usage();
369     String JavaDoc host = args[0];
370     int port = 23;
371     if (args.length > 1)
372       {
373     port = Integer.parseInt(args[1]);
374       }
375     try
376       {
377     java.net.Socket JavaDoc socket = new java.net.Socket JavaDoc(host, port);
378     Telnet telnet = new Telnet(socket, false);
379     TelnetOutputStream tout = telnet.getOutputStream();
380     Thread JavaDoc t = new Thread JavaDoc(telnet);
381
382     t.setPriority(Thread.currentThread().getPriority() + 1);
383     t.start();
384
385     byte[] buffer = new byte[1024];
386     for (;;)
387       {
388         int ch = System.in.read();
389         if (ch < 0)
390           break; // send EOF FIXME
391
buffer[0] = (byte) ch;
392         int avail = System.in.available();
393         if (avail > 0)
394           {
395         if (avail > buffer.length-1)
396           avail = buffer.length - 1;
397         avail = System.in.read(buffer, 1, avail);
398           }
399         tout.write(buffer, 0, avail+1);
400       }
401     t.stop();
402       }
403     catch (Exception JavaDoc ex)
404       {
405     System.err.println(ex);
406       }
407   }
408
409   public Telnet (java.net.Socket JavaDoc socket, boolean isServer)
410     throws java.io.IOException JavaDoc
411   {
412     sin = socket.getInputStream();
413     sout = socket.getOutputStream();
414     out = new TelnetOutputStream(sout);
415     in = new TelnetInputStream(sin, this);
416     this.isServer = isServer;
417   }
418
419   public void run ()
420   {
421     try
422       {
423     TelnetInputStream tin = getInputStream();
424     byte[] buffer = new byte[1024];
425     for (;;)
426       {
427         int ch = tin.read();
428         if (ch < 0)
429           break; // ??? FIXME
430
buffer[0] = (byte) ch;
431         int avail = tin.available();
432         if (avail > 0)
433           {
434         if (avail > buffer.length-1)
435           avail = buffer.length - 1;
436         avail = tin.read(buffer, 1, avail);
437           }
438         System.out.write(buffer, 0, avail+1);
439       }
440       }
441     catch (java.io.IOException JavaDoc ex)
442       {
443     System.err.println(ex);
444     System.exit(-1);
445       }
446   }
447 }
448
Popular Tags