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 * @see SocketClient 62 * @see RCommandClient 63 * @see RLoginClient 64 ***/ 65 66 public class RExecClient extends SocketClient 67 { 68 /** 69 * @since 3.3 70 */ 71 protected static final char NULL_CHAR = '\0'; 72 73 /*** 74 * The default rexec port. Set to 512 in BSD Unix. 75 ***/ 76 public static final int DEFAULT_PORT = 512; 77 78 private boolean __remoteVerificationEnabled; 79 80 /*** 81 * If a separate error stream is requested, <code>_errorStream_</code> 82 * will point to an InputStream from which the standard error of the 83 * remote process can be read (after a call to rexec()). Otherwise, 84 * <code> _errorStream_ </code> will be null. 85 ***/ 86 protected InputStream _errorStream_; 87 88 // This can be overridden in local package to implement port range 89 // limitations of rcmd and rlogin 90 InputStream _createErrorStream() throws IOException 91 { 92 ServerSocket server; 93 Socket socket; 94 95 server = _serverSocketFactory_.createServerSocket(0, 1, getLocalAddress()); 96 97 _output_.write(Integer.toString(server.getLocalPort()).getBytes("UTF-8")); // $NON-NLS-1$ 98 _output_.write(NULL_CHAR); 99 _output_.flush(); 100 101 socket = server.accept(); 102 server.close(); 103 104 if (__remoteVerificationEnabled && !verifyRemote(socket)) 105 { 106 socket.close(); 107 throw new IOException( 108 "Security violation: unexpected connection attempt by " + 109 socket.getInetAddress().getHostAddress()); 110 } 111 112 return (new SocketInputStream(socket, socket.getInputStream())); 113 } 114 115 116 /*** 117 * The default RExecClient constructor. Initializes the 118 * default port to <code> DEFAULT_PORT </code>. 119 ***/ 120 public RExecClient() 121 { 122 _errorStream_ = null; 123 setDefaultPort(DEFAULT_PORT); 124 } 125 126 127 /*** 128 * Returns the InputStream from which the standard outputof the remote 129 * process can be read. The input stream will only be set after a 130 * successful rexec() invocation. 131 * <p> 132 * @return The InputStream from which the standard output of the remote 133 * process can be read. 134 ***/ 135 public InputStream getInputStream() 136 { 137 return _input_; 138 } 139 140 141 /*** 142 * Returns the OutputStream through which the standard input of the remote 143 * process can be written. The output stream will only be set after a 144 * successful rexec() invocation. 145 * <p> 146 * @return The OutputStream through which the standard input of the remote 147 * process can be written. 148 ***/ 149 public OutputStream getOutputStream() 150 { 151 return _output_; 152 } 153 154 155 /*** 156 * Returns the InputStream from which the standard error of the remote 157 * process can be read if a separate error stream is requested from 158 * the server. Otherwise, null will be returned. The error stream 159 * will only be set after a successful rexec() invocation. 160 * <p> 161 * @return The InputStream from which the standard error of the remote 162 * process can be read if a separate error stream is requested from 163 * the server. Otherwise, null will be returned. 164 ***/ 165 public InputStream getErrorStream() 166 { 167 return _errorStream_; 168 } 169 170 171 /*** 172 * Remotely executes a command through the rexecd daemon on the server 173 * to which the RExecClient is connected. After calling this method, 174 * you may interact with the remote process through its standard input, 175 * output, and error streams. You will typically be able to detect 176 * the termination of the remote process after reaching end of file 177 * on its standard output (accessible through 178 * {@link #getInputStream getInputStream() }. Disconnecting 179 * from the server or closing the process streams before reaching 180 * end of file will not necessarily terminate the remote process. 181 * <p> 182 * If a separate error stream is requested, the remote server will 183 * connect to a local socket opened by RExecClient, providing an 184 * independent stream through which standard error will be transmitted. 185 * RExecClient will do a simple security check when it accepts a 186 * connection for this error stream. If the connection does not originate 187 * from the remote server, an IOException will be thrown. This serves as 188 * a simple protection against possible hijacking of the error stream by 189 * an attacker monitoring the rexec() negotiation. You may disable this 190 * behavior with {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()} 191 * . 192 * <p> 193 * @param username The account name on the server through which to execute 194 * the command. 195 * @param password The plain text password of the user account. 196 * @param command The command, including any arguments, to execute. 197 * @param separateErrorStream True if you would like the standard error 198 * to be transmitted through a different stream than standard output. 199 * False if not. 200 * @exception IOException If the rexec() attempt fails. The exception 201 * will contain a message indicating the nature of the failure. 202 ***/ 203 public void rexec(String username, String password, 204 String command, boolean separateErrorStream) 205 throws IOException 206 { 207 int ch; 208 209 if (separateErrorStream) 210 { 211 _errorStream_ = _createErrorStream(); 212 } 213 else 214 { 215 _output_.write(NULL_CHAR); 216 } 217 218 _output_.write(username.getBytes(getCharsetName())); // Java 1.6 can use getCharset() 219 _output_.write(NULL_CHAR); 220 _output_.write(password.getBytes(getCharsetName())); // Java 1.6 can use getCharset() 221 _output_.write(NULL_CHAR); 222 _output_.write(command.getBytes(getCharsetName())); // Java 1.6 can use getCharset() 223 _output_.write(NULL_CHAR); 224 _output_.flush(); 225 226 ch = _input_.read(); 227 if (ch > 0) { 228 StringBuilder buffer = new StringBuilder(); 229 230 while ((ch = _input_.read()) != -1 && ch != '\n') { 231 buffer.append((char)ch); 232 } 233 234 throw new IOException(buffer.toString()); 235 } else if (ch < 0) { 236 throw new IOException("Server closed connection."); 237 } 238 } 239 240 241 /*** 242 * Same as <code> rexec(username, password, command, false); </code> 243 ***/ 244 public void rexec(String username, String password, 245 String command) 246 throws IOException 247 { 248 rexec(username, password, command, false); 249 } 250 251 /*** 252 * Disconnects from the server, closing all associated open sockets and 253 * streams. 254 * <p> 255 * @exception IOException If there an error occurs while disconnecting. 256 ***/ 257 @Override 258 public void disconnect() throws IOException 259 { 260 if (_errorStream_ != null) { 261 _errorStream_.close(); 262 } 263 _errorStream_ = null; 264 super.disconnect(); 265 } 266 267 268 /*** 269 * Enable or disable verification that the remote host connecting to 270 * create a separate error stream is the same as the host to which 271 * the standard out stream is connected. The default is for verification 272 * to be enabled. You may set this value at any time, whether the 273 * client is currently connected or not. 274 * <p> 275 * @param enable True to enable verification, false to disable verification. 276 ***/ 277 public final void setRemoteVerificationEnabled(boolean enable) 278 { 279 __remoteVerificationEnabled = enable; 280 } 281 282 /*** 283 * Return whether or not verification of the remote host providing a 284 * separate error stream is enabled. The default behavior is for 285 * verification to be enabled. 286 * <p> 287 * @return True if verification is enabled, false if not. 288 ***/ 289 public final boolean isRemoteVerificationEnabled() 290 { 291 return __remoteVerificationEnabled; 292 } 293 294 } 295