001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.net.bsd; 019 020 import java.io.IOException; 021 import java.io.InputStream; 022 import java.io.OutputStream; 023 import java.net.ServerSocket; 024 import java.net.Socket; 025 026 import org.apache.commons.net.SocketClient; 027 import org.apache.commons.net.io.SocketInputStream; 028 029 /*** 030 * RExecClient implements the rexec() facility that first appeared in 031 * 4.2BSD Unix. This class will probably only be of use for connecting 032 * to Unix systems and only when the rexecd daemon is configured to run, 033 * which is a rarity these days because of the security risks involved. 034 * However, rexec() can be very useful for performing administrative tasks 035 * on a network behind a firewall. 036 * <p> 037 * As with virtually all of the client classes in org.apache.commons.net, this 038 * class derives from SocketClient, inheriting its connection methods. 039 * The way to use RExecClient is to first connect 040 * to the server, call the {@link #rexec rexec() } method, and then 041 * fetch the connection's input, output, and optionally error streams. 042 * Interaction with the remote command is controlled entirely through the 043 * I/O streams. Once you have finished processing the streams, you should 044 * invoke {@link #disconnect disconnect() } to clean up properly. 045 * <p> 046 * By default the standard output and standard error streams of the 047 * remote process are transmitted over the same connection, readable 048 * from the input stream returned by 049 * {@link #getInputStream getInputStream() }. However, it is 050 * possible to tell the rexecd daemon to return the standard error 051 * stream over a separate connection, readable from the input stream 052 * returned by {@link #getErrorStream getErrorStream() }. You 053 * can specify that a separate connection should be created for standard 054 * error by setting the boolean <code> separateErrorStream </code> 055 * parameter of {@link #rexec rexec() } to <code> true </code>. 056 * The standard input of the remote process can be written to through 057 * the output stream returned by 058 * {@link #getOutputStream getOutputSream() }. 059 * <p> 060 * <p> 061 * @see SocketClient 062 * @see RCommandClient 063 * @see RLoginClient 064 ***/ 065 066 public class RExecClient extends SocketClient 067 { 068 /*** 069 * The default rexec port. Set to 512 in BSD Unix. 070 ***/ 071 public static final int DEFAULT_PORT = 512; 072 073 private boolean __remoteVerificationEnabled; 074 075 /*** 076 * If a separate error stream is requested, <code>_errorStream_</code> 077 * will point to an InputStream from which the standard error of the 078 * remote process can be read (after a call to rexec()). Otherwise, 079 * <code> _errorStream_ </code> will be null. 080 ***/ 081 protected InputStream _errorStream_; 082 083 // This can be overridden in local package to implement port range 084 // limitations of rcmd and rlogin 085 InputStream _createErrorStream() throws IOException 086 { 087 ServerSocket server; 088 Socket socket; 089 090 server = _serverSocketFactory_.createServerSocket(0, 1, getLocalAddress()); 091 092 _output_.write(Integer.toString(server.getLocalPort()).getBytes("UTF-8")); // $NON-NLS-1$ 093 _output_.write('\0'); 094 _output_.flush(); 095 096 socket = server.accept(); 097 server.close(); 098 099 if (__remoteVerificationEnabled && !verifyRemote(socket)) 100 { 101 socket.close(); 102 throw new IOException( 103 "Security violation: unexpected connection attempt by " + 104 socket.getInetAddress().getHostAddress()); 105 } 106 107 return (new SocketInputStream(socket, socket.getInputStream())); 108 } 109 110 111 /*** 112 * The default RExecClient constructor. Initializes the 113 * default port to <code> DEFAULT_PORT </code>. 114 ***/ 115 public RExecClient() 116 { 117 _errorStream_ = null; 118 setDefaultPort(DEFAULT_PORT); 119 } 120 121 122 /*** 123 * Returns the InputStream from which the standard outputof the remote 124 * process can be read. The input stream will only be set after a 125 * successful rexec() invocation. 126 * <p> 127 * @return The InputStream from which the standard output of the remote 128 * process can be read. 129 ***/ 130 public InputStream getInputStream() 131 { 132 return _input_; 133 } 134 135 136 /*** 137 * Returns the OutputStream through which the standard input of the remote 138 * process can be written. The output stream will only be set after a 139 * successful rexec() invocation. 140 * <p> 141 * @return The OutputStream through which the standard input of the remote 142 * process can be written. 143 ***/ 144 public OutputStream getOutputStream() 145 { 146 return _output_; 147 } 148 149 150 /*** 151 * Returns the InputStream from which the standard error of the remote 152 * process can be read if a separate error stream is requested from 153 * the server. Otherwise, null will be returned. The error stream 154 * will only be set after a successful rexec() invocation. 155 * <p> 156 * @return 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. 159 ***/ 160 public InputStream getErrorStream() 161 { 162 return _errorStream_; 163 } 164 165 166 /*** 167 * Remotely executes a command through the rexecd daemon on the server 168 * to which the RExecClient is connected. After calling this method, 169 * you may interact with the remote process through its standard input, 170 * output, and error streams. You will typically be able to detect 171 * the termination of the remote process after reaching end of file 172 * on its standard output (accessible through 173 * {@link #getInputStream getInputStream() }. Disconnecting 174 * from the server or closing the process streams before reaching 175 * end of file will not necessarily terminate the remote process. 176 * <p> 177 * If a separate error stream is requested, the remote server will 178 * connect to a local socket opened by RExecClient, providing an 179 * independent stream through which standard error will be transmitted. 180 * RExecClient will do a simple security check when it accepts a 181 * connection for this error stream. If the connection does not originate 182 * from the remote server, an IOException will be thrown. This serves as 183 * a simple protection against possible hijacking of the error stream by 184 * an attacker monitoring the rexec() negotiation. You may disable this 185 * behavior with {@link #setRemoteVerificationEnabled setRemoteVerificationEnabled()} 186 * . 187 * <p> 188 * @param username The account name on the server through which to execute 189 * the command. 190 * @param password The plain text password of the user account. 191 * @param command The command, including any arguments, to execute. 192 * @param separateErrorStream True if you would like the standard error 193 * to be transmitted through a different stream than standard output. 194 * False if not. 195 * @exception IOException If the rexec() attempt fails. The exception 196 * will contain a message indicating the nature of the failure. 197 ***/ 198 public void rexec(String username, String password, 199 String command, boolean separateErrorStream) 200 throws IOException 201 { 202 int ch; 203 204 if (separateErrorStream) 205 { 206 _errorStream_ = _createErrorStream(); 207 } 208 else 209 { 210 _output_.write('\0'); 211 } 212 213 _output_.write(username.getBytes()); 214 _output_.write('\0'); 215 _output_.write(password.getBytes()); 216 _output_.write('\0'); 217 _output_.write(command.getBytes()); 218 _output_.write('\0'); 219 _output_.flush(); 220 221 ch = _input_.read(); 222 if (ch > 0) { 223 StringBuilder buffer = new StringBuilder(); 224 225 while ((ch = _input_.read()) != -1 && ch != '\n') { 226 buffer.append((char)ch); 227 } 228 229 throw new IOException(buffer.toString()); 230 } else if (ch < 0) { 231 throw new IOException("Server closed connection."); 232 } 233 } 234 235 236 /*** 237 * Same as <code> rexec(username, password, command, false); </code> 238 ***/ 239 public void rexec(String username, String password, 240 String command) 241 throws IOException 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 @Override 253 public void disconnect() throws IOException 254 { 255 if (_errorStream_ != null) { 256 _errorStream_.close(); 257 } 258 _errorStream_ = null; 259 super.disconnect(); 260 } 261 262 263 /*** 264 * Enable or disable verification that the remote host connecting to 265 * create a separate error stream is the same as the host to which 266 * the standard out stream is connected. The default is for verification 267 * to be enabled. You may set this value at any time, whether the 268 * client is currently connected or not. 269 * <p> 270 * @param enable True to enable verification, false to disable verification. 271 ***/ 272 public final void setRemoteVerificationEnabled(boolean enable) 273 { 274 __remoteVerificationEnabled = enable; 275 } 276 277 /*** 278 * Return whether or not verification of the remote host providing a 279 * separate error stream is enabled. The default behavior is for 280 * verification to be enabled. 281 * <p> 282 * @return True if verification is enabled, false if not. 283 ***/ 284 public final boolean isRemoteVerificationEnabled() 285 { 286 return __remoteVerificationEnabled; 287 } 288 289 } 290