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