1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.commons.net.bsd;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.OutputStream;
23 import java.net.ServerSocket;
24 import java.net.Socket;
25
26 import org.apache.commons.net.SocketClient;
27 import org.apache.commons.net.io.SocketInputStream;
28
29 /***
30 * RExecClient implements the rexec() facility that first appeared in
31 * 4.2BSD Unix. This class will probably only be of use for connecting
32 * to Unix systems and only when the rexecd daemon is configured to run,
33 * which is a rarity these days because of the security risks involved.
34 * However, rexec() can be very useful for performing administrative tasks
35 * on a network behind a firewall.
36 * <p>
37 * As with virtually all of the client classes in org.apache.commons.net, this
38 * class derives from SocketClient, inheriting its connection methods.
39 * The way to use RExecClient is to first connect
40 * to the server, call the {@link #rexec rexec() } method, and then
41 * fetch the connection's input, output, and optionally error streams.
42 * Interaction with the remote command is controlled entirely through the
43 * I/O streams. Once you have finished processing the streams, you should
44 * invoke {@link #disconnect disconnect() } to clean up properly.
45 * <p>
46 * By default the standard output and standard error streams of the
47 * remote process are transmitted over the same connection, readable
48 * from the input stream returned by
49 * {@link #getInputStream getInputStream() }. However, it is
50 * possible to tell the rexecd daemon to return the standard error
51 * stream over a separate connection, readable from the input stream
52 * returned by {@link #getErrorStream getErrorStream() }. You
53 * can specify that a separate connection should be created for standard
54 * error by setting the boolean <code> separateErrorStream </code>
55 * parameter of {@link #rexec rexec() } to <code> true </code>.
56 * The standard input of the remote process can be written to through
57 * the output stream returned by
58 * {@link #getOutputStream getOutputSream() }.
59 * <p>
60 * <p>
61 * @author Daniel F. Savarese
62 * @see SocketClient
63 * @see RCommandClient
64 * @see RLoginClient
65 ***/
66
67 public class RExecClient extends SocketClient
68 {
69 /***
70 * The default rexec port. Set to 512 in BSD Unix.
71 ***/
72 public static final int DEFAULT_PORT = 512;
73
74 private boolean __remoteVerificationEnabled;
75
76 /***
77 * If a separate error stream is requested, <code>_errorStream_</code>
78 * will point to an InputStream from which the standard error of the
79 * remote process can be read (after a call to rexec()). Otherwise,
80 * <code> _errorStream_ </code> will be null.
81 ***/
82 protected InputStream _errorStream_;
83
84 // This can be overridden in local package to implement port range
85 // limitations of rcmd and rlogin
86 InputStream _createErrorStream() throws IOException
87 {
88 ServerSocket server;
89 Socket socket;
90
91 server = _serverSocketFactory_.createServerSocket(0, 1, getLocalAddress());
92
93 _output_.write(Integer.toString(server.getLocalPort()).getBytes());
94 _output_.write('\0');
95 _output_.flush();
96
97 socket = server.accept();
98 server.close();
99
100 if (__remoteVerificationEnabled && !verifyRemote(socket))
101 {
102 socket.close();
103 throw new IOException(
104 "Security violation: unexpected connection attempt by " +
105 socket.getInetAddress().getHostAddress());
106 }
107
108 return (new SocketInputStream(socket, socket.getInputStream()));
109 }
110
111
112 /***
113 * The default RExecClient constructor. Initializes the
114 * default port to <code> DEFAULT_PORT </code>.
115 ***/
116 public RExecClient()
117 {
118 _errorStream_ = null;
119 setDefaultPort(DEFAULT_PORT);
120 }
121
122
123 /***
124 * Returns the InputStream from which the standard outputof the remote
125 * process can be read. The input stream will only be set after a
126 * successful rexec() invocation.
127 * <p>
128 * @return The InputStream from which the standard output of the remote
129 * process can be read.
130 ***/
131 public InputStream getInputStream()
132 {
133 return _input_;
134 }
135
136
137 /***
138 * Returns the OutputStream through which the standard input of the remote
139 * process can be written. The output stream will only be set after a
140 * successful rexec() invocation.
141 * <p>
142 * @return The OutputStream through which the standard input of the remote
143 * process can be written.
144 ***/
145 public OutputStream getOutputStream()
146 {
147 return _output_;
148 }
149
150
151 /***
152 * Returns the InputStream from which the standard error of the remote
153 * process can be read if a separate error stream is requested from
154 * the server. Otherwise, null will be returned. The error stream
155 * will only be set after a successful rexec() invocation.
156 * <p>
157 * @return The InputStream from which the standard error of the remote
158 * process can be read if a separate error stream is requested from
159 * the server. Otherwise, null will be returned.
160 ***/
161 public InputStream getErrorStream()
162 {
163 return _errorStream_;
164 }
165
166
167 /***
168 * Remotely executes a command through the rexecd daemon on the server
169 * to which the RExecClient is connected. After calling this method,
170 * you may interact with the remote process through its standard input,
171 * output, and error streams. You will typically be able to detect
172 * the termination of the remote process after reaching end of file
173 * on its standard output (accessible through
174 * {@link #getInputStream getInputStream() }. Disconnecting
175 * from the server or closing the process streams before reaching
176 * end of file will not necessarily terminate the remote process.
177 * <p>
178 * If a separate error stream is requested, the remote server will
179 * connect to a local socket opened by RExecClient, providing an
180 * independent stream through which standard error will be transmitted.
181 * RExecClient will do a simple security check when it accepts a
182 * connection for this error stream. If the connection does not originate
183 * from the remote server, an IOException will be thrown. This serves as
184 * a simple protection against possible hijacking of the error stream by
185 * an attacker monitoring the rexec() negotiation. You may disable this
186 * behavior with {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()}
187 * .
188 * <p>
189 * @param username The account name on the server through which to execute
190 * the command.
191 * @param password The plain text password of the user account.
192 * @param command The command, including any arguments, to execute.
193 * @param separateErrorStream True if you would like the standard error
194 * to be transmitted through a different stream than standard output.
195 * False if not.
196 * @exception IOException If the rexec() attempt fails. The exception
197 * will contain a message indicating the nature of the failure.
198 ***/
199 public void rexec(String username, String password,
200 String command, boolean separateErrorStream)
201 throws IOException
202 {
203 int ch;
204
205 if (separateErrorStream)
206 {
207 _errorStream_ = _createErrorStream();
208 }
209 else
210 {
211 _output_.write('\0');
212 }
213
214 _output_.write(username.getBytes());
215 _output_.write('\0');
216 _output_.write(password.getBytes());
217 _output_.write('\0');
218 _output_.write(command.getBytes());
219 _output_.write('\0');
220 _output_.flush();
221
222 ch = _input_.read();
223 if (ch > 0)
224 {
225 StringBuffer buffer = new StringBuffer();
226
227 while ((ch = _input_.read()) != -1 && ch != '\n')
228 buffer.append((char)ch);
229
230 throw new IOException(buffer.toString());
231 }
232 else if (ch < 0)
233 {
234 throw new IOException("Server closed connection.");
235 }
236 }
237
238
239 /***
240 * Same as <code> rexec(username, password, command, false); </code>
241 ***/
242 public void rexec(String username, String password,
243 String command)
244 throws IOException
245 {
246 rexec(username, password, command, false);
247 }
248
249 /***
250 * Disconnects from the server, closing all associated open sockets and
251 * streams.
252 * <p>
253 * @exception IOException If there an error occurs while disconnecting.
254 ***/
255 @Override
256 public void disconnect() throws IOException
257 {
258 if (_errorStream_ != null)
259 _errorStream_.close();
260 _errorStream_ = null;
261 super.disconnect();
262 }
263
264
265 /***
266 * Enable or disable verification that the remote host connecting to
267 * create a separate error stream is the same as the host to which
268 * the standard out stream is connected. The default is for verification
269 * to be enabled. You may set this value at any time, whether the
270 * client is currently connected or not.
271 * <p>
272 * @param enable True to enable verification, false to disable verification.
273 ***/
274 public final void setRemoteVerificationEnabled(boolean enable)
275 {
276 __remoteVerificationEnabled = enable;
277 }
278
279 /***
280 * Return whether or not verification of the remote host providing a
281 * separate error stream is enabled. The default behavior is for
282 * verification to be enabled.
283 * <p>
284 * @return True if verification is enabled, false if not.
285 ***/
286 public final boolean isRemoteVerificationEnabled()
287 {
288 return __remoteVerificationEnabled;
289 }
290
291 }
292