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