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;
019
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.OutputStream;
023import java.net.InetAddress;
024import java.net.InetSocketAddress;
025import java.net.Socket;
026import java.net.SocketException;
027import java.net.UnknownHostException;
028
029import javax.net.ServerSocketFactory;
030import 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 org.apache.commons.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 instanting
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 * @author Daniel F. Savarese
052 * @see SocketFactory
053 */
054public abstract class SocketClient
055{
056    /**
057     * The end of line character sequence used by most IETF protocols.  That
058     * is a carriage return followed by a newline: "\r\n"
059     */
060    public static final String NETASCII_EOL = "\r\n";
061
062    /** The default SocketFactory shared by all SocketClient instances. */
063    private static final SocketFactory __DEFAULT_SOCKET_FACTORY =
064            SocketFactory.getDefault();
065    
066    private static final ServerSocketFactory __DEFAULT_SERVER_SOCKET_FACTORY = 
067            ServerSocketFactory.getDefault();
068
069    /** The timeout to use after opening a socket. */
070    protected int _timeout_;
071
072    /** The socket used for the connection. */
073    protected Socket _socket_;
074
075    /** The default port the client should connect to. */
076    protected int _defaultPort_;
077
078    /** The socket's InputStream. */
079    protected InputStream _input_;
080
081    /** The socket's OutputStream. */
082    protected OutputStream _output_;
083
084    /** The socket's SocketFactory. */
085    protected SocketFactory _socketFactory_;
086    
087    /** The socket's ServerSocket Factory. */
088    protected ServerSocketFactory _serverSocketFactory_;
089    
090    /** The socket's connect timeout (0 = infinite timeout) */
091    private static final int DEFAULT_CONNECT_TIMEOUT = 0;
092    protected int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
093
094    /**
095     * Default constructor for SocketClient.  Initializes
096     * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
097     * _isConnected_ to false, and _socketFactory_ to a shared instance of
098     * {@link org.apache.commons.net.DefaultSocketFactory}.
099     */
100    public SocketClient()
101    {
102        _socket_ = null;
103        _input_ = null;
104        _output_ = null;
105        _timeout_ = 0;
106        _defaultPort_ = 0;
107        _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
108        _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
109    }
110
111
112    /**
113     * Because there are so many connect() methods, the _connectAction_()
114     * method is provided as a means of performing some action immediately
115     * after establishing a connection, rather than reimplementing all
116     * of the connect() methods.  The last action performed by every
117     * connect() method after opening a socket is to call this method.
118     * <p>
119     * This method sets the timeout on the just opened socket to the default
120     * timeout set by {@link #setDefaultTimeout  setDefaultTimeout() },
121     * sets _input_ and _output_ to the socket's InputStream and OutputStream
122     * respectively, and sets _isConnected_ to true.
123     * <p>
124     * Subclasses overriding this method should start by calling
125     * <code> super._connectAction_() </code> first to ensure the
126     * initialization of the aforementioned protected variables.
127     */
128    protected void _connectAction_() throws IOException
129    {
130        _socket_.setSoTimeout(_timeout_);
131        _input_ = _socket_.getInputStream();
132        _output_ = _socket_.getOutputStream();
133    }
134
135
136    /**
137     * Opens a Socket connected to a remote host at the specified port and
138     * originating from the current host at a system assigned port.
139     * Before returning, {@link #_connectAction_  _connectAction_() }
140     * is called to perform connection initialization actions.
141     * <p>
142     * @param host  The remote host.
143     * @param port  The port to connect to on the remote host.
144     * @exception SocketException If the socket timeout could not be set.
145     * @exception IOException If the socket could not be opened.  In most
146     *  cases you will only want to catch IOException since SocketException is
147     *  derived from it.
148     */
149    public void connect(InetAddress host, int port)
150    throws SocketException, IOException
151    {
152        _socket_ = _socketFactory_.createSocket();
153        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
154
155        _connectAction_();
156    }
157
158    /**
159     * Opens a Socket connected to a remote host at the specified port and
160     * originating from the current host at a system assigned port.
161     * Before returning, {@link #_connectAction_  _connectAction_() }
162     * is called to perform connection initialization actions.
163     * <p>
164     * @param hostname  The name of the remote host.
165     * @param port  The port to connect to on the remote host.
166     * @exception SocketException If the socket timeout could not be set.
167     * @exception IOException If the socket could not be opened.  In most
168     *  cases you will only want to catch IOException since SocketException is
169     *  derived from it.
170     * @exception UnknownHostException If the hostname cannot be resolved.
171     */
172    public void connect(String hostname, int port)
173    throws SocketException, IOException
174    {
175        _socket_= _socketFactory_.createSocket();
176        _socket_.connect(new InetSocketAddress(hostname, port), connectTimeout);
177        
178        _connectAction_();
179    }
180
181
182    /**
183     * Opens a Socket connected to a remote host at the specified port and
184     * originating from the specified local address and port.
185     * Before returning, {@link #_connectAction_  _connectAction_() }
186     * is called to perform connection initialization actions.
187     * <p>
188     * @param host  The remote host.
189     * @param port  The port to connect to on the remote host.
190     * @param localAddr  The local address to use.
191     * @param localPort  The local port to use.
192     * @exception SocketException If the socket timeout could not be set.
193     * @exception IOException If the socket could not be opened.  In most
194     *  cases you will only want to catch IOException since SocketException is
195     *  derived from it.
196     */
197    public void connect(InetAddress host, int port,
198                        InetAddress localAddr, int localPort)
199    throws SocketException, IOException
200    {
201        _socket_ = _socketFactory_.createSocket();
202        _socket_.bind(new InetSocketAddress(localAddr, localPort));
203        _socket_.connect(new InetSocketAddress(host, port), connectTimeout);
204        
205        _connectAction_();
206    }
207
208
209    /**
210     * Opens a Socket connected to a remote host at the specified port and
211     * originating from the specified local address and port.
212     * Before returning, {@link #_connectAction_  _connectAction_() }
213     * is called to perform connection initialization actions.
214     * <p>
215     * @param hostname  The name of the remote host.
216     * @param port  The port to connect to on the remote host.
217     * @param localAddr  The local address to use.
218     * @param localPort  The local port to use.
219     * @exception SocketException If the socket timeout could not be set.
220     * @exception IOException If the socket could not be opened.  In most
221     *  cases you will only want to catch IOException since SocketException is
222     *  derived from it.
223     * @exception UnknownHostException If the hostname cannot be resolved.
224     */
225    public void connect(String hostname, int port,
226                        InetAddress localAddr, int localPort)
227    throws SocketException, IOException
228    {
229        _socket_ =
230            _socketFactory_.createSocket(hostname, port, localAddr, localPort);
231        _connectAction_();
232    }
233
234
235    /**
236     * Opens a Socket connected to a remote host at the current default port
237     * and originating from the current host at a system assigned port.
238     * Before returning, {@link #_connectAction_  _connectAction_() }
239     * is called to perform connection initialization actions.
240     * <p>
241     * @param host  The remote host.
242     * @exception SocketException If the socket timeout could not be set.
243     * @exception IOException If the socket could not be opened.  In most
244     *  cases you will only want to catch IOException since SocketException is
245     *  derived from it.
246     */
247    public void connect(InetAddress host) throws SocketException, IOException
248    {
249        connect(host, _defaultPort_);
250    }
251
252
253    /**
254     * Opens a Socket connected to a remote host at the current default
255     * port and originating from the current host at a system assigned port.
256     * Before returning, {@link #_connectAction_  _connectAction_() }
257     * is called to perform connection initialization actions.
258     * <p>
259     * @param hostname  The name of the remote host.
260     * @exception SocketException If the socket timeout could not be set.
261     * @exception IOException If the socket could not be opened.  In most
262     *  cases you will only want to catch IOException since SocketException is
263     *  derived from it.
264     * @exception UnknownHostException If the hostname cannot be resolved.
265     */
266    public void connect(String hostname) throws SocketException, IOException
267    {
268        connect(hostname, _defaultPort_);
269    }
270
271
272    /**
273     * Disconnects the socket connection.
274     * You should call this method after you've finished using the class
275     * instance and also before you call
276     * {@link #connect connect() }
277     * again.  _isConnected_ is set to false, _socket_ is set to null,
278     * _input_ is set to null, and _output_ is set to null.
279     * <p>
280     * @exception IOException  If there is an error closing the socket.
281     */
282    public void disconnect() throws IOException
283    {
284        if (_socket_ != null) _socket_.close();
285        if (_input_ != null) _input_.close();
286        if (_output_ != null) _output_.close();
287        if (_socket_ != null) _socket_ = null;
288        _input_ = null;
289        _output_ = null;
290    }
291
292
293    /**
294     * Returns true if the client is currently connected to a server.
295     * <p>
296     * @return True if the client is currently connected to a server,
297     *         false otherwise.
298     */
299    public boolean isConnected()
300    {
301        if (_socket_ == null)
302            return false;
303        
304        return _socket_.isConnected();
305    }
306
307
308    /**
309     * Sets the default port the SocketClient should connect to when a port
310     * is not specified.  The {@link #_defaultPort_  _defaultPort_ }
311     * variable stores this value.  If never set, the default port is equal
312     * to zero.
313     * <p>
314     * @param port  The default port to set.
315     */
316    public void setDefaultPort(int port)
317    {
318        _defaultPort_ = port;
319    }
320
321    /**
322     * Returns the current value of the default port (stored in
323     * {@link #_defaultPort_  _defaultPort_ }).
324     * <p>
325     * @return The current value of the default port.
326     */
327    public int getDefaultPort()
328    {
329        return _defaultPort_;
330    }
331
332
333    /**
334     * Set the default timeout in milliseconds to use when opening a socket.
335     * This value is only used previous to a call to
336     * {@link #connect connect()}
337     * and should not be confused with {@link #setSoTimeout setSoTimeout()}
338     * which operates on an the currently opened socket.  _timeout_ contains
339     * the new timeout value.
340     * <p>
341     * @param timeout  The timeout in milliseconds to use for the socket
342     *                 connection.
343     */
344    public void setDefaultTimeout(int timeout)
345    {
346        _timeout_ = timeout;
347    }
348
349
350    /**
351     * Returns the default timeout in milliseconds that is used when
352     * opening a socket.
353     * <p>
354     * @return The default timeout in milliseconds that is used when
355     *         opening a socket.
356     */
357    public int getDefaultTimeout()
358    {
359        return _timeout_;
360    }
361
362
363    /**
364     * Set the timeout in milliseconds of a currently open connection.
365     * Only call this method after a connection has been opened
366     * by {@link #connect connect()}.
367     * <p>
368     * @param timeout  The timeout in milliseconds to use for the currently
369     *                 open socket connection.
370     * @exception SocketException If the operation fails.
371     */
372    public void setSoTimeout(int timeout) throws SocketException
373    {
374        _socket_.setSoTimeout(timeout);
375    }
376    
377    
378    /**
379     * Set the underlying socket send buffer size.
380     * <p>
381     * @param size The size of the buffer in bytes.
382     * @throws SocketException 
383     * @since 2.0
384     */
385    public void setSendBufferSize(int size) throws SocketException {
386        _socket_.setSendBufferSize(size);
387    }
388    
389    
390    /**
391     * Sets the underlying socket receive buffer size.
392     * <p>
393     * @param size The size of the buffer in bytes.
394     * @throws SocketException 
395     * @since 2.0
396     */
397    public void setReceiveBufferSize(int size) throws SocketException  {
398        _socket_.setReceiveBufferSize(size);
399    }
400
401
402    /**
403     * Returns the timeout in milliseconds of the currently opened socket.
404     * <p>
405     * @return The timeout in milliseconds of the currently opened socket.
406     * @exception SocketException If the operation fails.
407     */
408    public int getSoTimeout() throws SocketException
409    {
410        return _socket_.getSoTimeout();
411    }
412
413    /**
414     * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
415     * currently opened socket.
416     * <p>
417     * @param on  True if Nagle's algorithm is to be enabled, false if not.
418     * @exception SocketException If the operation fails.
419     */
420    public void setTcpNoDelay(boolean on) throws SocketException
421    {
422        _socket_.setTcpNoDelay(on);
423    }
424
425
426    /**
427     * Returns true if Nagle's algorithm is enabled on the currently opened
428     * socket.
429     * <p>
430     * @return True if Nagle's algorithm is enabled on the currently opened
431     *        socket, false otherwise.
432     * @exception SocketException If the operation fails.
433     */
434    public boolean getTcpNoDelay() throws SocketException
435    {
436        return _socket_.getTcpNoDelay();
437    }
438
439
440    /**
441     * Sets the SO_LINGER timeout on the currently opened socket.
442     * <p>
443     * @param on  True if linger is to be enabled, false if not.
444     * @param val The linger timeout (in hundredths of a second?)
445     * @exception SocketException If the operation fails.
446     */
447    public void setSoLinger(boolean on, int val) throws SocketException
448    {
449        _socket_.setSoLinger(on, val);
450    }
451
452
453    /**
454     * Returns the current SO_LINGER timeout of the currently opened socket.
455     * <p>
456     * @return The current SO_LINGER timeout.  If SO_LINGER is disabled returns
457     *         -1.
458     * @exception SocketException If the operation fails.
459     */
460    public int getSoLinger() throws SocketException
461    {
462        return _socket_.getSoLinger();
463    }
464
465
466    /**
467     * Returns the port number of the open socket on the local host used
468     * for the connection.
469     * <p>
470     * @return The port number of the open socket on the local host used
471     *         for the connection.
472     */
473    public int getLocalPort()
474    {
475        return _socket_.getLocalPort();
476    }
477
478
479    /**
480     * Returns the local address to which the client's socket is bound.
481     * <p>
482     * @return The local address to which the client's socket is bound.
483     */
484    public InetAddress getLocalAddress()
485    {
486        return _socket_.getLocalAddress();
487    }
488
489    /**
490     * Returns the port number of the remote host to which the client is
491     * connected.
492     * <p>
493     * @return The port number of the remote host to which the client is
494     *         connected.
495     */
496    public int getRemotePort()
497    {
498        return _socket_.getPort();
499    }
500
501
502    /**
503     * @return The remote address to which the client is connected.
504     */
505    public InetAddress getRemoteAddress()
506    {
507        return _socket_.getInetAddress();
508    }
509
510
511    /**
512     * Verifies that the remote end of the given socket is connected to the
513     * the same host that the SocketClient is currently connected to.  This
514     * is useful for doing a quick security check when a client needs to
515     * accept a connection from a server, such as an FTP data connection or
516     * a BSD R command standard error stream.
517     * <p>
518     * @return True if the remote hosts are the same, false if not.
519     */
520    public boolean verifyRemote(Socket socket)
521    {
522        InetAddress host1, host2;
523
524        host1 = socket.getInetAddress();
525        host2 = getRemoteAddress();
526
527        return host1.equals(host2);
528    }
529
530
531    /**
532     * Sets the SocketFactory used by the SocketClient to open socket
533     * connections.  If the factory value is null, then a default
534     * factory is used (only do this to reset the factory after having
535     * previously altered it).
536     * <p>
537     * @param factory  The new SocketFactory the SocketClient should use.
538     */
539    public void setSocketFactory(SocketFactory factory)
540    {
541        if (factory == null)
542            _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
543        else
544            _socketFactory_ = factory;
545    }
546    
547    /**
548     * Sets the ServerSocketFactory used by the SocketClient to open ServerSocket
549     * connections.  If the factory value is null, then a default
550     * factory is used (only do this to reset the factory after having
551     * previously altered it).
552     * <p>
553     * @param factory  The new ServerSocketFactory the SocketClient should use.
554     * @since 2.0
555     */
556    public void setServerSocketFactory(ServerSocketFactory factory) {
557        if (factory == null)
558            _serverSocketFactory_ = __DEFAULT_SERVER_SOCKET_FACTORY;
559        else
560            _serverSocketFactory_ = factory;
561    }
562    
563    /**
564     * Sets the connection timeout in milliseconds, which will be passed to the {@link Socket} object's
565     * connect() method. 
566     * @param connectTimeout The connection timeout to use (in ms)
567     * @since 2.0
568     */
569    public void setConnectTimeout(int connectTimeout) {
570        this.connectTimeout = connectTimeout;
571    }
572    
573    /**
574     * Get the underlying socket connection timeout.
575     * @return
576     * @since 2.0
577     */
578    public int getConnectTimeout() {
579        return connectTimeout;
580    }
581    
582    
583    
584}
585
586