001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.net.telnet; 019 020import java.io.BufferedInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024 025/*** 026 * The TelnetClient class implements the simple network virtual 027 * terminal (NVT) for the Telnet protocol according to RFC 854. It 028 * does not implement any of the extra Telnet options because it 029 * is meant to be used within a Java program providing automated 030 * access to Telnet accessible resources. 031 * <p> 032 * The class can be used by first connecting to a server using the 033 * SocketClient 034 * {@link org.apache.commons.net.SocketClient#connect connect} 035 * method. Then an InputStream and OutputStream for sending and 036 * receiving data over the Telnet connection can be obtained by 037 * using the {@link #getInputStream getInputStream() } and 038 * {@link #getOutputStream getOutputStream() } methods. 039 * When you finish using the streams, you must call 040 * {@link #disconnect disconnect } rather than simply 041 * closing the streams. 042 ***/ 043 044public class TelnetClient extends Telnet 045{ 046 private InputStream __input; 047 private OutputStream __output; 048 protected boolean readerThread = true; 049 private TelnetInputListener inputListener; 050 051 /*** 052 * Default TelnetClient constructor, sets terminal-type {@code VT100}. 053 ***/ 054 public TelnetClient() 055 { 056 /* TERMINAL-TYPE option (start)*/ 057 super ("VT100"); 058 /* TERMINAL-TYPE option (end)*/ 059 __input = null; 060 __output = null; 061 } 062 063 /** 064 * Construct an instance with the specified terminal type. 065 * 066 * @param termtype the terminal type to use, e.g. {@code VT100} 067 */ 068 /* TERMINAL-TYPE option (start)*/ 069 public TelnetClient(String termtype) 070 { 071 super (termtype); 072 __input = null; 073 __output = null; 074 } 075 /* TERMINAL-TYPE option (end)*/ 076 077 void _flushOutputStream() throws IOException 078 { 079 _output_.flush(); 080 } 081 void _closeOutputStream() throws IOException 082 { 083 try { 084 _output_.close(); 085 } finally { 086 _output_ = null; 087 } 088 } 089 090 /*** 091 * Handles special connection requirements. 092 * 093 * @throws IOException If an error occurs during connection setup. 094 ***/ 095 @Override 096 protected void _connectAction_() throws IOException 097 { 098 super._connectAction_(); 099 TelnetInputStream tmp = new TelnetInputStream(_input_, this, readerThread); 100 if(readerThread) 101 { 102 tmp._start(); 103 } 104 // __input CANNOT refer to the TelnetInputStream. We run into 105 // blocking problems when some classes use TelnetInputStream, so 106 // we wrap it with a BufferedInputStream which we know is safe. 107 // This blocking behavior requires further investigation, but right 108 // now it looks like classes like InputStreamReader are not implemented 109 // in a safe manner. 110 __input = new BufferedInputStream(tmp); 111 __output = new TelnetOutputStream(this); 112 } 113 114 /*** 115 * Disconnects the telnet session, closing the input and output streams 116 * as well as the socket. If you have references to the 117 * input and output streams of the telnet connection, you should not 118 * close them yourself, but rather call disconnect to properly close 119 * the connection. 120 ***/ 121 @Override 122 public void disconnect() throws IOException 123 { 124 try { 125 if (__input != null) { 126 __input.close(); 127 } 128 if (__output != null) { 129 __output.close(); 130 } 131 } finally { // NET-594 132 __output = null; 133 __input = null; 134 super.disconnect(); 135 } 136 } 137 138 /*** 139 * Returns the telnet connection output stream. You should not close the 140 * stream when you finish with it. Rather, you should call 141 * {@link #disconnect disconnect }. 142 * 143 * @return The telnet connection output stream. 144 ***/ 145 public OutputStream getOutputStream() 146 { 147 return __output; 148 } 149 150 /*** 151 * Returns the telnet connection input stream. You should not close the 152 * stream when you finish with it. Rather, you should call 153 * {@link #disconnect disconnect }. 154 * 155 * @return The telnet connection input stream. 156 ***/ 157 public InputStream getInputStream() 158 { 159 return __input; 160 } 161 162 /*** 163 * Returns the state of the option on the local side. 164 * 165 * @param option - Option to be checked. 166 * 167 * @return The state of the option on the local side. 168 ***/ 169 public boolean getLocalOptionState(int option) 170 { 171 /* BUG (option active when not already acknowledged) (start)*/ 172 return (_stateIsWill(option) && _requestedWill(option)); 173 /* BUG (option active when not already acknowledged) (end)*/ 174 } 175 176 /*** 177 * Returns the state of the option on the remote side. 178 * 179 * @param option - Option to be checked. 180 * 181 * @return The state of the option on the remote side. 182 ***/ 183 public boolean getRemoteOptionState(int option) 184 { 185 /* BUG (option active when not already acknowledged) (start)*/ 186 return (_stateIsDo(option) && _requestedDo(option)); 187 /* BUG (option active when not already acknowledged) (end)*/ 188 } 189 /* open TelnetOptionHandler functionality (end)*/ 190 191 /* Code Section added for supporting AYT (start)*/ 192 193 /*** 194 * Sends an Are You There sequence and waits for the result. 195 * 196 * @param timeout - Time to wait for a response (millis.) 197 * 198 * @return true if AYT received a response, false otherwise 199 * 200 * @throws InterruptedException on error 201 * @throws IllegalArgumentException on error 202 * @throws IOException on error 203 ***/ 204 public boolean sendAYT(long timeout) 205 throws IOException, IllegalArgumentException, InterruptedException 206 { 207 return (_sendAYT(timeout)); 208 } 209 /* Code Section added for supporting AYT (start)*/ 210 211 /*** 212 * Sends a protocol-specific subnegotiation message to the remote peer. 213 * {@link TelnetClient} will add the IAC SB & IAC SE framing bytes; 214 * the first byte in {@code message} should be the appropriate telnet 215 * option code. 216 * 217 * <p> 218 * This method does not wait for any response. Subnegotiation messages 219 * sent by the remote end can be handled by registering an approrpriate 220 * {@link TelnetOptionHandler}. 221 * </p> 222 * 223 * @param message option code followed by subnegotiation payload 224 * @throws IllegalArgumentException if {@code message} has length zero 225 * @throws IOException if an I/O error occurs while writing the message 226 * @since 3.0 227 ***/ 228 public void sendSubnegotiation(int[] message) 229 throws IOException, IllegalArgumentException 230 { 231 if (message.length < 1) { 232 throw new IllegalArgumentException("zero length message"); 233 } 234 _sendSubnegotiation(message); 235 } 236 237 /*** 238 * Sends a command byte to the remote peer, adding the IAC prefix. 239 * 240 * <p> 241 * This method does not wait for any response. Messages 242 * sent by the remote end can be handled by registering an approrpriate 243 * {@link TelnetOptionHandler}. 244 * </p> 245 * 246 * @param command the code for the command 247 * @throws IOException if an I/O error occurs while writing the message 248 * @throws IllegalArgumentException on error 249 * @since 3.0 250 ***/ 251 public void sendCommand(byte command) 252 throws IOException, IllegalArgumentException 253 { 254 _sendCommand(command); 255 } 256 257 /* open TelnetOptionHandler functionality (start)*/ 258 259 /*** 260 * Registers a new TelnetOptionHandler for this telnet client to use. 261 * 262 * @param opthand - option handler to be registered. 263 * 264 * @throws InvalidTelnetOptionException on error 265 * @throws IOException on error 266 ***/ 267 @Override 268 public void addOptionHandler(TelnetOptionHandler opthand) 269 throws InvalidTelnetOptionException, IOException 270 { 271 super.addOptionHandler(opthand); 272 } 273 /* open TelnetOptionHandler functionality (end)*/ 274 275 /*** 276 * Unregisters a TelnetOptionHandler. 277 * 278 * @param optcode - Code of the option to be unregistered. 279 * 280 * @throws InvalidTelnetOptionException on error 281 * @throws IOException on error 282 ***/ 283 @Override 284 public void deleteOptionHandler(int optcode) 285 throws InvalidTelnetOptionException, IOException 286 { 287 super.deleteOptionHandler(optcode); 288 } 289 290 /* Code Section added for supporting spystreams (start)*/ 291 /*** 292 * Registers an OutputStream for spying what's going on in 293 * the TelnetClient session. 294 * 295 * @param spystream - OutputStream on which session activity 296 * will be echoed. 297 ***/ 298 public void registerSpyStream(OutputStream spystream) 299 { 300 super._registerSpyStream(spystream); 301 } 302 303 /*** 304 * Stops spying this TelnetClient. 305 * 306 ***/ 307 public void stopSpyStream() 308 { 309 super._stopSpyStream(); 310 } 311 /* Code Section added for supporting spystreams (end)*/ 312 313 /*** 314 * Registers a notification handler to which will be sent 315 * notifications of received telnet option negotiation commands. 316 * 317 * @param notifhand - TelnetNotificationHandler to be registered 318 ***/ 319 @Override 320 public void registerNotifHandler(TelnetNotificationHandler notifhand) 321 { 322 super.registerNotifHandler(notifhand); 323 } 324 325 /*** 326 * Unregisters the current notification handler. 327 * 328 ***/ 329 @Override 330 public void unregisterNotifHandler() 331 { 332 super.unregisterNotifHandler(); 333 } 334 335 /*** 336 * Sets the status of the reader thread. 337 * 338 * <p> 339 * When enabled, a seaparate internal reader thread is created for new 340 * connections to read incoming data as it arrives. This results in 341 * immediate handling of option negotiation, notifications, etc. 342 * (at least until the fixed-size internal buffer fills up). 343 * Otherwise, no thread is created an all negotiation and option 344 * handling is deferred until a read() is performed on the 345 * {@link #getInputStream input stream}. 346 * </p> 347 * 348 * <p> 349 * The reader thread must be enabled for {@link TelnetInputListener} 350 * support. 351 * </p> 352 * 353 * <p> 354 * When this method is invoked, the reader thread status will apply to all 355 * subsequent connections; the current connection (if any) is not affected. 356 * </p> 357 * 358 * @param flag true to enable the reader thread, false to disable 359 * @see #registerInputListener 360 ***/ 361 public void setReaderThread(boolean flag) 362 { 363 readerThread = flag; 364 } 365 366 /*** 367 * Gets the status of the reader thread. 368 * 369 * @return true if the reader thread is enabled, false otherwise 370 ***/ 371 public boolean getReaderThread() 372 { 373 return (readerThread); 374 } 375 376 /*** 377 * Register a listener to be notified when new incoming data is 378 * available to be read on the {@link #getInputStream input stream}. 379 * Only one listener is supported at a time. 380 * 381 * <p> 382 * More precisely, notifications are issued whenever the number of 383 * bytes available for immediate reading (i.e., the value returned 384 * by {@link InputStream#available}) transitions from zero to non-zero. 385 * Note that (in general) multiple reads may be required to empty the 386 * buffer and reset this notification, because incoming bytes are being 387 * added to the internal buffer asynchronously. 388 * </p> 389 * 390 * <p> 391 * Notifications are only supported when a {@link #setReaderThread 392 * reader thread} is enabled for the connection. 393 * </p> 394 * 395 * @param listener listener to be registered; replaces any previous 396 * @since 3.0 397 ***/ 398 public synchronized void registerInputListener(TelnetInputListener listener) 399 { 400 this.inputListener = listener; 401 } 402 403 /*** 404 * Unregisters the current {@link TelnetInputListener}, if any. 405 * 406 * @since 3.0 407 ***/ 408 public synchronized void unregisterInputListener() 409 { 410 this.inputListener = null; 411 } 412 413 // Notify input listener 414 void notifyInputListener() { 415 TelnetInputListener listener; 416 synchronized (this) { 417 listener = this.inputListener; 418 } 419 if (listener != null) { 420 listener.telnetInputAvailable(); 421 } 422 } 423}