| %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. |