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.IOException;
021    import java.io.InputStream;
022    import java.io.OutputStream;
023    import java.net.InetAddress;
024    import java.net.InetSocketAddress;
025    import java.net.Socket;
026    import java.net.SocketException;
027    import java.net.UnknownHostException;
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 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     */
054    public 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