CH-J SM Mobileการจัดการเซิร์ฟเวอร์ผ่าน SSH
เมนู
ซอร์สที่เผยแพร่

CH-J SM Mobile

เรียกดูไดเรกทอรีและไฟล์ของรุ่นแอปพลิเคชันที่ระบุ

ดาวน์โหลด ZIP ของซอร์ส
CH-J / SM Mobile 175 ไฟล์
sm-mobile-20261016T185235Z-8e93ecc1b59a วันที่เพิ่ม: · วันที่บิลด์:
icmp_test.dart วันที่เพิ่ม: ไม่ได้บันทึกวันที่ 14.7 KB · 454 บรรทัด
โค้ด ดาวน์โหลดไฟล์
1import 'dart:async';
2import 'dart:io';
3import 'dart:typed_data';
4import 'package:flutter_test/flutter_test.dart';
5import 'package:sm_mobile/services/net/icmp_packet.dart';
6import 'package:sm_mobile/services/net/icmp_probe.dart';
7import 'package:sm_mobile/services/net/posix.dart';
9/// IPv4 header (20 bytes, protocol ICMP) in front of [icmp].
10Uint8List ipv4(List<int> icmp, {int protocol = 1}) => Uint8List.fromList([
11 0x45, 0, 0, 0, 0, 0, 0, 0, 64, protocol, 0, 0, //
12 192, 0, 2, 1, 192, 0, 2, 2, ...icmp,
13]);
15/// IPv6 header (40 bytes, next header ICMPv6) in front of [icmp].
16Uint8List ipv6(List<int> icmp, {int next = 58}) => Uint8List.fromList([
17 0x60,
18 0,
19 0,
20 0,
21 0,
22 8,
23 next,
24 64,
25 ...List.filled(32, 0),
26 ...icmp,
27]);
29Future<List<ProbeEvent>> collect(IcmpRun run) =>
30 run.events.toList().timeout(const Duration(seconds: 60));
32void main() {
33 group('ICMP packets', () {
34 test('echo request layout and RFC 1071 checksum', () {
35 final p = buildEchoRequest(v6: false, id: 0x1234, seq: 7, size: 4);
36 expect(p.sublist(0, 2), [8, 0]);
37 expect(p.sublist(4, 8), [0x12, 0x34, 0, 7]);
38 expect(icmpChecksum(p), 0, reason: 'a valid packet sums to zero');
39 final v6 = buildEchoRequest(v6: true, id: 1, seq: 2, size: 0);
40 expect(v6[0], 128);
41 expect(v6.length, 8);
42 // RFC 1071 example data.
43 expect(
44 icmpChecksum(
45 Uint8List.fromList([0, 1, 0xf2, 3, 0xf4, 0xf5, 0xf6, 0xf7]),
46 ),
47 ~0xddf2 & 0xffff,
48 );
49 expect(icmpChecksum(Uint8List.fromList([1])), ~0x0100 & 0xffff);
50 });
52 test('echo replies with and without the IPv4 header', () {
53 final reply = [0, 0, 0, 0, 0xab, 0xcd, 0, 9];
54 final darwin = parseIcmp(ipv4(reply), v6: false, ipHeader: true)!;
55 expect(
56 (darwin.kind, darwin.id, darwin.seq),
57 (IcmpKind.echoReply, 0xabcd, 9),
58 );
59 final linux = parseIcmp(Uint8List.fromList(reply), v6: false)!;
60 expect((linux.kind, linux.seq), (IcmpKind.echoReply, 9));
61 final six = parseIcmp(
62 Uint8List.fromList([129, 0, 0, 0, 0, 1, 0, 2]),
63 v6: true,
64 )!;
65 expect((six.kind, six.id, six.seq), (IcmpKind.echoReply, 1, 2));
66 });
68 test('errors are matched by the quoted echo request', () {
69 final quoted = ipv4([8, 0, 0, 0, 0x12, 0x34, 0, 5]);
70 final exceeded = parseIcmp(
71 ipv4([11, 0, 0, 0, 0, 0, 0, 0, ...quoted]),
72 v6: false,
73 ipHeader: true,
74 )!;
75 expect(
76 (exceeded.kind, exceeded.id, exceeded.seq),
77 (IcmpKind.timeExceeded, 0x1234, 5),
78 );
79 final unreachable = parseIcmp(
80 Uint8List.fromList([3, 3, 0, 0, 0, 0, 0, 0, ...quoted]),
81 v6: false,
82 )!;
83 expect((unreachable.kind, unreachable.code), (IcmpKind.unreachable, 3));
84 expect(unreachable.seq, 5);
85 final quoted6 = ipv6([128, 0, 0, 0, 0, 7, 0, 8]);
86 final six = parseIcmp(
87 Uint8List.fromList([3, 0, 0, 0, 0, 0, 0, 0, ...quoted6]),
88 v6: true,
89 )!;
90 expect((six.kind, six.id, six.seq), (IcmpKind.timeExceeded, 7, 8));
91 final unreachable6 = parseIcmp(
92 Uint8List.fromList([1, 4, 0, 0, 0, 0, 0, 0, ...quoted6]),
93 v6: true,
94 )!;
95 expect((unreachable6.kind, unreachable6.code), (IcmpKind.unreachable, 4));
96 });
98 test('foreign, truncated and malformed messages are not matched', () {
99 expect(parseIcmp(Uint8List(3), v6: false), isNull);
100 expect(
101 parseIcmp(Uint8List.fromList([0x60, 0, 0]), v6: false, ipHeader: true),
102 isNull,
103 );
104 // Time exceeded quoting a UDP packet (someone else's traceroute).
105 final udp = parseIcmp(
106 ipv4([
107 11,
108 0,
109 0,
110 0,
111 0,
112 0,
113 0,
114 0,
115 ...ipv4(List.filled(8, 0), protocol: 17),
116 ]),
117 v6: false,
118 ipHeader: true,
119 )!;
120 expect((udp.kind, udp.seq), (IcmpKind.timeExceeded, null));
121 // Quoted packet cut short.
122 expect(
123 parseIcmp(
124 Uint8List.fromList([11, 0, 0, 0, 0, 0, 0, 0, 0x45]),
125 v6: false,
126 )!.seq,
127 isNull,
128 );
129 // ICMPv6 error quoting a non-ICMPv6 packet.
130 expect(
131 parseIcmp(
132 Uint8List.fromList([
133 3,
134 0,
135 0,
136 0,
137 0,
138 0,
139 0,
140 0,
141 ...ipv6(List.filled(8, 0), next: 17),
142 ]),
143 v6: true,
144 )!.seq,
145 isNull,
146 );
147 // Router advertisements and others are ignored.
148 expect(
149 parseIcmp(
150 Uint8List.fromList([134, 0, 0, 0, 0, 0, 0, 0]),
151 v6: true,
152 )!.kind,
153 IcmpKind.other,
154 );
155 expect(
156 sentEcho(Uint8List.fromList([8, 0, 0, 0, 0, 1, 0, 2]), v6: false),
157 (id: 1, seq: 2),
158 );
159 expect(
160 sentEcho(Uint8List.fromList([0, 0, 0, 0, 0, 1, 0, 2]), v6: false),
161 isNull,
162 );
163 });
165 test('statistics: min, average, maximum and loss', () {
166 final stats = PingStats.of([
167 const ProbeEvent(1, ProbeStatus.reply, rttMs: 10),
168 const ProbeEvent(2, ProbeStatus.timeout),
169 const ProbeEvent(3, ProbeStatus.reply, rttMs: 30),
170 const ProbeEvent(4, ProbeStatus.unreachable),
171 ]);
172 expect((stats.sent, stats.received), (4, 2));
173 expect((stats.min, stats.avg, stats.max), (10.0, 20.0, 30.0));
174 expect(stats.lossPercent, 50);
175 final none = PingStats.of(const []);
176 expect((none.lossPercent, none.avg), (0.0, null));
177 });
179 test('socket addresses for both platform layouts', () {
180 final p = Posix.current!;
181 for (final a in [
182 InternetAddress('192.0.2.7'),
183 InternetAddress('2001:db8::7'),
184 ]) {
185 final raw = p.sockaddr(a);
186 expect(p.address(raw, raw.length), a);
187 }
188 });
189 });
191 // Real sockets on this host. On macOS this is the same Darwin socket API
192 // that iOS uses; Android uses the Linux implementation (device test).
193 group('real ICMP on this host (loopback)', () {
194 test('ping 127.0.0.1 and ::1', () async {
195 for (final target in [
196 InternetAddress.loopbackIPv4,
197 InternetAddress.loopbackIPv6,
198 ]) {
199 final events = await collect(
200 IcmpProbe.ping(
201 target,
202 const PingSettings(count: 3, interval: Duration(milliseconds: 200)),
203 ),
204 );
205 expect(events.map((e) => e.seq), [1, 2, 3], reason: '$target');
206 expect(
207 events.every((e) => e.status == ProbeStatus.reply),
208 true,
209 reason: '$target',
210 );
211 expect(events.every((e) => e.from == target), true);
212 expect(events.every((e) => e.rttMs! >= 0 && e.rttMs! < 1000), true);
213 }
214 });
216 test('payload sizes from 0 to 1400 bytes', () async {
217 for (final size in [0, 8, 1400]) {
218 final events = await collect(
219 IcmpProbe.ping(
220 InternetAddress.loopbackIPv4,
221 PingSettings(count: 1, size: size),
222 ),
223 );
224 expect(events.single.status, ProbeStatus.reply, reason: '$size B');
225 }
226 });
228 test('traceroute to loopback reaches the target at hop 1', () async {
229 final events = await collect(
230 IcmpProbe.traceroute(
231 InternetAddress.loopbackIPv4,
232 const TraceSettings(),
233 ),
234 );
235 expect(events.length, 3);
236 expect(
237 events.every((e) => e.ttl == 1 && e.status == ProbeStatus.reply),
238 true,
239 );
240 });
242 test('cancel stops at once and sends nothing more', () async {
243 final run = IcmpProbe.ping(
244 InternetAddress.loopbackIPv4,
245 const PingSettings(count: 100, interval: Duration(seconds: 1)),
246 );
247 final events = <ProbeEvent>[];
248 final first = Completer<void>();
249 final done = Completer<void>();
250 run.events.listen((e) {
251 events.add(e);
252 if (!first.isCompleted) first.complete();
253 }, onDone: done.complete);
254 await first.future.timeout(const Duration(seconds: 10));
255 final watch = Stopwatch()..start();
256 await run.cancel();
257 await done.future.timeout(const Duration(seconds: 2));
258 expect(watch.elapsedMilliseconds, lessThan(1000));
259 final count = events.length;
260 await Future<void>.delayed(const Duration(seconds: 2));
261 expect(events.length, count, reason: 'no events after cancel');
262 expect(count, lessThan(5));
263 });
265 test('an unanswered target times out instead of hanging', () async {
266 // 192.0.2.0/24 is TEST-NET-1 (RFC 5737): never answered.
267 final events = await collect(
268 IcmpProbe.ping(
269 InternetAddress('192.0.2.1'),
270 const PingSettings(
271 count: 2,
272 interval: Duration(milliseconds: 200),
273 timeout: Duration(milliseconds: 500),
274 ),
275 ),
276 );
277 expect(events.length, 2);
278 expect(
279 events.every(
280 (e) =>
281 e.status == ProbeStatus.timeout ||
282 e.status == ProbeStatus.unreachable ||
283 e.status == ProbeStatus.sendFailed,
284 ),
285 true,
286 );
287 expect(PingStats.of(events).lossPercent, 100);
288 });
289 });
291 group('extended probes on this host (loopback)', () {
292 test('UDP traceroute: the destination answers port unreachable', () async {
293 for (final target in [
294 InternetAddress.loopbackIPv4,
295 InternetAddress.loopbackIPv6,
296 ]) {
297 final events = await collect(
298 IcmpProbe.traceroute(
299 target,
300 const TraceSettings(method: TraceMethod.udp, probes: 2),
301 ),
302 );
303 expect(events, hasLength(2), reason: '$target');
304 expect(
305 events.every(
306 (e) =>
307 e.ttl == 1 &&
308 e.status == ProbeStatus.reply &&
309 e.note == 'portUnreachable' &&
310 e.from == target,
311 ),
312 true,
313 reason: '$target',
314 );
315 }
316 });
318 test(
319 'TCP traceroute: open port (SYN-ACK) and closed port (reset)',
320 () async {
321 final server = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
322 final port = server.port;
323 final open = await collect(
324 IcmpProbe.traceroute(
325 InternetAddress.loopbackIPv4,
326 TraceSettings(method: TraceMethod.tcp, port: port, probes: 2),
327 ),
328 );
329 expect(open.map((e) => e.note), ['tcpOpen', 'tcpOpen']);
330 await server.close();
331 final closed = await collect(
332 IcmpProbe.traceroute(
333 InternetAddress.loopbackIPv4,
334 TraceSettings(method: TraceMethod.tcp, port: port, probes: 1),
335 ),
336 );
337 expect(closed.single.status, ProbeStatus.reply);
338 expect(closed.single.note, 'tcpRefused');
339 },
340 skip: Posix.current?.darwin == true
341 ? false
342 : 'TCP traceroute is Darwin only',
343 );
345 test('first TTL and probe interval are honoured', () async {
346 final watch = Stopwatch()..start();
347 final events = await collect(
348 IcmpProbe.traceroute(
349 InternetAddress.loopbackIPv4,
350 const TraceSettings(
351 firstTtl: 5,
352 probes: 3,
353 interval: Duration(milliseconds: 150),
354 ),
355 ),
356 );
357 expect(events.every((e) => e.ttl == 5), true);
358 expect(watch.elapsedMilliseconds, greaterThanOrEqualTo(290));
359 });
361 test('ping with TTL, Don\'t Fragment and continuous mode', () async {
362 final ttl = await collect(
363 IcmpProbe.ping(
364 InternetAddress.loopbackIPv4,
365 const PingSettings(count: 2, ttl: 3, dontFragment: true),
366 ),
367 );
368 expect(ttl.every((e) => e.status == ProbeStatus.reply), true);
369 expect(ttl.every((e) => e.time != null), true);
370 // A payload above the loopback MTU cannot leave unfragmented.
371 final big = await collect(
372 IcmpProbe.ping(
373 InternetAddress.loopbackIPv4,
374 const PingSettings(count: 1, size: 20000, dontFragment: true),
375 ),
376 );
377 expect(big.single.status, ProbeStatus.sendFailed);
378 expect(big.single.note, 'messageTooLong');
380 final run = IcmpProbe.ping(
381 InternetAddress.loopbackIPv4,
382 const PingSettings(count: 0, interval: Duration(milliseconds: 200)),
383 );
384 final got = <ProbeEvent>[];
385 final done = Completer<void>();
386 run.events.listen(got.add, onDone: done.complete);
387 await Future<void>.delayed(const Duration(milliseconds: 1500));
388 await run.cancel();
389 await done.future.timeout(const Duration(seconds: 2));
390 expect(got.length, greaterThanOrEqualTo(4));
391 expect(got.map((e) => e.seq).take(4), [1, 2, 3, 4]);
392 }, skip: Posix.current?.darwin == true ? false : 'checked on device');
393 });
395 // Real Internet measurements; opt-in because they need network access.
396 final internet = Platform.environment['SM_NET_TEST'] == '1';
397 group('real ICMP over the Internet', () {
398 for (final target in ['1.1.1.1', '2606:4700:4700::1111']) {
399 test('ping and traceroute $target', () async {
400 final address = InternetAddress(target);
401 final ping = await collect(
402 IcmpProbe.ping(address, const PingSettings(count: 3)),
403 );
404 expect(PingStats.of(ping).received, greaterThan(0));
405 final trace = await collect(
406 IcmpProbe.traceroute(address, const TraceSettings(maxHops: 30)),
407 );
408 final hops = trace.map((e) => e.ttl).toSet();
409 expect(hops.length, greaterThan(1), reason: 'intermediate routers');
410 expect(
411 trace.any((e) => e.status == ProbeStatus.timeExceeded),
412 true,
413 reason: 'routers answered with Time Exceeded',
414 );
415 expect(
416 trace
417 .where((e) => e.status == ProbeStatus.reply)
418 .every((e) => e.from == address),
419 true,
420 );
421 for (final method in [TraceMethod.udp, TraceMethod.tcp]) {
422 if (IcmpProbe.unsupported(method) != null) continue;
423 final other = await collect(
424 IcmpProbe.traceroute(
425 address,
426 TraceSettings(
427 method: method,
428 port: method == TraceMethod.tcp ? 443 : null,
429 ),
430 ),
431 );
432 expect(
433 other.any((e) => e.status == ProbeStatus.timeExceeded),
434 true,
435 reason: '$method: routers answered',
436 );
437 expect(
438 other.any((e) => e.status == ProbeStatus.reply),
439 true,
440 reason: '$method: destination reached',
441 );
442 }
443 // ignore: avoid_print
444 print(
445 [
446 for (final e in trace)
447 '${e.ttl}.${e.seq} ${e.status.name} ${e.from?.address ?? '*'} '
448 '${e.rttMs?.toStringAsFixed(1) ?? ''}',
449 ].join('\n'),
450 );
451 }, timeout: const Timeout(Duration(minutes: 5)));
452 }
453 }, skip: !internet);
SHA-2564f53b964f9c7115f0a443f110114746a412915cb4a6fa9a93bfd3818a00690f4

CH-J Proprietary Software License 1.14 · ซอร์สโค้ดนี้ให้ไว้ภายใต้ CH-J Proprietary Software License 1.14 การเปิดให้เข้าถึงไม่ได้เปลี่ยนแปลงเงื่อนไขของสัญญาอนุญาตหรือให้สิทธิ์เพิ่มเติม

SHA-256 ของไฟล์บีบอัด46e427a43e87af091004f36145590bda8fe90086dfb0f631afeb5029fb0b5655
ไฟล์