-
Notifications
You must be signed in to change notification settings - Fork 0
/
rinetdin.cpp
291 lines (233 loc) · 10.4 KB
/
rinetdin.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/*
* 程序名:rinetdin.cpp,网络代理服务程序-内网端。
* 作者:任振华
*/
#include "_public.h"
int cmdconnsock; // 内网程序与外网程序的控制通道。
int epollfd = 0; // epoll的句柄。
int tfd = 0; // 定时器的句柄。
#define MAXSOCK 1024
int clientsocks[MAXSOCK]; // 存放每个socket连接对端的socket的值。
int clientatime[MAXSOCK]; // 存放每个socket连接最后一次收发报文的时间。
// 向目标ip和端口发起socket连接。
int conntodst(const char *ip, const int port);
void EXIT(int sig); // 进程退出函数。
CLogFile logfile;
CPActive PActive; // 进程心跳。
int main(int argc, char *argv[])
{
if (argc != 4)
{
printf("\n");
printf("Using :./rinetdin logfile ip port\n\n");
printf("Sample:./rinetdin /tmp/rinetdin.log 175.178.53.221 4000\n\n");
printf(" /project/tools1/bin/procctl 5 /project/tools1/bin/rinetdin /tmp/rinetdin.log 175.178.53.221 4000\n\n");
printf("logfile 本程序运行的日志文件名。\n");
printf("ip 外网代理服务端的地址。\n");
printf("port 外网代理服务端的端口。\n\n\n");
return -1;
}
// 关闭全部的信号和输入输出。
// 设置信号,在shell状态下可用 "kill + 进程号" 正常终止些进程。
// 但请不要用 "kill -9 +进程号" 强行终止。
CloseIOAndSignal();
signal(SIGINT, EXIT);
signal(SIGTERM, EXIT);
// 打开日志文件。
if (logfile.Open(argv[1], "a+") == false)
{
printf("打开日志文件失败(%s)。\n", argv[1]);
return -1;
}
PActive.AddPInfo(30, "inetd"); // 设置进程的心跳超时间为30秒。
// 建立内网程序与程序的控制通道。
CTcpClient TcpClient;
if (TcpClient.ConnectToServer(argv[2], atoi(argv[3])) == false)
{
logfile.Write("TcpClient.ConnectToServer(%s,%s) 失败。\n", argv[2], argv[3]);
return -1;
}
cmdconnsock = TcpClient.m_connfd;
fcntl(cmdconnsock, F_SETFL, fcntl(cmdconnsock, F_GETFD, 0) | O_NONBLOCK);
logfile.Write("与外部的控制通道已建立(cmdconnsock=%d)。\n", cmdconnsock);
// 创建epoll句柄。
epollfd = epoll_create(1);
struct epoll_event ev; // 声明事件的数据结构。
// 为控制通道的socket准备可读事件。
ev.events = EPOLLIN;
ev.data.fd = cmdconnsock;
epoll_ctl(epollfd, EPOLL_CTL_ADD, cmdconnsock, &ev);
// 创建定时器。
tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); // 创建timerfd。
struct itimerspec timeout;
memset(&timeout, 0, sizeof(struct itimerspec));
timeout.it_value.tv_sec = 20; // 超时时间为20秒。
timeout.it_value.tv_nsec = 0;
timerfd_settime(tfd, 0, &timeout, NULL); // 设置定时器。
// 为定时器准备事件。
ev.events = EPOLLIN | EPOLLET; // 读事件,注意,一定要ET模式。
ev.data.fd = tfd;
epoll_ctl(epollfd, EPOLL_CTL_ADD, tfd, &ev);
PActive.AddPInfo(30, "rinetdin"); // 设置进程的心跳超时间为30秒。
struct epoll_event evs[10]; // 存放epoll返回的事件。
while (true)
{
// 等待监视的socket有事件发生。
int infds = epoll_wait(epollfd, evs, 10, -1);
// 返回失败。
if (infds < 0)
{
logfile.Write("epoll() failed。\n");
break;
}
// 遍历epoll返回的已发生事件的数组evs。
for (int ii = 0; ii < infds; ii++)
{
// logfile.Write("events=%d, data.fd=%d\n", evs[ii].events, evs[ii].data.fd);
////////////////////////////////////////////////////////
// 如果定时器的时间已到,设置进程的心跳,清理空闲的客户端socket。
if (evs[ii].data.fd == tfd)
{
timerfd_settime(tfd, 0, &timeout, NULL); // 重新设置定时器。
PActive.UptATime(); // 更新进程心跳。
for (int jj = 0; jj < MAXSOCK; jj++)
{
// 如果客户端socket空闲的时间超过80秒就关掉它。
if ((clientsocks[jj] > 0) && ((time(0) - clientatime[jj]) > 80))
{
logfile.Write("client(%d,%d) timeout。\n", clientsocks[jj], clientsocks[clientsocks[jj]]);
close(clientsocks[jj]);
close(clientsocks[clientsocks[jj]]);
// 把数组中对端的socket置空,这一行代码和下一行代码的顺序不能乱。
clientsocks[clientsocks[jj]] = 0;
// 把数组中本端的socket置空,这一行代码和上一行代码的顺序不能乱。
clientsocks[jj] = 0;
}
}
continue;
}
////////////////////////////////////////////////////////
// 如果发生事件的是控制通道。
if (evs[ii].data.fd == cmdconnsock)
{
// 读取控制报文内容。
char buffer[256];
memset(buffer, 0, sizeof(buffer));
if (recv(cmdconnsock, buffer, 200, 0) <= 0)
{
logfile.Write("与外网的控制通道已断开。\n");
EXIT(-1);
}
// 如果收到的是心跳报文。
if (strcmp(buffer, "<activetest>") == 0)
continue;
// 如果收到的是新建连接的命令。
// 向外网服务端发起连接请求。
int srcsock = conntodst(argv[2], atoi(argv[3]));
if (srcsock < 0)
continue;
if (srcsock >= MAXSOCK)
{
logfile.Write("连接数已超过最大值%d。\n", MAXSOCK);
close(srcsock);
continue;
}
// 从控制报文内容中获取目标服务地址和端口。
char dstip[11];
int dstport;
GetXMLBuffer(buffer, "dstip", dstip, 30);
GetXMLBuffer(buffer, "dstport", &dstport);
// 向目标服务地址和端口发起socket连接。
int dstsock = conntodst(dstip, dstport);
if (dstsock < 0)
{
close(srcsock);
continue;
}
if (dstsock >= MAXSOCK)
{
logfile.Write("连接数已超过最大值%d。\n", MAXSOCK);
close(srcsock);
close(dstsock);
continue;
}
// 把内网和外网的socket对接在一起。
logfile.Write("新建内外网通道(%d,%d) ok。\n", srcsock, dstsock);
// 为新连接的两个socket准备可读事件,并添加到epoll中。
ev.data.fd = srcsock;
ev.events = EPOLLIN;
epoll_ctl(epollfd, EPOLL_CTL_ADD, srcsock, &ev);
ev.data.fd = dstsock;
ev.events = EPOLLIN;
epoll_ctl(epollfd, EPOLL_CTL_ADD, dstsock, &ev);
// 更新clientsocks数组中两端soccket的值和活动时间。
clientsocks[srcsock] = dstsock;
clientsocks[dstsock] = srcsock;
clientatime[srcsock] = time(0);
clientatime[dstsock] = time(0);
continue;
}
////////////////////////////////////////////////////////
////////////////////////////////////////////////////////
// 以下流程处理内外网通讯链路socket的事件。
char buffer[5000]; // 从socket中读到的数据。
int buflen = 0; // 从socket中读到的数据的大小。
// 从一端读取数据。
memset(buffer, 0, sizeof(buffer));
if ((buflen = recv(evs[ii].data.fd, buffer, sizeof(buffer), 0)) <= 0)
{
// 如果连接已断开,需要关闭两个通道的socket。
logfile.Write("client(%d,%d) disconnected。\n", evs[ii].data.fd, clientsocks[evs[ii].data.fd]);
close(evs[ii].data.fd); // 关闭客户端的连接。
close(clientsocks[evs[ii].data.fd]); // 关闭客户端对端的连接。
clientsocks[clientsocks[evs[ii].data.fd]] = 0; // 这一行代码和下一行代码的顺序不能乱。
clientsocks[evs[ii].data.fd] = 0; // 这一行代码和上一行代码的顺序不能乱。
continue;
}
// 成功的读取到了数据,把接收到的报文内容原封不动的发给对端。
// logfile.Write("from %d to %d,%d bytes。\n", evs[ii].data.fd, clientsocks[evs[ii].data.fd], buflen);
send(clientsocks[evs[ii].data.fd], buffer, buflen, 0);
// 更新两端socket连接的活动时间。
clientatime[evs[ii].data.fd] = time(0);
clientatime[clientsocks[evs[ii].data.fd]] = time(0);
}
}
return 0;
}
// 向目标ip和端口发起socket连接。
int conntodst(const char *ip, const int port)
{
// 第1步:创建客户端的socket。
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
return -1;
// 第2步:向服务器发起连接请求。
struct hostent *h;
if ((h = gethostbyname(ip)) == 0)
{
close(sockfd);
return -1;
}
struct sockaddr_in servaddr;
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port); // 指定服务端的通讯端口。
memcpy(&servaddr.sin_addr, h->h_addr, h->h_length);
// 把socket设置为非阻塞。
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0) | O_NONBLOCK);
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
return sockfd;
}
void EXIT(int sig)
{
logfile.Write("程序退出,sig=%d。\n\n", sig);
// 关闭内网程序与外网程序的控制通道。
close(cmdconnsock);
// 关闭全部客户端的socket。
for (int ii = 0; ii < MAXSOCK; ii++)
if (clientsocks[ii] > 0)
close(clientsocks[ii]);
close(epollfd); // 关闭epoll。
close(tfd); // 关闭定时器。
exit(0);
}