-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathtracing_worker.cpp
More file actions
297 lines (233 loc) · 8.28 KB
/
Copy pathtracing_worker.cpp
File metadata and controls
297 lines (233 loc) · 8.28 KB
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
292
293
294
295
296
297
#include <QDebug>
#include "tracing_worker.h"
#include "configs.h"
/**
* TracingWorker
* 具体的追踪线程,每个线程负责一跳,当得到当前跳的数据之后返回。
*/
TracingWorker::TracingWorker() {
// 不能自动销毁,要等派发进程回收(避免脏写)
setAutoDelete(false);
// 分配当前跳地址的存储空间
currentHopIPAddress = new sockaddr_storage;
ZeroMemory(currentHopIPAddress, sizeof(sockaddr_storage));
}
TracingWorker::~TracingWorker() {
// 销毁需要销毁的东西
delete currentHopIPAddress;
}
void TracingWorker::run() {
// 标记为非停止
isStopping = false;
// 标记 IP 簇
currentHopIPAddress->ss_family = targetIPAddress->ss_family;
// 执行第一项任务:获得 IP
switch (targetIPAddress->ss_family) {
case AF_INET:
GetIPv4();
break;
case AF_INET6:
GetIPv6();
break;
}
// 回报一份权重,作为进度条增长的数值
emit incProgress(1);
// 如果获得的结果有效,并且并非正在停止,再执行第二项任务:获得 IP 信息
if (isIPValid && !isStopping) {
GetInfo();
}
// 回报一份权重,作为进度条增长的数值
emit incProgress(1);
// 如果获得的结果有效,并且并非正在停止,再执行第三项任务:获得对应的主机名
if (isIPValid && !isStopping) {
GetHostname(); // 这一步最耗时,所以放到最后
}
// 回报一份权重,作为进度条增长的数值
emit incProgress(1);
// 全部任务完成
emit fin(iTTL);
}
void TracingWorker::GetIPv4() {
// 第一步:追踪
// ICMP 包发送缓冲区和接收缓冲区
IP_OPTION_INFORMATION IpOption;
char SendData[DEF_ICMP_DATA_SIZE];
char ReplyBuf[sizeof(ICMP_ECHO_REPLY) + DEF_ICMP_DATA_SIZE];
// ICMP 回复的结果
PICMP_ECHO_REPLY pEchoReply;
// 初始化内存区间
ZeroMemory(&IpOption,sizeof(IP_OPTION_INFORMATION));
ZeroMemory(SendData, sizeof(SendData));
pEchoReply = (PICMP_ECHO_REPLY)ReplyBuf;
// 设置 TTL
IpOption.Ttl = iTTL;
// 设置有效值
isIPValid = false;
// 设置超时次数
int timeoutCount = 0;
while (!isStopping) {
// 发送数据报并等待消息到达
if (
IcmpSendEcho2(
hIcmp, NULL, NULL, NULL,
((sockaddr_in*)targetIPAddress)->sin_addr.s_addr, SendData, sizeof (SendData), &IpOption,
ReplyBuf, sizeof(ReplyBuf),
gCfg->GetTraceTimeout()
) != 0
) {
// 得到返回
((sockaddr_in*)currentHopIPAddress)->sin_addr.s_addr = pEchoReply->Address;
isIPValid = true;
// 任务完成,退出线程
break;
} else {
// 出现错误
qWarning() << "[Trace Worker]"
<< "ICMP send echo failed with error: "
<< GetLastError();
// 这里可以无视条件回报,因为失败的请求一定不会被认为是目标主机
timeoutCount++;
emit reportIPAndTimeConsumption(
iTTL, timeoutCount, 0, false, false
);
}
}
char printIPAddress[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(*(sockaddr_in*)currentHopIPAddress).sin_addr, printIPAddress, INET_ADDRSTRLEN);
// 判断是否为末端主机(最后一跳)
bool isTargetHost = ((sockaddr_in*)currentHopIPAddress)->sin_addr.s_addr == ((sockaddr_in*)targetIPAddress)->sin_addr.s_addr;
// 完成追踪
if (isIPValid && !isStopping) {
// 仅在没有被请求停止的时候回报
emit reportIPAndTimeConsumption(
iTTL, pEchoReply->RoundTripTime, QString(printIPAddress), true, isTargetHost
);
}
}
void TracingWorker::GetIPv6() {
// 第一步:追踪
// ICMP 包发送缓冲区和接收缓冲区
IP_OPTION_INFORMATION IpOption;
char SendData[DEF_ICMP_DATA_SIZE];
char ReplyBuf[sizeof(ICMPV6_ECHO_REPLY) + DEF_ICMP_DATA_SIZE];
// ICMP 回复的结果
PICMPV6_ECHO_REPLY pEchoReply;
// 初始化内存区间
ZeroMemory(&IpOption,sizeof(IP_OPTION_INFORMATION));
ZeroMemory(SendData, sizeof(SendData));
pEchoReply = (PICMPV6_ECHO_REPLY)ReplyBuf;
// 设置 TTL
IpOption.Ttl = iTTL;
// 设置有效值
isIPValid = false;
// 设置超时次数
int timeoutCount = 0;
while (!isStopping) {
// 发送数据报并等待消息到达
if (
Icmp6SendEcho2(
hIcmp6, NULL, NULL, NULL,
(sockaddr_in6*)sourceIPAddress, (sockaddr_in6*)targetIPAddress, SendData, sizeof (SendData), &IpOption,
ReplyBuf, sizeof(ReplyBuf),
gCfg->GetTraceTimeout()
) != 0
) {
// 得到返回
memcpy(&((sockaddr_in6*)currentHopIPAddress)->sin6_addr, &pEchoReply->Address.sin6_addr, sizeof(pEchoReply->Address.sin6_addr));
isIPValid = true;
// 任务完成,退出线程
break;
} else {
// 出现错误
qWarning() << "[Trace Worker]"
<< "ICMP send echo failed with error: "
<< GetLastError();
// 这里可以无视条件回报,因为失败的请求一定不会被认为是目标主机
timeoutCount++;
emit reportIPAndTimeConsumption(
iTTL, timeoutCount, NULL, false, false
);
}
}
char printIPAddress[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(*(sockaddr_in6*)currentHopIPAddress).sin6_addr, printIPAddress, INET6_ADDRSTRLEN);
// 判断是否为末端主机(最后一跳)
bool isTargetHost = memcmp(&(*(sockaddr_in6*)currentHopIPAddress).sin6_addr, &(*(sockaddr_in6*)targetIPAddress).sin6_addr, sizeof((*(sockaddr_in6*)targetIPAddress).sin6_addr)) == 0;
// 完成追踪
if (isIPValid && !isStopping) {
// 仅在没有被请求停止的时候回报
emit reportIPAndTimeConsumption(
iTTL, pEchoReply->RoundTripTime, QString(printIPAddress), true, isTargetHost
);
}
}
void TracingWorker::GetInfo() {
// 查询 IP 对应的信息
// 准备从 City 数据库中查询结果
QString cityName;
QString countryName;
double latitude = 0.0;
double longitude = 0.0;
uint16_t accuracyRadius = 0;
bool isLocationValid = true;
// 准备从 ISP 数据库中查询结果
QString isp;
QString org;
uint asn = 0;
QString asOrg;
// 在 City 数据库中查询当前 IP 对应信息
if (!ipdb->LookUpIPCityInfo(
(sockaddr *)currentHopIPAddress,
cityName,
countryName,
latitude,
longitude,
accuracyRadius,
isLocationValid
)) {
// 查询失败,使用填充字符
cityName = QString("未知");
countryName = QString("");
isLocationValid = false;
}
// 在 ISP 数据库中查询当前 IP 对应信息
if (!ipdb->LookUpIPISPInfo(
(sockaddr *)currentHopIPAddress,
isp,
org,
asn,
asOrg
)) {
// 查询失败,使用填充字符
isp = QString("未知");
org = QString("");
asOrg = QString("未知");
}
// 更新数据
emit reportInformation(
iTTL,
cityName, countryName, latitude, longitude, accuracyRadius, isLocationValid, // GeoIP2 City
isp, org, asn, asOrg // GeoIP2 ISP
);
}
void TracingWorker::GetHostname() {
// 这一步是最耗时的操作,它基本上请求必定会超时,所以放到这里来尽可能优化一下体验
// 参考 https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getnameinfo
char hostnameBuf[NI_MAXHOST];
if (
getnameinfo(
(sockaddr *)currentHopIPAddress,
sizeof (sockaddr),
hostnameBuf, NI_MAXHOST,
NULL, 0,
NI_NAMEREQD
) == 0
) {
// 查询到了主机名
emit reportHostname(iTTL, QString(hostnameBuf));
}
// 否则就是打咩
}
void TracingWorker::requestStop() {
isStopping = true;
}