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