main.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. /*
  2. * kcptun server
  3. *
  4. * @build: ./configure --with-kcp && make clean && make kcptun examples
  5. * @tcp_server: bin/tcp_echo_server 1234
  6. * @kcptun_server: bin/kcptun_server -l :4000 -t 127.0.0.1:1234
  7. * @kcptun_client: bin/kcptun_client -l :8388 -r 127.0.0.1:4000
  8. * @tcp_client: bin/nc 127.0.0.1 8388
  9. * > hello
  10. * < hello
  11. */
  12. #define WITH_KCP 1
  13. #include "hversion.h"
  14. #include "hmain.h"
  15. #include "hsocket.h"
  16. #include "hloop.h"
  17. #include "../smux/smux.h"
  18. // config
  19. static const char* localaddr = ":4000";
  20. static const char* targetaddr = "127.0.0.1:8080";
  21. static const char* mode = "fast";
  22. static int mtu = 1350;
  23. static int sndwnd = 1024;
  24. static int rcvwnd = 1024;
  25. // short options
  26. static const char options[] = "hvdl:t:m:";
  27. // long options
  28. static const option_t long_options[] = {
  29. {'h', "help", NO_ARGUMENT},
  30. {'v', "version", NO_ARGUMENT},
  31. {'d', "daemon", NO_ARGUMENT},
  32. {'l', "listen", REQUIRED_ARGUMENT},
  33. {'t', "target", REQUIRED_ARGUMENT},
  34. {'m', "mode", REQUIRED_ARGUMENT},
  35. { 0, "mtu", REQUIRED_ARGUMENT},
  36. { 0, "sndwnd", REQUIRED_ARGUMENT},
  37. { 0, "rcvwnd", REQUIRED_ARGUMENT},
  38. };
  39. static const char detail_options[] = R"(
  40. -h|--help Print this information
  41. -v|--version Print version
  42. -d|--daemon Daemonize
  43. -l|--listen value kcp server listen address (default: ":4000")
  44. -t|--target value target server address (default: "127.0.0.1:8080")
  45. -m|--mode value profiles: fast3, fast2, fast, normal (default: "fast")
  46. --mtu value set maximum transmission unit for UDP packets (default: 1350)
  47. --sndwnd value set send window size(num of packets) (default: 1024)
  48. --rcvwnd value set receive window size(num of packets) (default: 1024)
  49. )";
  50. static void print_version() {
  51. printf("%s version %s\n", g_main_ctx.program_name, hv_compile_version());
  52. }
  53. static void print_help() {
  54. printf("Usage: %s [%s]\n", g_main_ctx.program_name, options);
  55. printf("Options:\n%s\n", detail_options);
  56. }
  57. static kcp_setting_t s_kcp_setting;
  58. static char kcp_host[64] = "0.0.0.0";
  59. static int kcp_port = 4000;
  60. static hio_t* kcp_io = NULL;
  61. static char target_host[64] = "127.0.0.1";
  62. static int target_port = 8080;
  63. static smux_config_t smux_config;
  64. static smux_session_t smux_session;
  65. static int verbose = 1;
  66. /* workflow:
  67. *
  68. * hloop_create_udp_server -> on_recvfrom ->
  69. *
  70. * SYN -> hloop_create_tcp_client ->
  71. * on_connect -> hio_write(kcp_io, SYN) ->
  72. * on_read -> hio_write(kcp_io) ->
  73. * on_close -> hio_write(kcp_io, FIN) -> smux_session_close_stream
  74. *
  75. * PSH -> smux_session_get_stream -> hio_write(stream_io)
  76. *
  77. * FIN -> smux_session_get_stream -> hio_close(stream_io)
  78. *
  79. */
  80. // hloop_create_udp_server -> hio_set_kcp -> on_recvfrom ->
  81. // SYN -> hloop_create_tcp_client -> smux_session_open_stream ->
  82. // PSH -> hio_write(io)
  83. static void on_close(hio_t* io) {
  84. // printf("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io));
  85. if (verbose) {
  86. char localaddrstr[SOCKADDR_STRLEN] = {0};
  87. char peeraddrstr[SOCKADDR_STRLEN] = {0};
  88. printf("disconnected connfd=%d [%s] => [%s]\n", hio_fd(io),
  89. SOCKADDR_STR(hio_localaddr(io), localaddrstr),
  90. SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
  91. }
  92. smux_stream_t* smux_stream = (smux_stream_t*)hevent_userdata(io);
  93. if (smux_stream == NULL) return;
  94. // FIN
  95. int packlen = smux_stream_output(smux_stream, SMUX_CMD_FIN);
  96. if (packlen > 0) {
  97. // printf("FIN %d\n", packlen);
  98. hio_write(kcp_io, smux_stream->wbuf.base, packlen);
  99. }
  100. // kill timer
  101. if (smux_stream->timer) {
  102. htimer_del(smux_stream->timer);
  103. smux_stream->timer = NULL;
  104. }
  105. // free buffer
  106. HV_FREE(smux_stream->rbuf.base);
  107. HV_FREE(smux_stream->wbuf.base);
  108. smux_session_close_stream(&smux_session, smux_stream->stream_id);
  109. hevent_set_userdata(io, NULL);
  110. }
  111. static void on_recv(hio_t* io, void* buf, int readbytes) {
  112. // printf("on_recv %.*s \n", readbytes, (char*)buf);
  113. smux_stream_t* smux_stream = (smux_stream_t*)hevent_userdata(io);
  114. if (smux_stream == NULL) return;
  115. // PSH
  116. smux_frame_t frame;
  117. smux_frame_init(&frame);
  118. frame.head.sid = smux_stream->stream_id;
  119. frame.head.cmd = SMUX_CMD_PSH;
  120. frame.head.length = readbytes;
  121. frame.data = (const char*)buf;
  122. int packlen = smux_frame_pack(&frame, smux_stream->wbuf.base, smux_stream->wbuf.len);
  123. if (packlen > 0) {
  124. // printf("PSH %d\n", packlen);
  125. int nwrite = hio_write(kcp_io, smux_stream->wbuf.base, packlen);
  126. // printf("PSH ret=%d\n", nwrite);
  127. }
  128. }
  129. static void on_connect(hio_t* io) {
  130. // printf("on_connect fd=%d\n", hio_fd(io));
  131. if (verbose) {
  132. char localaddrstr[SOCKADDR_STRLEN] = {0};
  133. char peeraddrstr[SOCKADDR_STRLEN] = {0};
  134. printf("connected connfd=%d [%s] => [%s]\n", hio_fd(io),
  135. SOCKADDR_STR(hio_localaddr(io), localaddrstr),
  136. SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
  137. }
  138. smux_stream_t* smux_stream = (smux_stream_t*)hevent_userdata(io);
  139. if (smux_stream == NULL) return;
  140. // SYN
  141. int packlen = smux_stream_output(smux_stream, SMUX_CMD_SYN);
  142. if (packlen > 0) {
  143. // printf("SYN %d\n", packlen);
  144. hio_write(kcp_io, smux_stream->wbuf.base, packlen);
  145. }
  146. hio_setcb_read(io, on_recv);
  147. hio_read(io);
  148. }
  149. static void on_kcp_recvfrom(hio_t* io, void* buf, int readbytes) {
  150. // printf("on_kcp_recvfrom %d\n", readbytes);
  151. smux_frame_t frame;
  152. smux_frame_init(&frame);
  153. int packlen = smux_frame_unpack(&frame, buf, readbytes);
  154. assert(packlen == readbytes);
  155. if (packlen < 0 ||
  156. frame.head.version > 2 ||
  157. frame.head.cmd > SMUX_CMD_UPD) {
  158. fprintf(stderr, "smux_frame_unpack error: %d\n", packlen);
  159. return;
  160. }
  161. // printf("smux sid=%u cmd=%d length=%d\n", frame.head.sid, (int)frame.head.cmd, (int)frame.head.length);
  162. smux_stream_t* smux_stream = NULL;
  163. if (frame.head.cmd == SMUX_CMD_SYN) {
  164. hio_t* target_io = hio_create_socket(hevent_loop(kcp_io), target_host, target_port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
  165. if (target_io == NULL) {
  166. fprintf(stderr, "create tcp client error!\n");
  167. return;
  168. }
  169. smux_stream = smux_session_open_stream(&smux_session, frame.head.sid, target_io);
  170. // alloc buffer
  171. smux_stream->rbuf.len = mtu;
  172. smux_stream->wbuf.len = mtu;
  173. HV_ALLOC(smux_stream->rbuf.base, smux_stream->rbuf.len);
  174. HV_ALLOC(smux_stream->wbuf.base, smux_stream->wbuf.len);
  175. hio_set_readbuf(target_io, smux_stream->rbuf.base, smux_config.max_frame_size);
  176. hevent_set_userdata(target_io, smux_stream);
  177. hio_setcb_connect(target_io, on_connect);
  178. hio_setcb_close(target_io, on_close);
  179. hio_connect(target_io);
  180. } else {
  181. smux_stream = smux_session_get_stream(&smux_session, frame.head.sid);
  182. }
  183. if (smux_stream == NULL) {
  184. if (frame.head.sid != 0 && frame.head.cmd != SMUX_CMD_FIN) {
  185. fprintf(stderr, "recvfrom invalid smux package!\n");
  186. }
  187. return;
  188. }
  189. switch (frame.head.cmd) {
  190. case SMUX_CMD_FIN:
  191. hio_close(smux_stream->io);
  192. break;
  193. case SMUX_CMD_PSH:
  194. hio_write(smux_stream->io, frame.data, frame.head.length);
  195. break;
  196. case SMUX_CMD_NOP:
  197. break;
  198. default:
  199. break;
  200. }
  201. }
  202. int main(int argc, char** argv) {
  203. if (argc < 2) {
  204. print_help();
  205. exit(0);
  206. }
  207. // g_main_ctx
  208. main_ctx_init(argc, argv);
  209. //int ret = parse_opt(argc, argv, options);
  210. int ret = parse_opt_long(argc, argv, long_options, ARRAY_SIZE(long_options));
  211. if (ret != 0) {
  212. print_help();
  213. exit(ret);
  214. }
  215. // help
  216. if (get_arg("h")) {
  217. print_help();
  218. exit(0);
  219. }
  220. // version
  221. if (get_arg("v")) {
  222. print_version();
  223. exit(0);
  224. }
  225. #ifdef OS_UNIX
  226. // daemon
  227. if (get_arg("d")) {
  228. // nochdir, noclose
  229. int ret = daemon(1, 1);
  230. if (ret != 0) {
  231. printf("daemon error: %d\n", ret);
  232. exit(-10);
  233. }
  234. }
  235. #endif
  236. const char* arg = get_arg("l");
  237. if (arg) {
  238. localaddr = arg;
  239. }
  240. arg = get_arg("t");
  241. if (arg) {
  242. targetaddr = arg;
  243. }
  244. arg = get_arg("m");
  245. if (arg) {
  246. mode = arg;
  247. }
  248. arg = get_arg("mtu");
  249. if (arg) {
  250. mtu = atoi(arg);
  251. }
  252. arg = get_arg("sndwnd");
  253. if (arg) {
  254. sndwnd = atoi(arg);
  255. }
  256. arg = get_arg("rcvwnd");
  257. if (arg) {
  258. rcvwnd = atoi(arg);
  259. }
  260. const char* pos = strchr(localaddr, ':');
  261. int len = 0;
  262. if (pos) {
  263. len = pos - localaddr;
  264. if (len > 0) {
  265. memcpy(kcp_host, localaddr, len);
  266. kcp_host[len] = '\0';
  267. }
  268. kcp_port = atoi(pos + 1);
  269. }
  270. pos = strchr(targetaddr, ':');
  271. if (pos) {
  272. len = pos - targetaddr;
  273. if (len > 0) {
  274. memcpy(target_host, targetaddr, len);
  275. target_host[len] = '\0';
  276. }
  277. target_port = atoi(pos + 1);
  278. }
  279. if (strcmp(mode, "normal") == 0) {
  280. kcp_setting_init_with_normal_mode(&s_kcp_setting);
  281. } else if (strcmp(mode, "fast") == 0) {
  282. kcp_setting_init_with_fast_mode(&s_kcp_setting);
  283. } else if (strcmp(mode, "fast2") == 0) {
  284. kcp_setting_init_with_fast2_mode(&s_kcp_setting);
  285. } else if (strcmp(mode, "fast3") == 0) {
  286. kcp_setting_init_with_fast3_mode(&s_kcp_setting);
  287. } else {
  288. fprintf(stderr, "Unknown mode '%s'\n", mode);
  289. exit(-20);
  290. }
  291. s_kcp_setting.mtu = mtu;
  292. s_kcp_setting.sndwnd = sndwnd;
  293. s_kcp_setting.rcvwnd = rcvwnd;
  294. printf("smux version: 1\n");
  295. printf("%s:%d => %s:%d\n", kcp_host, kcp_port, target_host, target_port);
  296. printf("mode: %s\n", mode);
  297. printf("mtu: %d\n", mtu);
  298. printf("sndwnd: %d rcvwnd: %d\n", sndwnd, rcvwnd);
  299. hloop_t* loop = hloop_new(0);
  300. kcp_io = hloop_create_udp_server(loop, kcp_host, kcp_port);
  301. if (kcp_io == NULL) {
  302. fprintf(stderr, "create udp server error!\n");
  303. return -20;
  304. }
  305. hio_set_kcp(kcp_io, &s_kcp_setting);
  306. hio_setcb_read(kcp_io, on_kcp_recvfrom);
  307. hio_read(kcp_io);
  308. // smux
  309. smux_config.max_frame_size = 1024;
  310. smux_session.next_stream_id = 0;
  311. hloop_run(loop);
  312. hloop_free(&loop);
  313. return 0;
  314. }