技术宅的结界

 找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 2168|回复: 4
收起左侧

【C】一个跨平台TCPIP协议通讯的例子,网络包裹转发代理,非阻塞IO,单线程,可实用

[复制链接]

1049

主题

2342

帖子

6万

积分

用户组: 管理员

一只技术宅

UID
1
精华
218
威望
294 点
宅币
18483 个
贡献
37875 次
宅之契约
0 份
在线时间
1760 小时
注册时间
2014-1-26
发表于 2017-1-16 00:25:22 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有帐号?立即注册→加入我们

x
原文链接:https://www.0xaa55.com/thread-1991-1-1.html
转载请保留出处。

我运营着一个正版Minecraft服务器,我本人在国外,而服务器在国内,服务器虽然是双线的,但我无论是通过电信还是联通的线路去连接它,延迟都很大,电信线路基本上260ms到1200ms不等,有时候能到4000ms甚至12000ms(在点进“多人游戏”界面后,看到的数值就是这么大)。而联通线路则是平时都500ms以上,偶尔电信gg的时候联通可以降到300ms左右,虽然“据说”联通比较容易连接国外,但我这边的效果就是,联通并不能联通……

关键是,我的数据发到服务器很快,而服务器的数据发到我这却延迟很大,通过在游戏界面打字说话可以看出,我在某个时间段发的话,在对应时间段插入到整体聊天记录的时候,我并不能看到我打的字,但其他玩家能看到。也就是上传快,响应慢。

我买了一个韩国VPN,当我设置了Win7的VPN连接以后,总体延迟稳定在了260ms到350ms左右,偶尔还是比单独连接差一些,但至少不会突然给我掉到1000ms左右。
vpn.png

不过这个并不是一个好的解决办法,用了VPN后,我机器上所有的网络出口就都是这个VPN了,包括我的虚拟机里的一堆东西,也是只能走VPN出去,但我只想让minecraft走VPN出去。。
正好手上有个闲置的亚马逊AWS,免费试用的那种,或许我可以让它连上VPN后,我再让它帮我把minecraft的网络包裹通过VPN转发到国内,估计速度会不错。
于是我先统计了一下各个peer之间响应的情况,结果如下:

我ping minecraft服务器的延迟是280ms左右,但下午6点之后就会到1000ms以上,并且保持到晚上22点……。
我ping我的亚马逊AWS的服,7ms,毫无波动。
我ping我的VPN,延迟50ms左右,偶尔上升到70ms,高峰期会到150ms。
我的亚马逊AWS服ping我的VPN,延迟40ms左右,偶尔到45ms。
我的亚马逊AWS服ping minecraft服务器,延迟平均270ms,如果持续一段时间没有ping它,然后再ping的话,延迟会增加到350ms
我的亚马逊AWS服连接我的VPN后ping minecraft服务器,延迟平均162ms,偶尔上下波动3ms左右。

于是我写了个C语言的TCPIP包裹转发器,让它在我的亚马逊AWS上运行,然后我让我的亚马逊AWS连接VPN,并且设置出口到我的minecraft服务器的所有地址都走VPN,这样我的程序就可以把包裹转发进VPN了。
这其中为了测试它的效果,我把它写成了能在Windows运行的版本,只不过在Windows里面它并不是一个Daemon,也不是一个Service,而是一个控制台程序,不断地用printf来打印连接与断开的情况。
顺带我已经把这玩意儿搞到github上了:https://github.com/0xAA55/tcpfwd

这里面有很多坑,是需要Windows开发者编写跨平台的Linux程序所需要注意的。

1、头文件的有无的问题,这个请看https://www.0xaa55.com/thread-1997-1-1.html
2、Linux如何写Daemon守护进程(也就是服务),这个不难,但输出信息到Log文件对于调试不是十分直观。建议调试的时候,不写成daemon,而是直接把内容输出到stdout,然后用screen把它单独弄成一个窗口,可随时观看。
·因为你要是写了个呆萌,然后用less +F看log的话,你的呆萌挂掉了你都不知道,详见第五条。
3、Linux的socket套接字是一个文件描述符,Windows的不是。
4、Linux的socket编程,错误码看errno,Windows看WSAGetLastError(),注意返回的数值不同,Windows的有个WSAE开头。Windows的socket编程,错误码不会被写入到errno。
5、有一个可能让Windows开发者崩溃的神坑:Linux下如果你send或recv的那个TCPIP套接字的连接被断开了,你会收到一个SIGPIPE信号,也就是“管道断开”,你要是不处理它,它默认给你exit(0),直接杀进程。
6、用非阻塞IO套接字,轮询(poll)所有的套接字接受数据的时候,如果你想释放CPU使用率,注意Windows的Sleep()按毫秒算,Linux的sleep()按秒算。其次是Linux写sleep(0)没啥问题,Windows写Sleep(0)会在Win98系统里永久地睡下去。。。因此Windows里写Sleep(1)比较安全。然后,你要是用了#define sleep Sleep,并且参数给的是(1)的话,到时候延迟上千不是梦。。。

源码:tcpfwd.c
  1. //#include"config.h" // tired to configure

  2. // These we could have
  3. #include<stdio.h>
  4. #include<stdlib.h>
  5. #include<stdint.h>
  6. #include<stdarg.h>
  7. #include<errno.h>
  8. #include<ctype.h>
  9. #include<signal.h>
  10. #include<string.h>
  11. #include<time.h>

  12. // There's something we have and there's something we didn't have
  13. #ifdef WIN32
  14. #include<WinSock2.h>
  15. #include<ws2tcpip.h> // for using struct sockaddr_in6 ... but now it didn't support IPv6
  16. #include<xmmintrin.h>
  17. #else
  18. #include<fcntl.h>
  19. #include<unistd.h>
  20. #include<arpa/inet.h>
  21. #include<sys/socket.h>
  22. #include<netinet/ip.h>
  23. #include<netinet/tcp.h>
  24. #endif

  25. #define EXEC_NAME   "tcpfwd"                        // program name
  26. #define LOCK_FILE        "/tmp/tcpfwd.lock"        // PID file name
  27. #define LOG_FILE        "tcpfwd.log"                // log file name
  28. #define CFG_FILE        "tcpfwd.conf"                // configure file name

  29. #define backlog_default 200                                // default max connections
  30. #define num_conn_alloc 32                                // how much units allocated when RAM usage grows
  31. #define fwd_buffer_size 8192                        // forward buffer size
  32. #define def_packet_size 1472                        // mtu for splitting packets

  33. #define MAX_BACKOFF_ITERS (RAND_MAX > 0x10000 ? 0x10000 : RAND_MAX)
  34. #define MAX_BO_SLEEPS 20

  35. typedef int bool_t, *bool_p;

  36. // my address structure
  37. typedef union address_u
  38. {
  39.     struct sockaddr sa;
  40.     struct sockaddr_in sa_in;
  41.     struct sockaddr_in6 sa_in6;
  42.     struct sockaddr_storage sa_stor;
  43. }address_t, *address_p;

  44. #ifdef WIN32
  45. #define cpu_relax() _mm_pause()
  46. typedef INT_PTR ssize_t; // signed size_t in windows
  47. typedef int socklen_t;
  48. static void sleep_relax(unsigned sleeps)
  49. {
  50.         Sleep(sleeps); // In windows, Sleep(0) will die in win98
  51. }
  52. static int _socket_errno() // Translate error number
  53. {
  54.         int Err = WSAGetLastError();
  55.         switch(Err)
  56.         {
  57.         case WSAEADDRINUSE:
  58.                 return EADDRINUSE;
  59.         case WSAEADDRNOTAVAIL:
  60.                 return EADDRNOTAVAIL;
  61.         case WSAEAFNOSUPPORT:
  62.                 return EAFNOSUPPORT;
  63.         case WSAEALREADY:
  64.                 return EALREADY;
  65.         case WSAECONNABORTED:
  66.                 return ECONNABORTED;
  67.         case WSAECONNREFUSED:
  68.                 return ECONNREFUSED;
  69.         case WSAECONNRESET:
  70.                 return ECONNRESET;
  71.         case WSAEDESTADDRREQ:
  72.                 return EDESTADDRREQ;
  73.         case WSAEHOSTUNREACH:
  74.                 return EHOSTUNREACH;
  75.         case WSAEINPROGRESS:
  76.                 return EINPROGRESS;
  77.         case WSAEISCONN:
  78.                 return EISCONN;
  79.         case WSAELOOP:
  80.                 return ELOOP;
  81.         case WSAEMSGSIZE:
  82.                 return EMSGSIZE;
  83.         case WSAENETDOWN:
  84.                 return ENETDOWN;
  85.         case WSAENETRESET:
  86.                 return ENETRESET;
  87.         case WSAENETUNREACH:
  88.                 return ENETUNREACH;
  89.         case WSAENOBUFS:
  90.                 return ENOBUFS;
  91.         case WSAENOPROTOOPT:
  92.                 return ENOPROTOOPT;
  93.         case WSAENOTCONN:
  94.                 return ENOTCONN;
  95.         case WSAENOTSOCK:
  96.                 return ENOTSOCK;
  97.         case WSAEOPNOTSUPP:
  98.                 return EOPNOTSUPP;
  99.         case WSAEPROTONOSUPPORT:
  100.                 return EPROTONOSUPPORT;
  101.         case WSAEPROTOTYPE:
  102.                 return EPROTOTYPE;
  103.         case WSAETIMEDOUT:
  104.                 return ETIMEDOUT;
  105.         case WSAEWOULDBLOCK:
  106.                 return EWOULDBLOCK;
  107.         default:
  108.                 return Err;
  109.         }
  110. }
  111. #define socket_errno _socket_errno() // pretend as a variable
  112. #define MSG_NOSIGNAL 0 // Windows didn't have this, pretend as we have
  113. #else // we don't think about what other systems, just for unix
  114. #if __x86_64__ || i386
  115. #define cpu_relax() __asm__ __volatile__("pause")
  116. #elif __arm__ || __aarch64__
  117. #define cpu_relax() __asm__ __volatile__("yield")
  118. #elif __mips__
  119. #define cpu_relax() __asm__ __volatile__(".word 0x00000140")
  120. #else
  121. #define cpu_relax() __asm__ __volatile__("pause")
  122. #endif
  123. static void sleep_relax(unsigned sleeps)
  124. {
  125.         if(sleeps)
  126.                 usleep(sleeps * 1000);
  127.         else
  128.                 usleep(500);
  129. }
  130. #define socket_errno errno
  131. static int closesocket(int sockfd) // sockets in linux is a file descriptor
  132. {
  133.         return close(sockfd);
  134. }
  135. #define _tzset tzset
  136. static char*_strtime(char*timestr)
  137. {
  138.         time_t now;
  139.         struct tm *l_time;
  140.         static char _timestr[10] = {0};
  141.        
  142.         now = time(NULL);
  143.         l_time = localtime(&now);
  144.         sprintf(_timestr, "%.2d:%.2d:%.2d", l_time->tm_hour, l_time->tm_min, l_time->tm_sec);
  145.         strncpy(timestr, _timestr, sizeof _timestr);
  146.         return _timestr;
  147. }
  148. #endif

  149. //=============================================================================
  150. // socket connection
  151. typedef struct fwdconn_struct
  152. {
  153.         int sockfd_src; // source
  154.         int sockfd_dst; // destination
  155.         bool_t connected; // was that connected?
  156.         char buffer_src[fwd_buffer_size]; // data from source
  157.         int cb_src; // data bytes
  158.         char buffer_dst[fwd_buffer_size]; // data from destination
  159.         int cb_dst; // data bytes
  160.         address_t addr_from; // source address
  161.         socklen_t addr_size; // source address length
  162. }fwdconn_t, *fwdconn_p;

  163. typedef struct fwdroute_struct
  164. {
  165.         int listen_socket; // a socket for accepting connections
  166.        
  167.         int import; // listening port
  168.         int export; // output port
  169.         unsigned packet_size_to_src; // mtu split size for source
  170.         unsigned packet_size_to_dst; // mtu split size for destination
  171.         int status_of_tcp_nodelay; // use nagle algorithm? 1 for no, 0 for yes
  172.        
  173.         address_t DestAddr; // destination address
  174.         socklen_t cbDestAddr; // destination address length
  175.        
  176.         fwdconn_p fwd_conn; // active connection array
  177.         size_t num_fwd_conn; // how many
  178.         size_t max_fwd_conn; // array capacity
  179. }fwdroute_t, *fwdroute_p;

  180. typedef struct fwdinst_struct
  181. {
  182.         fwdroute_p        pRoute; // routers
  183.         size_t                num_route; // how many
  184.         size_t                max_route; // array capacity
  185.        
  186.         int                        listen_backlog; // max connections
  187.         bool_t                log_traffic; // do we log any packets?
  188. }fwdinst_t, *fwdinst_p;

  189. typedef struct bo_struct
  190. {
  191.         unsigned cr;
  192.         unsigned sr;
  193.         unsigned max_cr;
  194.         unsigned max_sr;
  195. }bo_t, *bo_p;

  196. static void bo_reset(bo_p bo)
  197. {
  198.         memset(bo, 0, sizeof *bo);
  199.         bo->max_cr = MAX_BACKOFF_ITERS;
  200.         bo->max_sr = MAX_BO_SLEEPS;
  201. }
  202. static void bo_update(bo_p bo)
  203. {
  204.         int s = 0;
  205.        
  206.         if(bo->cr < bo->max_cr)
  207.         {
  208.                 if(!bo->cr) bo->cr = 1;
  209.                 else bo->cr <<= 1;
  210.         }
  211.         else
  212.         {
  213.                 bo->cr = bo->max_cr;
  214.                 s = 1;
  215.         }
  216.        
  217.         if(s)
  218.         {
  219.                 if(bo->sr < bo->max_sr)
  220.                 {
  221.                         if(!bo->sr) bo->sr = 1;
  222.                         else bo->sr <<= 1;
  223.                 }
  224.                 else bo->sr = bo->max_sr;
  225.                 sleep_relax(rand() % bo->sr);
  226.         }
  227.         else
  228.         {
  229.                 int r = rand() % bo->cr + 1;
  230.                 while(r--) cpu_relax();
  231.         }
  232. }

  233. static volatile bool_t _g_Term = 0; // global quitting?
  234. static bool_t _g_LogToScreen = 1; // do we write log to screen

  235. void Inst_Log(char *message, ...);
  236. fwdinst_p Inst_Create(bool_t DoDaemonize, const char *cfg_file);
  237. int Inst_Run(fwdinst_p pInst);
  238. void Inst_Term(fwdinst_p pInst);

  239. static int _Inst_Daemonize();
  240. static bool_t _SetSocketBlockingEnabled(int fd, int blocking);
  241. static int _CreateNBIOTCPSocket();
  242. static void _MakeIPv4Address(address_p pOut, uint32_t IP, uint16_t Port);
  243. static bool_t _ListenPort(fwdinst_p pInst, int socket, int port);
  244. static bool_t _Inst_LoadCFG(fwdinst_p pInst, const char*cfg_file);
  245. static fwdroute_p _Inst_AddRoute
  246. (
  247.         fwdinst_p pInst,
  248.         int import,
  249.         uint32_t exportAddr,
  250.         int exportPort,
  251.         int status_of_tcp_nodelay,
  252.         int packet_size_to_src,
  253.         int packet_size_to_dst
  254. );
  255. static fwdconn_p _Inst_AddNewConnection
  256. (
  257.         fwdroute_p pRoute,
  258.         int sockfd,
  259.         address_p pAddr,
  260.         socklen_t cbAddr
  261. );
  262. static void _Inst_BreakConnection(fwdroute_p pRoute, size_t ic);
  263. static uint32_t _IpAddrV4ByNums(int n1, int n2, int n3, int n4);
  264. static bool_t _Inst_LoadCFG(fwdinst_p pInst, const char*cfg_file);
  265. static void _signal_handler(int sig);

  266. //==============================================================================
  267. //Func: _SetSocketBlockingEnabled
  268. //Desc: make a socket works for polling
  269. //------------------------------------------------------------------------------
  270. static bool_t _SetSocketBlockingEnabled(int fd, int blocking)
  271. {
  272.         if (fd < 0)
  273.                 return 0;

  274. #ifdef WIN32
  275.         {
  276.                 unsigned long mode = blocking ? 0 : 1;
  277.                 if(ioctlsocket(fd, FIONBIO, &mode) == 0)
  278.                         return 1;
  279.                 else
  280.                 {
  281.                         Inst_Log("Set socket to non blocking I/O mode failed. %d\n",
  282.                                 socket_errno);
  283.                         return 0;
  284.                 }
  285.         }
  286. #else
  287.         int flags = fcntl(fd, F_GETFL, 0);
  288.         if(flags < 0)return 0;
  289.         flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
  290.         if(!fcntl(fd, F_SETFL, flags))
  291.                 return 1;
  292.         else
  293.         {
  294.                 Inst_Log("Set socket to non blocking I/O mode failed.\n");
  295.                 return 0;
  296.         }
  297. #endif
  298. }

  299. //==============================================================================
  300. //Func: _signal_handler
  301. //Desc: signal handler
  302. //------------------------------------------------------------------------------
  303. static void _signal_handler(int sig)
  304. {
  305.         switch(sig)
  306.         {
  307. #ifndef WIN32
  308.         case SIGHUP:
  309.                 break;
  310. #endif
  311.         case SIGTERM:
  312.                 Inst_Log("Terminate signal.\n");
  313.                 _g_Term = 1;
  314.                 break;
  315.         }
  316. }

  317. void Inst_LogTime()
  318. {
  319.         char szTime[128] = {0};
  320.         _strtime(szTime);
  321.         Inst_Log("[%s]: ", szTime);
  322. }

  323. //==============================================================================
  324. //Func: Inst_Log
  325. //Desc: Print log to a specified file.
  326. //------------------------------------------------------------------------------
  327. void Inst_Log(char *format, ...)
  328. {
  329.         FILE *logfile = NULL;
  330.         va_list ap;
  331.        
  332.         logfile=fopen(LOG_FILE,"a");
  333.         if(logfile)
  334.         {
  335.                 va_start(ap, format);
  336.                 vfprintf(logfile, format, ap);
  337.                 va_end(ap);
  338.                 fclose(logfile);
  339.         }

  340.         if(_g_LogToScreen)
  341.         {
  342.                 va_start(ap, format);
  343.                 vprintf(format, ap);
  344.                 va_end(ap);
  345.         }
  346. }

  347. //==============================================================================
  348. //Func: Inst_LogAddrV4
  349. //Desc: Print an IPv4 address to log
  350. //------------------------------------------------------------------------------
  351. void Inst_LogAddrV4(const address_p pAddr)
  352. {
  353.         char strIp[INET_ADDRSTRLEN + 1];

  354. #ifdef WIN32
  355.         sprintf(strIp, "%u.%u.%u.%u",
  356.                 pAddr->sa_in.sin_addr.S_un.S_un_b.s_b1,
  357.                 pAddr->sa_in.sin_addr.S_un.S_un_b.s_b2,
  358.                 pAddr->sa_in.sin_addr.S_un.S_un_b.s_b3,
  359.                 pAddr->sa_in.sin_addr.S_un.S_un_b.s_b4);
  360. #else
  361.         inet_ntop(AF_INET, &pAddr->sa_in.sin_addr,
  362.                 strIp, INET_ADDRSTRLEN);
  363. #endif
  364.                
  365.         Inst_Log("%s:%u", strIp, htons(pAddr->sa_in.sin_port));
  366. }

  367. //==============================================================================
  368. //Func: Inst_Create
  369. //Desc: Create a new instance class
  370. //------------------------------------------------------------------------------
  371. fwdinst_p Inst_Create(bool_t DoDaemonize, const char*cfg_file)
  372. {
  373.         fwdinst_p pInst = NULL;

  374.         _tzset();
  375.        
  376.         if(DoDaemonize && !_Inst_Daemonize())
  377.                 return NULL;
  378.        
  379.         pInst = malloc(sizeof(fwdinst_t));
  380.         if(!pInst)
  381.         {
  382.                 Inst_LogTime();
  383.                 Inst_Log("Out of memory.\n");
  384.                 return NULL;
  385.         }
  386.         memset(pInst, 0, sizeof(fwdinst_t));
  387.        
  388.         Inst_LogTime();
  389.         Inst_Log("Starting "EXEC_NAME"\n");

  390.         if(!_Inst_LoadCFG(pInst, cfg_file))
  391.                 goto ErrHandler;
  392.        
  393.         return pInst;
  394. ErrHandler:
  395.         Inst_Term(pInst);
  396.         return NULL;
  397. }

  398. //==============================================================================
  399. //Func: _CreateNBIOTCPSocket
  400. //Desc: Create a new socket, and set it to non blocking I/O mode
  401. //------------------------------------------------------------------------------
  402. static int _CreateNBIOTCPSocket()
  403. {
  404.         int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  405.         if(fd == -1)
  406.         {
  407.                 Inst_LogTime();
  408.                 Inst_Log("Create socket failed. %d\n", socket_errno);
  409.                 return-1;
  410.         }
  411.        
  412.         if(!_SetSocketBlockingEnabled(fd, 0))
  413.         {
  414.                 closesocket(fd);
  415.                 return -1;
  416.         }
  417.         return fd;
  418. }

  419. //==============================================================================
  420. //Func: _CreateNBIOTCPSocketWithConnection
  421. //Desc: Create a new socket with connection, and set it to non blocking I/O mode
  422. //------------------------------------------------------------------------------
  423. static int _CreateNBIOTCPSocketWithConnection
  424. (
  425.         const address_p addr,
  426.         socklen_t addrlen
  427. )
  428. {
  429.         int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  430.         if(fd == -1)
  431.         {
  432.                 Inst_LogTime();
  433.                 Inst_Log("Create socket failed.\n");
  434.                 return-1;
  435.         }
  436.        
  437.         if(!_SetSocketBlockingEnabled(fd, 0))
  438.         {
  439.                 closesocket(fd);
  440.                 return -1;
  441.         }
  442.        
  443.         Inst_LogTime();
  444.         Inst_Log("Connecting ");
  445.         Inst_LogAddrV4(addr);
  446.         Inst_Log(" ...\n");

  447.         if(connect(fd, &addr->sa, addrlen) < 0)
  448.         {
  449.                 int Err = socket_errno;
  450.                 switch(Err)
  451.                 {
  452.                 case EINPROGRESS:
  453. #if EWOULDBLOCK != EAGAIN
  454.                 case EWOULDBLOCK:
  455. #endif
  456.                 case EAGAIN:
  457.                         break;
  458.                 default:
  459.                         Inst_LogTime();
  460.                         Inst_Log("Connect ");
  461.                         Inst_LogAddrV4(addr);
  462.                         Inst_Log(" failed: %d.\n", Err);
  463.                         closesocket(fd);
  464.                         return -1;
  465.                 }
  466.         }
  467.        
  468.         return fd;
  469. }

  470. //==============================================================================
  471. //Func: _MakeIPv4Address
  472. //Desc: Make an IPv4 address by using given IP:Port
  473. //------------------------------------------------------------------------------
  474. static void _MakeIPv4Address(address_p pOut, uint32_t IP, uint16_t Port)
  475. {
  476.         memset(pOut, 0, sizeof(pOut->sa_in));
  477.        
  478.         pOut->sa_in.sin_family = AF_INET;
  479.         pOut->sa_in.sin_port = htons(Port);
  480.         pOut->sa_in.sin_addr.s_addr = htonl(IP);
  481. }

  482. //==============================================================================
  483. //Func: _ListenPort
  484. //Desc: Let a socket listen to a specified port.
  485. //------------------------------------------------------------------------------
  486. static bool_t _ListenPort(fwdinst_p pInst, int sockfd, int port)
  487. {
  488.         address_t Addr;
  489.         int reuse_addr = 1;
  490.         _MakeIPv4Address(&Addr, 0, port);

  491.         if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr)) < 0)
  492.         {
  493.                 Inst_LogTime();
  494.                 Inst_Log("Warning: set SO_REUSEADDR failed. %d\n", socket_errno);
  495.                 return 0;
  496.         }

  497.         if(bind(sockfd, &Addr.sa, sizeof(Addr.sa)) < 0)
  498.         {
  499.                 Inst_LogTime();
  500.                 Inst_Log("Bind port %d failed. %d\n", port, socket_errno);
  501.                 return 0;
  502.         }
  503.        
  504.         if(listen(sockfd, pInst->listen_backlog) < 0)
  505.         {
  506.                 int Err = socket_errno;
  507.                 switch(Err)
  508.                 {
  509.                 case EADDRINUSE:
  510.                         Inst_LogTime();
  511.                         Inst_Log("Listen to port failed: port already in use.\n");
  512.                         return 0;
  513.                 default:
  514.                         Inst_LogTime();
  515.                         Inst_Log("Listen to port failed: %d\n", Err);
  516.                         return 0;
  517.                 }
  518.         }
  519.        
  520.         Inst_LogTime();
  521.         Inst_Log("Listening to port %d.\n", port);

  522.         return 1;
  523. }

  524. //==============================================================================
  525. //Func: _Inst_AddRoute
  526. //Desc: Add a route rule for the instance
  527. //------------------------------------------------------------------------------
  528. static fwdroute_p _Inst_AddRoute
  529. (
  530.         fwdinst_p pInst,
  531.         int import,
  532.         uint32_t exportAddr,
  533.         int exportPort,
  534.         int status_of_tcp_nodelay, // Use nagle algorithm? 1=no, 0=yes
  535.         int packet_size_to_src,
  536.         int packet_size_to_dst
  537. )
  538. {
  539.         size_t cur;
  540.         if(pInst->num_route >= pInst->max_route)
  541.         {
  542.                 fwdroute_p pnew = realloc(pInst->pRoute,
  543.                         (pInst->max_route + 16) * sizeof(fwdroute_t));
  544.                 if(!pnew)
  545.                 {
  546.                         Inst_LogTime();
  547.                         Inst_Log("Out of memory.\n");
  548.                         return NULL;
  549.                 }
  550.                 pInst->pRoute = pnew;
  551.                 pInst->max_route += 16;
  552.         }
  553.        
  554.         cur = pInst->num_route;
  555.         memset(&pInst->pRoute[cur], 0, sizeof(fwdroute_t));
  556.        
  557.         if(packet_size_to_src <= 0)
  558.                 packet_size_to_src = def_packet_size;
  559.         if(packet_size_to_dst <= 0)
  560.                 packet_size_to_dst = def_packet_size;

  561.         //Initialize
  562.         pInst->pRoute[cur].import = import;
  563.         pInst->pRoute[cur].export = exportPort;
  564.         pInst->pRoute[cur].packet_size_to_src = packet_size_to_src;
  565.         pInst->pRoute[cur].packet_size_to_dst = packet_size_to_dst;
  566.         _MakeIPv4Address(&pInst->pRoute[cur].DestAddr, exportAddr, exportPort);
  567.         pInst->pRoute[cur].cbDestAddr = sizeof(struct sockaddr);
  568.         pInst->pRoute[cur].status_of_tcp_nodelay = status_of_tcp_nodelay;
  569.        
  570.         Inst_LogTime();
  571.         Inst_Log("Redirect: localhost:%d -> ", import);
  572.         Inst_LogAddrV4(&pInst->pRoute[cur].DestAddr);
  573.         Inst_Log(", TCP_NODELAY = %d, packet_size_to_src = %d, packet_size_to_dst = %d\n",
  574.                 pInst->pRoute[cur].status_of_tcp_nodelay,
  575.                 pInst->pRoute[cur].packet_size_to_src,
  576.                 pInst->pRoute[cur].packet_size_to_dst);

  577.         if(pInst->pRoute[cur].packet_size_to_src > 8192)
  578.         {
  579.                 Inst_LogTime();
  580.                 Inst_Log("Warning: `packet to source' should not exceed 8192 bytes.\n");
  581.                 pInst->pRoute[cur].packet_size_to_src = 8192;
  582.         }
  583.         if(pInst->pRoute[cur].packet_size_to_dst > 8192)
  584.         {
  585.                 Inst_LogTime();
  586.                 Inst_Log("Warning: `packet to dest' size should not exceed 8192 bytes.\n");
  587.                 pInst->pRoute[cur].packet_size_to_dst = 8192;
  588.         }
  589.        
  590.         //Create the socket
  591.         pInst->pRoute[cur].listen_socket = _CreateNBIOTCPSocket();
  592.         if(pInst->pRoute[cur].listen_socket == -1)
  593.                 return NULL;
  594.        
  595.         if(!_ListenPort(pInst, pInst->pRoute[cur].listen_socket, import))
  596.                 return NULL;
  597.        
  598.         pInst->num_route++;
  599.         return &pInst->pRoute[cur];
  600. }

  601. //==============================================================================
  602. //Func: _Inst_AddNewConnection
  603. //Desc: Add a socket to a route, called when accepted a new connection.
  604. //------------------------------------------------------------------------------
  605. static fwdconn_p _Inst_AddNewConnection
  606. (
  607.         fwdroute_p pRoute,
  608.         int sockfd,
  609.         address_p pAddr,
  610.         socklen_t cbAddr
  611. )
  612. {
  613.         int sockfd_out = _CreateNBIOTCPSocketWithConnection(&pRoute->DestAddr,
  614.                 pRoute->cbDestAddr);
  615.         if(sockfd_out == -1)
  616.                 return NULL;

  617.         if(setsockopt(sockfd_out, IPPROTO_TCP, TCP_NODELAY,
  618.                 (char*)&pRoute->status_of_tcp_nodelay,
  619.                 sizeof(pRoute->status_of_tcp_nodelay)) < 0)
  620.         {
  621.                 Inst_LogTime();
  622.                 Inst_Log("Warning: setsockopt(TCP_NODELAY) failed:%d.\n", socket_errno);
  623.         }
  624.        
  625.         if(pRoute->num_fwd_conn >= pRoute->max_fwd_conn)
  626.         {
  627.                 fwdconn_p pnew = realloc(pRoute->fwd_conn,
  628.                         (pRoute->max_fwd_conn + num_conn_alloc) * sizeof(fwdconn_t));
  629.                 if(!pnew)
  630.                 {
  631.                         Inst_LogTime();
  632.                         Inst_Log("Out of memory.\n");
  633.                         return 0;
  634.                 }
  635.                 pRoute->fwd_conn = pnew;
  636.                 pRoute->max_fwd_conn += num_conn_alloc;
  637.         }

  638.         memset(&pRoute->fwd_conn[pRoute->num_fwd_conn], 0, sizeof(fwdconn_t));
  639.        
  640.         pRoute->fwd_conn[pRoute->num_fwd_conn].sockfd_src = sockfd;
  641.         pRoute->fwd_conn[pRoute->num_fwd_conn].sockfd_dst = sockfd_out;
  642.         pRoute->fwd_conn[pRoute->num_fwd_conn].addr_from = *pAddr;
  643.         pRoute->fwd_conn[pRoute->num_fwd_conn].addr_size = cbAddr;
  644.         return &pRoute->fwd_conn[pRoute->num_fwd_conn++];
  645. }

  646. //==============================================================================
  647. //Func: _Inst_BreakConnection
  648. //Desc: close a specific connection.
  649. //------------------------------------------------------------------------------
  650. static void _Inst_BreakConnection(fwdroute_p pRoute, size_t ic)
  651. {
  652.         if(ic >= pRoute->num_fwd_conn)
  653.                 return;

  654.         Inst_LogTime();
  655.         Inst_Log("Connection closed: from ");
  656.         Inst_LogAddrV4(&pRoute->fwd_conn[ic].addr_from);
  657.         Inst_Log(" to ");
  658.         Inst_LogAddrV4(&pRoute->DestAddr);
  659.         Inst_Log("\n");
  660.        
  661.         if(pRoute->fwd_conn[ic].sockfd_src != -1)
  662.                 closesocket(pRoute->fwd_conn[ic].sockfd_src);
  663.         pRoute->fwd_conn[ic].sockfd_src = -1;
  664.        
  665.         if(pRoute->fwd_conn[ic].sockfd_dst != -1)
  666.                 closesocket(pRoute->fwd_conn[ic].sockfd_dst);
  667.         pRoute->fwd_conn[ic].sockfd_dst = -1;
  668.        
  669.         if(pRoute->num_fwd_conn > 1)
  670.         {
  671.                 pRoute->fwd_conn[ic] = pRoute->fwd_conn[--pRoute->num_fwd_conn];
  672.                 if(pRoute->num_fwd_conn + num_conn_alloc < pRoute->max_fwd_conn)
  673.                 {
  674.                         pRoute->max_fwd_conn -= num_conn_alloc;
  675.                         if(pRoute->max_fwd_conn)
  676.                         {
  677.                                 fwdconn_p pshrink = realloc(pRoute->fwd_conn,
  678.                                         pRoute->max_fwd_conn * sizeof(fwdconn_t));
  679.                                 if(pshrink)
  680.                                         pRoute->fwd_conn = pshrink;
  681.                         }
  682.                 }
  683.         }
  684.         else
  685.                 pRoute->num_fwd_conn = 0;
  686. }

  687. //==============================================================================
  688. //Func: _IpAddrV4ByNums
  689. //Desc: Combine 4 numbers to an ipv4 address
  690. //------------------------------------------------------------------------------
  691. static uint32_t _IpAddrV4ByNums(int n1, int n2, int n3, int n4)
  692. {
  693.         return
  694.                 ((n4 & 0xFF)) |
  695.                 ((n3 & 0xFF) << 8) |
  696.                 ((n2 & 0xFF) << 16) |
  697.                 ((n1 & 0xFF) << 24);
  698. }

  699. //==============================================================================
  700. //Func: _Inst_LoadCFG
  701. //Desc: Load configurations
  702. //------------------------------------------------------------------------------
  703. static bool_t _Inst_LoadCFG(fwdinst_p pInst, const char*cfg_file)
  704. {
  705.         FILE*cfgfile = NULL;
  706.         char LineBuf[256] = {0};
  707.         char*pChr;
  708.         unsigned LineNo = 0;
  709.        
  710.         cfgfile = fopen(cfg_file, "r");
  711.         if(!cfgfile)
  712.         {
  713.                 Inst_LogTime();
  714.                 Inst_Log("Configure file not found: %s\nCreating DEMO configure file.\n",
  715.                         cfg_file);
  716.                 cfgfile = fopen(cfg_file, "w");
  717.                 if(!cfgfile)
  718.                 {
  719.                         Inst_LogTime();
  720.                         Inst_Log("Could not create config file %s\n", cfg_file);
  721.                         return 0;
  722.                 }
  723.                
  724.                 fprintf(cfgfile,
  725. "# Max acceptable connections\n"
  726. "listen_backlog %u\n"
  727. "\n"
  728. "# Do we write any packet traffics to the log file?\n"
  729. "# If yes, set it to 1, this may let the log file become very big.\n"
  730. "log_traffic 0\n"
  731. "\n"
  732. "# Set a forwarding rule, the parameters is:\n"
  733. "# redirect <port>, <destination ip>:<destination port>, [1|0 for set TCP_NODELAY], [source MTU], [destnation MTU]\n"
  734. "# Default MTU is %u\n"
  735. "# Here comes an example\n"
  736. "redirect 80, 192.168.1.105:80, 1, 8192, 8192\n",
  737.                         backlog_default, def_packet_size);
  738.                
  739.                 fclose(cfgfile);
  740.                 return 0;
  741.         }
  742.        
  743.         pInst->listen_backlog = backlog_default;
  744.         pInst->log_traffic = 0;
  745.         do
  746.         {
  747.                 int n1, n2, n3, n4, n5, n6, n7, n8, n9;
  748.                 int fields;
  749.                 if(!fgets(LineBuf, sizeof(LineBuf), cfgfile))
  750.                         break;
  751.                 LineNo++;
  752.                 pChr = LineBuf;
  753.                 while(isspace(*pChr))
  754.                         pChr++;
  755.                 if(*pChr == '#') // Comment
  756.                         continue;
  757.                 if(sscanf(pChr, "listen_backlog %d", &n1) == 1)
  758.                 {
  759.                         pInst->listen_backlog = n1;
  760.                         Inst_LogTime();
  761.                         Inst_Log("listen backlog = %d\n", n1);
  762.                 }
  763.                 else if(sscanf(pChr, "log_traffic %d", &n1) == 1)
  764.                 {
  765.                         pInst->log_traffic = n1;
  766.                         Inst_LogTime();
  767.                         Inst_Log("log traffic = %d\n", n1);
  768.                 }
  769.                 else
  770.                 {
  771.                         n7 = 0;
  772.                         n8 = n9 = def_packet_size;
  773.                         fields = sscanf(pChr, "redirect %d,%d.%d.%d.%d:%d,%d,%d,%d",
  774.                                 &n1, &n2, &n3, &n4, &n5, &n6, &n7, &n8, &n9);

  775.                         if(fields >= 6)
  776.                         {
  777.                                 fwdroute_p pnew = _Inst_AddRoute(pInst, n1,
  778.                                         _IpAddrV4ByNums(n2, n3, n4, n5), n6, n7, n8, n9);
  779.                                 if(!pnew)
  780.                                 {
  781.                                         Inst_LogTime();
  782.                                         Inst_Log("Add redirect option failed at line %u\n", LineNo);
  783.                                 }
  784.                         }
  785.                 }
  786.         }while(!feof(cfgfile));
  787.        
  788.         fclose(cfgfile);
  789.        
  790.         if(!pInst->num_route)
  791.         {
  792.                 Inst_LogTime();
  793.                 Inst_Log("No routes added. Quitting.\n");
  794.                 return 0;
  795.         }
  796.         return 1;
  797. }

  798. //==============================================================================
  799. //Func: Inst_Term
  800. //Desc: Terminate instance, clear memory
  801. //------------------------------------------------------------------------------
  802. void Inst_Term(fwdinst_p pInst)
  803. {
  804.         Inst_LogTime();
  805.         Inst_Log(EXEC_NAME" stopped.\n\n");
  806.         if(pInst)
  807.         {
  808.                 size_t i;
  809.                 for(i = 0; i < pInst->num_route; i++)
  810.                 {
  811.                         size_t j;
  812.                         for(j = 0; j < pInst->pRoute[i].num_fwd_conn; j++)
  813.                         {
  814.                                 _Inst_BreakConnection(&pInst->pRoute[i], j);
  815.                         }
  816.                         free(pInst->pRoute[i].fwd_conn);
  817.                 }
  818.                 free(pInst->pRoute);
  819.                 free(pInst);
  820.         }
  821. }

  822. //==============================================================================
  823. //Func: Inst_Run
  824. //Desc: Run instance, do forwarding
  825. //------------------------------------------------------------------------------
  826. int Inst_Run(fwdinst_p pInst)
  827. {
  828.         size_t i;
  829.         int rv = 0;

  830.         if(pInst->max_route > pInst->num_route)
  831.         {
  832.                 pInst->max_route = pInst->num_route;
  833.                 pInst->pRoute = realloc(pInst->pRoute,
  834.                         pInst->max_route * sizeof(fwdroute_t));
  835.         }
  836.        
  837.         for(i = 0; i < pInst->num_route; i++)
  838.         {
  839.                 fwdroute_p pRoute = &pInst->pRoute[i];
  840.                 address_t addr_from;
  841.                 socklen_t cb_addr_from = sizeof(addr_from.sa);
  842.                 int sockfd;
  843.                 size_t j;
  844.                
  845.                 sockfd = accept(pRoute->listen_socket, &addr_from.sa, &cb_addr_from);
  846.                 if(sockfd == -1)
  847.                 {
  848.                         int Err = socket_errno;
  849.                         switch(Err)
  850.                         {
  851. #if EWOULDBLOCK != EAGAIN
  852.                 case EWOULDBLOCK:
  853. #endif
  854.                         case EAGAIN:
  855.                                 break;
  856.                         case EPERM:
  857.                                 Inst_LogTime();
  858.                                 Inst_Log("Accept connection failed: firewall rules forbid connection.\n");
  859.                                 break;
  860.                         default:
  861.                                 Inst_LogTime();
  862.                                 Inst_Log("Accept connection failed: %d.\n", Err);
  863.                                 break;
  864.                         }
  865.                 }
  866.                 else
  867.                 {
  868.                         rv = 1;
  869.                         if(_SetSocketBlockingEnabled(sockfd, 0))
  870.                         {
  871.                                 if(addr_from.sa.sa_family == AF_INET)
  872.                                 {
  873.                                         fwdconn_p pConn;
  874.                                
  875.                                         Inst_LogTime();
  876.                                         Inst_Log("Accepted new connection from ");
  877.                                         Inst_LogAddrV4(&addr_from);
  878.                                         Inst_Log("\n");
  879.                                
  880.                                         pConn = _Inst_AddNewConnection(pRoute, sockfd, &addr_from, cb_addr_from);
  881.                                         if(!pConn)
  882.                                         {
  883.                                                 closesocket(sockfd);
  884.                                                 break;
  885.                                         }
  886.                                 }
  887.                                 else
  888.                                         ;//TODO: Add IPv6 support
  889.                         }
  890.                 }
  891.                
  892.                 for(j = 0; j < pRoute->num_fwd_conn; j++)
  893.                 {
  894.                         size_t cbSendSize;

  895.                         //=================================================================
  896.                         //Receive data from source
  897.                         //-----------------------------------------------------------------
  898.                         if(pRoute->fwd_conn[j].cb_src < fwd_buffer_size)
  899.                         {
  900.                                 ssize_t cbRecv = recv(pRoute->fwd_conn[j].sockfd_src,
  901.                                         (char*)pRoute->fwd_conn[j].buffer_src
  902.                                         + pRoute->fwd_conn[j].cb_src,
  903.                                         fwd_buffer_size - pRoute->fwd_conn[j].cb_src, 0);
  904.                                 if(cbRecv > 0)
  905.                                 {
  906.                                         rv = 1;
  907.                                         pRoute->fwd_conn[j].cb_src += cbRecv;
  908.                                         if(pInst->log_traffic)
  909.                                         {
  910.                                                 Inst_LogTime();
  911.                                                 Inst_Log("Received %u bytes from source ", cbRecv);
  912.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  913.                                                 Inst_Log("\n");
  914.                                         }
  915.                                 }
  916.                                 else if(cbRecv == 0)
  917.                                 {
  918.                                         Inst_LogTime();
  919.                                         Inst_Log("Lost connection from source: ");
  920.                                         Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  921.                                         Inst_Log("\n");
  922.                                         _Inst_BreakConnection(pRoute, j);
  923.                                         goto BreakLoop;
  924.                                 }
  925.                                 else if(cbRecv < 0)
  926.                                 {
  927.                                         int Err = socket_errno;
  928.                                         switch(Err)
  929.                                         {
  930.                                         case EINPROGRESS:
  931. #if EWOULDBLOCK != EAGAIN
  932.                                         case EWOULDBLOCK:
  933. #endif
  934.                                         case EAGAIN:
  935.                                                 break;
  936.                                         case ECONNABORTED:
  937.                                         case ENOTCONN:
  938.                                                 Inst_LogTime();
  939.                                                 Inst_Log("Lost connection from source: ");
  940.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  941.                                                 Inst_Log("\n");
  942.                                                 _Inst_BreakConnection(pRoute, j);
  943.                                                 goto BreakLoop;
  944.                                         case ECONNRESET:
  945.                                                 Inst_LogTime();
  946.                                                 Inst_Log("Connection reset by source: ");
  947.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  948.                                                 Inst_Log("\n");
  949.                                                 _Inst_BreakConnection(pRoute, j);
  950.                                                 goto BreakLoop;
  951.                                         case ECONNREFUSED:
  952.                                                 Inst_LogTime();
  953.                                                 Inst_Log("Connection refused by source: ");
  954.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  955.                                                 Inst_Log("\n");
  956.                                                 _Inst_BreakConnection(pRoute, j);
  957.                                                 goto BreakLoop;
  958.                                         default:
  959.                                                 Inst_LogTime();
  960.                                                 Inst_Log("An error occured (%d) while receiving data from "
  961.                                                         "source ", Err);
  962.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  963.                                                 Inst_Log("\n");
  964.                                                 _Inst_BreakConnection(pRoute, j);
  965.                                                 goto BreakLoop;
  966.                                         }
  967.                                 }
  968.                         }

  969.                         //=================================================================
  970.                         //Send data to destination
  971.                         //-----------------------------------------------------------------
  972.                         if(pRoute->fwd_conn[j].cb_src > 0 )
  973.                         {
  974.                                 ssize_t cbSend;
  975.                                 cbSendSize = pRoute->fwd_conn[j].cb_src;
  976.                                 if(cbSendSize > pRoute->packet_size_to_dst)
  977.                                         cbSendSize = pRoute->packet_size_to_dst;
  978.                                 cbSend = send(pRoute->fwd_conn[j].sockfd_dst,
  979.                                         pRoute->fwd_conn[j].buffer_src,
  980.                                         cbSendSize, MSG_NOSIGNAL);
  981.                                 if(cbSend > 0 && cbSend <= pRoute->fwd_conn[j].cb_src)
  982.                                 {
  983.                                         rv = 1;
  984.                                         pRoute->fwd_conn[j].cb_src -= cbSend;
  985.                                         if(pRoute->fwd_conn[j].cb_src)
  986.                                         {
  987.                                                 memmove(pRoute->fwd_conn[j].buffer_src,
  988.                                                         (char*)pRoute->fwd_conn[j].buffer_src + cbSend,
  989.                                                         pRoute->fwd_conn[j].cb_src);
  990.                                         }
  991.                                         if(!pRoute->fwd_conn[j].connected)
  992.                                         {
  993.                                                 Inst_LogTime();
  994.                                                 Inst_Log("Connected to destination: ");
  995.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  996.                                                 Inst_Log("\n");
  997.                                                 pRoute->fwd_conn[j].connected = 1;
  998.                                         }
  999.                                         if(pInst->log_traffic)
  1000.                                         {
  1001.                                                 Inst_LogTime();
  1002.                                                 Inst_Log("Sent %u bytes from source ", cbSend);
  1003.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1004.                                                 Inst_Log(" to destination ");
  1005.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1006.                                                 Inst_Log("\n");
  1007.                                         }
  1008.                                 }
  1009.                                 else if(cbSend < 0)
  1010.                                 {
  1011.                                         int Err = socket_errno;
  1012.                                         switch(Err)
  1013.                                         {
  1014.                                         case EINPROGRESS:
  1015.                                         case ENOTCONN:
  1016.                                                 // Inst_Log("c");
  1017.                                                 pRoute->fwd_conn[j].connected = 0;
  1018.                                                 break;
  1019. #if EWOULDBLOCK != EAGAIN
  1020.                                         case EWOULDBLOCK:
  1021. #endif
  1022.                                         case EAGAIN:
  1023.                                                 break;
  1024.                                         case ECONNABORTED:
  1025.                                                 Inst_LogTime();
  1026.                                                 Inst_Log("Connection aborted by destination: ");
  1027.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1028.                                                 Inst_Log("\n");
  1029.                                                 _Inst_BreakConnection(pRoute, j);
  1030.                                                 goto BreakLoop;
  1031.                                         case ECONNRESET:
  1032.                                                 Inst_LogTime();
  1033.                                                 Inst_Log("Connection reset by destination: ");
  1034.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1035.                                                 Inst_Log("\n");
  1036.                                                 _Inst_BreakConnection(pRoute, j);
  1037.                                                 goto BreakLoop;
  1038.                                         case ECONNREFUSED:
  1039.                                                 Inst_LogTime();
  1040.                                                 Inst_Log("Connection refused by destination: ");
  1041.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1042.                                                 Inst_Log("\n");
  1043.                                                 _Inst_BreakConnection(pRoute, j);
  1044.                                                 goto BreakLoop;
  1045.                                         default:
  1046.                                                 Inst_LogTime();
  1047.                                                 Inst_Log("An error occured (%d) while sending data to destination ", Err);
  1048.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1049.                                                 Inst_Log("\n");
  1050.                                                 _Inst_BreakConnection(pRoute, j);
  1051.                                                 goto BreakLoop;
  1052.                                         }
  1053.                                 }
  1054.                         }

  1055.                        
  1056.                         //=================================================================
  1057.                         //Receive data from destination
  1058.                         //-----------------------------------------------------------------
  1059.                         if(pRoute->fwd_conn[j].cb_dst < fwd_buffer_size)
  1060.                         {
  1061.                                 ssize_t cbRecv = recv(pRoute->fwd_conn[j].sockfd_dst,
  1062.                                         (char*)pRoute->fwd_conn[j].buffer_dst
  1063.                                         + pRoute->fwd_conn[j].cb_dst,
  1064.                                         fwd_buffer_size - pRoute->fwd_conn[j].cb_dst, 0);
  1065.                                 if(cbRecv > 0)
  1066.                                 {
  1067.                                         rv = 1;
  1068.                                         pRoute->fwd_conn[j].cb_dst += cbRecv;
  1069.                                         if(pInst->log_traffic)
  1070.                                         {
  1071.                                                 Inst_LogTime();
  1072.                                                 Inst_Log("Received %u bytes from destination ", cbRecv);
  1073.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1074.                                                 Inst_Log("\n");
  1075.                                         }
  1076.                                 }
  1077.                                 else if(cbRecv == 0)
  1078.                                 {
  1079.                                         Inst_LogTime();
  1080.                                         Inst_Log("Lost connection from source: ");
  1081.                                         Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1082.                                         Inst_Log("\n");
  1083.                                         _Inst_BreakConnection(pRoute, j);
  1084.                                         goto BreakLoop;
  1085.                                 }
  1086.                                 else if(cbRecv < 0)
  1087.                                 {
  1088.                                         int Err = socket_errno;
  1089.                                         switch(Err)
  1090.                                         {
  1091.                                         case EINPROGRESS:
  1092.                                         case ENOTCONN:
  1093.                                                 pRoute->fwd_conn[j].connected = 0;
  1094.                                                 break;
  1095. #if EWOULDBLOCK != EAGAIN
  1096.                                         case EWOULDBLOCK:
  1097. #endif
  1098.                                         case EAGAIN:
  1099.                                                 break;
  1100.                                         case ECONNABORTED:
  1101.                                                 Inst_LogTime();
  1102.                                                 Inst_Log("Connection aborted by destination: ");
  1103.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1104.                                                 Inst_Log("\n");
  1105.                                                 _Inst_BreakConnection(pRoute, j);
  1106.                                                 goto BreakLoop;
  1107.                                         case ECONNRESET:
  1108.                                                 Inst_LogTime();
  1109.                                                 Inst_Log("Connection reset by destination: ");
  1110.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1111.                                                 Inst_Log("\n");
  1112.                                                 _Inst_BreakConnection(pRoute, j);
  1113.                                                 goto BreakLoop;
  1114.                                         case ECONNREFUSED:
  1115.                                                 Inst_LogTime();
  1116.                                                 Inst_Log("Connection refused by destination: ");
  1117.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1118.                                                 Inst_Log("\n");
  1119.                                                 _Inst_BreakConnection(pRoute, j);
  1120.                                                 goto BreakLoop;
  1121.                                         default:
  1122.                                                 Inst_LogTime();
  1123.                                                 Inst_Log("An error occured (%d) while receiving data from destination ", Err);
  1124.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1125.                                                 Inst_Log("\n");
  1126.                                                 _Inst_BreakConnection(pRoute, j);
  1127.                                                 goto BreakLoop;
  1128.                                         }
  1129.                                 }
  1130.                         }
  1131.                        

  1132.                         //=================================================================
  1133.                         //Send data to source
  1134.                         //-----------------------------------------------------------------
  1135.                         if(pRoute->fwd_conn[j].cb_dst > 0 )
  1136.                         {
  1137.                                 ssize_t cbSend;
  1138.                                 cbSendSize = pRoute->fwd_conn[j].cb_dst;
  1139.                                 if(cbSendSize > pRoute->packet_size_to_src)
  1140.                                         cbSendSize = pRoute->packet_size_to_src;
  1141.                                 cbSend = send(pRoute->fwd_conn[j].sockfd_src,
  1142.                                         pRoute->fwd_conn[j].buffer_dst,
  1143.                                         cbSendSize, MSG_NOSIGNAL);
  1144.                                 if(cbSend > 0 && cbSend <= pRoute->fwd_conn[j].cb_dst)
  1145.                                 {
  1146.                                         rv = 1;
  1147.                                         pRoute->fwd_conn[j].cb_dst -= cbSend;
  1148.                                         if(pRoute->fwd_conn[j].cb_dst)
  1149.                                         {
  1150.                                                 memmove(pRoute->fwd_conn[j].buffer_dst,
  1151.                                                         (char*)pRoute->fwd_conn[j].buffer_dst + cbSend,
  1152.                                                         pRoute->fwd_conn[j].cb_dst);
  1153.                                         }
  1154.                                         if(pInst->log_traffic)
  1155.                                         {
  1156.                                                 Inst_LogTime();
  1157.                                                 Inst_Log("Sent %u bytes from destination ", cbSend);
  1158.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1159.                                                 Inst_Log(" to source ");
  1160.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1161.                                                 Inst_Log("\n");
  1162.                                         }
  1163.                                 }
  1164.                                 else if(cbSend < 0)
  1165.                                 {
  1166.                                         int Err = socket_errno;
  1167.                                         switch(Err)
  1168.                                         {
  1169.                                         case EINPROGRESS:
  1170. #if EWOULDBLOCK != EAGAIN
  1171.                                         case EWOULDBLOCK:
  1172. #endif
  1173.                                         case EAGAIN:
  1174.                                                 break;
  1175.                                         case ENOTCONN:
  1176.                                         case ECONNABORTED:
  1177.                                                 Inst_LogTime();
  1178.                                                 Inst_Log("Connection aborted by source: ");
  1179.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1180.                                                 Inst_Log("\n");
  1181.                                                 _Inst_BreakConnection(pRoute, j);
  1182.                                                 goto BreakLoop;
  1183.                                         case ECONNRESET:
  1184.                                                 Inst_LogTime();
  1185.                                                 Inst_Log("Connection reset by source: ");
  1186.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1187.                                                 Inst_Log("\n");
  1188.                                                 _Inst_BreakConnection(pRoute, j);
  1189.                                                 goto BreakLoop;
  1190.                                         case ECONNREFUSED:
  1191.                                                 Inst_LogTime();
  1192.                                                 Inst_Log("Connection refused by source: ");
  1193.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1194.                                                 Inst_Log("\n");
  1195.                                                 _Inst_BreakConnection(pRoute, j);
  1196.                                                 goto BreakLoop;
  1197.                                         default:
  1198.                                                 Inst_LogTime();
  1199.                                                 Inst_Log("An error occured (%d) while sending data to source ", Err);
  1200.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1201.                                                 Inst_Log("\n");
  1202.                                                 _Inst_BreakConnection(pRoute, j);
  1203.                                                 goto BreakLoop;
  1204.                                         }
  1205.                                 }
  1206.                         }

  1207.                 }
  1208.         }
  1209. BreakLoop:
  1210.         return rv;
  1211. }

  1212. //==============================================================================
  1213. //Func: _Inst_Daemonize
  1214. //Desc: Make program run as a daemon
  1215. //------------------------------------------------------------------------------
  1216. static int _Inst_Daemonize()
  1217. {
  1218. #ifndef WIN32
  1219.         int i,lfp;
  1220.         char str[10];
  1221.        
  1222.         if(getppid() == 1)
  1223.         {
  1224.                 fprintf(stderr, EXEC_NAME" was still running.\n");
  1225.                 return 0;
  1226.         }
  1227.        
  1228.         i = fork();
  1229.         if(i < 0)
  1230.         {
  1231.                 // fork error
  1232.                 return 0;
  1233.         }
  1234.         if(i > 0)
  1235.         {
  1236.                 // Parent exits, child (daemon) continues
  1237.                 return 0;
  1238.         }
  1239.        
  1240.         setsid(); // obtain a new process group
  1241.        
  1242.         fclose(stdin);
  1243.         fclose(stdout);
  1244.         fclose(stderr);
  1245.        
  1246.         lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0644);
  1247.         if(lfp<0)
  1248.         {
  1249.                 // Could not open lock file.
  1250.                 Inst_LogTime();
  1251.                 Inst_Log("Open lock file failed.\n");
  1252.                 return 0;
  1253.         }
  1254.        
  1255.         if(lockf(lfp, F_TLOCK, 0) < 0)
  1256.         {
  1257.                 // can not lock
  1258.                 Inst_LogTime();
  1259.                 Inst_Log("Lock lock file failed.\n");
  1260.                 return 0;
  1261.         }
  1262.        
  1263.         // first instance continues
  1264.         sprintf(str,"%d\n",getpid());
  1265.         write(lfp,str,strlen(str)); // record pid to lockfile
  1266.        
  1267.         signal(SIGCHLD,SIG_IGN); // ignore child
  1268.         signal(SIGTSTP,SIG_IGN); // ignore tty signals
  1269.         signal(SIGTTOU,SIG_IGN);
  1270.         signal(SIGTTIN,SIG_IGN);
  1271.         signal(SIGPIPE,SIG_IGN);
  1272.         signal(SIGHUP,_signal_handler); // catch hangup signal
  1273. #endif
  1274.         signal(SIGTERM,_signal_handler); // catch kill signal
  1275.        
  1276.         return 1;
  1277. }

  1278. //==============================================================================
  1279. //Func: main
  1280. //Desc: Entry point
  1281. //------------------------------------------------------------------------------
  1282. int main(int argc, char**argv)
  1283. {
  1284.         fwdinst_p pInst;
  1285.         bool_t DoDaemonize = 0;
  1286.         char*sz_cfg_file = CFG_FILE;

  1287. #ifdef WIN32
  1288.         {
  1289.                 WSADATA wsaData;
  1290.                 WSAStartup(WINSOCK_VERSION, &wsaData);
  1291.         }
  1292. #else
  1293.         {
  1294.                 int c;

  1295.                 for(;;)
  1296.                 {
  1297.                         c = getopt(argc, argv, "c:dvh");
  1298.                         if(c == -1)
  1299.                                 break;

  1300.                         switch(c)
  1301.                         {
  1302.                         case'c'://Customize config file
  1303.                                 sz_cfg_file = optarg;
  1304.                                 break;
  1305.                         case'd'://Run as a daemon
  1306.                                 DoDaemonize = 1;
  1307.                                 _g_LogToScreen = 0;
  1308.                                 break;
  1309.                         default:
  1310.                                 fprintf(stderr, "Unknown option '%c'\n", c);
  1311.                         case'h':
  1312.                                 fprintf(stderr, "Usage:\n"
  1313.                                         EXEC_NAME" [-c cfgfile][-d][-v][-h]\n"
  1314.                                         " -c: set config file"
  1315.                                         " -d: run as a daemon\n"
  1316.                                         " -h: show this help\n");
  1317.                                 break;
  1318.                         }
  1319.                 }
  1320.         }
  1321. #endif
  1322.        
  1323.         pInst = Inst_Create(DoDaemonize, sz_cfg_file);
  1324.         if(pInst)
  1325.         {
  1326.                 bo_t bo;
  1327.                 bo_reset(&bo);
  1328.                 while(!_g_Term)
  1329.                 {
  1330.                         if(Inst_Run(pInst))
  1331.                                 bo_reset(&bo);
  1332.                         else
  1333.                                 bo_update(&bo);
  1334.                 }
  1335.                 Inst_Term(pInst);
  1336.         }
  1337. #ifdef WIN32
  1338.         WSACleanup();
  1339. #endif
  1340.         return 0;
  1341. }
复制代码
编译方法:写makefile
  1. CC=gcc -std=gnu99
  2. LD=gcc -std=gnu99
  3. CCFLAGS=@CCFLAGS@

  4. tcpfwd: tcpfwd.o
  5.         $(LD) -o $@ $^

  6. .PHONY: clean
  7. clean:
  8.         rm -f tcpfwd *.o
复制代码
然后make就行。CentOS下可用。
用法:
1、它会在当前目录找配置文件,找不到的话会自动生成一个模板,照着模板改就行。
2、命令行:

-c 指定自己的配置文件路径
-d 作为守护进程运行。也就是“呆萌”Daemon。没有这个选项的时候并不作为守护进程运行。
-v 作为守护进程运行的时候,打印Log到屏幕,不推荐设置它,否则神烦。

开发完以后,接下来设置CentOS连接VPN。

先安装 ppp 和 pptp:

# yum -y install ppp pptp

然后配置VPN的用户名和密码。

# vi /etc/ppp/chap-secrets

假设你的用户名是foo,密码是bar,你这么写:
  1. foo PPTP bar *
复制代码
然后保存。

设置VPN服务器的地址和加密,此时编辑另一个文件。

# vi /etc/ppp/peers/连接名

其中连接名可以换成你要的名字,随便取。
它的内容这样写:
  1. pty "pptp xxx.xxx.xxx.xxx --nolaunchpppd"
  2. name foo
  3. remotename PPTP
  4. require-mppe-128
  5. file /etc/ppp/options.pptp
  6. ipparam 连接名[code]其中的xxx.xxx.xxx.xxx是你的VPN的地址,“foo”是你的用户名,也就是之前记录在/etc/ppp/chap-secrets里面的。然后“连接名”必须和上面的一致。
  7. 写好以后保存,然后就可以准备播VPN连接了。

  8. # modprobe nf_conntrack_pptp
  9. # pppd call 连接名

  10. 把上面的“连接名”换成你取好的名字,然后看/var/log/messages就可以判断你连上了没。[code]Jan 16 01:10:23 ip-xxx-xxx-xxx-xxx pppd[28736]: pppd 2.4.5 started by ec2-user, uid 0
  11. Jan 16 01:10:23 ip-xxx-xxx-xxx-xxx pppd[28736]: Using interface ppp0
  12. Jan 16 01:10:23 ip-xxx-xxx-xxx-xxx pppd[28736]: Connect: ppp0 <--> /dev/pts/0
  13. Jan 16 01:10:23 ip-xxx-xxx-xxx-xxx pptp[28739]: anon log[main:pptp.c:314]: The synchronous pptp option is NOT activated
  14. Jan 16 01:10:24 ip-xxx-xxx-xxx-xxx pptp[28752]: anon log[ctrlp_rep:pptp_ctrl.c:251]: Sent control packet type is 1 'Start-Control-Connection-Request'
  15. Jan 16 01:10:24 ip-xxx-xxx-xxx-xxx pptp[28752]: anon log[ctrlp_disp:pptp_ctrl.c:739]: Received Start Control Connection Reply
  16. Jan 16 01:10:24 ip-xxx-xxx-xxx-xxx pptp[28752]: anon log[ctrlp_disp:pptp_ctrl.c:773]: Client connection established.
  17. Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp[28752]: anon log[ctrlp_rep:pptp_ctrl.c:251]: Sent control packet type is 7 'Outgoing-Call-Request'
  18. Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp[28752]: anon log[ctrlp_disp:pptp_ctrl.c:858]: Received Outgoing Call Reply.
  19. Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp[28752]: anon log[ctrlp_disp:pptp_ctrl.c:897]: Outgoing call established (call ID 0, peer's call ID 43295).
  20. Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp[28752]: anon log[ctrlp_disp:pptp_ctrl.c:950]: PPTP_SET_LINK_INFO received from peer_callid 0
  21. Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp[28752]: anon log[ctrlp_disp:pptp_ctrl.c:953]:   send_accm is 00000000, recv_accm is FFFFFFFF
  22. Jan 16 01:10:25 ip-xxx-xxx-xxx-xxx pptp[28752]: anon warn[ctrlp_disp:pptp_ctrl.c:956]: Non-zero Async Control Character Maps are not supported!
  23. Jan 16 01:10:26 ip-xxx-xxx-xxx-xxx pppd[28736]: CHAP authentication succeeded
  24. Jan 16 01:10:26 ip-xxx-xxx-xxx-xxx pppd[28736]: MPPE 128-bit stateless compression enabled
  25. Jan 16 01:10:27 ip-xxx-xxx-xxx-xxx pppd[28736]: local  IP address xxx.xxx.xxx.xxx
  26. Jan 16 01:10:27 ip-xxx-xxx-xxx-xxx pppd[28736]: remote IP address 192.168.xxx.1
复制代码
显示为这样的就是已经连接上的了。也就是分配到了IP地址。

连接上VPN后,我需要让所有发往我的minecraft服务器的流量全部经过VPN转发,因此我需要加入路由规则。

# route add xxx.xxx.xxx.xxx ppp0

其中“xxx.xxx.xxx.xxx”是我的minecraft服务器的地址,这里先藏起来了哈。
然后这样的话,所有发往服务器的包都会经过ppp0转发,这样就能达成目的了。

设置好tcpfwd(也就是我写的那个转发器)的配置文件、启动它、然后设置好VPN和转发规则以后,我就可以在游戏的登录界面输入我的AWS的IP地址了(因为我的转发器是在AWS上运行的嘛)。
20170116011816.png
看,直连都连不上,但用了我的转发服务器后却能连上了,延迟还不高,150ms左右,赞。
2017-01-16_01.23.47.png
这样的话,国外的用户用这个代理,也能低延迟畅玩咱们的服务器啦~~

顺带打个小广告:冒险者大陆服务器是一个办了很多年的正版服务器,游戏模式为困难难度生存模式,我们没有OP,不接收赞助,不使用命令方块,不添加MOD,因为我们相信,凭借我们的创造力,不需要多余的附加就能建立精彩的世界。
游戏QQ群号:339821483
请在加入QQ群后,通过群公告的内容,下载游戏客户端并连接我们的服务器的IP地址。

本帖被以下淘专辑推荐:

0

主题

76

帖子

6735

积分

用户组: 真·技术宅

UID
604
精华
0
威望
1 点
宅币
804 个
贡献
5853 次
宅之契约
0 份
在线时间
97 小时
注册时间
2014-12-20
发表于 2017-1-16 09:25:20 | 显示全部楼层
mark一下, 说不定哪天就用到了

1049

主题

2342

帖子

6万

积分

用户组: 管理员

一只技术宅

UID
1
精华
218
威望
294 点
宅币
18483 个
贡献
37875 次
宅之契约
0 份
在线时间
1760 小时
注册时间
2014-1-26
 楼主| 发表于 2017-2-2 15:47:14 | 显示全部楼层
已更新:现在这玩意儿支持用户通过配置文件来配置TCP_NODELAY了

1049

主题

2342

帖子

6万

积分

用户组: 管理员

一只技术宅

UID
1
精华
218
威望
294 点
宅币
18483 个
贡献
37875 次
宅之契约
0 份
在线时间
1760 小时
注册时间
2014-1-26
 楼主| 发表于 2017-2-8 21:00:59 | 显示全部楼层
顺手写了一个给它用的init.d的脚本,这样就可以用chkconfig将其选为系统服务,然后执行了。

但这个需要你自己找到第19行,将其中的路径改成你自己的tcpfwd的路径,我的是“cd /home/tcpfwd/”,你得改成你的。
除此以外第20行也需要改。
  1. #!/bin/sh
  2. #
  3. # /etc/init.d/tcpfwd
  4. # TCP connection forwarder
  5. #
  6. # chkconfig: 2345 80 05
  7. # description: TCP connection forwarder daemon
  8. # config: /home/tcpfwd/tcpfwd.conf
  9. # pidfile: /temp/tcpfwd.lock

  10. # source function library
  11. . /etc/rc.d/init.d/functions

  12. RETVAL=0
  13. prog="tcpfwd"

  14. start() {
  15.         echo -n $"Starting $prog:"
  16.         cd /home/tcpfwd/
  17.         ./tcpfwd -c /home/tcpfwd/tcpfwd.conf -d
  18.         RETVAL=$?
  19.         if [ "$RETVAL" = 0 ]; then
  20.                 touch /var/lock/subsys/$prog
  21.                 success "Starting $prog service"
  22.         else
  23.                 failure "Starting $prog service"
  24.         fi
  25.         echo
  26. }

  27. stop() {
  28.         echo -n $"Stopping $prog:"
  29.         killproc $prog -TERM
  30.         RETVAL=$?
  31.         [ "$RETVAL" = 0 ] && rm -f /var/lock/subsys/$prog
  32.         echo
  33. }

  34. case "$1" in
  35.         start)
  36.                 start
  37.                 ;;
  38.         stop)
  39.                 stop
  40.                 ;;
  41.         restart)
  42.                 stop
  43.                 start
  44.                 ;;
  45.         status)
  46.                 status $prog
  47.                 RETVAL=$?
  48.                 ;;
  49.         *)
  50.                 echo $"Usage: $0 {start|stop|restart|status}"
  51.                 RETVAL=1
  52.                 ;;
  53. esac
  54. exit $RETVAL
复制代码

1049

主题

2342

帖子

6万

积分

用户组: 管理员

一只技术宅

UID
1
精华
218
威望
294 点
宅币
18483 个
贡献
37875 次
宅之契约
0 份
在线时间
1760 小时
注册时间
2014-1-26
 楼主| 发表于 2019-4-25 04:43:41 | 显示全部楼层
已更新。追加了退避逻辑,降低了CPU占用,修复若干错误,并且可以控制唤醒时间了。

本版积分规则

QQ|申请友链||Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2019-12-15 05:06 , Processed in 0.117438 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表