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
018 package org.apache.commons.net;
019
020 import java.io.Closeable;
021 import java.io.IOException;
022 import java.io.InputStream;
023 import java.io.OutputStream;
024 import java.net.InetAddress;
025 import java.net.InetSocketAddress;
026 import java.net.Socket;
027 import java.net.SocketException;
028
029 import javax.net.ServerSocketFactory;
030 import javax.net.SocketFactory;
031
032
033 /**
034 * The SocketClient provides the basic operations that are required of
035 * client objects accessing sockets. It is meant to be
036 * subclassed to avoid having to rewrite the same code over and over again
037 * to open a socket, close a socket, set timeouts, etc. Of special note
038 * is the {@link #setSocketFactory setSocketFactory }
039 * method, which allows you to control the type of Socket the SocketClient
040 * creates for initiating network connections. This is especially useful
041 * for adding SSL or proxy support as well as better support for applets. For
042 * example, you could create a
043 * {@link javax.net.SocketFactory} that
044 * requests browser security capabilities before creating a socket.
045 * All classes derived from SocketClient should use the
046 * {@link #_socketFactory_ _socketFactory_ } member variable to
047 * create Socket and ServerSocket instances rather than instantiating
048 * them by directly invoking a constructor. By honoring this contract
049 * you guarantee that a user will always be able to provide his own
050 * Socket implementations by substituting his own SocketFactory.
051 * @see SocketFactory
052 */
053 public abstract class SocketClient
054 {
055 /**
056 * The end of line character sequence used by most IETF protocols. That
057 * is a carriage return followed by a newline: "\r\n"
058 */
059 public static final String NETASCII_EOL = "\r\n";
060
061 /** The default SocketFactory shared by all SocketClient instances. */
062 private static final SocketFactory __DEFAULT_SOCKET_FACTORY =
063 SocketFactory.getDefault();
064
065 /** The default {@link ServerSocketFactory} */
066 private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY =
067 ServerSocketFactory.getDefault();
068
069 /**
070 * A ProtocolCommandSupport object used to manage the registering of
071 * ProtocolCommandListeners and te firing of ProtocolCommandEvents.
072 */
073 private ProtocolCommandSupport __commandSupport;
074
075 /** The timeout to use after opening a socket. */
076 protected int _timeout_;
077
078 /** The socket used for the connection. */
079 protected Socket _socket_;
080
081 /** The default port the client should connect to. */
082 protected int _defaultPort_;
083
084 /** The socket's InputStream. */
085 protected InputStream _input_;
086
087 /** The socket's OutputStream. */
088 protected OutputStream _output_;
089
090 /** The socket's SocketFactory. */
091 protected SocketFactory _socketFactory_;
092
093 /** The socket's ServerSocket Factory. */
094 protected ServerSocketFactory _serverSocketFactory_;
095
096 /** The socket's connect timeout (0 = infinite timeout) */
097 private static final int DEFAULT_CONNECT_TIMEOUT = 0;
098 protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
099
100 /** Hint for SO_RCVBUF size */
101 private int receiveBufferSize = -1;
102
103 /** Hint for SO_SNDBUF size */
104 private int sendBufferSize = -1;
105
106 /**
107 * Default constructor for SocketClient. Initializes
108 * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
109 * _isConnected_ to false, and _socketFactory_ to a shared instance of
110 * {@link org.apache.commons.net.DefaultSocketFactory}.
111 */
112 public SocketClient()
113 {
114 _socket_ = null;
115 _input_ = null;
116 _output_ = null;
117 _timeout_ = 0;
118 _defaultPort_ = 0;
119 _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
120 _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
121 }
122
123
124 /**
125 * Because there are so many connect() methods, the _connectAction_()
126 * method is provided as a means of performing some action immediately
127 * after establishing a connection, rather than reimplementing all
128 * of the connect() methods. The last action performed by every
129 * connect() method after opening a socket is to call this method.
130 * <p>
131 * This method sets the timeout on the just opened socket to the default
132 * timeout set by {@link #setDefaultTimeout setDefaultTimeout() },
133 * sets _input_ and _output_ to the socket's InputStream and OutputStream
134 * respectively, and sets _isConnected_ to true.
135 * <p>
136 * Subclasses overriding this method should start by calling
137 * <code> super._connectAction_() </code> first to ensure the
138 * initialization of the aforementioned protected variables.
139 */
140 protected void _connectAction_() throws IOException
141 {
142 _socket_.setSoTimeout(_timeout_);
143 _input_ = _socket_.getInputStream();
144 _output_ = _socket_.getOutputStream();
145 }
146
147
148 /**
149 * Opens a Socket connected to a remote host at the specified port and
150 * originating from the current host at a system assigned port.
151 * Before returning, {@link #_connectAction_ _connectAction_() }
152 * is called to perform connection initialization actions.
153 * <p>
154 * @param host The remote host.
155 * @param port The port to connect to on the remote host.
156 * @exception SocketException If the socket timeout could not be set.
157 * @exception IOException If the socket could not be opened. In most
158 * cases you will only want to catch IOException since SocketException is
159 * derived from it.
160 */
161 public void connect(InetAddress host, int port)
162 throws SocketException, IOException
163 {
164 _socket_ = _socketFactory_.createSocket();
165 if (receiveBufferSize != -1) {
166 _socket_.setReceiveBufferSize(receiveBufferSize);
167 }
168 if (sendBufferSize != -1) {
169 _socket_.setSendBufferSize(sendBufferSize);
170 }
171 _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
172 _connectAction_();
173 }
174
175 /**
176 * Opens a Socket connected to a remote host at the specified port and
177 * originating from the current host at a system assigned port.
178 * Before returning, {@link #_connectAction_ _connectAction_() }
179 * is called to perform connection initialization actions.
180 * <p>
181 * @param hostname The name of the remote host.
182 * @param port The port to connect to on the remote host.
183 * @exception SocketException If the socket timeout could not be set.
184 * @exception IOException If the socket could not be opened. In most
185 * cases you will only want to catch IOException since SocketException is
186 * derived from it.
187 * @exception java.net.UnknownHostException If the hostname cannot be resolved.
188 */
189 public void connect(String hostname, int port)
190 throws SocketException, IOException
191 {
192 connect(InetAddress.getByName(hostname), port);
193 }
194
195
196 /**
197 * Opens a Socket connected to a remote host at the specified port and
198 * originating from the specified local address and port.
199 * Before returning, {@link #_connectAction_ _connectAction_() }
200 * is called to perform connection initialization actions.
201 * <p>
202 * @param host The remote host.
203 * @param port The port to connect to on the remote host.
204 * @param localAddr The local address to use.
205 * @param localPort The local port to use.
206 * @exception SocketException If the socket timeout could not be set.
207 * @exception IOException If the socket could not be opened. In most
208 * cases you will only want to catch IOException since SocketException is
209 * derived from it.
210 */
211 public void connect(InetAddress host, int port,
212 InetAddress localAddr, int localPort)
213 throws SocketException, IOException
214 {
215 _socket_ = _socketFactory_.createSocket();
216 if (receiveBufferSize != -1) {
217 _socket_.setReceiveBufferSize(receiveBufferSize);
218 }
219 if (sendBufferSize != -1) {
220 _socket_.setSendBufferSize(sendBufferSize);
221 }
222 _socket_.bind(new InetSocketAddress(localAddr, localPort));
223 _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
224 _connectAction_();
225 }
226
227
228 /**
229 * Opens a Socket connected to a remote host at the specified port and
230 * originating from the specified local address and port.
231 * Before returning, {@link #_connectAction_ _connectAction_() }
232 * is called to perform connection initialization actions.
233 * <p>
234 * @param hostname The name of the remote host.
235 * @param port The port to connect to on the remote host.
236 * @param localAddr The local address to use.
237 * @param localPort The local port to use.
238 * @exception SocketException If the socket timeout could not be set.
239 * @exception IOException If the socket could not be opened. In most
240 * cases you will only want to catch IOException since SocketException is
241 * derived from it.
242 * @exception java.net.UnknownHostException If the hostname cannot be resolved.
243 */
244 public void connect(String hostname, int port,
245 InetAddress localAddr, int localPort)
246 throws SocketException, IOException
247 {
248 connect(InetAddress.getByName(hostname), port, localAddr, localPort);
249 }
250
251
252 /**
253 * Opens a Socket connected to a remote host at the current default port
254 * and originating from the current host at a system assigned port.
255 * Before returning, {@link #_connectAction_ _connectAction_() }
256 * is called to perform connection initialization actions.
257 * <p>
258 * @param host The remote host.
259 * @exception SocketException If the socket timeout could not be set.
260 * @exception IOException If the socket could not be opened. In most
261 * cases you will only want to catch IOException since SocketException is
262 * derived from it.
263 */
264 public void connect(InetAddress host) throws SocketException, IOException
265 {
266 connect(host, _defaultPort_);
267 }
268
269
270 /**
271 * Opens a Socket connected to a remote host at the current default
272 * port and originating from the current host at a system assigned port.
273 * Before returning, {@link #_connectAction_ _connectAction_() }
274 * is called to perform connection initialization actions.
275 * <p>
276 * @param hostname The name of the remote host.
277 * @exception SocketException If the socket timeout could not be set.
278 * @exception IOException If the socket could not be opened. In most
279 * cases you will only want to catch IOException since SocketException is
280 * derived from it.
281 * @exception java.net.UnknownHostException If the hostname cannot be resolved.
282 */
283 public void connect(String hostname) throws SocketException, IOException
284 {
285 connect(hostname, _defaultPort_);
286 }
287
288
289 /**
290 * Disconnects the socket connection.
291 * You should call this method after you've finished using the class
292 * instance and also before you call
293 * {@link #connect connect() }
294 * again. _isConnected_ is set to false, _socket_ is set to null,
295 * _input_ is set to null, and _output_ is set to null.
296 * <p>
297 * @exception IOException If there is an error closing the socket.
298 */
299 public void disconnect() throws IOException
300 {
301 closeQuietly(_socket_);
302 closeQuietly(_input_);
303 closeQuietly(_output_);
304 _socket_ = null;
305 _input_ = null;
306 _output_ = null;
307 }
308
309 private void closeQuietly(Socket socket) {
310 if (socket != null){
311 try {
312 socket.close();
313 } catch (IOException e) {
314 }
315 }
316 }
317
318 private void closeQuietly(Closeable close){
319 if (close != null){
320 try {
321 close.close();
322 } catch (IOException e) {
323 }
324 }
325 }
326 /**
327 * Returns true if the client is currently connected to a server.
328 * <p>
329 * Delegates to {@link Socket#isConnected()}
330 * @return True if the client is currently connected to a server,
331 * false otherwise.
332 */
333 public boolean isConnected()
334 {
335 if (_socket_ == null) {
336 return false;
337 }
338
339 return _socket_.isConnected();
340 }
341
342 /**
343 * Make various checks on the socket to test if it is available for use.
344 * Note that the only sure test is to use it, but these checks may help
345 * in some cases.
346 * @see <a href="https://issues.apache.org/jira/browse/NET-350">NET-350</a>
347 * @return {@code true} if the socket appears to be available for use
348 * @since 3.0
349 */
350 public boolean isAvailable(){
351 if (isConnected()) {
352 try
353 {
354 if (_socket_.getInetAddress() == null) {
355 return false;
356 }
357 if (_socket_.getPort() == 0) {
358 return false;
359 }
360 if (_socket_.getRemoteSocketAddress() == null) {
361 return false;
362 }
363 if (_socket_.isClosed()) {
364 return false;
365 }
366 /* these aren't exact checks (a Socket can be half-open),
367 but since we usually require two-way data transfer,
368 we check these here too: */
369 if (_socket_.isInputShutdown()) {
370 return false;
371 }
372 if (_socket_.isOutputShutdown()) {
373 return false;
374 }
375 /* ignore the result, catch exceptions: */
376 _socket_.getInputStream();
377 _socket_.getOutputStream();
378 }
379 catch (IOException ioex)
380 {
381 return false;
382 }
383 return true;
384 } else {
385 return false;
386 }
387 }
388
389 /**
390 * Sets the default port the SocketClient should connect to when a port
391 * is not specified. The {@link #_defaultPort_ _defaultPort_ }
392 * variable stores this value. If never set, the default port is equal
393 * to zero.
394 * <p>
395 * @param port The default port to set.
396 */
397 public void setDefaultPort(int port)
398 {
399 _defaultPort_ = port;
400 }
401
402 /**
403 * Returns the current value of the default port (stored in
404 * {@link #_defaultPort_ _defaultPort_ }).
405 * <p>
406 * @return The current value of the default port.
407 */
408 public int getDefaultPort()
409 {
410 return _defaultPort_;
411 }
412
413
414 /**
415 * Set the default timeout in milliseconds to use when opening a socket.
416 * This value is only used previous to a call to
417 * {@link #connect connect()}
418 * and should not be confused with {@link #setSoTimeout setSoTimeout()}
419 * which operates on an the currently opened socket. _timeout_ contains
420 * the new timeout value.
421 * <p>
422 * @param timeout The timeout in milliseconds to use for the socket
423 * connection.
424 */
425 public void setDefaultTimeout(int timeout)
426 {
427 _timeout_ = timeout;
428 }
429
430
431 /**
432 * Returns the default timeout in milliseconds that is used when
433 * opening a socket.
434 * <p>
435 * @return The default timeout in milliseconds that is used when
436 * opening a socket.
437 */
438 public int getDefaultTimeout()
439 {
440 return _timeout_;
441 }
442
443
444 /**
445 * Set the timeout in milliseconds of a currently open connection.
446 * Only call this method after a connection has been opened
447 * by {@link #connect connect()}.
448 * <p>
449 * @param timeout The timeout in milliseconds to use for the currently
450 * open socket connection.
451 * @exception SocketException If the operation fails.
452 */
453 public void setSoTimeout(int timeout) throws SocketException
454 {
455 _socket_.setSoTimeout(timeout);
456 }
457
458
459 /**
460 * Set the underlying socket send buffer size.
461 * <p>
462 * @param size The size of the buffer in bytes.
463 * @throws SocketException
464 * @since 2.0
465 */
466 public void setSendBufferSize(int size) throws SocketException {
467 sendBufferSize = size;
468 }
469
470 /**
471 * Get the current sendBuffer size
472 * @return the size, or -1 if not initialised
473 * @since 3.0
474 */
475 protected int getSendBufferSize(){
476 return sendBufferSize;
477 }
478
479 /**
480 * Sets the underlying socket receive buffer size.
481 * <p>
482 * @param size The size of the buffer in bytes.
483 * @throws SocketException
484 * @since 2.0
485 */
486 public void setReceiveBufferSize(int size) throws SocketException {
487 receiveBufferSize = size;
488 }
489
490 /**
491 * Get the current receivedBuffer size
492 * @return the size, or -1 if not initialised
493 * @since 3.0
494 */
495 protected int getReceiveBufferSize(){
496 return receiveBufferSize;
497 }
498
499 /**
500 * Returns the timeout in milliseconds of the currently opened socket.
501 * <p>
502 * @return The timeout in milliseconds of the currently opened socket.
503 * @exception SocketException If the operation fails.
504 */
505 public int getSoTimeout() throws SocketException
506 {
507 return _socket_.getSoTimeout();
508 }
509
510 /**
511 * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
512 * currently opened socket.
513 * <p>
514 * @param on True if Nagle's algorithm is to be enabled, false if not.
515 * @exception SocketException If the operation fails.
516 */
517 public void setTcpNoDelay(boolean on) throws SocketException
518 {
519 _socket_.setTcpNoDelay(on);
520 }
521
522
523 /**
524 * Returns true if Nagle's algorithm is enabled on the currently opened
525 * socket.
526 * <p>
527 * @return True if Nagle's algorithm is enabled on the currently opened
528 * socket, false otherwise.
529 * @exception SocketException If the operation fails.
530 */
531 public boolean getTcpNoDelay() throws SocketException
532 {
533 return _socket_.getTcpNoDelay();
534 }
535
536 /**
537 * Sets the SO_KEEPALIVE flag on the currently opened socket.
538 *
539 * From the Javadocs, the default keepalive time is 2 hours (although this is
540 * implementation dependent). It looks as though the Windows WSA sockets implementation
541 * allows a specific keepalive value to be set, although this seems not to be the case on
542 * other systems.
543 * @param keepAlive If true, keepAlive is turned on
544 * @throws SocketException
545 * @since 2.2
546 */
547 public void setKeepAlive(boolean keepAlive) throws SocketException {
548 _socket_.setKeepAlive(keepAlive);
549 }
550
551 /**
552 * Returns the current value of the SO_KEEPALIVE flag on the currently opened socket.
553 * Delegates to {@link Socket#getKeepAlive()}
554 * @return True if SO_KEEPALIVE is enabled.
555 * @throws SocketException
556 * @since 2.2
557 */
558 public boolean getKeepAlive() throws SocketException {
559 return _socket_.getKeepAlive();
560 }
561
562 /**
563 * Sets the SO_LINGER timeout on the currently opened socket.
564 * <p>
565 * @param on True if linger is to be enabled, false if not.
566 * @param val The linger timeout (in hundredths of a second?)
567 * @exception SocketException If the operation fails.
568 */
569 public void setSoLinger(boolean on, int val) throws SocketException
570 {
571 _socket_.setSoLinger(on, val);
572 }
573
574
575 /**
576 * Returns the current SO_LINGER timeout of the currently opened socket.
577 * <p>
578 * @return The current SO_LINGER timeout. If SO_LINGER is disabled returns
579 * -1.
580 * @exception SocketException If the operation fails.
581 */
582 public int getSoLinger() throws SocketException
583 {
584 return _socket_.getSoLinger();
585 }
586
587
588 /**
589 * Returns the port number of the open socket on the local host used
590 * for the connection.
591 * Delegates to {@link Socket#getLocalPort()}
592 * <p>
593 * @return The port number of the open socket on the local host used
594 * for the connection.
595 */
596 public int getLocalPort()
597 {
598 return _socket_.getLocalPort();
599 }
600
601
602 /**
603 * Returns the local address to which the client's socket is bound.
604 * Delegates to {@link Socket#getLocalAddress()}
605 * <p>
606 * @return The local address to which the client's socket is bound.
607 */
608 public InetAddress getLocalAddress()
609 {
610 return _socket_.getLocalAddress();
611 }
612
613 /**
614 * Returns the port number of the remote host to which the client is
615 * connected.
616 * Delegates to {@link Socket#getPort()}
617 * <p>
618 * @return The port number of the remote host to which the client is
619 * connected.
620 */
621 public int getRemotePort()
622 {
623 return _socket_.getPort();
624 }
625
626
627 /**
628 * @return The remote address to which the client is connected.
629 * Delegates to {@link Socket#getInetAddress()}
630 */
631 public InetAddress getRemoteAddress()
632 {
633 return _socket_.getInetAddress();
634 }
635
636
637 /**
638 * Verifies that the remote end of the given socket is connected to the
639 * the same host that the SocketClient is currently connected to. This
640 * is useful for doing a quick security check when a client needs to
641 * accept a connection from a server, such as an FTP data connection or
642 * a BSD R command standard error stream.
643 * <p>
644 * @return True if the remote hosts are the same, false if not.
645 */
646 public boolean verifyRemote(Socket socket)
647 {
648 InetAddress host1, host2;
649
650 host1 = socket.getInetAddress();
651 host2 = getRemoteAddress();
652
653 return host1.equals(host2);
654 }
655
656
657 /**
658 * Sets the SocketFactory used by the SocketClient to open socket
659 * connections. If the factory value is null, then a default
660 * factory is used (only do this to reset the factory after having
661 * previously altered it).
662 * <p>
663 * @param factory The new SocketFactory the SocketClient should use.
664 */
665 public void setSocketFactory(SocketFactory factory)
666 {
667 if (factory == null) {
668 _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
669 } else {
670 _socketFactory_ = factory;
671 }
672 }
673
674 /**
675 * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
676 * connections. If the factory value is null, then a default
677 * factory is used (only do this to reset the factory after having
678 * previously altered it).
679 * <p>
680 * @param factory The new ServerSocketFactory the SocketClient should use.
681 * @since 2.0
682 */
683 public void setServerSocketFactory(ServerSocketFactory factory) {
684 if (factory == null) {
685 _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
686 } else {
687 _serverSocketFactory_ = factory;
688 }
689 }
690
691 /**
692 * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
693 * connect() method.
694 * @param connectTimeout The connection timeout to use (in ms)
695 * @since 2.0
696 */
697 public void setConnectTimeout(int connectTimeout) {
698 this.connectTimeout = connectTimeout;
699 }
700
701 /**
702 * Get the underlying socket connection timeout.
703 * @return timeout (in ms)
704 * @since 2.0
705 */
706 public int getConnectTimeout() {
707 return connectTimeout;
708 }
709
710 /**
711 * Get the underlying {@link ServerSocketFactory}
712 * @return The server socket factory
713 * @since 2.2
714 */
715 public ServerSocketFactory getServerSocketFactory() {
716 return _serverSocketFactory_;
717 }
718
719
720 /**
721 * Adds a ProtocolCommandListener.
722 *
723 * @param listener The ProtocolCommandListener to add.
724 * @since 3.0
725 */
726 public void addProtocolCommandListener(ProtocolCommandListener listener) {
727 getCommandSupport().addProtocolCommandListener(listener);
728 }
729
730 /**
731 * Removes a ProtocolCommandListener.
732 *
733 * @param listener The ProtocolCommandListener to remove.
734 * @since 3.0
735 */
736 public void removeProtocolCommandListener(ProtocolCommandListener listener) {
737 getCommandSupport().removeProtocolCommandListener(listener);
738 }
739
740 /**
741 * If there are any listeners, send them the reply details.
742 *
743 * @param replyCode the code extracted from the reply
744 * @param reply the full reply text
745 * @since 3.0
746 */
747 protected void fireReplyReceived(int replyCode, String reply) {
748 if (getCommandSupport().getListenerCount() > 0) {
749 getCommandSupport().fireReplyReceived(replyCode, reply);
750 }
751 }
752
753 /**
754 * If there are any listeners, send them the command details.
755 *
756 * @param command the command name
757 * @param message the complete message, including command name
758 * @since 3.0
759 */
760 protected void fireCommandSent(String command, String message) {
761 if (getCommandSupport().getListenerCount() > 0) {
762 getCommandSupport().fireCommandSent(command, message);
763 }
764 }
765
766 /**
767 * Create the CommandSupport instance if required
768 */
769 protected void createCommandSupport(){
770 __commandSupport = new ProtocolCommandSupport(this);
771 }
772
773 /**
774 * Subclasses can override this if they need to provide their own
775 * instance field for backwards compatibilty.
776 *
777 * @return the CommandSupport instance, may be {@code null}
778 * @since 3.0
779 */
780 protected ProtocolCommandSupport getCommandSupport() {
781 return __commandSupport;
782 }
783
784 /*
785 * N.B. Fields cannot be pulled up into a super-class without breaking binary compatibility,
786 * so the abstract method is needed to pass the instance to the methods which were moved here.
787 */
788 }
789
790