    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.bsd;
    019
    020import java.io.IOException;
    021import java.io.InputStream;
    022import java.io.OutputStream;
    023import java.net.ServerSocket;
    024import java.net.Socket;
    025
    026import org.apache.commons.net.SocketClient;
    027import 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
    066public class RExecClient extends SocketClient
    067{
    068    /**
    069     * @since 3.3
    070     */
    071    protected static final char NULL_CHAR = '\0';
    072
    073    /***
    074     * The default rexec port.  Set to 512 in BSD Unix.
    075     ***/
    076    public static final int DEFAULT_PORT = 512;
    077
    078    private boolean __remoteVerificationEnabled;
    079
    080    /***
    081     * If a separate error stream is requested, <code>_errorStream_</code>
    082     * will point to an InputStream from which the standard error of the
    083     * remote process can be read (after a call to rexec()).  Otherwise,
    084     * <code> _errorStream_ </code> will be null.
    085     ***/
    086    protected InputStream _errorStream_;
    087
    088    // This can be overridden in local package to implement port range
    089    // limitations of rcmd and rlogin
    090    InputStream _createErrorStream() throws IOException
    091    {
    092        ServerSocket server;
    093        Socket socket;
    094
    095        server = _serverSocketFactory_.createServerSocket(0, 1, getLocalAddress());
    096
    097        _output_.write(Integer.toString(server.getLocalPort()).getBytes("UTF-8")); // $NON-NLS-1$
    098        _output_.write(NULL_CHAR);
    099        _output_.flush();
    100
    101        socket = server.accept();
    102        server.close();
    103
    104        if (__remoteVerificationEnabled && !verifyRemote(socket))
    105        {
    106            socket.close();
    107            throw new IOException(
    108                "Security violation: unexpected connection attempt by " +
    109                socket.getInetAddress().getHostAddress());
    110        }
    111
    112        return (new SocketInputStream(socket, socket.getInputStream()));
    113    }
    114
    115
    116    /***
    117     * The default RExecClient constructor.  Initializes the
    118     * default port to <code> DEFAULT_PORT </code>.
    119     ***/
    120    public RExecClient()
    121    {
    122        _errorStream_ = null;
    123        setDefaultPort(DEFAULT_PORT);
    124    }
    125
    126
    127    /***
    128     * Returns the InputStream from which the standard outputof the remote
    129     * process can be read.  The input stream will only be set after a
    130     * successful rexec() invocation.
    131     * <p>
    132     * @return The InputStream from which the standard output of the remote
    133     * process can be read.
    134     ***/
    135    public InputStream getInputStream()
    136    {
    137        return _input_;
    138    }
    139
    140
    141    /***
    142     * Returns the OutputStream through which the standard input of the remote
    143     * process can be written.  The output stream will only be set after a
    144     * successful rexec() invocation.
    145     * <p>
    146     * @return The OutputStream through which the standard input of the remote
    147     * process can be written.
    148     ***/
    149    public OutputStream getOutputStream()
    150    {
    151        return _output_;
    152    }
    153
    154
    155    /***
    156     * Returns 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.  The error stream
    159     * will only be set after a successful rexec() invocation.
    160     * <p>
    161     * @return The InputStream from which the standard error of the remote
    162     * process can be read if a separate error stream is requested from
    163     * the server.  Otherwise, null will be returned.
    164     ***/
    165    public InputStream getErrorStream()
    166    {
    167        return _errorStream_;
    168    }
    169
    170
    171    /***
    172     * Remotely executes a command through the rexecd daemon on the server
    173     * to which the RExecClient is connected.  After calling this method,
    174     * you may interact with the remote process through its standard input,
    175     * output, and error streams.  You will typically be able to detect
    176     * the termination of the remote process after reaching end of file
    177     * on its standard output (accessible through
    178     * {@link #getInputStream  getInputStream() }.    Disconnecting
    179     * from the server or closing the process streams before reaching
    180     * end of file will not necessarily terminate the remote process.
    181     * <p>
    182     * If a separate error stream is requested, the remote server will
    183     * connect to a local socket opened by RExecClient, providing an
    184     * independent stream through which standard error will be transmitted.
    185     * RExecClient will do a simple security check when it accepts a
    186     * connection for this error stream.  If the connection does not originate
    187     * from the remote server, an IOException will be thrown.  This serves as
    188     * a simple protection against possible hijacking of the error stream by
    189     * an attacker monitoring the rexec() negotiation.  You may disable this
    190     * behavior with {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}
    191     * .
    192     * <p>
    193     * @param username  The account name on the server through which to execute
    194     *                  the command.
    195     * @param password  The plain text password of the user account.
    196     * @param command   The command, including any arguments, to execute.
    197     * @param separateErrorStream True if you would like the standard error
    198     *        to be transmitted through a different stream than standard output.
    199     *        False if not.
    200     * @exception IOException If the rexec() attempt fails.  The exception
    201     *            will contain a message indicating the nature of the failure.
    202     ***/
    203    public void rexec(String username, String password,
    204                      String command, boolean separateErrorStream)
    205    throws IOException
    206    {
    207        int ch;
    208
    209        if (separateErrorStream)
    210        {
    211            _errorStream_ = _createErrorStream();
    212        }
    213        else
    214        {
    215            _output_.write(NULL_CHAR);
    216        }
    217
    218        _output_.write(username.getBytes(getCharsetName())); // Java 1.6 can use getCharset()
    219        _output_.write(NULL_CHAR);
    220        _output_.write(password.getBytes(getCharsetName())); // Java 1.6 can use getCharset()
    221        _output_.write(NULL_CHAR);
    222        _output_.write(command.getBytes(getCharsetName())); // Java 1.6 can use getCharset()
    223        _output_.write(NULL_CHAR);
    224        _output_.flush();
    225
    226        ch = _input_.read();
    227        if (ch > 0) {
    228            StringBuilder buffer = new StringBuilder();
    229
    230            while ((ch = _input_.read()) != -1 && ch != '\n') {
    231                buffer.append((char)ch);
    232            }
    233
    234            throw new IOException(buffer.toString());
    235        } else if (ch < 0) {
    236            throw new IOException("Server closed connection.");
    237        }
    238    }
    239
    240
    241    /***
    242     * Same as <code> rexec(username, password, command, false); </code>
    243     ***/
    244    public void rexec(String username, String password,
    245                      String command)
    246    throws IOException
    247    {
    248        rexec(username, password, command, false);
    249    }
    250
    251    /***
    252     * Disconnects from the server, closing all associated open sockets and
    253     * streams.
    254     * <p>
    255     * @exception IOException If there an error occurs while disconnecting.
    256     ***/
    257    @Override
    258    public void disconnect() throws IOException
    259    {
    260        if (_errorStream_ != null) {
    261            _errorStream_.close();
    262        }
    263        _errorStream_ = null;
    264        super.disconnect();
    265    }
    266
    267
    268    /***
    269     * Enable or disable verification that the remote host connecting to
    270     * create a separate error stream is the same as the host to which
    271     * the standard out stream is connected.  The default is for verification
    272     * to be enabled.  You may set this value at any time, whether the
    273     * client is currently connected or not.
    274     * <p>
    275     * @param enable True to enable verification, false to disable verification.
    276     ***/
    277    public final void setRemoteVerificationEnabled(boolean enable)
    278    {
    279        __remoteVerificationEnabled = enable;
    280    }
    281
    282    /***
    283     * Return whether or not verification of the remote host providing a
    284     * separate error stream is enabled.  The default behavior is for
    285     * verification to be enabled.
    286     * <p>
    287     * @return True if verification is enabled, false if not.
    288     ***/
    289    public final boolean isRemoteVerificationEnabled()
    290    {
    291        return __remoteVerificationEnabled;
    292    }
    293
    294}
    295