001    /*
002     * Copyright 2001-2005 The Apache Software Foundation
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *     http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.apache.commons.net.bsd;
017    
018    import java.io.IOException;
019    import java.io.InputStream;
020    import java.net.ServerSocket;
021    import java.net.Socket;
022    import org.apache.commons.net.io.SocketInputStream;
023    import org.apache.commons.net.SocketClient;
024    import java.io.OutputStream;
025    
026    /***
027     * RExecClient implements the rexec() facility that first appeared in
028     * 4.2BSD Unix.  This class will probably only be of use for connecting
029     * to Unix systems and only when the rexecd daemon is configured to run,
030     * which is a rarity these days because of the security risks involved.
031     * However, rexec() can be very useful for performing administrative tasks
032     * on a network behind a firewall.
033     * <p>
034     * As with virtually all of the client classes in org.apache.commons.net, this
035     * class derives from SocketClient, inheriting its connection methods.
036     * The way to use RExecClient is to first connect
037     * to the server, call the {@link #rexec  rexec() } method, and then
038     * fetch the connection's input, output, and optionally error streams.
039     * Interaction with the remote command is controlled entirely through the
040     * I/O streams.  Once you have finished processing the streams, you should
041     * invoke {@link #disconnect  disconnect() } to clean up properly.
042     * <p>
043     * By default the standard output and standard error streams of the
044     * remote process are transmitted over the same connection, readable
045     * from the input stream returned by
046     * {@link #getInputStream  getInputStream() }.  However, it is
047     * possible to tell the rexecd daemon to return the standard error
048     * stream over a separate connection, readable from the input stream
049     * returned by {@link #getErrorStream  getErrorStream() }.  You
050     * can specify that a separate connection should be created for standard
051     * error by setting the boolean <code> separateErrorStream </code>
052     * parameter of {@link #rexec  rexec() } to <code> true </code>.
053     * The standard input of the remote process can be written to through
054     * the output stream returned by
055     * {@link #getOutputStream  getOutputSream() }.
056     * <p>
057     * <p>
058     * @author Daniel F. Savarese
059     * @see SocketClient
060     * @see RCommandClient
061     * @see RLoginClient
062     ***/
063    
064    public class RExecClient extends SocketClient
065    {
066        /***
067         * The default rexec port.  Set to 512 in BSD Unix.
068         ***/
069        public static final int DEFAULT_PORT = 512;
070    
071        private boolean __remoteVerificationEnabled;
072    
073        /***
074         * If a separate error stream is requested, <code>_errorStream_</code>
075         * will point to an InputStream from which the standard error of the
076         * remote process can be read (after a call to rexec()).  Otherwise,
077         * <code> _errorStream_ </code> will be null.
078         ***/
079        protected InputStream _errorStream_;
080    
081        // This can be overridden in local package to implement port range
082        // limitations of rcmd and rlogin
083        InputStream _createErrorStream() throws IOException
084        {
085            ServerSocket server;
086            Socket socket;
087    
088            server = _socketFactory_.createServerSocket(0, 1, getLocalAddress());
089    
090            _output_.write(Integer.toString(server.getLocalPort()).getBytes());
091            _output_.write('\0');
092            _output_.flush();
093    
094            socket = server.accept();
095            server.close();
096    
097            if (__remoteVerificationEnabled && !verifyRemote(socket))
098            {
099                socket.close();
100                throw new IOException(
101                    "Security violation: unexpected connection attempt by " +
102                    socket.getInetAddress().getHostAddress());
103            }
104    
105            return (new SocketInputStream(socket, socket.getInputStream()));
106        }
107    
108    
109        /***
110         * The default RExecClient constructor.  Initializes the
111         * default port to <code> DEFAULT_PORT </code>.
112         ***/
113        public RExecClient()
114        {
115            _errorStream_ = null;
116            setDefaultPort(DEFAULT_PORT);
117        }
118    
119    
120        /***
121         * Returns the InputStream from which the standard outputof the remote
122         * process can be read.  The input stream will only be set after a
123         * successful rexec() invocation.
124         * <p>
125         * @return The InputStream from which the standard output of the remote
126         * process can be read.
127         ***/
128        public InputStream getInputStream()
129        {
130            return _input_;
131        }
132    
133    
134        /***
135         * Returns the OutputStream through which the standard input of the remote
136         * process can be written.  The output stream will only be set after a
137         * successful rexec() invocation.
138         * <p>
139         * @return The OutputStream through which the standard input of the remote
140         * process can be written.
141         ***/
142        public OutputStream getOutputStream()
143        {
144            return _output_;
145        }
146    
147    
148        /***
149         * Returns the InputStream from which the standard error of the remote
150         * process can be read if a separate error stream is requested from
151         * the server.  Otherwise, null will be returned.  The error stream
152         * will only be set after a successful rexec() invocation.
153         * <p>
154         * @return The InputStream from which the standard error of the remote
155         * process can be read if a separate error stream is requested from
156         * the server.  Otherwise, null will be returned.
157         ***/
158        public InputStream getErrorStream()
159        {
160            return _errorStream_;
161        }
162    
163    
164        /***
165         * Remotely executes a command through the rexecd daemon on the server
166         * to which the RExecClient is connected.  After calling this method,
167         * you may interact with the remote process through its standard input,
168         * output, and error streams.  You will typically be able to detect
169         * the termination of the remote process after reaching end of file
170         * on its standard output (accessible through
171         * {@link #getInputStream  getInputStream() }.    Disconnecting
172         * from the server or closing the process streams before reaching
173         * end of file will not necessarily terminate the remote process.
174         * <p>
175         * If a separate error stream is requested, the remote server will
176         * connect to a local socket opened by RExecClient, providing an
177         * independent stream through which standard error will be transmitted.
178         * RExecClient will do a simple security check when it accepts a
179         * connection for this error stream.  If the connection does not originate
180         * from the remote server, an IOException will be thrown.  This serves as
181         * a simple protection against possible hijacking of the error stream by
182         * an attacker monitoring the rexec() negotiation.  You may disable this
183         * behavior with {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}
184         * .
185         * <p>
186         * @param username  The account name on the server through which to execute
187         *                  the command.
188         * @param password  The plain text password of the user account.
189         * @param command   The command, including any arguments, to execute.
190         * @param separateErrorStream True if you would like the standard error
191         *        to be transmitted through a different stream than standard output.
192         *        False if not.
193         * @exception IOException If the rexec() attempt fails.  The exception
194         *            will contain a message indicating the nature of the failure.
195         ***/
196        public void rexec(String username, String password,
197                          String command, boolean separateErrorStream)
198        throws IOException
199        {
200            int ch;
201    
202            if (separateErrorStream)
203            {
204                _errorStream_ = _createErrorStream();
205            }
206            else
207            {
208                _output_.write('\0');
209            }
210    
211            _output_.write(username.getBytes());
212            _output_.write('\0');
213            _output_.write(password.getBytes());
214            _output_.write('\0');
215            _output_.write(command.getBytes());
216            _output_.write('\0');
217            _output_.flush();
218    
219            ch = _input_.read();
220            if (ch > 0)
221            {
222                StringBuffer buffer = new StringBuffer();
223    
224                while ((ch = _input_.read()) != -1 && ch != '\n')
225                    buffer.append((char)ch);
226    
227                throw new IOException(buffer.toString());
228            }
229            else if (ch < 0)
230            {
231                throw new IOException("Server closed connection.");
232            }
233        }
234    
235    
236        /***
237         * Same as <code> rexec(username, password, command, false); </code>
238         ***/
239        public void rexec(String username, String password,
240                          String command)
241        throws IOException
242        {
243            rexec(username, password, command, false);
244        }
245    
246        /***
247         * Disconnects from the server, closing all associated open sockets and
248         * streams.
249         * <p>
250         * @exception IOException If there an error occurs while disconnecting.
251         ***/
252        public void disconnect() throws IOException
253        {
254            if (_errorStream_ != null)
255                _errorStream_.close();
256            _errorStream_ = null;
257            super.disconnect();
258        }
259    
260    
261        /***
262         * Enable or disable verification that the remote host connecting to
263         * create a separate error stream is the same as the host to which
264         * the standard out stream is connected.  The default is for verification
265         * to be enabled.  You may set this value at any time, whether the
266         * client is currently connected or not.
267         * <p>
268         * @param enable True to enable verification, false to disable verification.
269         ***/
270        public final void setRemoteVerificationEnabled(boolean enable)
271        {
272            __remoteVerificationEnabled = enable;
273        }
274    
275        /***
276         * Return whether or not verification of the remote host providing a
277         * separate error stream is enabled.  The default behavior is for
278         * verification to be enabled.
279         * <p>
280         * @return True if verification is enabled, false if not.
281         ***/
282        public final boolean isRemoteVerificationEnabled()
283        {
284            return __remoteVerificationEnabled;
285        }
286    
287    }
288