%line | %branch | |||||||||
---|---|---|---|---|---|---|---|---|---|---|
org.apache.commons.net.bsd.RCommandClient |
|
|
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; |
|
19 | import java.io.InputStream; |
|
20 | import java.net.BindException; |
|
21 | import java.net.InetAddress; |
|
22 | import java.net.ServerSocket; |
|
23 | import java.net.Socket; |
|
24 | import java.net.SocketException; |
|
25 | ||
26 | import org.apache.commons.net.io.SocketInputStream; |
|
27 | ||
28 | /*** |
|
29 | * RCommandClient is very similar to |
|
30 | * {@link org.apache.commons.net.bsd.RExecClient}, |
|
31 | * from which it is derived, and implements the rcmd() facility that |
|
32 | * first appeared in 4.2BSD Unix. rcmd() is the facility used by the rsh |
|
33 | * (rshell) and other commands to execute a command on another machine |
|
34 | * from a trusted host without issuing a password. The trust relationship |
|
35 | * between two machines is established by the contents of a machine's |
|
36 | * /etc/hosts.equiv file and a user's .rhosts file. These files specify |
|
37 | * from which hosts and accounts on those hosts rcmd() requests will be |
|
38 | * accepted. The only additional measure for establishing trust is that |
|
39 | * all client connections must originate from a port between 512 and 1023. |
|
40 | * Consequently, there is an upper limit to the number of rcmd connections |
|
41 | * that can be running simultaneously. The required ports are reserved |
|
42 | * ports on Unix systems, and can only be bound by a |
|
43 | * process running with root permissions (to accomplish this rsh, rlogin, |
|
44 | * and related commands usualy have the suid bit set). Therefore, on a |
|
45 | * Unix system, you will only be able to successfully use the RCommandClient |
|
46 | * class if the process runs as root. However, there is no such restriction |
|
47 | * on Windows95 and some other systems. The security risks are obvious. |
|
48 | * However, when carefully used, rcmd() can be very useful when used behind |
|
49 | * a firewall. |
|
50 | * <p> |
|
51 | * As with virtually all of the client classes in org.apache.commons.net, this |
|
52 | * class derives from SocketClient. But it overrides most of its connection |
|
53 | * methods so that the local Socket will originate from an acceptable |
|
54 | * rshell port. The way to use RCommandClient is to first connect |
|
55 | * to the server, call the {@link #rcommand rcommand() } method, |
|
56 | * and then |
|
57 | * fetch the connection's input, output, and optionally error streams. |
|
58 | * Interaction with the remote command is controlled entirely through the |
|
59 | * I/O streams. Once you have finished processing the streams, you should |
|
60 | * invoke {@link org.apache.commons.net.bsd.RExecClient#disconnect disconnect() } |
|
61 | * to clean up properly. |
|
62 | * <p> |
|
63 | * By default the standard output and standard error streams of the |
|
64 | * remote process are transmitted over the same connection, readable |
|
65 | * from the input stream returned by |
|
66 | * {@link org.apache.commons.net.bsd.RExecClient#getInputStream getInputStream() } |
|
67 | * . However, it is |
|
68 | * possible to tell the rshd daemon to return the standard error |
|
69 | * stream over a separate connection, readable from the input stream |
|
70 | * returned by {@link org.apache.commons.net.bsd.RExecClient#getErrorStream getErrorStream() } |
|
71 | * . You |
|
72 | * can specify that a separate connection should be created for standard |
|
73 | * error by setting the boolean <code> separateErrorStream </code> |
|
74 | * parameter of {@link #rcommand rcommand() } to <code> true </code>. |
|
75 | * The standard input of the remote process can be written to through |
|
76 | * the output stream returned by |
|
77 | * {@link org.apache.commons.net.bsd.RExecClient#getOutputStream getOutputStream() } |
|
78 | * . |
|
79 | * <p> |
|
80 | * <p> |
|
81 | * @author Daniel F. Savarese |
|
82 | * @see org.apache.commons.net.SocketClient |
|
83 | * @see RExecClient |
|
84 | * @see RLoginClient |
|
85 | ***/ |
|
86 | ||
87 | public class RCommandClient extends RExecClient |
|
88 | { |
|
89 | /*** |
|
90 | * The default rshell port. Set to 514 in BSD Unix. |
|
91 | ***/ |
|
92 | public static final int DEFAULT_PORT = 514; |
|
93 | ||
94 | /*** |
|
95 | * The smallest port number an rcmd client may use. By BSD convention |
|
96 | * this number is 512. |
|
97 | ***/ |
|
98 | public static final int MIN_CLIENT_PORT = 512; |
|
99 | ||
100 | /*** |
|
101 | * The largest port number an rcmd client may use. By BSD convention |
|
102 | * this number is 1023. |
|
103 | ***/ |
|
104 | public static final int MAX_CLIENT_PORT = 1023; |
|
105 | ||
106 | // Overrides method in RExecClient in order to implement proper |
|
107 | // port number limitations. |
|
108 | InputStream _createErrorStream() throws IOException |
|
109 | { |
|
110 | int localPort; |
|
111 | ServerSocket server; |
|
112 | Socket socket; |
|
113 | ||
114 | 0 | localPort = MAX_CLIENT_PORT; |
115 | 0 | server = null; // Keep compiler from barfing |
116 | ||
117 | 0 | for (localPort = MAX_CLIENT_PORT; localPort >= MIN_CLIENT_PORT; --localPort) |
118 | { |
|
119 | try |
|
120 | { |
|
121 | 0 | server = _socketFactory_.createServerSocket(localPort, 1, |
122 | getLocalAddress()); |
|
123 | } |
|
124 | 0 | catch (SocketException e) |
125 | { |
|
126 | 0 | continue; |
127 | 0 | } |
128 | break; |
|
129 | } |
|
130 | ||
131 | 0 | if (localPort < MIN_CLIENT_PORT) |
132 | 0 | throw new BindException("All ports in use."); |
133 | ||
134 | 0 | _output_.write(Integer.toString(server.getLocalPort()).getBytes()); |
135 | 0 | _output_.write('\0'); |
136 | 0 | _output_.flush(); |
137 | ||
138 | 0 | socket = server.accept(); |
139 | 0 | server.close(); |
140 | ||
141 | 0 | if (isRemoteVerclass="keyword">ificationEnabled() && !verclass="keyword">ifyRemote(socket)) |
142 | { |
|
143 | 0 | socket.close(); |
144 | 0 | throw new IOException( |
145 | "Security violation: unexpected connection attempt by " + |
|
146 | socket.getInetAddress().getHostAddress()); |
|
147 | } |
|
148 | ||
149 | 0 | return (new SocketInputStream(socket, socket.getInputStream())); |
150 | } |
|
151 | ||
152 | /*** |
|
153 | * The default RCommandClient constructor. Initializes the |
|
154 | * default port to <code> DEFAULT_PORT </code>. |
|
155 | ***/ |
|
156 | public RCommandClient() |
|
157 | 0 | { |
158 | 0 | setDefaultPort(DEFAULT_PORT); |
159 | 0 | } |
160 | ||
161 | ||
162 | /*** |
|
163 | * Opens a Socket connected to a remote host at the specified port and |
|
164 | * originating from the specified local address using a port in a range |
|
165 | * acceptable to the BSD rshell daemon. |
|
166 | * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } |
|
167 | * is called to perform connection initialization actions. |
|
168 | * <p> |
|
169 | * @param host The remote host. |
|
170 | * @param port The port to connect to on the remote host. |
|
171 | * @param localAddr The local address to use. |
|
172 | * @exception SocketException If the socket timeout could not be set. |
|
173 | * @exception BindException If all acceptable rshell ports are in use. |
|
174 | * @exception IOException If the socket could not be opened. In most |
|
175 | * cases you will only want to catch IOException since SocketException is |
|
176 | * derived from it. |
|
177 | ***/ |
|
178 | public void connect(InetAddress host, int port, InetAddress localAddr) |
|
179 | throws SocketException, BindException, IOException |
|
180 | { |
|
181 | int localPort; |
|
182 | ||
183 | 0 | localPort = MAX_CLIENT_PORT; |
184 | ||
185 | 0 | for (localPort = MAX_CLIENT_PORT; localPort >= MIN_CLIENT_PORT; --localPort) |
186 | { |
|
187 | try |
|
188 | { |
|
189 | 0 | _socket_ = |
190 | _socketFactory_.createSocket(host, port, localAddr, localPort); |
|
191 | } |
|
192 | 0 | catch (SocketException e) |
193 | { |
|
194 | 0 | continue; |
195 | 0 | } |
196 | break; |
|
197 | } |
|
198 | ||
199 | 0 | if (localPort < MIN_CLIENT_PORT) |
200 | 0 | throw new BindException("All ports in use or insufficient permssion."); |
201 | ||
202 | 0 | _connectAction_(); |
203 | 0 | } |
204 | ||
205 | ||
206 | ||
207 | /*** |
|
208 | * Opens a Socket connected to a remote host at the specified port and |
|
209 | * originating from the current host at a port in a range acceptable |
|
210 | * to the BSD rshell daemon. |
|
211 | * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } |
|
212 | * is called to perform connection initialization actions. |
|
213 | * <p> |
|
214 | * @param host The remote host. |
|
215 | * @param port The port to connect to on the remote host. |
|
216 | * @exception SocketException If the socket timeout could not be set. |
|
217 | * @exception BindException If all acceptable rshell ports are in use. |
|
218 | * @exception IOException If the socket could not be opened. In most |
|
219 | * cases you will only want to catch IOException since SocketException is |
|
220 | * derived from it. |
|
221 | ***/ |
|
222 | public void connect(InetAddress host, int port) |
|
223 | throws SocketException, IOException |
|
224 | { |
|
225 | 0 | connect(host, port, InetAddress.getLocalHost()); |
226 | 0 | } |
227 | ||
228 | ||
229 | /*** |
|
230 | * Opens a Socket connected to a remote host at the specified port and |
|
231 | * originating from the current host at a port in a range acceptable |
|
232 | * to the BSD rshell daemon. |
|
233 | * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } |
|
234 | * is called to perform connection initialization actions. |
|
235 | * <p> |
|
236 | * @param hostname The name of the remote host. |
|
237 | * @param port The port to connect to on the remote host. |
|
238 | * @exception SocketException If the socket timeout could not be set. |
|
239 | * @exception BindException If all acceptable rshell ports are in use. |
|
240 | * @exception IOException If the socket could not be opened. In most |
|
241 | * cases you will only want to catch IOException since SocketException is |
|
242 | * derived from it. |
|
243 | * @exception UnknownHostException If the hostname cannot be resolved. |
|
244 | ***/ |
|
245 | public void connect(String hostname, int port) |
|
246 | throws SocketException, IOException |
|
247 | { |
|
248 | 0 | connect(InetAddress.getByName(hostname), port, InetAddress.getLocalHost()); |
249 | 0 | } |
250 | ||
251 | ||
252 | /*** |
|
253 | * Opens a Socket connected to a remote host at the specified port and |
|
254 | * originating from the specified local address using a port in a range |
|
255 | * acceptable to the BSD rshell daemon. |
|
256 | * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } |
|
257 | * is called to perform connection initialization actions. |
|
258 | * <p> |
|
259 | * @param hostname The remote host. |
|
260 | * @param port The port to connect to on the remote host. |
|
261 | * @param localAddr The local address to use. |
|
262 | * @exception SocketException If the socket timeout could not be set. |
|
263 | * @exception BindException If all acceptable rshell ports are in use. |
|
264 | * @exception IOException If the socket could not be opened. In most |
|
265 | * cases you will only want to catch IOException since SocketException is |
|
266 | * derived from it. |
|
267 | ***/ |
|
268 | public void connect(String hostname, int port, InetAddress localAddr) |
|
269 | throws SocketException, IOException |
|
270 | { |
|
271 | 0 | connect(InetAddress.getByName(hostname), port, localAddr); |
272 | 0 | } |
273 | ||
274 | ||
275 | /*** |
|
276 | * Opens a Socket connected to a remote host at the specified port and |
|
277 | * originating from the specified local address and port. The |
|
278 | * local port must lie between <code> MIN_CLIENT_PORT </code> and |
|
279 | * <code> MAX_CLIENT_PORT </code> or an IllegalArgumentException will |
|
280 | * be thrown. |
|
281 | * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } |
|
282 | * is called to perform connection initialization actions. |
|
283 | * <p> |
|
284 | * @param host The remote host. |
|
285 | * @param port The port to connect to on the remote host. |
|
286 | * @param localAddr The local address to use. |
|
287 | * @param localPort The local port to use. |
|
288 | * @exception SocketException If the socket timeout could not be set. |
|
289 | * @exception IOException If the socket could not be opened. In most |
|
290 | * cases you will only want to catch IOException since SocketException is |
|
291 | * derived from it. |
|
292 | * @exception IllegalArgumentException If an invalid local port number |
|
293 | * is specified. |
|
294 | ***/ |
|
295 | public void connect(InetAddress host, int port, |
|
296 | InetAddress localAddr, int localPort) |
|
297 | throws SocketException, IOException, IllegalArgumentException |
|
298 | { |
|
299 | 0 | if (localPort < MIN_CLIENT_PORT || localPort > MAX_CLIENT_PORT) |
300 | 0 | throw new IllegalArgumentException("Invalid port number " + localPort); |
301 | 0 | super.connect(host, port, localAddr, localPort); |
302 | 0 | } |
303 | ||
304 | ||
305 | /*** |
|
306 | * Opens a Socket connected to a remote host at the specified port and |
|
307 | * originating from the specified local address and port. The |
|
308 | * local port must lie between <code> MIN_CLIENT_PORT </code> and |
|
309 | * <code> MAX_CLIENT_PORT </code> or an IllegalArgumentException will |
|
310 | * be thrown. |
|
311 | * Before returning, {@link org.apache.commons.net.SocketClient#_connectAction_ _connectAction_() } |
|
312 | * is called to perform connection initialization actions. |
|
313 | * <p> |
|
314 | * @param hostname The name of the remote host. |
|
315 | * @param port The port to connect to on the remote host. |
|
316 | * @param localAddr The local address to use. |
|
317 | * @param localPort The local port to use. |
|
318 | * @exception SocketException If the socket timeout could not be set. |
|
319 | * @exception IOException If the socket could not be opened. In most |
|
320 | * cases you will only want to catch IOException since SocketException is |
|
321 | * derived from it. |
|
322 | * @exception UnknownHostException If the hostname cannot be resolved. |
|
323 | * @exception IllegalArgumentException If an invalid local port number |
|
324 | * is specified. |
|
325 | ***/ |
|
326 | public void connect(String hostname, int port, |
|
327 | InetAddress localAddr, int localPort) |
|
328 | throws SocketException, IOException, IllegalArgumentException |
|
329 | { |
|
330 | 0 | if (localPort < MIN_CLIENT_PORT || localPort > MAX_CLIENT_PORT) |
331 | 0 | throw new IllegalArgumentException("Invalid port number " + localPort); |
332 | 0 | super.connect(hostname, port, localAddr, localPort); |
333 | 0 | } |
334 | ||
335 | ||
336 | /*** |
|
337 | * Remotely executes a command through the rshd daemon on the server |
|
338 | * to which the RCommandClient is connected. After calling this method, |
|
339 | * you may interact with the remote process through its standard input, |
|
340 | * output, and error streams. You will typically be able to detect |
|
341 | * the termination of the remote process after reaching end of file |
|
342 | * on its standard output (accessible through |
|
343 | * {@link #getInputStream getInputStream() }. Disconnecting |
|
344 | * from the server or closing the process streams before reaching |
|
345 | * end of file will not necessarily terminate the remote process. |
|
346 | * <p> |
|
347 | * If a separate error stream is requested, the remote server will |
|
348 | * connect to a local socket opened by RCommandClient, providing an |
|
349 | * independent stream through which standard error will be transmitted. |
|
350 | * The local socket must originate from a secure port (512 - 1023), |
|
351 | * and rcommand() ensures that this will be so. |
|
352 | * RCommandClient will also do a simple security check when it accepts a |
|
353 | * connection for this error stream. If the connection does not originate |
|
354 | * from the remote server, an IOException will be thrown. This serves as |
|
355 | * a simple protection against possible hijacking of the error stream by |
|
356 | * an attacker monitoring the rexec() negotiation. You may disable this |
|
357 | * behavior with |
|
358 | * {@link org.apache.commons.net.bsd.RExecClient#setRemoteVerificationEnabled setRemoteVerificationEnabled()} |
|
359 | * . |
|
360 | * <p> |
|
361 | * @param localUsername The user account on the local machine that is |
|
362 | * requesting the command execution. |
|
363 | * @param remoteUsername The account name on the server through which to |
|
364 | * execute the command. |
|
365 | * @param command The command, including any arguments, to execute. |
|
366 | * @param separateErrorStream True if you would like the standard error |
|
367 | * to be transmitted through a different stream than standard output. |
|
368 | * False if not. |
|
369 | * @exception IOException If the rcommand() attempt fails. The exception |
|
370 | * will contain a message indicating the nature of the failure. |
|
371 | ***/ |
|
372 | public void rcommand(String localUsername, String remoteUsername, |
|
373 | String command, boolean separateErrorStream) |
|
374 | throws IOException |
|
375 | { |
|
376 | 0 | rexec(localUsername, remoteUsername, command, separateErrorStream); |
377 | 0 | } |
378 | ||
379 | ||
380 | /*** |
|
381 | * Same as |
|
382 | * <code> rcommand(localUsername, remoteUsername, command, false); </code> |
|
383 | ***/ |
|
384 | public void rcommand(String localUsername, String remoteUsername, |
|
385 | String command) |
|
386 | throws IOException |
|
387 | { |
|
388 | 0 | rcommand(localUsername, remoteUsername, command, false); |
389 | 0 | } |
390 | ||
391 | } |
|
392 |
This report is generated by jcoverage, Maven and Maven JCoverage Plugin. |