技术宅的结界

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

QQ登录

只需一步,快速开始

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

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

[复制链接]

995

主题

2202

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
197
威望
261 点
宅币
16296 个
贡献
31900 次
宅之契约
0 份
在线时间
1555 小时
注册时间
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" // 懒得配置

  2. // 这些可以有
  3. #include<stdio.h>
  4. #include<errno.h>
  5. #include<ctype.h>
  6. #include<signal.h>
  7. #include<stdarg.h>
  8. #include<malloc.h>
  9. #include<memory.h>
  10. #include<time.h>

  11. // 这里有的有有的没有
  12. #ifdef WIN32
  13. #include<stdint.h> // VS没有inttypes.h
  14. #include<WinSock2.h>
  15. #include<ws2tcpip.h> // 为了struct sockaddr_in6……虽然我不用它
  16. #else
  17. #include<fcntl.h>
  18. #include<unistd.h>
  19. #include<inttypes.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"                        // 程序名
  26. #define LOCK_FILE        "/tmp/tcpfwd.lock"        // PID文件名
  27. #define LOG_FILE        "tcpfwd.log"                // 日志文件名
  28. #define CFG_FILE        "tcpfwd.conf"                // 配置文件名

  29. #define backlog_default 200                                // 默认最大连接数
  30. #define num_conn_alloc 32                                // 内存增长粒度
  31. #define fwd_buffer_size 8192                        // 转发缓冲区大小
  32. #define def_packet_size 1472                        // 默认包裹拆分大小

  33. typedef int bool_t, *bool_p;

  34. typedef union address
  35. {
  36.     struct sockaddr sa;
  37.     struct sockaddr_in sa_in;
  38.     struct sockaddr_in6 sa_in6;
  39.     struct sockaddr_storage sa_stor;
  40. }address_t, *address_p;

  41. #ifdef WIN32
  42. typedef INT_PTR ssize_t;
  43. typedef int socklen_t;
  44. static void _done()
  45. {
  46.         Sleep(1); // Windows的Sleep()按毫秒算
  47. }
  48. static int _socket_errno() // 翻译错误号
  49. {
  50.         int Err = WSAGetLastError();
  51.         switch(Err)
  52.         {
  53.         case WSAEADDRINUSE:
  54.                 return EADDRINUSE;
  55.         case WSAEADDRNOTAVAIL:
  56.                 return EADDRNOTAVAIL;
  57.         case WSAEAFNOSUPPORT:
  58.                 return EAFNOSUPPORT;
  59.         case WSAEALREADY:
  60.                 return EALREADY;
  61.         case WSAECONNABORTED:
  62.                 return ECONNABORTED;
  63.         case WSAECONNREFUSED:
  64.                 return ECONNREFUSED;
  65.         case WSAECONNRESET:
  66.                 return ECONNRESET;
  67.         case WSAEDESTADDRREQ:
  68.                 return EDESTADDRREQ;
  69.         case WSAEHOSTUNREACH:
  70.                 return EHOSTUNREACH;
  71.         case WSAEINPROGRESS:
  72.                 return EINPROGRESS;
  73.         case WSAEISCONN:
  74.                 return EISCONN;
  75.         case WSAELOOP:
  76.                 return ELOOP;
  77.         case WSAEMSGSIZE:
  78.                 return EMSGSIZE;
  79.         case WSAENETDOWN:
  80.                 return ENETDOWN;
  81.         case WSAENETRESET:
  82.                 return ENETRESET;
  83.         case WSAENETUNREACH:
  84.                 return ENETUNREACH;
  85.         case WSAENOBUFS:
  86.                 return ENOBUFS;
  87.         case WSAENOPROTOOPT:
  88.                 return ENOPROTOOPT;
  89.         case WSAENOTCONN:
  90.                 return ENOTCONN;
  91.         case WSAENOTSOCK:
  92.                 return ENOTSOCK;
  93.         case WSAEOPNOTSUPP:
  94.                 return EOPNOTSUPP;
  95.         case WSAEPROTONOSUPPORT:
  96.                 return EPROTONOSUPPORT;
  97.         case WSAEPROTOTYPE:
  98.                 return EPROTOTYPE;
  99.         case WSAETIMEDOUT:
  100.                 return ETIMEDOUT;
  101.         case WSAEWOULDBLOCK:
  102.                 return EWOULDBLOCK;
  103.         default:
  104.                 return Err;
  105.         }
  106. }
  107. #define socket_errno _socket_errno() // 假装是个变量
  108. #define MSG_NOSIGNAL 0 // Windows没这个选项,假装有
  109. #else // 其它系统不考虑,只考虑Unix系
  110. static void _done()
  111. {
  112.         sleep(0); // Linux的sleep()按秒算
  113. }
  114. #define socket_errno errno
  115. static int closesocket(int sockfd) // Linux的Socket是一个文件描述符
  116. {
  117.         return close(sockfd);
  118. }
  119. #define _tzset tzset
  120. static char*_strtime(char*timestr)
  121. {
  122.         time_t now;
  123.         struct tm *l_time;
  124.         static char _timestr[10] = {0};
  125.        
  126.         now = time(NULL);
  127.         l_time = localtime(&now);
  128.         sprintf(_timestr, "%.2d:%.2d:%.2d", l_time->tm_hour, l_time->tm_min, l_time->tm_sec);
  129.         strncpy(timestr, _timestr, sizeof _timestr);
  130.         return _timestr;
  131. }
  132. #endif

  133. //=============================================================================
  134. // socket连接
  135. typedef struct
  136. {
  137.         int sockfd_src; // 源
  138.         int sockfd_dst; // 目标
  139.         bool_t connected; // 是否连上了目标
  140.         char buffer_src[fwd_buffer_size]; // 源发来的数据
  141.         int cb_src; // 数据的大小
  142.         char buffer_dst[fwd_buffer_size]; // 目标发来的数据
  143.         int cb_dst; // 数据的大小
  144.         address_t addr_from; // 源的地址
  145.         socklen_t addr_size; // 源的地址的长度
  146. }fwdconn_t, *fwdconn_p;

  147. typedef struct
  148. {
  149.         int listen_socket; // 用于接受连接的套接字
  150.        
  151.         int import; // 监听端口
  152.         int export; // 连出端口
  153.         unsigned packet_size_to_src; // 源拆分包裹大小
  154.         unsigned packet_size_to_dst; // 目标拆分包裹大小
  155.         int status_of_tcp_nodelay; // 是否启用Nagle算法,1为不启用,0为启用
  156.        
  157.         address_t DestAddr; // 目标的地址
  158.         socklen_t cbDestAddr; // 目标的地址的长度
  159.        
  160.         fwdconn_p fwd_conn; // 活动连接数组
  161.         size_t num_fwd_conn; // 活动连接的数量
  162.         size_t max_fwd_conn; // 数组的容量
  163. }fwdroute_t, *fwdroute_p;

  164. typedef struct
  165. {
  166.         fwdroute_p        pRoute; // 转发规则数组
  167.         size_t                num_route; // 规则数量
  168.         size_t                max_route; // 数组容量
  169.        
  170.         int                        listen_backlog; // 最大连接数
  171.         bool_t                log_traffic; // 是否记录每一次转发的动作
  172. }fwdinst_t, *fwdinst_p;

  173. static volatile bool_t _g_Term = 0; // 是否退出
  174. static bool_t _g_LogToScreen = 1; // 是否在写入log的时候也打印到屏幕

  175. void Inst_Log(char *message, ...);
  176. fwdinst_p Inst_Create(bool_t DoDaemonize, const char *cfg_file);
  177. void Inst_Run(fwdinst_p pInst);
  178. void Inst_Term(fwdinst_p pInst);

  179. static int _Inst_Daemonize();
  180. static bool_t _SetSocketBlockingEnabled(int fd, int blocking);
  181. static int _CreateNBIOTCPSocket();
  182. static void _MakeIPv4Address(address_p pOut, uint32_t IP, uint16_t Port);
  183. static bool_t _ListenPort(fwdinst_p pInst, int socket, int port);
  184. static bool_t _Inst_LoadCFG(fwdinst_p pInst, const char*cfg_file);
  185. static fwdroute_p _Inst_AddRoute
  186. (
  187.         fwdinst_p pInst,
  188.         int import,
  189.         uint32_t exportAddr,
  190.         int exportPort,
  191.         int status_of_tcp_nodelay,
  192.         int packet_size_to_src,
  193.         int packet_size_to_dst
  194. );
  195. static fwdconn_p _Inst_AddNewConnection
  196. (
  197.         fwdroute_p pRoute,
  198.         int sockfd,
  199.         address_p pAddr,
  200.         socklen_t cbAddr
  201. );
  202. static void _Inst_BreakConnection(fwdroute_p pRoute, size_t ic);
  203. static uint32_t _IpAddrV4ByNums(int n1, int n2, int n3, int n4);
  204. static bool_t _Inst_LoadCFG(fwdinst_p pInst, const char*cfg_file);
  205. static void _signal_handler(int sig);

  206. //==============================================================================
  207. //Func: _SetSocketBlockingEnabled
  208. //Desc: 设置套接字为阻塞或者非阻塞IO
  209. //------------------------------------------------------------------------------
  210. static bool_t _SetSocketBlockingEnabled(int fd, int blocking)
  211. {
  212.         if (fd < 0)
  213.                 return 0;

  214. #ifdef WIN32
  215.         {
  216.                 unsigned long mode = blocking ? 0 : 1;
  217.                 if(ioctlsocket(fd, FIONBIO, &mode) == 0)
  218.                         return 1;
  219.                 else
  220.                 {
  221.                         Inst_Log("Set socket to non blocking I/O mode failed. %d\n",
  222.                                 socket_errno);
  223.                         return 0;
  224.                 }
  225.         }
  226. #else
  227.         int flags = fcntl(fd, F_GETFL, 0);
  228.         if(flags < 0)return 0;
  229.         flags = blocking ? (flags&~O_NONBLOCK) : (flags|O_NONBLOCK);
  230.         if(!fcntl(fd, F_SETFL, flags))
  231.                 return 1;
  232.         else
  233.         {
  234.                 Inst_Log("Set socket to non blocking I/O mode failed.\n");
  235.                 return 0;
  236.         }
  237. #endif
  238. }

  239. //==============================================================================
  240. //Func: _signal_handler
  241. //Desc: 处理信号
  242. //------------------------------------------------------------------------------
  243. static void _signal_handler(int sig)
  244. {
  245.         switch(sig)
  246.         {
  247. #ifndef WIN32
  248.         case SIGHUP:
  249.                 break;
  250. #endif
  251.         case SIGTERM:
  252.                 Inst_Log("Terminate signal.\n");
  253.                 _g_Term = 1;
  254.                 break;
  255.         }
  256. }

  257. void Inst_LogTime()
  258. {
  259.         char szTime[128] = {0};
  260.         _strtime(szTime);
  261.         Inst_Log("[%s]: ", szTime);
  262. }

  263. //==============================================================================
  264. //Func: Inst_Log
  265. //Desc: Print log to a specified file.
  266. //------------------------------------------------------------------------------
  267. void Inst_Log(char *format, ...)
  268. {
  269.         FILE *logfile = NULL;
  270.         va_list ap;
  271.        
  272.         logfile=fopen(LOG_FILE,"a");
  273.         if(logfile)
  274.         {
  275.                 va_start(ap, format);
  276.                 vfprintf(logfile, format, ap);
  277.                 va_end(ap);
  278.                 fclose(logfile);
  279.         }

  280.         if(_g_LogToScreen)
  281.         {
  282.                 va_start(ap, format); // Linux似乎会把ap当作迭代器用,所以必须恢复它
  283.                 vfprintf(stderr, format, ap);
  284.                 va_end(ap);
  285.         }
  286. }

  287. //==============================================================================
  288. //Func: Inst_LogAddrV4
  289. //Desc: Print an IPv4 address to log
  290. //------------------------------------------------------------------------------
  291. void Inst_LogAddrV4(const address_p pAddr)
  292. {
  293.         char strIp[INET_ADDRSTRLEN + 1];

  294. #ifdef WIN32
  295.         sprintf(strIp, "%u.%u.%u.%u",
  296.                 pAddr->sa_in.sin_addr.S_un.S_un_b.s_b1,
  297.                 pAddr->sa_in.sin_addr.S_un.S_un_b.s_b2,
  298.                 pAddr->sa_in.sin_addr.S_un.S_un_b.s_b3,
  299.                 pAddr->sa_in.sin_addr.S_un.S_un_b.s_b4);
  300. #else
  301.         inet_ntop(AF_INET, &pAddr->sa_in.sin_addr,
  302.                 strIp, INET_ADDRSTRLEN);
  303. #endif
  304.                
  305.         Inst_Log("%s:%u", strIp, htons(pAddr->sa_in.sin_port));
  306. }

  307. //==============================================================================
  308. //Func: Inst_Create
  309. //Desc: Create a new instance class
  310. //------------------------------------------------------------------------------
  311. fwdinst_p Inst_Create(bool_t DoDaemonize, const char*cfg_file)
  312. {
  313.         fwdinst_p pInst = NULL;

  314.         _tzset();
  315.        
  316.         if(DoDaemonize && !_Inst_Daemonize())
  317.                 return NULL;
  318.        
  319.         pInst = malloc(sizeof(fwdinst_t));
  320.         if(!pInst)
  321.         {
  322.                 Inst_LogTime();
  323.                 Inst_Log("Out of memory.\n");
  324.                 return NULL;
  325.         }
  326.         memset(pInst, 0, sizeof(fwdinst_t));
  327.        
  328.         Inst_LogTime();
  329.         Inst_Log("Starting "EXEC_NAME"\n");

  330.         if(!_Inst_LoadCFG(pInst, cfg_file))
  331.                 goto ErrHandler;
  332.        
  333.         return pInst;
  334. ErrHandler:
  335.         Inst_Term(pInst);
  336.         return NULL;
  337. }

  338. //==============================================================================
  339. //Func: _CreateNBIOTCPSocket
  340. //Desc: Create a new socket, and set it to non blocking I/O mode
  341. //------------------------------------------------------------------------------
  342. static int _CreateNBIOTCPSocket()
  343. {
  344.         int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  345.         if(fd == -1)
  346.         {
  347.                 Inst_LogTime();
  348.                 Inst_Log("Create socket failed. %d\n", socket_errno);
  349.                 return-1;
  350.         }
  351.        
  352.         if(!_SetSocketBlockingEnabled(fd, 0))
  353.         {
  354.                 closesocket(fd);
  355.                 return -1;
  356.         }
  357.         return fd;
  358. }

  359. //==============================================================================
  360. //Func: _CreateNBIOTCPSocketWithConnection
  361. //Desc: Create a new socket with connection, and set it to non blocking I/O mode
  362. //------------------------------------------------------------------------------
  363. static int _CreateNBIOTCPSocketWithConnection
  364. (
  365.         const address_p addr,
  366.         socklen_t addrlen
  367. )
  368. {
  369.         int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  370.         if(fd == -1)
  371.         {
  372.                 Inst_LogTime();
  373.                 Inst_Log("Create socket failed.\n");
  374.                 return-1;
  375.         }
  376.        
  377.         if(!_SetSocketBlockingEnabled(fd, 0))
  378.         {
  379.                 closesocket(fd);
  380.                 return -1;
  381.         }
  382.        
  383.         Inst_LogTime();
  384.         Inst_Log("Connecting ");
  385.         Inst_LogAddrV4(addr);
  386.         Inst_Log(" ...\n");

  387.         if(connect(fd, &addr->sa, addrlen) < 0)
  388.         {
  389.                 int Err = socket_errno;
  390.                 switch(Err)
  391.                 {
  392.                 case EINPROGRESS:
  393. #if EWOULDBLOCK != EAGAIN
  394.                 case EWOULDBLOCK:
  395. #endif
  396.                 case EAGAIN:
  397.                         break;
  398.                 default:
  399.                         Inst_LogTime();
  400.                         Inst_Log("Connect ");
  401.                         Inst_LogAddrV4(addr);
  402.                         Inst_Log(" failed: %d.\n", Err);
  403.                         closesocket(fd);
  404.                         return -1;
  405.                 }
  406.         }
  407.        
  408.         return fd;
  409. }

  410. //==============================================================================
  411. //Func: _MakeIPv4Address
  412. //Desc: Make an IPv4 address by using given IP:Port
  413. //------------------------------------------------------------------------------
  414. static void _MakeIPv4Address(address_p pOut, uint32_t IP, uint16_t Port)
  415. {
  416.         memset(pOut, 0, sizeof(pOut->sa_in));
  417.        
  418.         pOut->sa_in.sin_family = AF_INET;
  419.         pOut->sa_in.sin_port = htons(Port);
  420.         pOut->sa_in.sin_addr.s_addr = htonl(IP);
  421. }

  422. //==============================================================================
  423. //Func: _ListenPort
  424. //Desc: Let a socket listen to a specified port.
  425. //------------------------------------------------------------------------------
  426. static bool_t _ListenPort(fwdinst_p pInst, int sockfd, int port)
  427. {
  428.         address_t Addr;
  429.         int reuse_addr = 1;
  430.         _MakeIPv4Address(&Addr, 0, port);

  431.         if(setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&reuse_addr, sizeof(reuse_addr)) < 0)
  432.         {
  433.                 Inst_LogTime();
  434.                 Inst_Log("Warning: set SO_REUSEADDR failed. %d\n", socket_errno);
  435.                 return 0;
  436.         }

  437.         if(bind(sockfd, &Addr.sa, sizeof(Addr.sa)) < 0)
  438.         {
  439.                 Inst_LogTime();
  440.                 Inst_Log("Bind port %d failed. %d\n", port, socket_errno);
  441.                 return 0;
  442.         }
  443.        
  444.         if(listen(sockfd, pInst->listen_backlog) < 0)
  445.         {
  446.                 int Err = socket_errno;
  447.                 switch(Err)
  448.                 {
  449.                 case EADDRINUSE:
  450.                         Inst_LogTime();
  451.                         Inst_Log("Listen to port failed: port already in use.\n");
  452.                         return 0;
  453.                 default:
  454.                         Inst_LogTime();
  455.                         Inst_Log("Listen to port failed: %d\n", Err);
  456.                         return 0;
  457.                 }
  458.         }
  459.        
  460.         Inst_LogTime();
  461.         Inst_Log("Listening to port %d.\n", port);

  462.         return 1;
  463. }

  464. //==============================================================================
  465. //Func: _Inst_AddRoute
  466. //Desc: Add a route rule for the instance
  467. //------------------------------------------------------------------------------
  468. static fwdroute_p _Inst_AddRoute
  469. (
  470.         fwdinst_p pInst,
  471.         int import,
  472.         uint32_t exportAddr,
  473.         int exportPort,
  474.         int status_of_tcp_nodelay, // 是否启用Nagle算法,1为不启用,0为启用
  475.         int packet_size_to_src,
  476.         int packet_size_to_dst
  477. )
  478. {
  479.         size_t cur;
  480.         if(pInst->num_route >= pInst->max_route)
  481.         {
  482.                 fwdroute_p pnew = realloc(pInst->pRoute,
  483.                         (pInst->max_route + 16) * sizeof(fwdroute_t));
  484.                 if(!pnew)
  485.                 {
  486.                         Inst_LogTime();
  487.                         Inst_Log("Out of memory.\n");
  488.                         return NULL;
  489.                 }
  490.                 pInst->pRoute = pnew;
  491.                 pInst->max_route += 16;
  492.         }
  493.        
  494.         cur = pInst->num_route;
  495.         memset(&pInst->pRoute[cur], 0, sizeof(fwdroute_t));
  496.        
  497.         if(packet_size_to_src <= 0)
  498.                 packet_size_to_src = def_packet_size;
  499.         if(packet_size_to_dst <= 0)
  500.                 packet_size_to_dst = def_packet_size;

  501.         //Initialize
  502.         pInst->pRoute[cur].import = import;
  503.         pInst->pRoute[cur].export = exportPort;
  504.         pInst->pRoute[cur].packet_size_to_src = packet_size_to_src;
  505.         pInst->pRoute[cur].packet_size_to_dst = packet_size_to_dst;
  506.         _MakeIPv4Address(&pInst->pRoute[cur].DestAddr, exportAddr, exportPort);
  507.         pInst->pRoute[cur].cbDestAddr = sizeof(struct sockaddr);
  508.         pInst->pRoute[cur].status_of_tcp_nodelay = status_of_tcp_nodelay;
  509.        
  510.         Inst_LogTime();
  511.         Inst_Log("Redirect: localhost:%d -> ", import);
  512.         Inst_LogAddrV4(&pInst->pRoute[cur].DestAddr);
  513.         Inst_Log(", TCP_NODELAY = %d, packet_size_to_src = %d, packet_size_to_dst = %d\n",
  514.                 pInst->pRoute[cur].status_of_tcp_nodelay,
  515.                 pInst->pRoute[cur].packet_size_to_src,
  516.                 pInst->pRoute[cur].packet_size_to_dst);

  517.         if(pInst->pRoute[cur].packet_size_to_src > 8192)
  518.         {
  519.                 Inst_LogTime();
  520.                 Inst_Log("Warning: `packet to source' should not exceed 8192 bytes.\n");
  521.                 pInst->pRoute[cur].packet_size_to_src = 8192;
  522.         }
  523.         if(pInst->pRoute[cur].packet_size_to_dst > 8192)
  524.         {
  525.                 Inst_LogTime();
  526.                 Inst_Log("Warning: `packet to dest' size should not exceed 8192 bytes.\n");
  527.                 pInst->pRoute[cur].packet_size_to_dst = 8192;
  528.         }
  529.        
  530.         //Create the socket
  531.         pInst->pRoute[cur].listen_socket = _CreateNBIOTCPSocket();
  532.         if(pInst->pRoute[cur].listen_socket == -1)
  533.                 return NULL;
  534.        
  535.         if(!_ListenPort(pInst, pInst->pRoute[cur].listen_socket, import))
  536.                 return NULL;
  537.        
  538.         pInst->num_route++;
  539.         return &pInst->pRoute[cur];
  540. }

  541. //==============================================================================
  542. //Func: _Inst_AddNewConnection
  543. //Desc: Add a socket to a route, called when accepted a new connection.
  544. //------------------------------------------------------------------------------
  545. static fwdconn_p _Inst_AddNewConnection
  546. (
  547.         fwdroute_p pRoute,
  548.         int sockfd,
  549.         address_p pAddr,
  550.         socklen_t cbAddr
  551. )
  552. {
  553.         int sockfd_out = _CreateNBIOTCPSocketWithConnection(&pRoute->DestAddr,
  554.                 pRoute->cbDestAddr);
  555.         if(sockfd_out == -1)
  556.                 return NULL;

  557.         if(setsockopt(sockfd_out, IPPROTO_TCP, TCP_NODELAY,
  558.                 (char*)&pRoute->status_of_tcp_nodelay,
  559.                 sizeof(pRoute->status_of_tcp_nodelay)) < 0)
  560.         {
  561.                 Inst_LogTime();
  562.                 Inst_Log("Warning: setsockopt(TCP_NODELAY) failed:%d.\n", socket_errno);
  563.         }
  564.        
  565.         if(pRoute->num_fwd_conn >= pRoute->max_fwd_conn)
  566.         {
  567.                 fwdconn_p pnew = realloc(pRoute->fwd_conn,
  568.                         (pRoute->max_fwd_conn + num_conn_alloc) * sizeof(fwdconn_t));
  569.                 if(!pnew)
  570.                 {
  571.                         Inst_LogTime();
  572.                         Inst_Log("Out of memory.\n");
  573.                         return 0;
  574.                 }
  575.                 pRoute->fwd_conn = pnew;
  576.                 pRoute->max_fwd_conn += num_conn_alloc;
  577.         }

  578.         memset(&pRoute->fwd_conn[pRoute->num_fwd_conn], 0, sizeof(fwdconn_t));
  579.        
  580.         pRoute->fwd_conn[pRoute->num_fwd_conn].sockfd_src = sockfd;
  581.         pRoute->fwd_conn[pRoute->num_fwd_conn].sockfd_dst = sockfd_out;
  582.         pRoute->fwd_conn[pRoute->num_fwd_conn].addr_from = *pAddr;
  583.         pRoute->fwd_conn[pRoute->num_fwd_conn].addr_size = cbAddr;
  584.         return &pRoute->fwd_conn[pRoute->num_fwd_conn++];
  585. }

  586. //==============================================================================
  587. //Func: _Inst_BreakConnection
  588. //Desc: close a specific connection.
  589. //------------------------------------------------------------------------------
  590. static void _Inst_BreakConnection(fwdroute_p pRoute, size_t ic)
  591. {
  592.         if(ic >= pRoute->num_fwd_conn)
  593.                 return;

  594.         Inst_LogTime();
  595.         Inst_Log("Connection closed: from ");
  596.         Inst_LogAddrV4(&pRoute->fwd_conn[ic].addr_from);
  597.         Inst_Log(" to ");
  598.         Inst_LogAddrV4(&pRoute->DestAddr);
  599.         Inst_Log("\n");
  600.        
  601.         if(pRoute->fwd_conn[ic].sockfd_src != -1)
  602.                 closesocket(pRoute->fwd_conn[ic].sockfd_src);
  603.         pRoute->fwd_conn[ic].sockfd_src = -1;
  604.        
  605.         if(pRoute->fwd_conn[ic].sockfd_dst != -1)
  606.                 closesocket(pRoute->fwd_conn[ic].sockfd_dst);
  607.         pRoute->fwd_conn[ic].sockfd_dst = -1;
  608.        
  609.         if(pRoute->num_fwd_conn > 1)
  610.         {
  611.                 pRoute->fwd_conn[ic] = pRoute->fwd_conn[--pRoute->num_fwd_conn];
  612.                 if(pRoute->num_fwd_conn + num_conn_alloc < pRoute->max_fwd_conn)
  613.                 {
  614.                         pRoute->max_fwd_conn -= num_conn_alloc;
  615.                         if(pRoute->max_fwd_conn)
  616.                         {
  617.                                 fwdconn_p pshrink = realloc(pRoute->fwd_conn,
  618.                                         pRoute->max_fwd_conn * sizeof(fwdconn_t));
  619.                                 if(pshrink)
  620.                                         pRoute->fwd_conn = pshrink;
  621.                         }
  622.                 }
  623.         }
  624.         else
  625.                 pRoute->num_fwd_conn = 0;
  626. }

  627. //==============================================================================
  628. //Func: _IpAddrV4ByNums
  629. //Desc: Combine 4 numbers to an ipv4 address
  630. //------------------------------------------------------------------------------
  631. static uint32_t _IpAddrV4ByNums(int n1, int n2, int n3, int n4)
  632. {
  633.         return
  634.                 ((n4 & 0xFF)) |
  635.                 ((n3 & 0xFF) << 8) |
  636.                 ((n2 & 0xFF) << 16) |
  637.                 ((n1 & 0xFF) << 24);
  638. }

  639. //==============================================================================
  640. //Func: _Inst_LoadCFG
  641. //Desc: Load configurations
  642. //------------------------------------------------------------------------------
  643. static bool_t _Inst_LoadCFG(fwdinst_p pInst, const char*cfg_file)
  644. {
  645.         FILE*cfgfile = NULL;
  646.         char LineBuf[256] = {0};
  647.         char*pChr;
  648.         unsigned LineNo = 0;
  649.        
  650.         cfgfile = fopen(cfg_file, "r");
  651.         if(!cfgfile)
  652.         {
  653.                 Inst_LogTime();
  654.                 Inst_Log("Configure file not found: %s\nCreating DEMO configure file.\n",
  655.                         cfg_file);
  656.                 cfgfile = fopen(cfg_file, "w");
  657.                 if(!cfgfile)
  658.                 {
  659.                         Inst_LogTime();
  660.                         Inst_Log("Could not create config file %s\n", cfg_file);
  661.                         return 0;
  662.                 }
  663.                
  664.                 fprintf(cfgfile,
  665. "# backlog parameter of a listen() function call.\n"
  666. "listen_backlog %u\n"
  667. "# write traffic information to log file.\n"
  668. "log_traffic 1\n"
  669. "# redirect <source port>, <dest address>:<dest port> [tcp_nodelay],[, packet to source] [, packet to destination]\n"
  670. "# packet size defaults to %u\n"
  671. "#redirect 80, 12.34.56.78:8080\n"
  672. "# Which means any tcp packet to this server's 80 port will be sent to 12.34.56.78:8080\n",
  673.                         backlog_default, def_packet_size);
  674.                
  675.                 fclose(cfgfile);
  676.                 return 0;
  677.         }
  678.        
  679.         pInst->listen_backlog = backlog_default;
  680.         pInst->log_traffic = 0;
  681.         do
  682.         {
  683.                 int n1, n2, n3, n4, n5, n6, n7, n8, n9;
  684.                 int fields;
  685.                 if(!fgets(LineBuf, sizeof(LineBuf), cfgfile))
  686.                         break;
  687.                 LineNo++;
  688.                 pChr = LineBuf;
  689.                 while(isspace(*pChr))
  690.                         pChr++;
  691.                 if(*pChr == '#') // Comment
  692.                         continue;
  693.                 if(sscanf(pChr, "listen_backlog %d", &n1) == 1)
  694.                 {
  695.                         pInst->listen_backlog = n1;
  696.                         Inst_LogTime();
  697.                         Inst_Log("listen backlog = %d\n", n1);
  698.                 }
  699.                 else if(sscanf(pChr, "log_traffic %d", &n1) == 1)
  700.                 {
  701.                         pInst->log_traffic = n1;
  702.                         Inst_LogTime();
  703.                         Inst_Log("log traffic = %d\n", n1);
  704.                 }
  705.                 else
  706.                 {
  707.                         n7 = 0;
  708.                         n8 = n9 = def_packet_size;
  709.                         fields = sscanf(pChr, "redirect %d,%d.%d.%d.%d:%d,%d,%d,%d",
  710.                                 &n1, &n2, &n3, &n4, &n5, &n6, &n7, &n8, &n9);

  711.                         if(fields >= 6)
  712.                         {
  713.                                 fwdroute_p pnew = _Inst_AddRoute(pInst, n1,
  714.                                         _IpAddrV4ByNums(n2, n3, n4, n5), n6, n7, n8, n9);
  715.                                 if(!pnew)
  716.                                 {
  717.                                         Inst_LogTime();
  718.                                         Inst_Log("Add redirect option failed at line %u\n", LineNo);
  719.                                 }
  720.                         }
  721.                 }
  722.         }while(!feof(cfgfile));
  723.        
  724.         fclose(cfgfile);
  725.        
  726.         if(!pInst->num_route)
  727.         {
  728.                 Inst_LogTime();
  729.                 Inst_Log("No routes added. Quitting.\n");
  730.                 return 0;
  731.         }
  732.         return 1;
  733. }

  734. //==============================================================================
  735. //Func: Inst_Term
  736. //Desc: Terminate instance, clear memory
  737. //------------------------------------------------------------------------------
  738. void Inst_Term(fwdinst_p pInst)
  739. {
  740.         Inst_LogTime();
  741.         Inst_Log(EXEC_NAME" stopped.\n\n");
  742.         if(pInst)
  743.         {
  744.                 size_t i;
  745.                 for(i = 0; i < pInst->num_route; i++)
  746.                 {
  747.                         size_t j;
  748.                         for(j = 0; j < pInst->pRoute[i].num_fwd_conn; j++)
  749.                         {
  750.                                 _Inst_BreakConnection(&pInst->pRoute[i], j);
  751.                         }
  752.                         free(pInst->pRoute[i].fwd_conn);
  753.                 }
  754.                 free(pInst->pRoute);
  755.                 free(pInst);
  756.         }
  757. }

  758. //==============================================================================
  759. //Func: Inst_Run
  760. //Desc: Run instance, do forwarding
  761. //------------------------------------------------------------------------------
  762. void Inst_Run(fwdinst_p pInst)
  763. {
  764.         size_t i;

  765.         if(pInst->max_route > pInst->num_route)
  766.         {
  767.                 pInst->max_route = pInst->num_route;
  768.                 pInst->pRoute = realloc(pInst->pRoute,
  769.                         pInst->max_route * sizeof(fwdroute_t));
  770.         }
  771.        
  772.         for(i = 0; i < pInst->num_route; i++)
  773.         {
  774.                 fwdroute_p pRoute = &pInst->pRoute[i];
  775.                 address_t addr_from;
  776.                 socklen_t cb_addr_from = sizeof(addr_from.sa);
  777.                 int sockfd;
  778.                 size_t j;
  779.                
  780.                 sockfd = accept(pRoute->listen_socket, &addr_from.sa, &cb_addr_from);
  781.                 if(sockfd == -1)
  782.                 {
  783.                         int Err = socket_errno;
  784.                         switch(Err)
  785.                         {
  786. #if EWOULDBLOCK != EAGAIN
  787.                 case EWOULDBLOCK:
  788. #endif
  789.                         case EAGAIN:
  790.                                 break;
  791.                         case EPERM:
  792.                                 Inst_LogTime();
  793.                                 Inst_Log("Accept connection failed: firewall rules forbid connection.\n");
  794.                                 break;
  795.                         default:
  796.                                 Inst_LogTime();
  797.                                 Inst_Log("Accept connection failed: %d.\n", Err);
  798.                                 break;
  799.                         }
  800.                 }
  801.                 else
  802.                 {
  803.                         if(_SetSocketBlockingEnabled(sockfd, 0))
  804.                         {
  805.                                 if(addr_from.sa.sa_family == AF_INET)
  806.                                 {
  807.                                         fwdconn_p pConn;
  808.                                
  809.                                         Inst_LogTime();
  810.                                         Inst_Log("Accepted new connection from ");
  811.                                         Inst_LogAddrV4(&addr_from);
  812.                                         Inst_Log("\n");
  813.                                
  814.                                         pConn = _Inst_AddNewConnection(pRoute, sockfd, &addr_from, cb_addr_from);
  815.                                         if(!pConn)
  816.                                         {
  817.                                                 closesocket(sockfd);
  818.                                                 break;
  819.                                         }
  820.                                 }
  821.                                 else
  822.                                         ;//TODO: Add IPv6 support
  823.                         }
  824.                 }
  825.                
  826.                 for(j = 0; j < pRoute->num_fwd_conn; j++)
  827.                 {
  828.                         size_t cbSendSize;

  829.                         //=================================================================
  830.                         //Receive data from source
  831.                         //-----------------------------------------------------------------
  832.                         if(pRoute->fwd_conn[j].cb_src < fwd_buffer_size)
  833.                         {
  834.                                 ssize_t cbRecv = recv(pRoute->fwd_conn[j].sockfd_src,
  835.                                         (char*)pRoute->fwd_conn[j].buffer_src
  836.                                         + pRoute->fwd_conn[j].cb_src,
  837.                                         fwd_buffer_size - pRoute->fwd_conn[j].cb_src, 0);
  838.                                 if(cbRecv > 0)
  839.                                 {
  840.                                         pRoute->fwd_conn[j].cb_src += cbRecv;
  841.                                         if(pInst->log_traffic)
  842.                                         {
  843.                                                 Inst_LogTime();
  844.                                                 Inst_Log("Received %u bytes from source ", cbRecv);
  845.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  846.                                                 Inst_Log("\n");
  847.                                         }
  848.                                 }
  849.                                 else if(cbRecv == 0)
  850.                                 {
  851.                                         Inst_LogTime();
  852.                                         Inst_Log("Lost connection from source: ");
  853.                                         Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  854.                                         Inst_Log("\n");
  855.                                         _Inst_BreakConnection(pRoute, j);
  856.                                         goto BreakLoop;
  857.                                 }
  858.                                 else if(cbRecv < 0)
  859.                                 {
  860.                                         int Err = socket_errno;
  861.                                         switch(Err)
  862.                                         {
  863.                                         case EINPROGRESS:
  864. #if EWOULDBLOCK != EAGAIN
  865.                                         case EWOULDBLOCK:
  866. #endif
  867.                                         case EAGAIN:
  868.                                                 break;
  869.                                         case ECONNABORTED:
  870.                                         case ENOTCONN:
  871.                                                 Inst_LogTime();
  872.                                                 Inst_Log("Lost connection from source: ");
  873.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  874.                                                 Inst_Log("\n");
  875.                                                 _Inst_BreakConnection(pRoute, j);
  876.                                                 goto BreakLoop;
  877.                                         case ECONNRESET:
  878.                                                 Inst_LogTime();
  879.                                                 Inst_Log("Connection reset by source: ");
  880.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  881.                                                 Inst_Log("\n");
  882.                                                 _Inst_BreakConnection(pRoute, j);
  883.                                                 goto BreakLoop;
  884.                                         case ECONNREFUSED:
  885.                                                 Inst_LogTime();
  886.                                                 Inst_Log("Connection refused by source: ");
  887.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  888.                                                 Inst_Log("\n");
  889.                                                 _Inst_BreakConnection(pRoute, j);
  890.                                                 goto BreakLoop;
  891.                                         default:
  892.                                                 Inst_LogTime();
  893.                                                 Inst_Log("An error occured (%d) while receiving data from "
  894.                                                         "source ", Err);
  895.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  896.                                                 Inst_Log("\n");
  897.                                                 _Inst_BreakConnection(pRoute, j);
  898.                                                 goto BreakLoop;
  899.                                         }
  900.                                 }
  901.                         }

  902.                         //=================================================================
  903.                         //Send data to destination
  904.                         //-----------------------------------------------------------------
  905.                         if(pRoute->fwd_conn[j].cb_src > 0 )
  906.                         {
  907.                                 ssize_t cbSend;
  908.                                 cbSendSize = pRoute->fwd_conn[j].cb_src;
  909.                                 if(cbSendSize > pRoute->packet_size_to_dst)
  910.                                         cbSendSize = pRoute->packet_size_to_dst;
  911.                                 cbSend = send(pRoute->fwd_conn[j].sockfd_dst,
  912.                                         pRoute->fwd_conn[j].buffer_src,
  913.                                         cbSendSize, MSG_NOSIGNAL);
  914.                                 if(cbSend > 0 && cbSend <= pRoute->fwd_conn[j].cb_src)
  915.                                 {
  916.                                         pRoute->fwd_conn[j].cb_src -= cbSend;
  917.                                         if(pRoute->fwd_conn[j].cb_src)
  918.                                         {
  919.                                                 memmove(pRoute->fwd_conn[j].buffer_src,
  920.                                                         (char*)pRoute->fwd_conn[j].buffer_src + cbSend,
  921.                                                         pRoute->fwd_conn[j].cb_src);
  922.                                         }
  923.                                         if(!pRoute->fwd_conn[j].connected)
  924.                                         {
  925.                                                 Inst_LogTime();
  926.                                                 Inst_Log("Connected to destination: ");
  927.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  928.                                                 Inst_Log("\n");
  929.                                                 pRoute->fwd_conn[j].connected = 1;
  930.                                         }
  931.                                         if(pInst->log_traffic)
  932.                                         {
  933.                                                 Inst_LogTime();
  934.                                                 Inst_Log("Sent %u bytes from source ", cbSend);
  935.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  936.                                                 Inst_Log(" to destination ");
  937.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  938.                                                 Inst_Log("\n");
  939.                                         }
  940.                                 }
  941.                                 else if(cbSend < 0)
  942.                                 {
  943.                                         int Err = socket_errno;
  944.                                         switch(Err)
  945.                                         {
  946.                                         case EINPROGRESS:
  947.                                         case ENOTCONN:
  948.                                                 // Inst_Log("c");
  949.                                                 pRoute->fwd_conn[j].connected = 0;
  950.                                                 break;
  951. #if EWOULDBLOCK != EAGAIN
  952.                                         case EWOULDBLOCK:
  953. #endif
  954.                                         case EAGAIN:
  955.                                                 break;
  956.                                         case ECONNABORTED:
  957.                                                 Inst_LogTime();
  958.                                                 Inst_Log("Connection aborted by destination: ");
  959.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  960.                                                 Inst_Log("\n");
  961.                                                 _Inst_BreakConnection(pRoute, j);
  962.                                                 goto BreakLoop;
  963.                                         case ECONNRESET:
  964.                                                 Inst_LogTime();
  965.                                                 Inst_Log("Connection reset by destination: ");
  966.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  967.                                                 Inst_Log("\n");
  968.                                                 _Inst_BreakConnection(pRoute, j);
  969.                                                 goto BreakLoop;
  970.                                         case ECONNREFUSED:
  971.                                                 Inst_LogTime();
  972.                                                 Inst_Log("Connection refused by destination: ");
  973.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  974.                                                 Inst_Log("\n");
  975.                                                 _Inst_BreakConnection(pRoute, j);
  976.                                                 goto BreakLoop;
  977.                                         default:
  978.                                                 Inst_LogTime();
  979.                                                 Inst_Log("An error occured (%d) while sending data to destination ", Err);
  980.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  981.                                                 Inst_Log("\n");
  982.                                                 _Inst_BreakConnection(pRoute, j);
  983.                                                 goto BreakLoop;
  984.                                         }
  985.                                 }
  986.                         }

  987.                        
  988.                         //=================================================================
  989.                         //Receive data from destination
  990.                         //-----------------------------------------------------------------
  991.                         if(pRoute->fwd_conn[j].cb_dst < fwd_buffer_size)
  992.                         {
  993.                                 ssize_t cbRecv = recv(pRoute->fwd_conn[j].sockfd_dst,
  994.                                         (char*)pRoute->fwd_conn[j].buffer_dst
  995.                                         + pRoute->fwd_conn[j].cb_dst,
  996.                                         fwd_buffer_size - pRoute->fwd_conn[j].cb_dst, 0);
  997.                                 if(cbRecv > 0)
  998.                                 {
  999.                                         pRoute->fwd_conn[j].cb_dst += cbRecv;
  1000.                                         if(pInst->log_traffic)
  1001.                                         {
  1002.                                                 Inst_LogTime();
  1003.                                                 Inst_Log("Received %u bytes from destination ", cbRecv);
  1004.                                                 Inst_LogAddrV4(&pRoute->DestAddr);
  1005.                                                 Inst_Log("\n");
  1006.                                         }
  1007.                                 }
  1008.                                 else if(cbRecv == 0)
  1009.                                 {
  1010.                                         Inst_LogTime();
  1011.                                         Inst_Log("Lost connection from source: ");
  1012.                                         Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1013.                                         Inst_Log("\n");
  1014.                                         _Inst_BreakConnection(pRoute, j);
  1015.                                         goto BreakLoop;
  1016.                                 }
  1017.                                 else if(cbRecv < 0)
  1018.                                 {
  1019.                                         int Err = socket_errno;
  1020.                                         switch(Err)
  1021.                                         {
  1022.                                         case EINPROGRESS:
  1023.                                         case ENOTCONN:
  1024.                                                 pRoute->fwd_conn[j].connected = 0;
  1025.                                                 break;
  1026. #if EWOULDBLOCK != EAGAIN
  1027.                                         case EWOULDBLOCK:
  1028. #endif
  1029.                                         case EAGAIN:
  1030.                                                 break;
  1031.                                         case ECONNABORTED:
  1032.                                                 Inst_LogTime();
  1033.                                                 Inst_Log("Connection aborted by destination: ");
  1034.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1035.                                                 Inst_Log("\n");
  1036.                                                 _Inst_BreakConnection(pRoute, j);
  1037.                                                 goto BreakLoop;
  1038.                                         case ECONNRESET:
  1039.                                                 Inst_LogTime();
  1040.                                                 Inst_Log("Connection reset by destination: ");
  1041.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1042.                                                 Inst_Log("\n");
  1043.                                                 _Inst_BreakConnection(pRoute, j);
  1044.                                                 goto BreakLoop;
  1045.                                         case ECONNREFUSED:
  1046.                                                 Inst_LogTime();
  1047.                                                 Inst_Log("Connection refused by destination: ");
  1048.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1049.                                                 Inst_Log("\n");
  1050.                                                 _Inst_BreakConnection(pRoute, j);
  1051.                                                 goto BreakLoop;
  1052.                                         default:
  1053.                                                 Inst_LogTime();
  1054.                                                 Inst_Log("An error occured (%d) while receiving data from destination ", Err);
  1055.                                                 Inst_LogAddrV4(&pRoute->fwd_conn[j].addr_from);
  1056.                                                 Inst_Log("\n");
  1057.                                                 _Inst_BreakConnection(pRoute, j);
  1058.                                                 goto BreakLoop;
  1059.                                         }
  1060.                                 }
  1061.                         }
  1062.                        

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

  1137.                 }
  1138.         }
  1139. BreakLoop:;
  1140. }

  1141. //==============================================================================
  1142. //Func: _Inst_Daemonize
  1143. //Desc: Make program run as a daemon
  1144. //------------------------------------------------------------------------------
  1145. static int _Inst_Daemonize()
  1146. {
  1147. #ifndef WIN32
  1148.         int i,lfp;
  1149.         char str[10];
  1150.        
  1151.         if(getppid() == 1)
  1152.         {
  1153.                 fprintf(stderr, EXEC_NAME" was still running.\n");
  1154.                 return 0;
  1155.         }
  1156.        
  1157.         i = fork();
  1158.         if(i < 0)
  1159.         {
  1160.                 // fork error
  1161.                 return 0;
  1162.         }
  1163.         if(i > 0)
  1164.         {
  1165.                 // Parent exits, child (daemon) continues
  1166.                 return 0;
  1167.         }
  1168.        
  1169.         setsid(); // obtain a new process group
  1170.        
  1171.         fclose(stdin);
  1172.         fclose(stdout);
  1173.         if(!_g_LogToScreen)
  1174.                 fclose(stderr);
  1175.        
  1176.         lfp=open(LOCK_FILE,O_RDWR|O_CREAT,0644);
  1177.         if(lfp<0)
  1178.         {
  1179.                 // Could not open lock file.
  1180.                 Inst_LogTime();
  1181.                 Inst_Log("Open lock file failed.\n");
  1182.                 return 0;
  1183.         }
  1184.        
  1185.         if(lockf(lfp, F_TLOCK, 0) < 0)
  1186.         {
  1187.                 // can not lock
  1188.                 Inst_LogTime();
  1189.                 Inst_Log("Lock lock file failed.\n");
  1190.                 return 0;
  1191.         }
  1192.        
  1193.         // first instance continues
  1194.         sprintf(str,"%d\n",getpid());
  1195.         write(lfp,str,strlen(str)); // record pid to lockfile
  1196.        
  1197.         signal(SIGCHLD,SIG_IGN); // ignore child
  1198.         signal(SIGTSTP,SIG_IGN); // ignore tty signals
  1199.         signal(SIGTTOU,SIG_IGN);
  1200.         signal(SIGTTIN,SIG_IGN);
  1201.         signal(SIGPIPE,SIG_IGN);
  1202.         signal(SIGHUP,_signal_handler); // catch hangup signal
  1203. #endif
  1204.         signal(SIGTERM,_signal_handler); // catch kill signal
  1205.        
  1206.         return 1;
  1207. }

  1208. //==============================================================================
  1209. //Func: main
  1210. //Desc: Entry point
  1211. //------------------------------------------------------------------------------
  1212. int main(int argc, char**argv)
  1213. {
  1214.         fwdinst_p pInst;
  1215.         bool_t DoDaemonize = 0;
  1216.         char*sz_cfg_file = CFG_FILE;

  1217. #ifdef WIN32
  1218.         {
  1219.                 WSADATA wsaData;
  1220.                 WSAStartup(WINSOCK_VERSION, &wsaData);
  1221.         }
  1222. #else
  1223.         {
  1224.                 int c;

  1225.                 for(;;)
  1226.                 {
  1227.                         c = getopt(argc, argv, "c:dvh");
  1228.                         if(c == -1)
  1229.                                 break;

  1230.                         switch(c)
  1231.                         {
  1232.                         case'c'://Customize config file
  1233.                                 sz_cfg_file = optarg;
  1234.                                 break;
  1235.                         case'd'://Run as a daemon
  1236.                                 DoDaemonize = 1;
  1237.                                 _g_LogToScreen = 0;
  1238.                                 break;
  1239.                         case'v':
  1240.                                 _g_LogToScreen = 1;
  1241.                                 break;
  1242.                         default:
  1243.                                 fprintf(stderr, "Unknown option '%c'\n", c);
  1244.                         case'h':
  1245.                                 fprintf(stderr, "Usage:\n"
  1246.                                         EXEC_NAME" [-c cfgfile][-d][-v][-h]\n"
  1247.                                         " -c: set config file"
  1248.                                         " -d: run as a daemon\n"
  1249.                                         " -v: print log to screen\n"
  1250.                                         " -h: show this help\n");
  1251.                                 break;
  1252.                         }
  1253.                 }
  1254.         }
  1255. #endif
  1256.        
  1257.         pInst = Inst_Create(DoDaemonize, sz_cfg_file);
  1258.         if(pInst)
  1259.         {
  1260.                 while(!_g_Term)
  1261.                 {
  1262.                         Inst_Run(pInst);
  1263.                         _done();
  1264.                 }
  1265.                 Inst_Term(pInst);
  1266.         }
  1267. #ifdef WIN32
  1268.         WSACleanup();
  1269. #endif
  1270.         return 0;
  1271. }
复制代码
编译方法:写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一下, 说不定哪天就用到了

995

主题

2202

帖子

5万

积分

用户组: 管理员

一只技术宅

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

995

主题

2202

帖子

5万

积分

用户组: 管理员

一只技术宅

UID
1
精华
197
威望
261 点
宅币
16296 个
贡献
31900 次
宅之契约
0 份
在线时间
1555 小时
注册时间
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
复制代码

本版积分规则

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

GMT+8, 2018-8-18 12:41 , Processed in 0.102938 second(s), 17 queries , Gzip On, Memcache On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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