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