1
0

socks5_proxy_server.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /*
  2. * socks5 proxy server
  3. *
  4. * @build: make examples
  5. *
  6. * @proxy_server bin/socks5_proxy_server 1080
  7. * bin/socks5_proxy_server 1080 username password
  8. *
  9. * @proxy_client curl -v http://www.example.com/ --proxy socks5://127.0.0.1:1080
  10. * curl -v http://www.example.com/ --proxy socks5://username:password@127.0.0.1:1080
  11. *
  12. */
  13. #include "hv.h"
  14. #include "hloop.h"
  15. static char proxy_host[64] = "0.0.0.0";
  16. static int proxy_port = 1080;
  17. static const char* auth_username = NULL;
  18. static const char* auth_password = NULL;
  19. #define SOCKS5_VERSION ((uint8_t)5)
  20. #define SOCKS5_AUTH_VERSION ((uint8_t)1)
  21. #define SOCKS5_AUTH_SUCCESS ((uint8_t)0)
  22. #define SOCKS5_AUTH_FAILURE ((uint8_t)1)
  23. typedef enum {
  24. NoAuth = 0,
  25. GssApiAuth = 1,
  26. UserPassAuth = 2,
  27. } socks5_auth_method;
  28. typedef enum {
  29. ConnectCommand = 1,
  30. BindCommand = 2,
  31. AssociateCommand= 3,
  32. } socks5_command;
  33. typedef enum {
  34. IPv4Addr = 1,
  35. FqdnAddr = 3,
  36. IPv6Addr = 4,
  37. } socks5_addr_type;
  38. typedef enum {
  39. SuccessReply = 0,
  40. ServerFailure = 1,
  41. RuleFailure = 2,
  42. NetworkUnreachable = 3,
  43. HostUnreachable = 4,
  44. ConnectRefused = 5,
  45. TtlExpired = 6,
  46. CommandNotSupported = 7,
  47. AddrTypeNotSupported= 8,
  48. } socks5_reply_code;
  49. typedef enum {
  50. s_begin,
  51. s_auth_methods_count,
  52. s_auth_methods,
  53. s_auth_username_len,
  54. s_auth_username,
  55. s_auth_password_len,
  56. s_auth_password,
  57. s_request,
  58. s_dst_addr_type,
  59. s_dst_addr_len,
  60. s_dst_addr,
  61. s_dst_port,
  62. s_upstream,
  63. s_end,
  64. } socks5_state_e;
  65. typedef struct {
  66. hio_t* io;
  67. socks5_state_e state;
  68. socks5_addr_type addr_type;
  69. sockaddr_u addr;
  70. } socks5_conn_t;
  71. /*
  72. * workflow:
  73. * hloop_new -> hloop_create_tcp_server -> hloop_run
  74. * on_accept -> HV_ALLOC(socks5_conn_t) -> hio_readbytes(s_auth_methods_count:2) ->
  75. * on_recv -> hio_readbytes(s_auth_methods) ->
  76. * on_recv -> hio_write(auth_method) -> hio_readbytes(s_auth_username_len:2) ->
  77. * on_recv -> hio_readbytes(s_auth_username) ->
  78. * on_recv -> hio_readbytes(s_auth_password_len:1) ->
  79. * on_recv -> hio_readbytes(s_auth_password) -> hio_write(auth_result) ->
  80. * on_recv -> hio_readbytes(s_request:3) ->
  81. * on_recv -> hio_readbytes(addr_type:1) ->
  82. * on_recv -> hio_readbytes(addr_len:1) ->
  83. * on_recv -> hio_readbytes(addr) ->
  84. * on_recv -> hio_readbytes(port:2) ->
  85. * on_recv -> hio_setup_tcp_upstream(io, addr, port) ->
  86. * on_close -> hio_close_upstream -> HV_FREE(socks5_conn_t)
  87. *
  88. */
  89. static void on_upstream_connect(hio_t* upstream_io) {
  90. // printf("on_upstream_connect connfd=%d\n", hio_fd(upstream_io));
  91. socks5_conn_t* conn = (socks5_conn_t*)hevent_userdata(upstream_io);
  92. sockaddr_u* localaddr = (sockaddr_u*)hio_localaddr(upstream_io);
  93. uint8_t resp[32] = { SOCKS5_VERSION, SuccessReply, 0, IPv4Addr, 127,0,0,1, 0,80, 0 };
  94. int resp_len = 3;
  95. if (localaddr->sa.sa_family == AF_INET) {
  96. resp[resp_len++] = IPv4Addr;
  97. memcpy(resp + resp_len, &localaddr->sin.sin_addr, 4); resp_len += 4;
  98. memcpy(resp + resp_len, &localaddr->sin.sin_port, 2); resp_len += 2;
  99. } else if (localaddr->sa.sa_family == AF_INET6) {
  100. resp[resp_len++] = IPv6Addr;
  101. memcpy(resp + resp_len, &localaddr->sin6.sin6_addr, 16); resp_len += 16;
  102. memcpy(resp + resp_len, &localaddr->sin6.sin6_port, 2); resp_len += 2;
  103. }
  104. hio_write(conn->io, resp, resp_len);
  105. hio_setcb_read(upstream_io, hio_write_upstream);
  106. hio_setcb_read(conn->io, hio_write_upstream);
  107. hio_read(conn->io);
  108. hio_read(upstream_io);
  109. }
  110. static void on_close(hio_t* io) {
  111. // printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
  112. socks5_conn_t* conn = (socks5_conn_t*)hevent_userdata(io);
  113. if (conn) {
  114. hevent_set_userdata(io, NULL);
  115. HV_FREE(conn);
  116. }
  117. hio_close_upstream(io);
  118. }
  119. static void on_recv(hio_t* io, void* buf, int readbytes) {
  120. socks5_conn_t* conn = (socks5_conn_t*)hevent_userdata(io);
  121. const uint8_t* bytes = (uint8_t*)buf;
  122. switch(conn->state) {
  123. case s_begin:
  124. // printf("s_begin\n");
  125. conn->state = s_auth_methods_count;
  126. case s_auth_methods_count:
  127. // printf("s_auth_methods_count\n");
  128. {
  129. assert(readbytes == 2);
  130. uint8_t version = bytes[0];
  131. uint8_t methods_count = bytes[1];
  132. if (version != SOCKS5_VERSION || methods_count == 0) {
  133. fprintf(stderr, "Unsupprted socks version: %d\n", (int)version);
  134. hio_close(io);
  135. return;
  136. }
  137. conn->state = s_auth_methods;
  138. hio_readbytes(io, methods_count);
  139. }
  140. break;
  141. case s_auth_methods:
  142. // printf("s_auth_methods\n");
  143. {
  144. // TODO: check auth methos
  145. uint8_t auth_method = NoAuth;
  146. if (auth_username && auth_password) {
  147. auth_method = UserPassAuth;
  148. } else {
  149. // TODO: Implement more auth methods
  150. }
  151. // send auth mothod
  152. uint8_t resp[2] = { SOCKS5_VERSION, NoAuth };
  153. resp[1] = auth_method;
  154. hio_write(io, resp, 2);
  155. if (auth_method == NoAuth) {
  156. conn->state = s_request;
  157. hio_readbytes(io, 3);
  158. } else if (auth_method == UserPassAuth) {
  159. conn->state = s_auth_username_len;
  160. hio_readbytes(io, 2);
  161. }
  162. }
  163. break;
  164. case s_auth_username_len:
  165. // printf("s_auth_username_len\n");
  166. {
  167. assert(readbytes == 2);
  168. uint8_t auth_version = bytes[0];
  169. uint8_t username_len = bytes[1];
  170. if (auth_version != SOCKS5_AUTH_VERSION || username_len == 0) {
  171. fprintf(stderr, "Unsupported auth version: %d\n", (int)auth_version);
  172. hio_close(io);
  173. return;
  174. }
  175. conn->state = s_auth_username;
  176. hio_readbytes(io, username_len);
  177. }
  178. break;
  179. case s_auth_username:
  180. // printf("s_auth_username\n");
  181. {
  182. char* username = (char*)bytes;
  183. printf("username=%.*s\n", readbytes, username);
  184. if (readbytes != strlen(auth_username) ||
  185. strncmp(username, auth_username, readbytes) != 0) {
  186. fprintf(stderr, "User authentication failed!\n");
  187. uint8_t resp[2] = { SOCKS5_AUTH_VERSION, SOCKS5_AUTH_FAILURE };
  188. hio_write(io, resp, 2);
  189. hio_close(io);
  190. return;
  191. }
  192. conn->state = s_auth_password_len;
  193. hio_readbytes(io, 1);
  194. }
  195. break;
  196. case s_auth_password_len:
  197. // printf("s_auth_password_len\n");
  198. {
  199. assert(readbytes == 1);
  200. uint8_t password_len = bytes[0];
  201. if (password_len == 0) {
  202. fprintf(stderr, "Miss password\n");
  203. uint8_t resp[2] = { SOCKS5_AUTH_VERSION, SOCKS5_AUTH_FAILURE };
  204. hio_write(io, resp, 2);
  205. hio_close(io);
  206. return;
  207. }
  208. conn->state = s_auth_password;
  209. hio_readbytes(io, password_len);
  210. }
  211. break;
  212. case s_auth_password:
  213. // printf("s_auth_password\n");
  214. {
  215. char* password = (char*)bytes;
  216. printf("password=%.*s\n", readbytes, password);
  217. uint8_t resp[2] = { SOCKS5_AUTH_VERSION, SOCKS5_AUTH_SUCCESS };
  218. if (readbytes != strlen(auth_password) ||
  219. strncmp(password, auth_password, readbytes) != 0) {
  220. fprintf(stderr, "User authentication failed!\n");
  221. resp[1] = SOCKS5_AUTH_FAILURE;
  222. hio_write(io, resp, 2);
  223. hio_close(io);
  224. return;
  225. }
  226. hio_write(io, resp, 2);
  227. conn->state = s_request;
  228. hio_readbytes(io, 3);
  229. }
  230. break;
  231. case s_request:
  232. // printf("s_request\n");
  233. {
  234. assert(readbytes == 3);
  235. uint8_t version = bytes[0];
  236. uint8_t cmd = bytes[1];
  237. if (version != SOCKS5_VERSION || cmd != ConnectCommand) {
  238. // TODO: Implement other commands
  239. fprintf(stderr, "Unsupported command: %d\n", (int)cmd);
  240. hio_close(io);
  241. return;
  242. }
  243. conn->state = s_dst_addr_type;
  244. hio_readbytes(io, 1);
  245. }
  246. break;
  247. case s_dst_addr_type:
  248. // printf("s_dst_addr_type\n");
  249. {
  250. assert(readbytes == 1);
  251. conn->addr_type = (socks5_addr_type)bytes[0];
  252. if (conn->addr_type == IPv4Addr) {
  253. conn->state = s_dst_addr;
  254. hio_readbytes(io, 4);
  255. } else if (conn->addr_type == FqdnAddr) {
  256. conn->state = s_dst_addr_len;
  257. hio_readbytes(io, 1);
  258. } else if (conn->addr_type == IPv6Addr) {
  259. conn->state = s_dst_addr;
  260. hio_readbytes(io, 16);
  261. } else {
  262. fprintf(stderr, "Unsupported addr type: %d\n", (int)conn->addr_type);
  263. hio_close(io);
  264. return;
  265. }
  266. }
  267. break;
  268. case s_dst_addr_len:
  269. // printf("s_dst_addr_len\n");
  270. {
  271. uint8_t addr_len = bytes[0];
  272. if (addr_len == 0) {
  273. fprintf(stderr, "Miss domain!\n");
  274. hio_close(io);
  275. return;
  276. }
  277. conn->state = s_dst_addr;
  278. hio_readbytes(io, addr_len);
  279. }
  280. break;
  281. case s_dst_addr:
  282. // printf("s_dst_addr\n");
  283. {
  284. if (conn->addr_type == IPv4Addr) {
  285. assert(readbytes == 4);
  286. conn->addr.sa.sa_family = AF_INET;
  287. memcpy(&conn->addr.sin.sin_addr, bytes, 4);
  288. } else if (conn->addr_type == IPv6Addr) {
  289. assert(readbytes == 16);
  290. conn->addr.sa.sa_family = AF_INET6;
  291. memcpy(&conn->addr.sin6.sin6_addr, bytes, 16);
  292. } else {
  293. char* host = NULL;
  294. STACK_OR_HEAP_ALLOC(host, readbytes + 1, 256);
  295. memcpy(host, bytes, readbytes);
  296. host[readbytes] = '\0';
  297. // TODO: async DNS
  298. int ret = ResolveAddr(host, &conn->addr);
  299. STACK_OR_HEAP_FREE(host);
  300. if (ret != 0) {
  301. fprintf(stderr, "Resolve %.*s failed!\n", readbytes, (char*)bytes);
  302. hio_close(io);
  303. return;
  304. }
  305. }
  306. conn->state = s_dst_port;
  307. hio_readbytes(io, 2);
  308. }
  309. break;
  310. case s_dst_port:
  311. // printf("s_dst_port\n");
  312. {
  313. assert(readbytes == 2);
  314. uint16_t port = ((uint16_t)bytes[0]) << 8 | bytes[1];
  315. // printf("port=%d\n", port);
  316. sockaddr_set_port(&conn->addr, port);
  317. hloop_t* loop = hevent_loop(io);
  318. // hio_t* upstream_io = hio_setup_tcp_upstream(io, conn->host, conn->port, 0);
  319. // hio_t* upstream_io = hio_create_socket(loop, conn->host, conn->port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
  320. int sockfd = socket(conn->addr.sa.sa_family, SOCK_STREAM, 0);
  321. if (sockfd < 0) {
  322. perror("socket");
  323. hio_close(io);
  324. return;
  325. }
  326. hio_t* upstream_io = hio_get(loop, sockfd);
  327. assert(upstream_io != NULL);
  328. hio_set_peeraddr(upstream_io, &conn->addr.sa, sockaddr_len(&conn->addr));
  329. hevent_set_userdata(upstream_io, conn);
  330. // io <=> upstream_io
  331. hio_setup_upstream(io, upstream_io);
  332. hio_setcb_connect(upstream_io, on_upstream_connect);
  333. hio_setcb_close(upstream_io, hio_close_upstream);
  334. conn->state = s_upstream;
  335. // printf("connect to ");
  336. // SOCKADDR_PRINT(hio_peeraddr(upstream_io));
  337. hio_connect(upstream_io);
  338. }
  339. break;
  340. case s_upstream:
  341. hio_write_upstream(io, buf, readbytes);
  342. break;
  343. case s_end:
  344. break;
  345. default:
  346. break;
  347. }
  348. }
  349. static void on_accept(hio_t* io) {
  350. /*
  351. printf("on_accept connfd=%d\n", hio_fd(io));
  352. char localaddrstr[SOCKADDR_STRLEN] = {0};
  353. char peeraddrstr[SOCKADDR_STRLEN] = {0};
  354. printf("accept connfd=%d [%s] <= [%s]\n", hio_fd(io),
  355. SOCKADDR_STR(hio_localaddr(io), localaddrstr),
  356. SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
  357. */
  358. hio_setcb_read(io, on_recv);
  359. hio_setcb_close(io, on_close);
  360. socks5_conn_t* conn = NULL;
  361. HV_ALLOC_SIZEOF(conn);
  362. conn->io = io;
  363. hevent_set_userdata(io, conn);
  364. // start read
  365. conn->state = s_auth_methods_count;
  366. hio_readbytes(io, 2);
  367. }
  368. int main(int argc, char** argv) {
  369. if (argc < 2) {
  370. printf("Usage: %s proxy_port [username] [password]\n", argv[0]);
  371. return -10;
  372. }
  373. proxy_port = atoi(argv[1]);
  374. if (argc > 3) {
  375. auth_username = argv[2];
  376. auth_password = argv[3];
  377. }
  378. hloop_t* loop = hloop_new(0);
  379. hio_t* listenio = hloop_create_tcp_server(loop, proxy_host, proxy_port, on_accept);
  380. if (listenio == NULL) {
  381. return -20;
  382. }
  383. printf("socks5 proxy server listening on %s:%d, listenfd=%d\n", proxy_host, proxy_port, hio_fd(listenio));
  384. hloop_run(loop);
  385. hloop_free(&loop);
  386. return 0;
  387. }