/GraduateProject/asmack/org/jivesoftware/smack/proxy/Socks5ProxySocketFactory.java

https://gitlab.com/xiaoyezi/yesheng · Java · 291 lines · 149 code · 37 blank · 105 comment · 16 complexity · 5ed9279afdd1de9aed6f4d319850b0a1 MD5 · raw file

  1. package org.jivesoftware.smack.proxy;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.io.OutputStream;
  5. import java.net.InetAddress;
  6. import java.net.Socket;
  7. import java.net.UnknownHostException;
  8. import javax.net.SocketFactory;
  9. /**
  10. * Socket factory for Socks5 proxy
  11. *
  12. * @author Atul Aggarwal
  13. */
  14. public class Socks5ProxySocketFactory extends SocketFactory {
  15. private ProxyInfo proxy;
  16. public Socks5ProxySocketFactory(ProxyInfo proxy) {
  17. this.proxy = proxy;
  18. }
  19. public Socket createSocket(String host, int port) throws IOException,
  20. UnknownHostException {
  21. return socks5ProxifiedSocket(host, port);
  22. }
  23. public Socket createSocket(String host, int port, InetAddress localHost,
  24. int localPort) throws IOException, UnknownHostException {
  25. return socks5ProxifiedSocket(host, port);
  26. }
  27. public Socket createSocket(InetAddress host, int port) throws IOException {
  28. return socks5ProxifiedSocket(host.getHostAddress(), port);
  29. }
  30. public Socket createSocket(InetAddress address, int port,
  31. InetAddress localAddress, int localPort) throws IOException {
  32. return socks5ProxifiedSocket(address.getHostAddress(), port);
  33. }
  34. private Socket socks5ProxifiedSocket(String host, int port)
  35. throws IOException {
  36. Socket socket = null;
  37. InputStream in = null;
  38. OutputStream out = null;
  39. String proxy_host = proxy.getProxyAddress();
  40. int proxy_port = proxy.getProxyPort();
  41. String user = proxy.getProxyUsername();
  42. String passwd = proxy.getProxyPassword();
  43. try {
  44. socket = new Socket(proxy_host, proxy_port);
  45. in = socket.getInputStream();
  46. out = socket.getOutputStream();
  47. socket.setTcpNoDelay(true);
  48. byte[] buf = new byte[1024];
  49. int index = 0;
  50. /*
  51. * +----+----------+----------+ |VER | NMETHODS | METHODS |
  52. * +----+----------+----------+ | 1 | 1 | 1 to 255 |
  53. * +----+----------+----------+
  54. *
  55. * The VER field is set to X'05' for this version of the protocol.
  56. * The NMETHODS field contains the number of method identifier
  57. * octets that appear in the METHODS field.
  58. *
  59. * The values currently defined for METHOD are:
  60. *
  61. * o X'00' NO AUTHENTICATION REQUIRED o X'01' GSSAPI o X'02'
  62. * USERNAME/PASSWORD o X'03' to X'7F' IANA ASSIGNED o X'80' to X'FE'
  63. * RESERVED FOR PRIVATE METHODS o X'FF' NO ACCEPTABLE METHODS
  64. */
  65. buf[index++] = 5;
  66. buf[index++] = 2;
  67. buf[index++] = 0; // NO AUTHENTICATION REQUIRED
  68. buf[index++] = 2; // USERNAME/PASSWORD
  69. out.write(buf, 0, index);
  70. /*
  71. * The server selects from one of the methods given in METHODS, and
  72. * sends a METHOD selection message:
  73. *
  74. * +----+--------+ |VER | METHOD | +----+--------+ | 1 | 1 |
  75. * +----+--------+
  76. */
  77. // in.read(buf, 0, 2);
  78. fill(in, buf, 2);
  79. boolean check = false;
  80. switch ((buf[1]) & 0xff) {
  81. case 0: // NO AUTHENTICATION REQUIRED
  82. check = true;
  83. break;
  84. case 2: // USERNAME/PASSWORD
  85. if (user == null || passwd == null) {
  86. break;
  87. }
  88. /*
  89. * Once the SOCKS V5 server has started, and the client has
  90. * selected the Username/Password Authentication protocol, the
  91. * Username/Password subnegotiation begins. This begins with the
  92. * client producing a Username/Password request:
  93. *
  94. * +----+------+----------+------+----------+ |VER | ULEN |
  95. * UNAME | PLEN | PASSWD |
  96. * +----+------+----------+------+----------+ | 1 | 1 | 1 to 255
  97. * | 1 | 1 to 255 | +----+------+----------+------+----------+
  98. *
  99. * The VER field contains the current version of the
  100. * subnegotiation, which is X'01'. The ULEN field contains the
  101. * length of the UNAME field that follows. The UNAME field
  102. * contains the username as known to the source operating
  103. * system. The PLEN field contains the length of the PASSWD
  104. * field that follows. The PASSWD field contains the password
  105. * association with the given UNAME.
  106. */
  107. index = 0;
  108. buf[index++] = 1;
  109. buf[index++] = (byte) (user.length());
  110. System.arraycopy(user.getBytes(), 0, buf, index, user.length());
  111. index += user.length();
  112. buf[index++] = (byte) (passwd.length());
  113. System.arraycopy(passwd.getBytes(), 0, buf, index,
  114. passwd.length());
  115. index += passwd.length();
  116. out.write(buf, 0, index);
  117. /*
  118. * The server verifies the supplied UNAME and PASSWD, and sends
  119. * the following response:
  120. *
  121. * +----+--------+ |VER | STATUS | +----+--------+ | 1 | 1 |
  122. * +----+--------+
  123. *
  124. * A STATUS field of X'00' indicates success. If the server
  125. * returns a `failure' (STATUS value other than X'00') status,
  126. * it MUST close the connection.
  127. */
  128. // in.read(buf, 0, 2);
  129. fill(in, buf, 2);
  130. if (buf[1] == 0) {
  131. check = true;
  132. }
  133. break;
  134. default:
  135. }
  136. if (!check) {
  137. try {
  138. socket.close();
  139. } catch (Exception eee) {
  140. }
  141. throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
  142. "fail in SOCKS5 proxy");
  143. }
  144. /*
  145. * The SOCKS request is formed as follows:
  146. *
  147. * +----+-----+-------+------+----------+----------+ |VER | CMD |
  148. * RSV | ATYP | DST.ADDR | DST.PORT |
  149. * +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00'
  150. * | 1 | Variable | 2 |
  151. * +----+-----+-------+------+----------+----------+
  152. *
  153. * Where:
  154. *
  155. * o VER protocol version: X'05' o CMD o CONNECT X'01' o BIND X'02'
  156. * o UDP ASSOCIATE X'03' o RSV RESERVED o ATYP address type of
  157. * following address o IP V4 address: X'01' o DOMAINNAME: X'03' o IP
  158. * V6 address: X'04' o DST.ADDR desired destination address o
  159. * DST.PORT desired destination port in network octet order
  160. */
  161. index = 0;
  162. buf[index++] = 5;
  163. buf[index++] = 1; // CONNECT
  164. buf[index++] = 0;
  165. byte[] hostb = host.getBytes();
  166. int len = hostb.length;
  167. buf[index++] = 3; // DOMAINNAME
  168. buf[index++] = (byte) (len);
  169. System.arraycopy(hostb, 0, buf, index, len);
  170. index += len;
  171. buf[index++] = (byte) (port >>> 8);
  172. buf[index++] = (byte) (port & 0xff);
  173. out.write(buf, 0, index);
  174. /*
  175. * The SOCKS request information is sent by the client as soon as it
  176. * has established a connection to the SOCKS server, and completed
  177. * the authentication negotiations. The server evaluates the
  178. * request, and returns a reply formed as follows:
  179. *
  180. * +----+-----+-------+------+----------+----------+ |VER | REP |
  181. * RSV | ATYP | BND.ADDR | BND.PORT |
  182. * +----+-----+-------+------+----------+----------+ | 1 | 1 | X'00'
  183. * | 1 | Variable | 2 |
  184. * +----+-----+-------+------+----------+----------+
  185. *
  186. * Where:
  187. *
  188. * o VER protocol version: X'05' o REP Reply field: o X'00'
  189. * succeeded o X'01' general SOCKS server failure o X'02' connection
  190. * not allowed by ruleset o X'03' Network unreachable o X'04' Host
  191. * unreachable o X'05' Connection refused o X'06' TTL expired o
  192. * X'07' Command not supported o X'08' Address type not supported o
  193. * X'09' to X'FF' unassigned o RSV RESERVED o ATYP address type of
  194. * following address o IP V4 address: X'01' o DOMAINNAME: X'03' o IP
  195. * V6 address: X'04' o BND.ADDR server bound address o BND.PORT
  196. * server bound port in network octet order
  197. */
  198. // in.read(buf, 0, 4);
  199. fill(in, buf, 4);
  200. if (buf[1] != 0) {
  201. try {
  202. socket.close();
  203. } catch (Exception eee) {
  204. }
  205. throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,
  206. "server returns " + buf[1]);
  207. }
  208. switch (buf[3] & 0xff) {
  209. case 1:
  210. // in.read(buf, 0, 6);
  211. fill(in, buf, 6);
  212. break;
  213. case 3:
  214. // in.read(buf, 0, 1);
  215. fill(in, buf, 1);
  216. // in.read(buf, 0, buf[0]+2);
  217. fill(in, buf, (buf[0] & 0xff) + 2);
  218. break;
  219. case 4:
  220. // in.read(buf, 0, 18);
  221. fill(in, buf, 18);
  222. break;
  223. default:
  224. }
  225. return socket;
  226. } catch (RuntimeException e) {
  227. throw e;
  228. } catch (Exception e) {
  229. try {
  230. if (socket != null) {
  231. socket.close();
  232. }
  233. } catch (Exception eee) {
  234. }
  235. String message = "ProxySOCKS5: " + e.toString();
  236. if (e instanceof Throwable) {
  237. throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, message,
  238. (Throwable) e);
  239. }
  240. throw new IOException(message);
  241. }
  242. }
  243. private void fill(InputStream in, byte[] buf, int len) throws IOException {
  244. int s = 0;
  245. while (s < len) {
  246. int i = in.read(buf, s, len - s);
  247. if (i <= 0) {
  248. throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, "stream "
  249. + "is closed");
  250. }
  251. s += i;
  252. }
  253. }
  254. }