KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > net > bsd > RExecClient


1 /*
2  * Copyright 2001-2005 The Apache Software Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.commons.net.bsd;
17
18 import java.io.IOException JavaDoc;
19 import java.io.InputStream JavaDoc;
20 import java.net.ServerSocket JavaDoc;
21 import java.net.Socket JavaDoc;
22 import org.apache.commons.net.io.SocketInputStream;
23 import org.apache.commons.net.SocketClient;
24 import java.io.OutputStream JavaDoc;
25
26 /***
27  * RExecClient implements the rexec() facility that first appeared in
28  * 4.2BSD Unix. This class will probably only be of use for connecting
29  * to Unix systems and only when the rexecd daemon is configured to run,
30  * which is a rarity these days because of the security risks involved.
31  * However, rexec() can be very useful for performing administrative tasks
32  * on a network behind a firewall.
33  * <p>
34  * As with virtually all of the client classes in org.apache.commons.net, this
35  * class derives from SocketClient, inheriting its connection methods.
36  * The way to use RExecClient is to first connect
37  * to the server, call the {@link #rexec rexec() } method, and then
38  * fetch the connection's input, output, and optionally error streams.
39  * Interaction with the remote command is controlled entirely through the
40  * I/O streams. Once you have finished processing the streams, you should
41  * invoke {@link #disconnect disconnect() } to clean up properly.
42  * <p>
43  * By default the standard output and standard error streams of the
44  * remote process are transmitted over the same connection, readable
45  * from the input stream returned by
46  * {@link #getInputStream getInputStream() }. However, it is
47  * possible to tell the rexecd daemon to return the standard error
48  * stream over a separate connection, readable from the input stream
49  * returned by {@link #getErrorStream getErrorStream() }. You
50  * can specify that a separate connection should be created for standard
51  * error by setting the boolean <code> separateErrorStream </code>
52  * parameter of {@link #rexec rexec() } to <code> true </code>.
53  * The standard input of the remote process can be written to through
54  * the output stream returned by
55  * {@link #getOutputStream getOutputSream() }.
56  * <p>
57  * <p>
58  * @author Daniel F. Savarese
59  * @see SocketClient
60  * @see RCommandClient
61  * @see RLoginClient
62  ***/

63
64 public class RExecClient extends SocketClient
65 {
66     /***
67      * The default rexec port. Set to 512 in BSD Unix.
68      ***/

69     public static final int DEFAULT_PORT = 512;
70
71     private boolean __remoteVerificationEnabled;
72
73     /***
74      * If a separate error stream is requested, <code>_errorStream_</code>
75      * will point to an InputStream from which the standard error of the
76      * remote process can be read (after a call to rexec()). Otherwise,
77      * <code> _errorStream_ </code> will be null.
78      ***/

79     protected InputStream JavaDoc _errorStream_;
80
81     // This can be overridden in local package to implement port range
82
// limitations of rcmd and rlogin
83
InputStream JavaDoc _createErrorStream() throws IOException JavaDoc
84     {
85         ServerSocket JavaDoc server;
86         Socket JavaDoc socket;
87
88         server = _socketFactory_.createServerSocket(0, 1, getLocalAddress());
89
90         _output_.write(Integer.toString(server.getLocalPort()).getBytes());
91         _output_.write('\0');
92         _output_.flush();
93
94         socket = server.accept();
95         server.close();
96
97         if (__remoteVerificationEnabled && !verifyRemote(socket))
98         {
99             socket.close();
100             throw new IOException JavaDoc(
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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc username, String JavaDoc password,
197                       String JavaDoc command, boolean separateErrorStream)
198     throws IOException JavaDoc
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 JavaDoc buffer = new StringBuffer JavaDoc();
223
224             while ((ch = _input_.read()) != -1 && ch != '\n')
225                 buffer.append((char)ch);
226
227             throw new IOException JavaDoc(buffer.toString());
228         }
229         else if (ch < 0)
230         {
231             throw new IOException JavaDoc("Server closed connection.");
232         }
233     }
234
235
236     /***
237      * Same as <code> rexec(username, password, command, false); </code>
238      ***/

239     public void rexec(String JavaDoc username, String JavaDoc password,
240                       String JavaDoc command)
241     throws IOException JavaDoc
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 JavaDoc
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
289
Popular Tags