1. /*
  2. * @(#)PlainSocketImpl.java 1.41 01/02/09
  3. *
  4. * Copyright 1995-2001 Sun Microsystems, Inc. All Rights Reserved.
  5. *
  6. * This software is the proprietary information of Sun Microsystems, Inc.
  7. * Use is subject to license terms.
  8. *
  9. */
  10. package java.net;
  11. import java.io.IOException;
  12. import java.io.InputStream;
  13. import java.io.OutputStream;
  14. import java.io.InterruptedIOException;
  15. import java.io.FileDescriptor;
  16. import java.io.ByteArrayOutputStream;
  17. /**
  18. * Default Socket Implementation. This implementation does
  19. * not implement any security checks. It does support SOCKS version 4.
  20. * Note this class should <b>NOT</b> be public.
  21. *
  22. * @author Steven B. Byrne
  23. * @version 1.41, 02/09/01
  24. */
  25. class PlainSocketImpl extends SocketImpl
  26. {
  27. /* timeout value for connection */
  28. static int preferredConnectionTimeout = 0;
  29. /* instance variable for SO_TIMEOUT */
  30. int timeout; // timeout in millisec
  31. /* SOCKS related constants */
  32. private static final int SOCKS_PROTO_VERS = 4;
  33. private static final int SOCKS_REPLY_VERS = 4;
  34. private static final int COMMAND_CONNECT = 1;
  35. private static final int COMMAND_BIND = 2;
  36. private static final int REQUEST_GRANTED = 90;
  37. private static final int REQUEST_REJECTED = 91;
  38. private static final int REQUEST_REJECTED_NO_IDENTD = 92;
  39. private static final int REQUEST_REJECTED_DIFF_IDENTS = 93;
  40. public static final String socksServerProp = "socksProxyHost";
  41. public static final String socksPortProp = "socksProxyPort";
  42. public static final String socksDefaultPortStr = "1080";
  43. private boolean shut_rd = false;
  44. private boolean shut_wr = false;
  45. private SocketInputStream socketInputStream = null;
  46. /**
  47. * Load net library into runtime.
  48. */
  49. static {
  50. java.security.AccessController.doPrivileged(
  51. new sun.security.action.LoadLibraryAction("net"));
  52. String s = (String)java.security.AccessController.doPrivileged(
  53. new sun.security.action.GetPropertyAction("java.net.connectiontimeout"));
  54. if (s != null) {
  55. preferredConnectionTimeout = Integer.parseInt(s);
  56. }
  57. initProto();
  58. }
  59. /**
  60. * Creates a socket with a boolean that specifies whether this
  61. * is a stream socket (true) or an unconnected UDP socket (false).
  62. */
  63. protected synchronized void create(boolean stream) throws IOException {
  64. fd = new FileDescriptor();
  65. socketCreate(stream);
  66. }
  67. /**
  68. * Creates a socket and connects it to the specified port on
  69. * the specified host.
  70. * @param host the specified host
  71. * @param port the specified port
  72. */
  73. protected void connect(String host, int port)
  74. throws UnknownHostException, IOException
  75. {
  76. IOException pending = null;
  77. try {
  78. InetAddress address = InetAddress.getByName(host);
  79. try {
  80. connectToAddress(address, port);
  81. return;
  82. } catch (IOException e) {
  83. pending = e;
  84. }
  85. } catch (UnknownHostException e) {
  86. pending = e;
  87. }
  88. // everything failed
  89. close();
  90. throw pending;
  91. }
  92. /**
  93. * Creates a socket and connects it to the specified address on
  94. * the specified port.
  95. * @param address the address
  96. * @param port the specified port
  97. */
  98. protected void connect(InetAddress address, int port) throws IOException {
  99. this.port = port;
  100. this.address = address;
  101. try {
  102. connectToAddress(address, port);
  103. return;
  104. } catch (IOException e) {
  105. // everything failed
  106. close();
  107. throw e;
  108. }
  109. }
  110. private void connectToAddress(InetAddress address, int port) throws IOException {
  111. if (usingSocks()) {
  112. doSOCKSConnect(address, port);
  113. } else {
  114. doConnect(address, port);
  115. }
  116. }
  117. public void setOption(int opt, Object val) throws SocketException {
  118. boolean on = true;
  119. switch (opt) {
  120. /* check type safety b4 going native. These should never
  121. * fail, since only java.Socket* has access to
  122. * PlainSocketImpl.setOption().
  123. */
  124. case SO_LINGER:
  125. if (val == null || (!(val instanceof Integer) && !(val instanceof Boolean)))
  126. throw new SocketException("Bad parameter for option");
  127. if (val instanceof Boolean) {
  128. /* true only if disabling - enabling should be Integer */
  129. on = false;
  130. }
  131. break;
  132. case SO_TIMEOUT:
  133. if (val == null || (!(val instanceof Integer)))
  134. throw new SocketException("Bad parameter for SO_TIMEOUT");
  135. int tmp = ((Integer) val).intValue();
  136. if (tmp < 0)
  137. throw new IllegalArgumentException("timeout < 0");
  138. timeout = tmp;
  139. return;
  140. case SO_BINDADDR:
  141. throw new SocketException("Cannot re-bind socket");
  142. case TCP_NODELAY:
  143. if (val == null || !(val instanceof Boolean))
  144. throw new SocketException("bad parameter for TCP_NODELAY");
  145. on = ((Boolean)val).booleanValue();
  146. break;
  147. case SO_SNDBUF:
  148. case SO_RCVBUF:
  149. if (val == null || !(val instanceof Integer) ||
  150. !(((Integer)val).intValue() > 0)) {
  151. throw new SocketException("bad parameter for SO_SNDBUF " +
  152. "or SO_RCVBUF");
  153. }
  154. break;
  155. case SO_KEEPALIVE:
  156. if (val == null || !(val instanceof Boolean))
  157. throw new SocketException("bad parameter for SO_KEEPALIVE");
  158. on = ((Boolean)val).booleanValue();
  159. break;
  160. default:
  161. throw new SocketException("unrecognized TCP option: " + opt);
  162. }
  163. socketSetOption(opt, on, val);
  164. }
  165. public Object getOption(int opt) throws SocketException {
  166. if (opt == SO_TIMEOUT) {
  167. return new Integer(timeout);
  168. }
  169. int ret = socketGetOption(opt);
  170. /*
  171. * The native socketGetOption() knows about 3 options.
  172. * The 32 bit value it returns will be interpreted according
  173. * to what we're asking. A return of -1 means it understands
  174. * the option but its turned off. It will raise a SocketException
  175. * if "opt" isn't one it understands.
  176. */
  177. switch (opt) {
  178. case TCP_NODELAY:
  179. return (ret == -1) ? new Boolean(false): new Boolean(true);
  180. case SO_LINGER:
  181. return (ret == -1) ? new Boolean(false): (Object)(new Integer(ret));
  182. case SO_BINDADDR:
  183. InetAddress in = new InetAddress();
  184. in.address = ret;
  185. return in;
  186. case SO_SNDBUF:
  187. case SO_RCVBUF:
  188. return new Integer(ret);
  189. case SO_KEEPALIVE:
  190. return (ret == -1) ? new Boolean(false): new Boolean(true);
  191. // should never get here
  192. default:
  193. return null;
  194. }
  195. }
  196. /**
  197. * Connect to the SOCKS server using the SOCKS connection protocol.
  198. */
  199. private void doSOCKSConnect(InetAddress address, int port)
  200. throws IOException {
  201. connectToSocksServer();
  202. sendSOCKSCommandPacket(COMMAND_CONNECT, address, port);
  203. int protoStatus = getSOCKSReply();
  204. switch (protoStatus) {
  205. case REQUEST_GRANTED:
  206. // connection set up, return control to the socket client
  207. return;
  208. case REQUEST_REJECTED:
  209. case REQUEST_REJECTED_NO_IDENTD:
  210. throw new SocketException("SOCKS server cannot connect to identd");
  211. case REQUEST_REJECTED_DIFF_IDENTS:
  212. throw new SocketException("User name does not match identd name");
  213. }
  214. }
  215. /**
  216. * Read the response from the socks server. Return the result code.
  217. */
  218. private int getSOCKSReply() throws IOException {
  219. InputStream in = getInputStream();
  220. byte response[] = new byte[8];
  221. int bytesReceived = 0;
  222. int len = response.length;
  223. for (int attempts = 0; bytesReceived<len && attempts<3; attempts++) {
  224. int count = in.read(response, bytesReceived, len - bytesReceived);
  225. if (count < 0)
  226. throw new SocketException("Malformed reply from SOCKS server");
  227. bytesReceived += count;
  228. }
  229. if (bytesReceived != len) {
  230. throw new SocketException("Reply from SOCKS server has bad length: " + bytesReceived);
  231. }
  232. if (response[0] != 0) { // should be version0
  233. throw new SocketException("Reply from SOCKS server has bad version " + response[0]);
  234. }
  235. return response[1]; // the response code
  236. }
  237. /**
  238. * Just set up a connection to the SOCKS server and return. The caller
  239. * needs to handle the SOCKS initiation protocol with the server after
  240. * the connection is established.
  241. */
  242. private void connectToSocksServer() throws IOException {
  243. String socksServerString = null;
  244. String socksPortString = null;
  245. socksServerString = (String) java.security.AccessController.doPrivileged(
  246. new sun.security.action.GetPropertyAction(socksServerProp));
  247. socksPortString = (String) java.security.AccessController.doPrivileged(
  248. new sun.security.action.GetPropertyAction(socksPortProp,
  249. socksDefaultPortStr));
  250. if (socksServerString == null) {
  251. // REMIND: this is too trusting of its (internal) callers --
  252. // needs to robustly assert that SOCKS are in fact being used,
  253. // and signal an error (in some manner) if SOCKS are not being
  254. // used.
  255. return;
  256. }
  257. InetAddress socksServer = InetAddress.getByName(socksServerString);
  258. int socksServerPort;
  259. try {
  260. socksServerPort = Integer.parseInt(socksPortString);
  261. } catch (Exception e) {
  262. throw new SocketException("Bad port number format");
  263. }
  264. doConnect(socksServer, socksServerPort);
  265. }
  266. /**
  267. * The workhorse of the connection operation. Tries several times to
  268. * establish a connection to the given <host, port>. If unsuccessful,
  269. * throws an IOException indicating what went wrong.
  270. */
  271. private void doConnect(InetAddress address, int port) throws IOException {
  272. IOException pending = null;
  273. for (int i = 0 ; i < 3 ; i++) {
  274. try {
  275. socketConnect(address, port);
  276. return;
  277. } catch (ProtocolException e) {
  278. // Try again in case of a protocol exception
  279. close();
  280. fd = new FileDescriptor();
  281. socketCreate(true);
  282. pending = e;
  283. } catch (IOException e) {
  284. // Let someone else deal with this exception
  285. close();
  286. throw e;
  287. }
  288. }
  289. // failed to connect -- tell our client the bad news
  290. close();
  291. throw pending;
  292. }
  293. /**
  294. * Just creates and sends out to the connected socket a SOCKS command
  295. * packet.
  296. */
  297. private void sendSOCKSCommandPacket(int command, InetAddress address,
  298. int port) throws IOException {
  299. byte commandPacket[] = makeCommandPacket(command, address, port);
  300. OutputStream out = getOutputStream();
  301. out.write(commandPacket);
  302. }
  303. /**
  304. * Create and return a SOCKS V4 command packet.
  305. */
  306. private byte[] makeCommandPacket(int command, InetAddress address,
  307. int port) {
  308. // base packet size = 8, + 1 null byte
  309. ByteArrayOutputStream byteStream = new ByteArrayOutputStream(8 + 1);
  310. byteStream.write(SOCKS_PROTO_VERS);
  311. byteStream.write(command);
  312. byteStream.write((port >> 8) & 0xff);
  313. byteStream.write((port >> 0) & 0xff);
  314. byte addressBytes[] = address.getAddress();
  315. byteStream.write(addressBytes, 0, addressBytes.length);
  316. String userName = (String) java.security.AccessController.doPrivileged(
  317. new sun.security.action.GetPropertyAction("user.name"));
  318. byte userNameBytes[] = new byte[userName.length()];
  319. userName.getBytes(0, userName.length(), userNameBytes, 0);
  320. byteStream.write(userNameBytes, 0, userNameBytes.length);
  321. byteStream.write(0); // null termination for user name
  322. return byteStream.toByteArray();
  323. }
  324. /**
  325. * Returns true if implementation should use the SOCKS protocol
  326. * (i.e. the user has set the required properties to enable SOCKS to
  327. * be used).
  328. */
  329. private boolean usingSocks() {
  330. String ssp = (String) java.security.AccessController.doPrivileged(
  331. new sun.security.action.GetPropertyAction(socksServerProp));
  332. return (ssp != null);
  333. }
  334. /**
  335. * Binds the socket to the specified address of the specified local port.
  336. * @param address the address
  337. * @param port the port
  338. */
  339. protected synchronized void bind(InetAddress address, int lport)
  340. throws IOException
  341. {
  342. socketBind(address, lport);
  343. }
  344. /**
  345. * Listens, for a specified amount of time, for connections.
  346. * @param count the amount of time to listen for connections
  347. */
  348. protected synchronized void listen(int count) throws IOException {
  349. socketListen(count);
  350. }
  351. /**
  352. * Accepts connections.
  353. * @param s the connection
  354. */
  355. protected synchronized void accept(SocketImpl s) throws IOException {
  356. socketAccept(s);
  357. }
  358. /**
  359. * Gets an InputStream for this socket.
  360. */
  361. protected synchronized InputStream getInputStream() throws IOException {
  362. if (fd == null) {
  363. throw new IOException("Socket Closed");
  364. }
  365. if (shut_rd) {
  366. throw new IOException("Socket input is shutdown");
  367. }
  368. if (socketInputStream == null) {
  369. socketInputStream = new SocketInputStream(this);
  370. }
  371. return socketInputStream;
  372. }
  373. /**
  374. * Gets an OutputStream for this socket.
  375. */
  376. protected synchronized OutputStream getOutputStream() throws IOException {
  377. if (fd == null) {
  378. throw new IOException("Socket Closed");
  379. }
  380. if (shut_wr) {
  381. throw new IOException("Socket output is shutdown");
  382. }
  383. return new SocketOutputStream(this);
  384. }
  385. /**
  386. * Returns the number of bytes that can be read without blocking.
  387. */
  388. protected synchronized int available() throws IOException {
  389. if (fd == null)
  390. throw new IOException("Stream closed.");
  391. return socketAvailable();
  392. }
  393. /**
  394. * Closes the socket.
  395. */
  396. protected void close() throws IOException {
  397. if (fd != null) {
  398. socketClose();
  399. fd = null;
  400. }
  401. }
  402. /**
  403. * Shutdown read-half of the socket connection;
  404. */
  405. protected void shutdownInput() throws IOException {
  406. if (fd != null) {
  407. socketShutdown(SHUT_RD);
  408. if (socketInputStream != null) {
  409. socketInputStream.setEOF(true);
  410. }
  411. shut_rd = true;
  412. }
  413. }
  414. /**
  415. * Shutdown write-half of the socket connection;
  416. */
  417. protected void shutdownOutput() throws IOException {
  418. if (fd != null) {
  419. socketShutdown(SHUT_WR);
  420. shut_wr = true;
  421. }
  422. }
  423. /**
  424. * Cleans up if the user forgets to close it.
  425. */
  426. protected void finalize() throws IOException {
  427. close();
  428. }
  429. private native void socketCreate(boolean isServer) throws IOException;
  430. private native void socketConnect(InetAddress address, int port)
  431. throws IOException;
  432. private native void socketBind(InetAddress address, int port)
  433. throws IOException;
  434. private native void socketListen(int count)
  435. throws IOException;
  436. private native void socketAccept(SocketImpl s)
  437. throws IOException;
  438. private native int socketAvailable()
  439. throws IOException;
  440. private native void socketClose()
  441. throws IOException;
  442. private native void socketShutdown(int howto)
  443. throws IOException;
  444. private static native void initProto();
  445. private native void socketSetOption(int cmd, boolean on, Object value)
  446. throws SocketException;
  447. private native int socketGetOption(int opt) throws SocketException;
  448. public final static int SHUT_RD = 0;
  449. public final static int SHUT_WR = 1;
  450. }