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