Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a82933a7a8 | ||
|
|
76444ef17d | ||
|
|
fde33380be | ||
|
|
eedb6684a5 | ||
|
|
a335a5bcdf | ||
|
|
4a347d853e | ||
|
|
79f55fe7fd | ||
|
|
affe8fb719 | ||
|
|
22eee048d0 | ||
|
|
2b2a550a98 | ||
|
|
14427ce437 | ||
|
|
39c170892e | ||
|
|
b82276e6fa | ||
|
|
154e8ef09a | ||
|
|
97a2f7c306 | ||
|
|
8d39d6045a | ||
|
|
04b60773b9 | ||
|
|
774a80dc46 | ||
|
|
1cba257531 | ||
|
|
16cc2abe7a | ||
|
|
595a49ee3f | ||
|
|
f894f62eb2 | ||
|
|
a98d6a7594 | ||
|
|
9674dfa5dc | ||
|
|
00068cdcfd | ||
|
|
846f128689 | ||
|
|
2225edeb2e | ||
|
|
ef48007922 | ||
|
|
1d5257363c | ||
|
|
0238d1ab7e | ||
|
|
ffbb85dfff | ||
|
|
563aca39aa | ||
|
|
274f4df43a | ||
|
|
1fae7c9979 | ||
|
|
46b37c0753 | ||
|
|
f737f591b6 | ||
|
|
ebf8337ad7 | ||
|
|
e2d8a462a2 | ||
|
|
b09cd690f0 | ||
|
|
76bd79d97a | ||
|
|
c0d8645833 | ||
|
|
501a9e8198 | ||
|
|
8aa4736b01 | ||
|
|
d5e10a8210 | ||
|
|
1165ee52c7 | ||
|
|
8a66bbe1af | ||
|
|
c91d8a2734 | ||
|
|
09a315a0f1 | ||
|
|
2979f2ef6f | ||
|
|
8225cb7877 | ||
|
|
129ca1ec85 | ||
|
|
96b67c6162 | ||
|
|
ce02b30b2b | ||
|
|
2fbdfe920e | ||
|
|
7901407d69 | ||
|
|
e6cfdec9c6 | ||
|
|
7e0de2c4e6 | ||
|
|
803af55246 | ||
|
|
8e1a8fa7f0 | ||
|
|
fc6bf407c4 | ||
|
|
dc4e472f20 | ||
|
|
afff49e61a | ||
|
|
0f9ac2ac87 | ||
|
|
cce23b9661 | ||
|
|
4347ed68c3 | ||
|
|
64d68c8a84 | ||
|
|
210b7c959e | ||
|
|
e8322adbcc | ||
|
|
e523b02b58 | ||
|
|
13df028cd9 | ||
|
|
4550bea063 | ||
|
|
be8d4cbb8b | ||
|
|
ace0268a8c | ||
|
|
56fd0d7a4f | ||
|
|
91ee751b07 | ||
|
|
fa9903ace1 | ||
|
|
c2854352df | ||
|
|
b3472fb5b3 | ||
|
|
e373c9ee16 | ||
|
|
13b7b1761d | ||
|
|
352f322f61 | ||
|
|
be966c4f83 | ||
|
|
b8cbf2cdac | ||
|
|
05cf0e1a6f | ||
|
|
f90ef96a5d | ||
|
|
81c6c8c1fc | ||
|
|
7ebdffcdd3 | ||
|
|
1abe33cb16 | ||
|
|
965cbfbb7e | ||
|
|
aa332b4f4d | ||
|
|
14ee9c4632 | ||
|
|
ddfa89167d | ||
|
|
1f9ed7b314 | ||
|
|
dc45602661 | ||
|
|
27c0f389ee | ||
|
|
59d6bfd957 | ||
|
|
31b8661b7d | ||
|
|
8ae20497c6 | ||
|
|
a70b4f30b5 | ||
|
|
df4795c9c4 | ||
|
|
c21a73f950 | ||
|
|
29cd111a49 | ||
|
|
367a56d346 | ||
|
|
d81ac6368e | ||
|
|
a228b62d23 | ||
|
|
995ff9b059 | ||
|
|
7dc78cc170 | ||
|
|
7e218355f2 | ||
|
|
98c394c993 | ||
|
|
31b06cd713 | ||
|
|
d7266926d8 | ||
|
|
1df5a1cfb1 | ||
|
|
8658bd3601 | ||
|
|
17d1ad3716 | ||
|
|
728598ba8e | ||
|
|
218483f431 | ||
|
|
1f80e06d9d | ||
|
|
5e56064ec6 | ||
|
|
31f0213fc5 | ||
|
|
54b1fef164 | ||
|
|
7c18df0a6f | ||
|
|
916fd8254b | ||
|
|
9e249862dc | ||
|
|
3773c09f57 | ||
|
|
6798c652f9 | ||
|
|
382d5b61ba | ||
|
|
ac162eba66 | ||
|
|
05a60b3cb7 | ||
|
|
7f210e9319 | ||
|
|
481c00629a | ||
|
|
bf55ec9512 | ||
|
|
073fa6ac6b | ||
|
|
51315efb52 | ||
|
|
30cbf39e7f | ||
|
|
2973d88df3 | ||
|
|
8b2d7293fe | ||
|
|
215d9aa362 | ||
|
|
c3f27e8727 | ||
|
|
755339189d | ||
|
|
71cf0aa66d | ||
|
|
d695efd787 | ||
|
|
66129c2a24 | ||
|
|
0bc7115102 | ||
|
|
dca33a3ec1 | ||
|
|
9acbae2554 | ||
|
|
ccfff7b1f6 | ||
|
|
39ae4c5dd6 | ||
|
|
350357076d | ||
|
|
1e5e15f1f5 | ||
|
|
1a385a777e | ||
|
|
16278dae62 | ||
|
|
8294e46003 | ||
|
|
dedc66875b | ||
|
|
3045e239ff | ||
|
|
5363558118 | ||
|
|
13ba7cf0b8 | ||
|
|
a5a43e762d | ||
|
|
cb6101a835 | ||
|
|
255f84c969 | ||
|
|
7617d6c409 | ||
|
|
f75691b483 | ||
|
|
c5dbb79ed2 | ||
|
|
60ba3c64b2 | ||
|
|
080e58eeb7 | ||
|
|
5d228b9c27 | ||
|
|
e36e45054c | ||
|
|
ba87a2780d | ||
|
|
fccc3478c8 | ||
|
|
a76280c53b | ||
|
|
90ff03e27c | ||
|
|
5eed05989f | ||
|
|
ac939746bd | ||
|
|
b7532e89c2 | ||
|
|
74f44d6961 | ||
|
|
86fc27c233 | ||
|
|
4b6bb0e5f7 | ||
|
|
215337fd8a | ||
|
|
e54341567a | ||
|
|
1ce3e9ff2e | ||
|
|
ffdb5ceb6b | ||
|
|
faa78445b1 | ||
|
|
39170b74ca | ||
|
|
440a832ace | ||
|
|
0e3f06429b | ||
|
|
01cf8806e2 | ||
|
|
ee7c597bd8 | ||
|
|
a85e0c6d06 | ||
|
|
fee3b24df4 | ||
|
|
7f500d72c7 | ||
|
|
d8a9ec5732 | ||
|
|
4dca4c4f22 | ||
|
|
0e4294415e | ||
|
|
9df324f402 | ||
|
|
3b1236cfd0 | ||
|
|
699262d79e | ||
|
|
f6cfa1b84c | ||
|
|
1338bcbbc5 | ||
|
|
4215488cb6 | ||
|
|
ccd6e44c2b | ||
|
|
b12b0b95a0 | ||
|
|
948f3d5d24 | ||
|
|
cf4fc54dfd | ||
|
|
5a9e31bded | ||
|
|
b61c140c24 | ||
|
|
035ae46ee5 | ||
|
|
b57a38e81e | ||
|
|
be767ae300 | ||
|
|
607c8c2e9f | ||
|
|
d3dce7a3cc | ||
|
|
ff2bbdd837 | ||
|
|
b771b6c074 | ||
|
|
ff389647c0 | ||
|
|
49bec2d6aa | ||
|
|
ee359dc0d8 | ||
|
|
d4fc28dca3 | ||
|
|
1130f69a82 | ||
|
|
e73ed238cd | ||
|
|
c6c1d297af | ||
|
|
f78f8de15e | ||
|
|
fcbc52dc2e | ||
|
|
bbfa255dd3 | ||
|
|
18fcee52db | ||
|
|
d36d0b9491 | ||
|
|
fb8d7ec5b8 | ||
|
|
24341808ef | ||
|
|
353809f91c | ||
|
|
d2b2dfbf1e | ||
|
|
e027cf9d98 | ||
|
|
484b1657e8 | ||
|
|
7dc4161f2f | ||
|
|
18eb4af016 | ||
|
|
635ec315f7 | ||
|
|
37eb9ec045 | ||
|
|
7c799589df | ||
|
|
4f52d58d6e | ||
|
|
441e374563 | ||
|
|
3272f3cdca | ||
|
|
5b40a9f147 | ||
|
|
12d4fc8e98 | ||
|
|
ca74827aaf | ||
|
|
0b570130e8 | ||
|
|
5bdc514606 | ||
|
|
137efe20b8 | ||
|
|
0cbc3e0f63 | ||
|
|
7fbcf0a832 | ||
|
|
7283863fe8 | ||
|
|
198f82fb2c | ||
|
|
c219af4f4b | ||
|
|
0352982c14 | ||
|
|
cc6cd6dc37 | ||
|
|
9155471c26 | ||
|
|
d916fd7573 | ||
|
|
5528557964 | ||
|
|
8dfc3b8c43 | ||
|
|
84824731a7 | ||
|
|
7131937d06 | ||
|
|
a657eae599 | ||
|
|
e9ec6e8903 | ||
|
|
c4a9e79dc2 | ||
|
|
aeebf45390 | ||
|
|
b818851b38 | ||
|
|
f47db0ab1b | ||
|
|
256a2adc3e | ||
|
|
28522f2f07 | ||
|
|
a33ffd96fd | ||
|
|
61f7352312 | ||
|
|
def33382e3 | ||
|
|
1d0d64bd30 | ||
|
|
e7e8f6b771 | ||
|
|
4317b06428 | ||
|
|
e9c695f4ac | ||
|
|
da714e97e8 | ||
|
|
ee2186aba5 | ||
|
|
ca91f97c27 | ||
|
|
284cf38f0b | ||
|
|
dc3e4c8253 | ||
|
|
c7b9f61bd2 | ||
|
|
b26c4b97b2 | ||
|
|
5569c67ba4 | ||
|
|
69da6de1ed | ||
|
|
ae983f014b | ||
|
|
499e3bbfdf | ||
|
|
01f6890fd3 | ||
|
|
9badbe180c | ||
|
|
30b1e71aa1 | ||
|
|
b36cc352d5 | ||
|
|
12d888cefc | ||
|
|
ad1df41029 | ||
|
|
67498dfc82 | ||
|
|
cf52fdde57 | ||
|
|
a20082d40b | ||
|
|
8e29b020e0 | ||
|
|
20132861e9 | ||
|
|
a1e9b3c133 | ||
|
|
8a4d21849e | ||
|
|
d2c5aac178 | ||
|
|
066e655c30 | ||
|
|
8627046deb | ||
|
|
76b86bbd0f | ||
|
|
e78994ceb4 | ||
|
|
c63813e590 | ||
|
|
39becec2e5 | ||
|
|
44e379df82 | ||
|
|
b2b58a23c1 | ||
|
|
03529bc339 | ||
|
|
996a80e710 | ||
|
|
ac837077f6 | ||
|
|
b25f330b0c | ||
|
|
c783a96e13 | ||
|
|
999a5b9117 | ||
|
|
6850681352 | ||
|
|
f2b21c9e4a | ||
|
|
d27f065507 | ||
|
|
5e913568a3 | ||
|
|
97b6db3485 | ||
|
|
ae63ce80b5 | ||
|
|
c0e930a3ef | ||
|
|
f8ee8e242f | ||
|
|
20e3ae4d58 | ||
|
|
a910d7a673 | ||
|
|
a4b5b2a028 | ||
|
|
a8a1346c98 | ||
|
|
734e613452 | ||
|
|
e2003d8dea | ||
|
|
f0a2edadfa | ||
|
|
4c174c3aac | ||
|
|
147156d96d | ||
|
|
64d62272a6 | ||
|
|
91713f0fed | ||
|
|
e7f2571469 | ||
|
|
48b41c4b22 | ||
|
|
042cc7c797 | ||
|
|
24c1edff8c | ||
|
|
ec2a74d06f | ||
|
|
63316ee23e | ||
|
|
f0139c68f7 | ||
|
|
590721a4da | ||
|
|
521280537a | ||
|
|
1eb2a0f5cf | ||
|
|
f0ac70c995 | ||
|
|
2570a9ead2 | ||
|
|
54da8ac71f | ||
|
|
bd73ed213e | ||
|
|
20fd060821 | ||
|
|
e8df40925a | ||
|
|
2c0a5d459a | ||
|
|
b67814f125 | ||
|
|
e3f328cc9b | ||
|
|
b6aba36510 | ||
|
|
22720a8f8f | ||
|
|
cc717a4060 | ||
|
|
5264455eba | ||
|
|
b0d6b1f884 | ||
|
|
7cd8647c0c | ||
|
|
19187aff47 | ||
|
|
23b536f771 | ||
|
|
119f719a9f | ||
|
|
9673eaab05 | ||
|
|
390b8dd242 | ||
|
|
1b1663c87e | ||
|
|
7bc4203deb | ||
|
|
c798c36914 | ||
|
|
14e7b518b5 | ||
|
|
88eb3f6514 | ||
|
|
d78e48211d | ||
|
|
55a510aa34 | ||
|
|
8d7eb5ba1b | ||
|
|
ceadadee68 | ||
|
|
e3e2a262e4 | ||
|
|
5850a59ec9 | ||
|
|
318c267c34 | ||
|
|
98bd713a91 | ||
|
|
fe499d0476 | ||
|
|
ff665d7dcf | ||
|
|
573537fbfb | ||
|
|
9e1bd28e2d | ||
|
|
27a6c686a7 | ||
|
|
2f6d743931 | ||
|
|
ec3aa01b9c | ||
|
|
02b988e898 | ||
|
|
b57cd2ed45 | ||
|
|
af5d284470 | ||
|
|
e7f1165287 | ||
|
|
e64e17267d | ||
|
|
7098111ad1 | ||
|
|
57b903ea29 | ||
|
|
d06082fc7a | ||
|
|
77a78daac1 | ||
|
|
d72fab04a2 | ||
|
|
76eaead0b9 | ||
|
|
858ca9bbe7 | ||
|
|
cbc4a7378c | ||
|
|
ee34f525e6 | ||
|
|
698e2531e8 | ||
|
|
16f833ec72 | ||
|
|
94d6caef7e | ||
|
|
364012f9dd | ||
|
|
cf4b1f418f | ||
|
|
c6465977ef | ||
|
|
c577e6381f | ||
|
|
134e31f563 | ||
|
|
0580b89f48 | ||
|
|
37080af275 | ||
|
|
26d68ac059 | ||
|
|
6292c1c381 | ||
|
|
b0c3f25a29 | ||
|
|
d2e09d3da5 | ||
|
|
293c5b90a4 | ||
|
|
e2323361f9 | ||
|
|
d05cff99ee | ||
|
|
e9b1b2ef3b | ||
|
|
a8ea2c14fc | ||
|
|
0688cb4515 | ||
|
|
c3261d7a57 | ||
|
|
ef8d13f61b | ||
|
|
fc104d5dee | ||
|
|
9ad0525216 | ||
|
|
3ea1250b53 | ||
|
|
9df34207f7 | ||
|
|
dacce892ff | ||
|
|
a9495f1d30 | ||
|
|
0892c6eee9 | ||
|
|
f746eadcf1 | ||
|
|
da2371541c | ||
|
|
cca91d4331 | ||
|
|
11aa3b1d2c | ||
|
|
d0b0c61a03 | ||
|
|
b0501db4e5 | ||
|
|
465a5c75ae | ||
|
|
978460aca8 | ||
|
|
4b86752c79 | ||
|
|
bd61cad7b6 | ||
|
|
40cbd2e26b | ||
|
|
2cab580b4e | ||
|
|
3d243fb01d | ||
|
|
ca4e8d14f0 | ||
|
|
9c098c55c9 | ||
|
|
707b07b8d3 | ||
|
|
de5a6971f0 | ||
|
|
056036c4d2 | ||
|
|
1cfb188845 | ||
|
|
73a2c31854 | ||
|
|
92eea536f8 | ||
|
|
00936a923e | ||
|
|
ba69ba78af | ||
|
|
17b206bace | ||
|
|
58d5a4f5da | ||
|
|
2438d14175 | ||
|
|
99f8526799 | ||
|
|
56fcbd64ed | ||
|
|
3766b2b7c1 | ||
|
|
baf0698fe4 | ||
|
|
301938b9fc | ||
|
|
16a37c713a | ||
|
|
d412a769dd | ||
|
|
c8eecc87fd | ||
|
|
6a11db70c8 | ||
|
|
d7c121a756 | ||
|
|
3429ee8bd6 | ||
|
|
4422f9f8b7 | ||
|
|
57ed454c93 | ||
|
|
236205c0f3 | ||
|
|
99b4bf0041 | ||
|
|
9495e39700 | ||
|
|
75e244e3a8 | ||
|
|
bf8397e6e3 | ||
|
|
8cee974866 | ||
|
|
5e018801db | ||
|
|
c4f7f4c19e | ||
|
|
29945eb3da | ||
|
|
7db415d243 | ||
|
|
808dcec795 | ||
|
|
ad8fecc319 | ||
|
|
27ae9a89da | ||
|
|
b3a4a4de5e | ||
|
|
ca76c35f6a | ||
|
|
073c820da6 | ||
|
|
954f0d2d05 | ||
|
|
a943f5bffc | ||
|
|
c3cff7c5b5 | ||
|
|
acb5a8a325 | ||
|
|
1a4e375dbf | ||
|
|
eec7d73ebe | ||
|
|
f4dda173c2 | ||
|
|
96fb8c881d | ||
|
|
aeebbd18fd | ||
|
|
baa71a51eb | ||
|
|
84e70149a1 | ||
|
|
959f2aa783 | ||
|
|
99b2aa9522 | ||
|
|
561fa9f8fe | ||
|
|
ad9dd6a7f7 | ||
|
|
65758eb94c | ||
|
|
fb7ccf4d11 | ||
|
|
b9fe5e0fe2 | ||
|
|
c3368481ad | ||
|
|
cdf5c3a508 | ||
|
|
f9217625e1 | ||
|
|
323f6c9221 | ||
|
|
2e507f1a9d | ||
|
|
e611d69a25 | ||
|
|
45d8c6717d | ||
|
|
756539c3bd | ||
|
|
3d4fcdbc96 | ||
|
|
3cd19dbc1f | ||
|
|
7b770a1bb0 | ||
|
|
0472b1590e | ||
|
|
81764433d8 | ||
|
|
9ee6e101c4 | ||
|
|
565e80b994 | ||
|
|
7ccc4535e4 | ||
|
|
d1bde297d6 | ||
|
|
c5398a51e8 | ||
|
|
fddb59992b | ||
|
|
665e25b1a3 | ||
|
|
fdb4bb1155 | ||
|
|
f104e191ed | ||
|
|
6311d75ac0 | ||
|
|
c3f134e332 | ||
|
|
5bccdf3bbe | ||
|
|
9e881eeecd | ||
|
|
fbe01d8cbf | ||
|
|
d336d938a9 | ||
|
|
598923c95a | ||
|
|
0afe4c4417 | ||
|
|
dff51caf35 | ||
|
|
7c30f2691a | ||
|
|
c6aca2c2dc | ||
|
|
28ac8cf88e | ||
|
|
62b2af54a2 | ||
|
|
a2d45da44d | ||
|
|
d937f392d3 | ||
|
|
2c9abf314a | ||
|
|
ab7abd0c1f | ||
|
|
71a2e3c592 | ||
|
|
aa6d3a6843 | ||
|
|
9b42c5d092 | ||
|
|
2f7817ce5b | ||
|
|
e7c6bcf9a9 | ||
|
|
b6b8971b12 | ||
|
|
ba4a0e008b | ||
|
|
4baccc5047 | ||
|
|
6cf71c8068 | ||
|
|
8e556a20ee | ||
|
|
5f28a5044a | ||
|
|
16d295bf32 | ||
|
|
32163e077e | ||
|
|
6a5a40a7d3 | ||
|
|
161ecc865c | ||
|
|
1a5d40ff2d | ||
|
|
6b988e0612 | ||
|
|
8d44934382 | ||
|
|
6b140b0f71 | ||
|
|
c2b7b02f3f | ||
|
|
24140c2145 | ||
|
|
a6050e5f59 | ||
|
|
50e97fd95f | ||
|
|
890e5f7391 | ||
|
|
be3bf82e35 | ||
|
|
e8af503130 | ||
|
|
466174ef88 | ||
|
|
ba2c792e2a | ||
|
|
17f3fcf9b0 | ||
|
|
11d1ff4b42 | ||
|
|
b640bc50ef | ||
|
|
4bbd5282ee | ||
|
|
d7fd504f8f | ||
|
|
cf4375b405 | ||
|
|
45da060c1a | ||
|
|
4dc84914dd | ||
|
|
c1ee7ac11c | ||
|
|
409b57c171 | ||
|
|
ff4580b9bf | ||
|
|
6daa75d2f2 | ||
|
|
59f07f2d75 | ||
|
|
44035685c8 | ||
|
|
c8d0f3850f | ||
|
|
b846f54d79 | ||
|
|
bb7b1d23af | ||
|
|
9e700b3094 | ||
|
|
38f28e313c | ||
|
|
63f03fb11f | ||
|
|
cf7854906f | ||
|
|
78ae5f3036 | ||
|
|
c412a256b8 | ||
|
|
94def558fc | ||
|
|
25e9c523d3 | ||
|
|
a009d06000 | ||
|
|
675e0c52ff | ||
|
|
dc4219753a | ||
|
|
c003b9acbb | ||
|
|
f4869ebd1d | ||
|
|
de4f42537d | ||
|
|
b9ebe80ef3 | ||
|
|
ea5ba750d4 | ||
|
|
b594afce16 | ||
|
|
ed3c44d6cf | ||
|
|
35ed7f7e45 | ||
|
|
068580e036 | ||
|
|
ea61b06e58 | ||
|
|
f260c26e4f | ||
|
|
9cea433a8b | ||
|
|
38dce9c13b | ||
|
|
04731f1ce5 | ||
|
|
3ddcb629c0 | ||
|
|
12bd058152 | ||
|
|
c9b1bf5a5e | ||
|
|
18df3c2c92 | ||
|
|
c08b9cefe9 | ||
|
|
258a35740f | ||
|
|
32cfe9a3a8 | ||
|
|
930a4fcf29 | ||
|
|
5a778d5fc3 | ||
|
|
1d64cfc930 | ||
|
|
3eab02bc68 | ||
|
|
dcdd03b746 | ||
|
|
51acb2da9b | ||
|
|
0d2c107e20 | ||
|
|
2089ba7997 | ||
|
|
8a032f86d8 | ||
|
|
5dcda4d088 | ||
|
|
8d76214193 | ||
|
|
24ddf04d7f | ||
|
|
79fa2a0823 | ||
|
|
ddfb5ac943 | ||
|
|
ed622409b0 | ||
|
|
b6beaa09ec | ||
|
|
7840f30b64 | ||
|
|
d7226d8522 | ||
|
|
8d5c9cedb3 | ||
|
|
6f7992ea9f | ||
|
|
5b2c2435d5 | ||
|
|
ebf84db204 | ||
|
|
94e83d80a6 | ||
|
|
bc80d5f641 | ||
|
|
81a5480250 | ||
|
|
e69d416dc4 | ||
|
|
15370203a1 | ||
|
|
8ca09d72bd | ||
|
|
d16cbe4ac6 | ||
|
|
49ac8827cd | ||
|
|
f2f686d31f | ||
|
|
6872ec3618 | ||
|
|
efb7053931 | ||
|
|
80109fd8aa | ||
|
|
b3abb175c1 | ||
|
|
f05577c927 | ||
|
|
c631f2bb0d | ||
|
|
ad5aac7ef5 | ||
|
|
5cd9179ce7 | ||
|
|
af58c3990d | ||
|
|
206c543e8c | ||
|
|
9820c56be6 | ||
|
|
2232965a1d | ||
|
|
6bfadfe5b8 | ||
|
|
c655d9650b | ||
|
|
61086089b3 | ||
|
|
c89d0124a1 | ||
|
|
a1a5839b20 | ||
|
|
c9e3dc83ab | ||
|
|
c179670b14 | ||
|
|
292893e9dd | ||
|
|
d956e493af | ||
|
|
05c85db52a | ||
|
|
49350933c3 | ||
|
|
fa3fed3fbd | ||
|
|
5df9a357ec | ||
|
|
bce9c57e60 | ||
|
|
f39de52c65 | ||
|
|
4001527a87 | ||
|
|
85be89ddf0 |
@@ -0,0 +1,272 @@
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
permissions:
|
||||
contents: write
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- TARGET: i686-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,openssl-vendored,wss
|
||||
- TARGET: x86_64-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: aarch64-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: armv7-unknown-linux-musleabihf
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: armv7-unknown-linux-musleabi
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: arm-unknown-linux-musleabihf
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: arm-unknown-linux-musleabi
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: x86_64-apple-darwin
|
||||
OS: macos-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: aarch64-apple-darwin
|
||||
OS: macos-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
OS: windows-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
OS: windows-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: mipsel-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
- TARGET: mips-unknown-linux-musl
|
||||
OS: ubuntu-latest
|
||||
FEATURES: ring-cipher,wss
|
||||
|
||||
runs-on: ${{ matrix.OS }}
|
||||
env:
|
||||
NAME: vnt-cli
|
||||
TARGET: ${{ matrix.TARGET }}
|
||||
OS: ${{ matrix.OS }}
|
||||
FEATURES: ${{ matrix.FEATURES }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Init submodules
|
||||
run: git submodule update --init --recursive --remote && git submodule status
|
||||
|
||||
- name: Cargo cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
./target
|
||||
key: build-cargo-registry-${{matrix.TARGET}}
|
||||
|
||||
- name: Use strawberry perl
|
||||
if: startsWith(matrix.OS, 'windows')
|
||||
run: echo OPENSSL_SRC_PERL=C:/Strawberry/perl/bin/perl >> $GITHUB_ENV
|
||||
|
||||
- name: List
|
||||
run: find ./
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
profile: minimal
|
||||
override: true
|
||||
|
||||
- name: Verify Rust version
|
||||
run: |
|
||||
rustc --version
|
||||
cargo --version
|
||||
|
||||
- name: Install dependencies on Ubuntu
|
||||
if: startsWith(matrix.OS, 'ubuntu')
|
||||
run: |
|
||||
sudo apt-get update && sudo apt-get install -y clang llvm libc6-dev-i386 \
|
||||
crossbuild-essential-arm64 crossbuild-essential-armhf musl-tools libboost-all-dev libc6-dev wget tar
|
||||
|
||||
# Install musl cross toolchains if needed
|
||||
case $TARGET in
|
||||
mipsel-unknown-linux-musl)
|
||||
MUSL_URI=mipsel-linux-muslsf-cross
|
||||
URL=mipsel-linux-muslsf
|
||||
;;
|
||||
aarch64-unknown-linux-musl)
|
||||
MUSL_URI=aarch64-linux-musl-cross
|
||||
;;
|
||||
armv7-unknown-linux-musleabihf)
|
||||
MUSL_URI=armv7l-linux-musleabihf-cross
|
||||
;;
|
||||
armv7-unknown-linux-musleabi)
|
||||
MUSL_URI=armv7m-linux-musleabi-cross
|
||||
;;
|
||||
arm-unknown-linux-musleabihf)
|
||||
MUSL_URI=arm-linux-musleabihf-cross
|
||||
;;
|
||||
arm-unknown-linux-musleabi)
|
||||
MUSL_URI=arm-linux-musleabi-cross
|
||||
;;
|
||||
mips-unknown-linux-musl)
|
||||
MUSL_URI=mips-linux-muslsf-cross
|
||||
URL=mips-linux-muslsf
|
||||
;;
|
||||
i686-unknown-linux-musl)
|
||||
MUSL_URI=i686-linux-musl-cross
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -n "$MUSL_URI" ]; then
|
||||
mkdir -p /opt/musl_gcc
|
||||
wget -c https://github.com/rustp2p/musl-cross/releases/download/0.0.1/$MUSL_URI.tgz -P /opt/musl_gcc/
|
||||
tar zxf /opt/musl_gcc/$MUSL_URI.tgz -C /opt/musl_gcc/
|
||||
sudo ln -s /opt/musl_gcc/$MUSL_URI/bin/*gcc /usr/bin/
|
||||
fi
|
||||
|
||||
if [[ $TARGET =~ ^mips.*$ ]]; then
|
||||
cd /opt/musl_gcc/${URL}-cross/lib/gcc/${URL}/11.2.1
|
||||
cp libgcc_eh.a libunwind.a
|
||||
rustup toolchain install nightly-x86_64-unknown-linux-gnu
|
||||
rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
|
||||
RUST_LIB_SRC=$HOME/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/
|
||||
if [[ -f $RUST_LIB_SRC/library/Cargo.lock && ! -f $RUST_LIB_SRC/Cargo.lock ]]; then
|
||||
cp -f $RUST_LIB_SRC/library/Cargo.lock $RUST_LIB_SRC/Cargo.lock
|
||||
fi
|
||||
fi
|
||||
|
||||
rustup -V
|
||||
|
||||
|
||||
- name: Configure cargo for cross-compilation
|
||||
run: |
|
||||
cat >>~/.cargo/config <<EOF
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-musl-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.armv7-unknown-linux-musleabihf]
|
||||
linker = "armv7l-linux-musleabihf-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.armv7-unknown-linux-musleabi]
|
||||
linker = "armv7m-linux-musleabi-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.arm-unknown-linux-musleabihf]
|
||||
linker = "arm-linux-musleabihf-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.arm-unknown-linux-musleabi]
|
||||
linker = "arm-linux-musleabi-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.mipsel-unknown-linux-musl]
|
||||
linker = "mipsel-linux-muslsf-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols","-C", "link-arg=-static",
|
||||
"-C", "relocation-model=static","-C", "link-arg=-no-pie","--cfg", "compiler_builtins_no_debug",
|
||||
"-L", "/opt/musl_gcc/mipsel-linux-muslsf-cross/mipsel-linux-muslsf/lib",
|
||||
"-L", "/opt/musl_gcc/mipsel-linux-muslsf-cross/lib/gcc/mipsel-linux-muslsf/11.2.1"]
|
||||
[target.mips-unknown-linux-musl]
|
||||
linker = "mips-linux-muslsf-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols","-C", "link-arg=-static",
|
||||
"-C", "relocation-model=static","-C", "link-arg=-no-pie","--cfg", "compiler_builtins_no_debug",
|
||||
"-L", "/opt/musl_gcc/mips-linux-muslsf-cross/mips-linux-muslsf/lib",
|
||||
"-L", "/opt/musl_gcc/mips-linux-muslsf-cross/lib/gcc/mips-linux-muslsf/11.2.1"]
|
||||
[target.x86_64-pc-windows-msvc]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.i686-pc-windows-msvc]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.x86_64-apple-darwin]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.aarch64-apple-darwin]
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
[target.i686-unknown-linux-musl]
|
||||
linker = "i686-linux-musl-gcc"
|
||||
rustflags = ["-C", "target-feature=+crt-static","-C", "strip=symbols"]
|
||||
EOF
|
||||
|
||||
- name: Install rust target
|
||||
if: ${{ ! endsWith(matrix.TARGET, 'freebsd') && ! startsWith(matrix.TARGET, 'mips') }}
|
||||
run: rustup target add $TARGET
|
||||
|
||||
- name: Run build vn-link-cli
|
||||
run: |
|
||||
if [[ $TARGET =~ ^mips.*$ ]]; then
|
||||
cargo +nightly build --package vn-link-cli --release --target $TARGET -Z build-std=std,panic_abort --features $FEATURES
|
||||
else
|
||||
cargo build --package vn-link-cli --release --target $TARGET --features $FEATURES
|
||||
fi
|
||||
|
||||
- name: Run build vnt-cli
|
||||
run: |
|
||||
if [[ $TARGET =~ ^mips.*$ ]]; then
|
||||
cargo +nightly build --package vnt-cli --release --target $TARGET -Z build-std=std,panic_abort --features $FEATURES
|
||||
else
|
||||
cargo build --package vnt-cli --release --target $TARGET --features $FEATURES
|
||||
fi
|
||||
- name: List target
|
||||
run: find ./target
|
||||
- name: Compress
|
||||
run: |
|
||||
mkdir -p ./artifacts
|
||||
if [[ $OS =~ ^windows.*$ ]]; then
|
||||
EXEC_VNT_CLI=vnt-cli.exe
|
||||
EXEC_VN_LINK_CLI=vn-link-cli.exe
|
||||
else
|
||||
EXEC_VNT_CLI=vnt-cli
|
||||
EXEC_VN_LINK_CLI=vn-link-cli
|
||||
fi
|
||||
if [[ $GITHUB_REF_TYPE =~ ^tag$ ]]; then
|
||||
TAG=$GITHUB_REF_NAME
|
||||
else
|
||||
TAG=$GITHUB_SHA
|
||||
fi
|
||||
mv ./target/$TARGET/release/$EXEC_VNT_CLI ./artifacts/$EXEC_VNT_CLI
|
||||
mv ./target/$TARGET/release/$EXEC_VN_LINK_CLI ./artifacts/$EXEC_VN_LINK_CLI
|
||||
mv ./README ./artifacts/README.txt
|
||||
cd ./artifacts
|
||||
tar -czf vnt-$TARGET-$TAG.tar.gz *
|
||||
mkdir upload_file
|
||||
mv vnt-$TARGET-$TAG.tar.gz ./upload_file/
|
||||
|
||||
- name: Archive artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: vnt-cli-${{ matrix.TARGET }}
|
||||
path: ./artifacts/upload_file
|
||||
|
||||
deploy:
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ./artifacts
|
||||
- name: List
|
||||
run: find ./artifacts
|
||||
- name: Release
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
file: ./artifacts/**/*.tar.gz
|
||||
tag: ${{ github.ref }}
|
||||
overwrite: true
|
||||
file_glob: true
|
||||
@@ -0,0 +1,6 @@
|
||||
target/*
|
||||
vnt/src/proto/*
|
||||
common/src/generated_serial_number.rs
|
||||
|
||||
# RustRover
|
||||
.idea
|
||||
+12
-43
@@ -1,44 +1,13 @@
|
||||
[package]
|
||||
name = "switch"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
[workspace]
|
||||
members = ["vnt", "common", "vnt-cli", "vn-link", "vn-link-cli"]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
packet = { path = "./packet" }
|
||||
bytes = "1.3.0"
|
||||
|
||||
libc = "0.2.137"
|
||||
|
||||
dashmap = "5.4.0"
|
||||
crossbeam = "0.8.2"
|
||||
parking_lot = "0.12.1"
|
||||
|
||||
rsa = "0.7.2"
|
||||
rand = "0.8.5"
|
||||
sha2 = { version = "0.10.6", features = ["oid"] }
|
||||
#colored = "2.0.0"
|
||||
|
||||
thiserror = "1.0.37"
|
||||
chrono = "0.4.23"
|
||||
lazy_static = "1.4.0"
|
||||
moka = "0.9.6"
|
||||
protobuf = "3.2.0"
|
||||
|
||||
console = "0.15.2"
|
||||
mac_address = "1.1.4"
|
||||
clap = { version = "4.0.32", features = ["derive"] }
|
||||
[target.'cfg(any(unix))'.dependencies]
|
||||
tun = { path = "./rust-tun" }
|
||||
sudo = "0.6.0"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
|
||||
wintun = "0.2.1"
|
||||
libloading = "0.7.4"
|
||||
runas = "0.2.1"
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "3.2.0"
|
||||
protoc-bin-vendored = "3.0.0"
|
||||
[profile.release]
|
||||
opt-level = 'z'
|
||||
debug = 0
|
||||
debug-assertions = false
|
||||
strip = "debuginfo"
|
||||
lto = true
|
||||
panic = 'abort'
|
||||
incremental = false
|
||||
codegen-units = 1
|
||||
rpath = false
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
一、程序说明
|
||||
1. vnt-cli vnt的命令行程序
|
||||
2. vn-link-cli 功能和vnt-cli基本一致,但是不依赖tun、不改变本地路由、不需要管理员/root权限
|
||||
|
||||
二、使用说明
|
||||
使用-k参数构建虚拟网络
|
||||
|
||||
|
||||
1. Program Description
|
||||
a. vnt-cli: Command-line program for VNT.
|
||||
b. vn-link-cli: Functions similarly to vnt-cli, but does not depend on TUN, does not change local routing, and does not require administrator/root permissions.
|
||||
|
||||
2. Instructions for Use
|
||||
Use the -k parameter to create a virtual network.
|
||||
@@ -1,38 +1,217 @@
|
||||
# switch
|
||||
Virtual Network Tools
|
||||
# VNT
|
||||
|
||||
将不同网络下的设备虚拟到一个局域网下
|
||||
🚀An efficient VPN
|
||||
|
||||
🚀一个简单、高效、能快速组建虚拟局域网的工具
|
||||
|
||||
### 示例:
|
||||
### vnt-cli参数详解 [参数说明](https://github.com/vnt-dev/vnt/blob/main/vnt-cli/README.md)
|
||||
|
||||
- 在一台mac设备上运行,获取到ip 10.13.0.2:
|
||||
### 快速开始:
|
||||
|
||||
<img width="506" alt="图片" src="https://user-images.githubusercontent.com/49143209/210379090-a3f21007-5a12-44d3-81d6-a69495209ea7.png">
|
||||
1. 指定一个token,在多台设备上运行该程序,例如:
|
||||
```shell
|
||||
# linux上
|
||||
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli -k 123456
|
||||
# 在另一台linux上使用nohup后台运行
|
||||
root@izj6cemne76ykdzkataftfz vnt# nohup ./vnt-cli -k 123456 &
|
||||
# windows上
|
||||
D:\vnt\bin_v1>vnt-cli.exe -k 123456
|
||||
```
|
||||
2. 可以执行info命令查看当前设备的虚拟ip
|
||||
```shell
|
||||
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --info
|
||||
Name: Ubuntu 18.04 (bionic) [64-bit]
|
||||
Virtual ip: 10.26.0.2
|
||||
Virtual gateway: 10.26.0.1
|
||||
Virtual netmask: 255.255.255.0
|
||||
Connection status: Connected
|
||||
NAT type: Cone
|
||||
Relay server: 43.139.56.10:29871
|
||||
Public ips: 120.228.76.75
|
||||
Local ip: 172.25.165.58
|
||||
```
|
||||
3. 也可以执行list命令查看其他设备的虚拟ip
|
||||
```shell
|
||||
root@DESKTOP-0BCHNIO:/opt# ./vnt-cli --list
|
||||
Name Virtual Ip P2P/Relay Rt Status
|
||||
Windows 10.0.22621 (Windows 11 Professional) [64-bit] 10.26.0.3 p2p 2 Online
|
||||
CentOS 7.9.2009 (Core) [64-bit] 10.26.0.4 p2p 35 Online
|
||||
```
|
||||
4. 最后可以用虚拟ip实现设备间相互访问
|
||||
|
||||
- 在另一台windows上运行,获取到ip 10.13.0.3:
|
||||
<img width="506" alt="ssh" src="https://raw.githubusercontent.com/vnt-dev/vnt/main/documents/img/ssh.jpg">
|
||||
5. 帮助,使用-h命令查看
|
||||
|
||||

|
||||
|
||||
- 此时这两个设备之间就能用ip相互访问了
|
||||
|
||||
<img width="437" alt="图片" src="https://user-images.githubusercontent.com/49143209/210380969-4a7c0f23-1e88-4ab6-9cc2-0c0f086848ac.png">
|
||||
### 使用须知
|
||||
|
||||
- token的作用是标识一个虚拟局域网,当使用公共服务器时,建议使用一个唯一值当token(比如uuid),否则有可能连接到其他人创建的虚拟局域网中
|
||||
- 公共服务器目前的配置是2核4G 4Mbps,有需要再扩展~
|
||||
- 默认使用公共服务器做注册和中继,目前的配置是2核4G 4Mbps,有需要再扩展~
|
||||
- vnt-cli需要使用命令行运行
|
||||
- Mac和Linux下需要加可执行权限(例如:chmod +x ./vnt-cli)
|
||||
- 可以自己搭中继服务器([server](https://github.com/vnt-dev/vnts))
|
||||
|
||||
### 直接使用
|
||||
|
||||
[**下载release文件**](https://github.com/vnt-dev/vnt/releases)
|
||||
|
||||
[**帮助文档**](https://rustvnt.com)
|
||||
|
||||
### 自行编译
|
||||
|
||||
<details> <summary>点击展开</summary>
|
||||
|
||||
前提条件:安装rust编译环境([install rust](https://www.rust-lang.org/zh-CN/tools/install))
|
||||
|
||||
```
|
||||
到项目根目录下执行 cargo build -p vnt-cli
|
||||
|
||||
也可按需编译,将得到更小的二进制文件,使用--no-default-features排除默认features
|
||||
|
||||
cargo build -p vnt-cli --no-default-features
|
||||
```
|
||||
|
||||
features说明
|
||||
|
||||
| feature | 说明 | 是否默认 |
|
||||
|-------------------|--------------------------------|------|
|
||||
| openssl | 使用openssl中的加密算法 | 否 |
|
||||
| openssl-vendored | 从源码编译openssl | 否 |
|
||||
| ring-cipher | 使用ring中的加密算法 | 否 |
|
||||
| aes_cbc | 支持aes_cbc加密 | 是 |
|
||||
| aes_ecb | 支持aes_ecb加密 | 是 |
|
||||
| aes_gcm | 支持aes_gcm加密 | 是 |
|
||||
| sm4_cbc | 支持sm4_cbc加密 | 是 |
|
||||
| chacha20_poly1305 | 支持chacha20和chacha20_poly1305加密 | 是 |
|
||||
| server_encrypt | 支持服务端加密 | 是 |
|
||||
| ip_proxy | 内置ip代理 | 是 |
|
||||
| port_mapping | 端口映射 | 是 |
|
||||
| log | 日志 | 是 |
|
||||
| command | list、route等命令 | 是 |
|
||||
| file_config | yaml配置文件 | 是 |
|
||||
| lz4 | lz4压缩 | 是 |
|
||||
| zstd | zstd压缩 | 否 |
|
||||
| upnp | upnp协议 | 否 |
|
||||
| ws | ws协议 | 是 |
|
||||
| wss | wss协议 | 是 |
|
||||
|
||||
</details>
|
||||
|
||||
### 支持平台
|
||||
|
||||
- Mac
|
||||
- Linux
|
||||
- Windows
|
||||
- 依赖 wintun.dll(https://www.wintun.net/)
|
||||
- 默认使用tun网卡 依赖wintun.dll([win-tun](https://www.wintun.net/))(将dll放到同目录下,建议使用版本0.14.1)
|
||||
- 可选择使用tap网卡 依赖tap-windows([win-tap](https://build.openvpn.net/downloads/releases/))(建议使用版本9.24.7)
|
||||
- Android
|
||||
|
||||
### GUI
|
||||
|
||||
支持安卓和Windows [下载](https://github.com/vnt-dev/VntApp/releases/)
|
||||
|
||||
### 特性
|
||||
|
||||
- IP层数据转发
|
||||
- tun虚拟网卡
|
||||
- NAT穿透
|
||||
- 点对点穿透
|
||||
- 服务端中继转发
|
||||
- 点对点穿透
|
||||
- 服务端中继转发
|
||||
- 客户端中继转发
|
||||
- IP代理(点对点、点对网)
|
||||
- p2p组播/广播
|
||||
- 客户端数据加密(`aes-gcm`、`chacha20-poly1305`等多种加密算法)
|
||||
- 服务端数据加密(`rsa` + `aes-gcm`)
|
||||
- 多通道UDP应对QOS
|
||||
- 支持TCP、UDP、WebSocket等多种协议
|
||||
- 支持数据压缩
|
||||
|
||||
### 更多玩法
|
||||
|
||||
1. 和远程桌面(如mstsc)搭配,超低延迟的体验
|
||||
2. 安装samba服务,共享磁盘
|
||||
3. 点对网,访问内网其他机器、IP代理(结合启动参数'-i'和'-o')
|
||||
|
||||
### Todo
|
||||
- 数据加密
|
||||
- 客户端中继转发
|
||||
|
||||
- ~~桌面UI(已支持)~~
|
||||
- 使用FEC、ARQ等方式提升弱网环境的稳定性
|
||||
|
||||
### 常见问题
|
||||
|
||||
<details> <summary>展开</summary>
|
||||
|
||||
#### 问题1: 设置网络地址失败
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
vnt默认使用10.26.0.0/24网段,和本地网络适配器的ip冲突
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 方法一:找到冲突的IP,将其改成别的
|
||||
2. 方法二:自建服务器,指定其他不会冲突的网段
|
||||
3. 方法三:增加参数-d <device-id> ,设置不同的id会让服务端分配不同的IP,从而绕开有冲突的IP
|
||||
|
||||
#### 问题2: windows系统上wintun.dll加载失败
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
没有下载wintun.dll 或者使用的wintun.dll有问题
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 下载最新版的wintun.dll [下载链接](https://www.wintun.net/builds/wintun-0.14.1.zip)
|
||||
2. 解压后找到对应架构的目录,通常是amd64
|
||||
3. 将对应的wintun.dll放到和vnt-cli同目录下(或者放到C盘Windows目录下)
|
||||
4. 再次启动vnt-cli
|
||||
|
||||
#### 问题3: 丢包严重,或是不能正常组网通信
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
某些宽带下(比如广电宽带)UDP丢包严重
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 使用TCP模式中继转发(vnt-cli增加--tcp参数)
|
||||
2. 如果p2p后效果很差,可以选择禁用p2p(vnt-cli增加--use-channel relay 参数)
|
||||
|
||||
#### 问题4:重启后虚拟IP发生变化,或指定了IP不能启动
|
||||
|
||||
##### 可能原因:
|
||||
|
||||
设备重启后程序自动获取的id值改变,导致注册时重新分配了新的IP,或是IP冲突
|
||||
|
||||
##### 解决方法:
|
||||
|
||||
1. 命令行启动增加-d参数(使用配置文件启动则在配置文件中增加device_id参数),要保证每个设备的值都不一样,取值可以任意64位以内字符串
|
||||
|
||||
</details>
|
||||
|
||||
### 交流群
|
||||
|
||||
对VNT有任何问题均可以加群联系作者
|
||||
|
||||
QQ群1: 1034868233(满人)
|
||||
|
||||
QQ群2: 950473757
|
||||
|
||||
QQ群3: 1060550456
|
||||
|
||||
### 赞助
|
||||
|
||||
如果VNT对你有帮助,欢迎打赏作者
|
||||
|
||||
<img width="300" alt="" src="https://github.com/vnt-dev/vnt/assets/49143209/0d3a7311-43fc-4ed7-9507-863b5d69b6b2">
|
||||
|
||||
### 其他
|
||||
|
||||
可使用社区小伙伴搭建的中继服务器
|
||||
|
||||
1. -s vnt.8443.eu.org:29871
|
||||
2. -s vnt.wherewego.top:29872
|
||||
|
||||
### 参与贡献
|
||||
|
||||
<a href="https://github.com/vnt-dev/vnt/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=vnt-dev/vnt" />
|
||||
</a>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version = "1.2.16"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
vnt = { path = "../vnt", package = "vnt", default-features = false }
|
||||
anyhow = "1.0.82"
|
||||
console = "0.15.2"
|
||||
log = "0.4.17"
|
||||
log4rs = { version = "1.3.0", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_yaml = "0.9.32"
|
||||
getopts = "0.2.21"
|
||||
gethostname = "0.4.3"
|
||||
uuid = { version = "1.8.0", features = ["v4"] }
|
||||
sys-locale = "0.3.1"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
openssl = ["vnt/openssl"]
|
||||
openssl-vendored = ["vnt/openssl-vendored"]
|
||||
ring-cipher = ["vnt/ring-cipher"]
|
||||
aes_cbc = ["vnt/aes_cbc"]
|
||||
aes_ecb = ["vnt/aes_ecb"]
|
||||
sm4_cbc = ["vnt/sm4_cbc"]
|
||||
aes_gcm = ["vnt/aes_gcm"]
|
||||
chacha20_poly1305 = ["vnt/chacha20_poly1305"]
|
||||
server_encrypt = ["vnt/server_encrypt"]
|
||||
ip_proxy = ["vnt/ip_proxy"]
|
||||
port_mapping = ["vnt/port_mapping"]
|
||||
lz4 = ["vnt/lz4_compress"]
|
||||
zstd = ["vnt/zstd_compress"]
|
||||
upnp = ["vnt/upnp"]
|
||||
ws = ["vnt/ws"]
|
||||
wss = ["vnt/wss"]
|
||||
command = []
|
||||
file_config = []
|
||||
log = ["log4rs"]
|
||||
integrated_tun = ["vnt/integrated_tun"]
|
||||
|
||||
[build-dependencies]
|
||||
rand = "0.8.5"
|
||||
chrono = "0.4.23"
|
||||
@@ -0,0 +1,16 @@
|
||||
use rand::Rng;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
fn main() {
|
||||
let now_time = chrono::Local::now();
|
||||
let serial_number = format!(
|
||||
"{}-{}",
|
||||
&now_time.format("%y%m%d%H%M").to_string(),
|
||||
rand::thread_rng().gen_range(100..1000)
|
||||
);
|
||||
let generated_code = format!(r#"pub const SERIAL_NUMBER: &str = "{}";"#, serial_number);
|
||||
let dest_path = "src/generated_serial_number.rs";
|
||||
let mut file = File::create(&dest_path).unwrap();
|
||||
file.write_all(generated_code.as_bytes()).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
pub fn ips_parse(ips: &Vec<String>) -> Result<Vec<(u32, u32, Ipv4Addr)>, String> {
|
||||
let mut in_ips_c = vec![];
|
||||
for x in ips {
|
||||
let mut split = x.split(",");
|
||||
let net = if let Some(net) = split.next() {
|
||||
net
|
||||
} else {
|
||||
return Err(format!("ipv4/mask,ipv4 {:?}", x));
|
||||
};
|
||||
let ip = if let Some(ip) = split.next() {
|
||||
ip
|
||||
} else {
|
||||
return Err(format!("ipv4/mask,ipv4 {:?}", x));
|
||||
};
|
||||
let ip = if let Ok(ip) = ip.parse::<Ipv4Addr>() {
|
||||
ip
|
||||
} else {
|
||||
return Err(format!("not ipv4 {:?}", ip));
|
||||
};
|
||||
let mut split = net.split("/");
|
||||
let dest = if let Some(dest) = split.next() {
|
||||
dest
|
||||
} else {
|
||||
return Err(format!("no ipv4/mask {:?}", net));
|
||||
};
|
||||
let mask = if let Some(mask) = split.next() {
|
||||
mask
|
||||
} else {
|
||||
return Err(format!("no netmask {:?}", net));
|
||||
};
|
||||
let dest = if let Ok(dest) = dest.parse::<Ipv4Addr>() {
|
||||
dest
|
||||
} else {
|
||||
return Err(format!("not ipv4 {:?}", dest));
|
||||
};
|
||||
let mask = to_ip(mask)?;
|
||||
in_ips_c.push((u32::from_be_bytes(dest.octets()), mask, ip));
|
||||
}
|
||||
Ok(in_ips_c)
|
||||
}
|
||||
|
||||
pub fn out_ips_parse(ips: &Vec<String>) -> Result<Vec<(u32, u32)>, String> {
|
||||
let mut in_ips_c = vec![];
|
||||
for x in ips {
|
||||
let mut split = x.split("/");
|
||||
let dest = if let Some(dest) = split.next() {
|
||||
dest
|
||||
} else {
|
||||
return Err(format!("no ipv4/mask {:?}", x));
|
||||
};
|
||||
let mask = if let Some(mask) = split.next() {
|
||||
mask
|
||||
} else {
|
||||
return Err(format!("no netmask {:?}", x));
|
||||
};
|
||||
let dest = if let Ok(dest) = dest.parse::<Ipv4Addr>() {
|
||||
dest
|
||||
} else {
|
||||
return Err(format!("not ipv4 {:?}", dest));
|
||||
};
|
||||
let mask = to_ip(mask)?;
|
||||
in_ips_c.push((u32::from_be_bytes(dest.octets()), mask));
|
||||
}
|
||||
Ok(in_ips_c)
|
||||
}
|
||||
|
||||
pub fn to_ip(mask: &str) -> Result<u32, String> {
|
||||
if let Ok(m) = mask.parse::<u32>() {
|
||||
if m > 32 {
|
||||
return Err("not netmask".to_string());
|
||||
}
|
||||
let mut mask = 0u32;
|
||||
for i in 0..m {
|
||||
mask = mask | (1 << (31 - i));
|
||||
}
|
||||
Ok(mask)
|
||||
} else {
|
||||
Err("not netmask".to_string())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
use std::process;
|
||||
|
||||
use console::style;
|
||||
use vnt::{ConnectInfo, ErrorInfo, ErrorType, HandshakeInfo, RegisterInfo, VntCallback};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VntHandler {}
|
||||
|
||||
impl VntCallback for VntHandler {
|
||||
fn success(&self) {
|
||||
println!(" {} ", style("====== Connect Successfully ======").green())
|
||||
}
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
fn create_tun(&self, info: vnt::DeviceInfo) {
|
||||
println!("create_tun {}", info)
|
||||
}
|
||||
|
||||
fn connect(&self, info: ConnectInfo) {
|
||||
println!("connect {}", info)
|
||||
}
|
||||
|
||||
fn handshake(&self, info: HandshakeInfo) -> bool {
|
||||
println!("handshake {}", info);
|
||||
true
|
||||
}
|
||||
|
||||
fn register(&self, info: RegisterInfo) -> bool {
|
||||
println!("register {}", style(info).green());
|
||||
true
|
||||
}
|
||||
|
||||
fn error(&self, info: ErrorInfo) {
|
||||
log::error!("error {:?}", info);
|
||||
println!("{}", style(format!("error {}", info)).red());
|
||||
match info.code {
|
||||
ErrorType::TokenError
|
||||
| ErrorType::AddressExhausted
|
||||
| ErrorType::IpAlreadyExists
|
||||
| ErrorType::InvalidIp
|
||||
| ErrorType::LocalIpExists
|
||||
| ErrorType::FailedToCrateDevice => {
|
||||
self.stop();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn stop(&self) {
|
||||
println!("stopped");
|
||||
process::exit(0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,620 @@
|
||||
use crate::args_parse::{ips_parse, out_ips_parse};
|
||||
#[cfg(feature = "command")]
|
||||
use crate::command;
|
||||
use crate::{config, generated_serial_number};
|
||||
use anyhow::anyhow;
|
||||
use console::style;
|
||||
use getopts::Options;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use sys_locale::get_locale;
|
||||
use vnt::channel::punch::PunchModel;
|
||||
use vnt::channel::UseChannelType;
|
||||
use vnt::cipher::CipherModel;
|
||||
use vnt::compression::Compressor;
|
||||
use vnt::core::Config;
|
||||
|
||||
pub fn app_home() -> io::Result<PathBuf> {
|
||||
let root_path = match std::env::current_exe() {
|
||||
Ok(path) => {
|
||||
if let Some(v) = path.as_path().parent() {
|
||||
v.to_path_buf()
|
||||
} else {
|
||||
log::warn!("current_exe parent none:{:?}", path);
|
||||
PathBuf::new()
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("current_exe err:{:?}", e);
|
||||
PathBuf::new()
|
||||
}
|
||||
};
|
||||
let path = root_path.join("env");
|
||||
if !path.exists() {
|
||||
std::fs::create_dir_all(&path)?;
|
||||
}
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn parse_args_config() -> anyhow::Result<Option<(Config, Vec<String>, bool)>> {
|
||||
#[cfg(feature = "log")]
|
||||
let _ = log4rs::init_file("log4rs.yaml", Default::default());
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let program = args[0].clone();
|
||||
let mut opts = Options::new();
|
||||
opts.optopt("k", "", "组网标识", "<token>");
|
||||
opts.optopt("n", "", "设备名称", "<name>");
|
||||
opts.optopt("d", "", "设备标识", "<id>");
|
||||
opts.optflag("c", "", "关闭交互式命令");
|
||||
opts.optopt("s", "", "注册和中继服务器地址", "<server>");
|
||||
opts.optmulti("e", "", "stun服务器", "<stun-server>");
|
||||
opts.optflag("a", "", "使用tap模式");
|
||||
opts.optopt("", "nic", "虚拟网卡名称,windows下使用tap则必填", "<tun0>");
|
||||
opts.optmulti("i", "", "配置点对网(IP代理)入站时使用", "<in-ip>");
|
||||
opts.optmulti("o", "", "配置点对网出站时使用", "<out-ip>");
|
||||
opts.optopt("w", "", "客户端加密", "<password>");
|
||||
opts.optflag("W", "", "服务端加密");
|
||||
opts.optopt("u", "", "自定义mtu(默认为1430)", "<mtu>");
|
||||
opts.optopt("", "ip", "指定虚拟ip", "<ip>");
|
||||
opts.optflag("", "relay", "仅使用服务器转发");
|
||||
opts.optopt("", "par", "任务并行度(必须为正整数)", "<parallel>");
|
||||
opts.optopt("", "model", "加密模式", "<model>");
|
||||
opts.optflag("", "finger", "指纹校验");
|
||||
opts.optopt("", "punch", "取值ipv4/ipv6", "<punch>");
|
||||
opts.optopt("", "ports", "监听的端口", "<port,port>");
|
||||
opts.optflag("", "cmd", "开启窗口输入");
|
||||
opts.optflag("", "no-proxy", "关闭内置代理");
|
||||
opts.optflag("", "first-latency", "优先延迟");
|
||||
opts.optopt("", "use-channel", "使用通道 relay/p2p", "<use-channel>");
|
||||
opts.optopt("", "packet-loss", "丢包率", "<packet-loss>");
|
||||
opts.optopt("", "packet-delay", "延迟", "<packet-delay>");
|
||||
opts.optmulti("", "dns", "dns", "<dns>");
|
||||
opts.optmulti("", "mapping", "mapping", "<mapping>");
|
||||
opts.optmulti("", "vnt-mapping", "vnt-mapping", "<mapping>");
|
||||
opts.optopt("f", "", "配置文件", "<conf>");
|
||||
opts.optopt("", "compressor", "压缩算法", "<lz4>");
|
||||
opts.optopt("", "local-dev", "指定本地ipv4网卡名称", "<NAME>");
|
||||
opts.optflag("", "disable-stats", "关闭流量统计");
|
||||
opts.optflag("", "allow-wg", "允许接入WireGuard");
|
||||
//"后台运行时,查看其他设备列表"
|
||||
opts.optflag("", "add", "后台运行时,添加地址");
|
||||
opts.optflag("", "list", "后台运行时,查看其他设备列表");
|
||||
opts.optflag("", "all", "后台运行时,查看其他设备完整信息");
|
||||
opts.optflag("", "info", "后台运行时,查看当前设备信息");
|
||||
opts.optflag("", "route", "后台运行时,查看数据转发路径");
|
||||
opts.optflag("", "chart_a", "后台运行时,查看流量统计");
|
||||
opts.optopt("", "chart_b", "后台运行时,查看流量统计", "<IP>");
|
||||
opts.optflag("", "stop", "停止后台运行");
|
||||
opts.optflag("h", "help", "帮助");
|
||||
let matches = match opts.parse(&args[1..]) {
|
||||
Ok(m) => m,
|
||||
Err(f) => {
|
||||
print_usage(&program, opts);
|
||||
return Err(anyhow::anyhow!("{}", f.to_string()));
|
||||
}
|
||||
};
|
||||
if matches.opt_present("h") || args.len() == 1 {
|
||||
print_usage(&program, opts);
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
#[cfg(feature = "command")]
|
||||
if matches.opt_present("list") {
|
||||
command::command(command::CommandEnum::List);
|
||||
return Ok(None);
|
||||
} else if matches.opt_present("info") {
|
||||
command::command(command::CommandEnum::Info);
|
||||
return Ok(None);
|
||||
} else if matches.opt_present("stop") {
|
||||
command::command(command::CommandEnum::Stop);
|
||||
return Ok(None);
|
||||
} else if matches.opt_present("route") {
|
||||
command::command(command::CommandEnum::Route);
|
||||
return Ok(None);
|
||||
} else if matches.opt_present("all") {
|
||||
command::command(command::CommandEnum::All);
|
||||
return Ok(None);
|
||||
}
|
||||
#[cfg(feature = "command")]
|
||||
if matches.opt_present("chart_a") {
|
||||
command::command(command::CommandEnum::ChartA);
|
||||
return Ok(None);
|
||||
}
|
||||
#[cfg(feature = "command")]
|
||||
if let Some(v) = matches.opt_str("chart_b") {
|
||||
command::command(command::CommandEnum::ChartB(v));
|
||||
return Ok(None);
|
||||
}
|
||||
let conf = matches.opt_str("f");
|
||||
let (config, vnt_link_config, cmd) = if conf.is_some() {
|
||||
match config::read_config(&conf.unwrap()) {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
return Err(anyhow::anyhow!("conf err {}", e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !matches.opt_present("k") {
|
||||
print_usage(&program, opts);
|
||||
return Err(anyhow::anyhow!("parameter -k not found ."));
|
||||
}
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
let device_name = matches.opt_str("nic");
|
||||
let token: String = matches.opt_get("k").unwrap().unwrap();
|
||||
let device_id = matches.opt_get_default("d", String::new()).unwrap();
|
||||
let device_id = if device_id.is_empty() {
|
||||
config::get_device_id()
|
||||
} else {
|
||||
device_id
|
||||
};
|
||||
if device_id.is_empty() {
|
||||
print_usage(&program, opts);
|
||||
return Err(anyhow::anyhow!("parameter -d not found ."));
|
||||
}
|
||||
let name = matches
|
||||
.opt_get_default(
|
||||
"n",
|
||||
gethostname::gethostname()
|
||||
.to_str()
|
||||
.unwrap_or("UnknownName")
|
||||
.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let server_address_str = matches
|
||||
.opt_get_default("s", "vnt.wherewego.top:29872".to_string())
|
||||
.unwrap();
|
||||
|
||||
let mut stun_server = matches.opt_strs("e");
|
||||
if stun_server.is_empty() {
|
||||
for x in config::PUB_STUN {
|
||||
stun_server.push(x.to_string());
|
||||
}
|
||||
}
|
||||
let dns = matches.opt_strs("dns");
|
||||
let in_ip = matches.opt_strs("i");
|
||||
let in_ip = match ips_parse(&in_ip) {
|
||||
Ok(in_ip) => in_ip,
|
||||
Err(e) => {
|
||||
print_usage(&program, opts);
|
||||
println!();
|
||||
println!("-i: {:?} {}", in_ip, e);
|
||||
return Err(anyhow::anyhow!("example: -i 192.168.0.0/24,10.26.0.3"));
|
||||
}
|
||||
};
|
||||
let out_ip = matches.opt_strs("o");
|
||||
let out_ip = match out_ips_parse(&out_ip) {
|
||||
Ok(out_ip) => out_ip,
|
||||
Err(e) => {
|
||||
print_usage(&program, opts);
|
||||
println!();
|
||||
println!("-o: {:?} {}", out_ip, e);
|
||||
return Err(anyhow::anyhow!("example: -o 0.0.0.0/0"));
|
||||
}
|
||||
};
|
||||
let password: Option<String> = matches.opt_get("w").unwrap();
|
||||
let server_encrypt = matches.opt_present("W");
|
||||
#[cfg(not(feature = "server_encrypt"))]
|
||||
{
|
||||
if server_encrypt {
|
||||
println!("Server encryption not supported");
|
||||
return Err(anyhow::anyhow!("Server encryption not supported"));
|
||||
}
|
||||
}
|
||||
let mtu: Option<String> = matches.opt_get("u").unwrap();
|
||||
let mtu = if let Some(mtu) = mtu {
|
||||
match u32::from_str(&mtu) {
|
||||
Ok(mtu) => Some(mtu),
|
||||
Err(e) => {
|
||||
print_usage(&program, opts);
|
||||
println!();
|
||||
println!("'-u {}' {}", mtu, e);
|
||||
return Err(anyhow::anyhow!("'-u {}' {}", mtu, e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let virtual_ip: Option<String> = matches.opt_get("ip").unwrap();
|
||||
let virtual_ip =
|
||||
virtual_ip.map(|v| Ipv4Addr::from_str(&v).expect(&format!("'--ip {}' error", v)));
|
||||
if let Some(virtual_ip) = virtual_ip {
|
||||
if virtual_ip.is_unspecified() || virtual_ip.is_broadcast() || virtual_ip.is_multicast()
|
||||
{
|
||||
return Err(anyhow::anyhow!("'--ip {}' invalid", virtual_ip));
|
||||
}
|
||||
}
|
||||
let relay = matches.opt_present("relay");
|
||||
|
||||
let cipher_model = match matches.opt_get::<CipherModel>("model") {
|
||||
Ok(model) => {
|
||||
#[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))]
|
||||
{
|
||||
if password.is_some() && model.is_none() {
|
||||
return Err(anyhow::anyhow!("'--model ' undefined"));
|
||||
}
|
||||
model.unwrap_or(CipherModel::None)
|
||||
}
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
model.unwrap_or(CipherModel::AesGcm)
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(anyhow::anyhow!("'--model ' invalid,{}", e));
|
||||
}
|
||||
};
|
||||
|
||||
let finger = matches.opt_present("finger");
|
||||
let punch_model = matches
|
||||
.opt_get::<PunchModel>("punch")
|
||||
.unwrap()
|
||||
.unwrap_or(PunchModel::All);
|
||||
let use_channel_type = matches
|
||||
.opt_get::<UseChannelType>("use-channel")
|
||||
.unwrap()
|
||||
.unwrap_or_else(|| {
|
||||
if relay {
|
||||
UseChannelType::Relay
|
||||
} else {
|
||||
UseChannelType::All
|
||||
}
|
||||
});
|
||||
|
||||
let ports = matches
|
||||
.opt_get::<String>("ports")
|
||||
.unwrap_or(None)
|
||||
.map(|v| v.split(",").map(|x| x.parse().unwrap_or(0)).collect());
|
||||
|
||||
let cmd = matches.opt_present("cmd");
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
let no_proxy = matches.opt_present("no-proxy");
|
||||
let first_latency = matches.opt_present("first-latency");
|
||||
let packet_loss = matches
|
||||
.opt_get::<f64>("packet-loss")
|
||||
.expect("--packet-loss");
|
||||
let packet_delay = matches
|
||||
.opt_get::<u32>("packet-delay")
|
||||
.expect("--packet-delay")
|
||||
.unwrap_or(0);
|
||||
#[cfg(feature = "port_mapping")]
|
||||
let port_mapping_list = matches.opt_strs("mapping");
|
||||
let vnt_mapping_list = matches.opt_strs("vnt-mapping");
|
||||
let local_dev: Option<String> = matches.opt_get("local-dev").unwrap();
|
||||
|
||||
let disable_stats = matches.opt_present("disable-stats");
|
||||
let allow_wire_guard = matches.opt_present("allow-wg");
|
||||
let compressor = if let Some(compressor) = matches.opt_str("compressor").as_ref() {
|
||||
Compressor::from_str(compressor)
|
||||
.map_err(|e| anyhow!("{}", e))
|
||||
.unwrap()
|
||||
} else {
|
||||
Compressor::None
|
||||
};
|
||||
let config = Config::new(
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
false,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
server_address_str,
|
||||
dns,
|
||||
stun_server,
|
||||
in_ip,
|
||||
out_ip,
|
||||
password,
|
||||
mtu,
|
||||
virtual_ip,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
no_proxy,
|
||||
server_encrypt,
|
||||
cipher_model,
|
||||
finger,
|
||||
punch_model,
|
||||
ports,
|
||||
first_latency,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
device_name,
|
||||
use_channel_type,
|
||||
packet_loss,
|
||||
packet_delay,
|
||||
#[cfg(feature = "port_mapping")]
|
||||
port_mapping_list,
|
||||
compressor,
|
||||
!disable_stats,
|
||||
allow_wire_guard,
|
||||
local_dev,
|
||||
)?;
|
||||
(config, vnt_mapping_list, cmd)
|
||||
};
|
||||
println!("version {}", vnt::VNT_VERSION);
|
||||
println!("Serial:{}", generated_serial_number::SERIAL_NUMBER);
|
||||
log::info!(
|
||||
"version:{},Serial:{}",
|
||||
vnt::VNT_VERSION,
|
||||
generated_serial_number::SERIAL_NUMBER
|
||||
);
|
||||
Ok(Some((config, vnt_link_config, cmd)))
|
||||
}
|
||||
|
||||
fn get_description(key: &str, language: &str) -> String {
|
||||
// 设置一个全局的映射来存储中英文对照
|
||||
let descriptions: HashMap<&str, (&str, &str)> = [
|
||||
("-k <token>", ("使用相同的token,就能组建一个局域网络", "Use the same token to form a local network")),
|
||||
("-n <name>", ("给设备一个名字,便于区分不同设备,默认使用系统版本", "Give the device a name to distinguish it, defaults to system version")),
|
||||
("-d <id>", ("设备唯一标识符,不使用--ip参数时,服务端凭此参数分配虚拟ip,注意不能重复", "Device unique identifier, used by the server to allocate virtual IP when --ip parameter is not used, must be unique")),
|
||||
("-s <server>", ("注册和中继服务器地址,协议支持使用tcp://和ws://和wss://,默认为udp://", "Registration and relay server address, protocols support using tcp://, ws://, and wss://, default is udp://")),
|
||||
("-e <stun-server>", ("stun服务器,用于探测NAT类型,可使用多个地址,如-e stun.miwifi.com -e turn.cloudflare.com", "STUN server for detecting NAT type, can specify multiple addresses, e.g., -e stun.miwifi.com -e turn.cloudflare.com")),
|
||||
("-i <in-ip>", ("配置点对网(IP代理)时使用,-i 192.168.0.0/24,10.26.0.3表示允许接收网段192.168.0.0/24的数据并转发到10.26.0.3,可指定多个网段", "Used when configuring point-to-point network (IP proxy), -i 192.168.0.0/24,10.26.0.3 allows receiving data from subnet 192.168.0.0/24 and forwarding to 10.26.0.3, specify multiple subnets")),
|
||||
("-o <out-ip>", ("配置点对网时使用,-o 192.168.0.0/24表示允许将数据转发到192.168.0.0/24,可指定多个网段", "Used when configuring point-to-point network, -o 192.168.0.0/24 allows forwarding data to 192.168.0.0/24, specify multiple subnets")),
|
||||
("-w <password>", ("使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密,使用相同密码的客户端才能通信", "Encrypt client data with keys generated by this password, server cannot decrypt, clients must use the same password to communicate")),
|
||||
("-W", ("加密当前客户端和服务端通信的数据,请留意服务端指纹是否正确", "Encrypt the data currently being communicated between the client and server, please pay attention to whether the server fingerprint is correct")),
|
||||
("-u <mtu>", ("自定义mtu(默认为1420)", "Customize MTU (default is 1420)")),
|
||||
("-f <conf_file>", ("读取配置文件中的配置", "Read configuration from file")),
|
||||
("--ip <ip>", ("指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配", "Specify virtual IP, must be unique and valid within server subnet, by default allocated by server")),
|
||||
("--model <model>", ("加密模式(默认aes_gcm),可选值", "Encryption mode (default aes_gcm), options ")),
|
||||
("--finger", ("增加数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启", "Add data fingerprint verification for increased security, client must enable if server does")),
|
||||
("--punch <punch>", ("取值ipv4/ipv6/ipv4-tcp/ipv4-udp/ipv6-tcp/ipv6-udp/all,ipv4表示仅使用ipv4打洞", "Values ipv4/ipv6/ipv4-tcp/ipv4-udp/ipv6-tcp/ipv6-udp/all, ipv4 for IPv4 hole punching only")),
|
||||
("--ports <port,port>", ("取值0~65535,指定本地监听的一组端口,默认监听两个随机端口,使用过多端口会增加网络负担", "Values 0~65535, specify a group of local listening ports, defaults to two random ports, using many ports increases network load")),
|
||||
("--cmd", ("开启交互式命令,使用此参数开启控制台输入", "Enable interactive command mode, use this parameter to enable console input")),
|
||||
("--no-proxy", ("关闭内置代理,如需点对网则需要配置网卡NAT转发", "Disable built-in proxy, configure network card NAT forwarding for point-to-point networking")),
|
||||
("--first-latency", ("优先低延迟的通道,默认情况优先使用p2p通道", "Prioritize low-latency channels, defaults to prioritizing p2p channel")),
|
||||
("--use-channel <p2p>", ("使用通道 relay/p2p/all,默认两者都使用", "Use channel relay/p2p/all, defaults to using both")),
|
||||
("--nic <tun0>", ("指定虚拟网卡名称", "Specify virtual network card name")),
|
||||
("--packet-loss <0>", ("模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包,可用于模拟弱网", "Simulate packet loss, value between 0 and 1, program actively drops packets based on set probability, useful for simulating weak networks")),
|
||||
("--packet-delay <0>", ("模拟延迟,正整数,单位毫秒,程序将根据设定值延迟发送数据包,可用于模拟弱网", "Simulate latency, integer, in milliseconds (ms). The program will delay sending packets according to the set value and can be used to simulate weak networks")),
|
||||
("--dns <host:port>", ("DNS服务器地址,可使用多个dns,不指定时使用系统解析", "DNS server address, can specify multiple DNS servers, defaults to system resolution if not specified")),
|
||||
("--mapping <mapping>", ("端口映射,例如 --mapping udp:0.0.0.0:80-domain:80 映射目标是本地路由能访问的设备", "Port mapping, e.g., --mapping udp:0.0.0.0:80-domain:80 maps to a device accessible by local routing")),
|
||||
("--compressor-all <lz4>", ("启用压缩,可选值lz4/zstd<,level>,level为压缩级别,例如 --compressor lz4 或--compressor zstd,10", "Enable compression, options lz4/zstd<,level>, level is compression level, e.g., --compressor lz4 or --compressor zstd,10")),
|
||||
("--compressor-lz4 <lz4>", ("启用压缩,可选值lz4,例如 --compressor lz4", "Enable compression, option lz4, e.g., --compressor lz4")),
|
||||
("--compressor-zstd <zstd>", ("启用压缩,可选值zstd<,level>,level为压缩级别,例如 --compressor zstd,10", "Enable compression, options zstd<,level>, level is compression level, e.g., --compressor zstd,10")),
|
||||
("--vnt-mapping <x>", ("vnt地址映射,例如 --vnt-mapping tcp:80-10.26.0.10:80 映射目标是vnt网络或其子网中的设备", "VNT address mapping, e.g., --vnt-mapping tcp:80-10.26.0.10:80 maps to a device in VNT network or its subnet")),
|
||||
("--local-dev", ("本地出口网卡的名称", "name of local export network card")),
|
||||
("--disable-stats", ("关闭流量统计", "Disable traffic statistics")),
|
||||
("--allow-wg", ("允许接入WireGuard客户端", "Allow access to WireGuard client")),
|
||||
("--list", ("后台运行时,查看其他设备列表", "View list of other devices when running in background")),
|
||||
("--all", ("后台运行时,查看其他设备完整信息", "View complete information of other devices when running in background")),
|
||||
("--info", ("后台运行时,查看当前设备信息", "View information of current device when running in background")),
|
||||
("--route", ("后台运行时,查看数据转发路径", "View data forwarding path when running in background")),
|
||||
("--chart_a", ("后台运行时,查看所有IP的流量统计", "View traffic statistics of all IPs when running in background")),
|
||||
("--chart_b <IP>", ("后台运行时,查看单个IP的历史流量", "View historical traffic of a single IP when running in background")),
|
||||
("--stop", ("停止后台运行", "Stop running in background"))
|
||||
// ... 其他选项
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
if let Some(&(zh, en)) = descriptions.get(key) {
|
||||
if language.starts_with("zh") {
|
||||
return zh.to_string(); // 返回 String 类型
|
||||
}
|
||||
// 默认返回英文
|
||||
return en.to_string(); // 返回 String 类型
|
||||
}
|
||||
// 如果没有找到对应的键,则返回空字符串
|
||||
String::new()
|
||||
}
|
||||
|
||||
fn print_usage(program: &str, _opts: Options) {
|
||||
// 获取系统语言 Locale::user_default().unwrap_or_else(|_| Locale::default());
|
||||
let language = get_locale().unwrap_or_else(|| String::from("en-US"));
|
||||
println!("Usage: {} [options]", program);
|
||||
println!("version:{}", vnt::VNT_VERSION);
|
||||
println!("Serial:{}", generated_serial_number::SERIAL_NUMBER);
|
||||
println!("Options:");
|
||||
println!(
|
||||
" -k <token> {}",
|
||||
green(get_description("-k <token>", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" -n <name> {}",
|
||||
get_description("-n <name>", &language)
|
||||
);
|
||||
println!(
|
||||
" -d <id> {}",
|
||||
get_description("-d <id>", &language)
|
||||
);
|
||||
println!(
|
||||
" -s <server> {}",
|
||||
get_description("-s <server>", &language)
|
||||
);
|
||||
println!(
|
||||
" -e <stun-server> {}",
|
||||
get_description("-e <stun-server>", &language)
|
||||
);
|
||||
|
||||
println!(
|
||||
" -i <in-ip> {}",
|
||||
get_description("-i <in-ip>", &language)
|
||||
);
|
||||
println!(
|
||||
" -o <out-ip> {}",
|
||||
get_description("-o <out-ip>", &language)
|
||||
);
|
||||
println!(
|
||||
" -w <password> {}",
|
||||
get_description("-w <password>", &language)
|
||||
);
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
println!(" -W {}", get_description("-W", &language));
|
||||
println!(
|
||||
" -u <mtu> {}",
|
||||
get_description("-u <mtu>", &language)
|
||||
);
|
||||
#[cfg(feature = "file_config")]
|
||||
println!(
|
||||
" -f <conf_file> {}",
|
||||
get_description("-f <conf_file>", &language)
|
||||
);
|
||||
|
||||
println!(
|
||||
" --ip <ip> {}",
|
||||
get_description("--ip <ip>", &language)
|
||||
);
|
||||
let mut enums = String::new();
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
enums.push_str("/aes_gcm");
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
enums.push_str("/chacha20_poly1305/chacha20");
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
enums.push_str("/aes_cbc");
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
enums.push_str("/aes_ecb");
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
enums.push_str("/sm4_cbc");
|
||||
enums.push_str("/xor");
|
||||
println!(
|
||||
" --model <model> {}{}",
|
||||
get_description("--model <model>", &language),
|
||||
&enums[1..]
|
||||
);
|
||||
#[cfg(any(
|
||||
feature = "aes_gcm",
|
||||
feature = "chacha20_poly1305",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
))]
|
||||
println!(
|
||||
" --finger {}",
|
||||
get_description("--finger", &language)
|
||||
);
|
||||
println!(
|
||||
" --punch <punch> {}",
|
||||
get_description("--punch <punch>", &language)
|
||||
);
|
||||
println!(
|
||||
" --ports <port,port> {}",
|
||||
get_description("--ports <port,port>", &language)
|
||||
);
|
||||
#[cfg(feature = "command")]
|
||||
println!(
|
||||
" --cmd {}",
|
||||
get_description("--cmd", &language)
|
||||
);
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
println!(
|
||||
" --no-proxy {}",
|
||||
get_description("--no-proxy", &language)
|
||||
);
|
||||
println!(
|
||||
" --first-latency {}",
|
||||
get_description("--first-latency", &language)
|
||||
);
|
||||
println!(
|
||||
" --use-channel <p2p> {}",
|
||||
get_description("--use-channel <p2p>", &language)
|
||||
);
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
println!(
|
||||
" --nic <tun0> {}",
|
||||
get_description("--nic <tun0>", &language)
|
||||
);
|
||||
println!(
|
||||
" --packet-loss <0> {}",
|
||||
get_description("--packet-loss <0>", &language)
|
||||
);
|
||||
println!(
|
||||
" --packet-delay <0> {}",
|
||||
get_description("--packet-delay <0>", &language)
|
||||
);
|
||||
println!(
|
||||
" --dns <host:port> {}",
|
||||
get_description("--dns <host:port>", &language)
|
||||
);
|
||||
|
||||
#[cfg(feature = "port_mapping")]
|
||||
println!(
|
||||
" --mapping <mapping> {}",
|
||||
get_description("--mapping <mapping>", &language)
|
||||
);
|
||||
|
||||
#[cfg(all(feature = "lz4", feature = "zstd"))]
|
||||
println!(
|
||||
" --compressor <lz4> {}",
|
||||
get_description("--compressor-all <lz4>", &language)
|
||||
);
|
||||
#[cfg(feature = "lz4")]
|
||||
#[cfg(not(feature = "zstd"))]
|
||||
println!(
|
||||
" --compressor <lz4> {}",
|
||||
get_description("--compressor-lz4 <lz4>", &language)
|
||||
);
|
||||
#[cfg(feature = "zstd")]
|
||||
#[cfg(not(feature = "lz4"))]
|
||||
println!(
|
||||
" --compressor <zstd> {}",
|
||||
get_description("--compressor-zstd <zstd>", &language)
|
||||
);
|
||||
|
||||
#[cfg(not(feature = "integrated_tun"))]
|
||||
println!(
|
||||
" --vnt-mapping <x> {}",
|
||||
green(get_description("--vnt-mapping <x>", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --local-dev <NAME> {}",
|
||||
get_description("--local-dev", &language)
|
||||
);
|
||||
println!(
|
||||
" --disable-stats {}",
|
||||
get_description("--disable-stats", &language)
|
||||
);
|
||||
println!(
|
||||
" --allow-wg {}",
|
||||
get_description("--allow-wg", &language)
|
||||
);
|
||||
println!();
|
||||
#[cfg(feature = "command")]
|
||||
{
|
||||
// #[cfg(not(feature = "integrated_tun"))]
|
||||
// println!(
|
||||
// " --add {}",
|
||||
// yellow("后台运行时,添加VNT地址映射 用法同'--vnt-mapping'".to_string())
|
||||
// );
|
||||
println!(
|
||||
" --list {}",
|
||||
yellow(get_description("--list", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --all {}",
|
||||
yellow(get_description("--all", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --info {}",
|
||||
yellow(get_description("--info", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --route {}",
|
||||
yellow(get_description("--route", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --chart_a {}",
|
||||
yellow(get_description("--chart_a", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --chart_b <IP> {}",
|
||||
yellow(get_description("--chart_b <IP>", &language).to_string())
|
||||
);
|
||||
println!(
|
||||
" --stop {}",
|
||||
yellow(get_description("--stop", &language).to_string())
|
||||
);
|
||||
}
|
||||
println!(" -h, --help display help information(显示帮助信息)");
|
||||
}
|
||||
|
||||
fn green(str: String) -> impl std::fmt::Display {
|
||||
style(str).green()
|
||||
}
|
||||
|
||||
#[cfg(feature = "command")]
|
||||
fn yellow(str: String) -> impl std::fmt::Display {
|
||||
style(str).yellow()
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
use serde::Deserialize;
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::command::entity::{ChartA, ChartB, DeviceItem, Info, RouteItem};
|
||||
|
||||
pub struct CommandClient {
|
||||
buf: Vec<u8>,
|
||||
udp: UdpSocket,
|
||||
}
|
||||
|
||||
impl CommandClient {
|
||||
pub fn new() -> io::Result<Self> {
|
||||
let port = read_command_port().unwrap_or_else(|e| {
|
||||
log::warn!("read_command_port:{:?}", e);
|
||||
39271
|
||||
});
|
||||
let udp = UdpSocket::bind("127.0.0.1:0")?;
|
||||
udp.set_read_timeout(Some(Duration::from_secs(5)))?;
|
||||
udp.connect(SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::new(127, 0, 0, 1),
|
||||
port,
|
||||
)))?;
|
||||
Ok(Self {
|
||||
udp,
|
||||
buf: vec![0; 65536 * 8],
|
||||
})
|
||||
}
|
||||
}
|
||||
fn read_command_port() -> io::Result<u16> {
|
||||
let path_buf = crate::cli::app_home()?.join("command-port");
|
||||
let port = std::fs::read_to_string(path_buf)?;
|
||||
match u16::from_str(&port) {
|
||||
Ok(port) => Ok(port),
|
||||
Err(_) => {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"'command-port' file error",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandClient {
|
||||
pub fn list(&mut self) -> io::Result<Vec<DeviceItem>> {
|
||||
self.send_cmd(b"list")
|
||||
}
|
||||
pub fn route(&mut self) -> io::Result<Vec<RouteItem>> {
|
||||
self.send_cmd(b"route")
|
||||
}
|
||||
pub fn info(&mut self) -> io::Result<Info> {
|
||||
self.send_cmd(b"info")
|
||||
}
|
||||
pub fn chart_a(&mut self) -> io::Result<ChartA> {
|
||||
self.send_cmd(b"chart_a")
|
||||
}
|
||||
pub fn chart_b(&mut self, input: &str) -> io::Result<ChartB> {
|
||||
let cmd = if input.is_empty() {
|
||||
"chart_b".to_string()
|
||||
} else {
|
||||
format!("chart_b:{}", input)
|
||||
};
|
||||
self.send_cmd(cmd.as_bytes())
|
||||
}
|
||||
fn send_cmd<'a, V: Deserialize<'a>>(&'a mut self, cmd: &[u8]) -> io::Result<V> {
|
||||
self.udp.send(cmd)?;
|
||||
let len = self.udp.recv(&mut self.buf)?;
|
||||
match serde_yaml::from_slice::<V>(&self.buf[..len]) {
|
||||
Ok(val) => Ok(val),
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"send_cmd {:?} {:?},{:?}",
|
||||
std::str::from_utf8(cmd),
|
||||
std::str::from_utf8(&self.buf[..len]),
|
||||
e
|
||||
);
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("data error {:?} buf_len={}", e, len),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn stop(&self) -> io::Result<String> {
|
||||
self.udp.send(b"stop")?;
|
||||
let mut buf = [0; 10240];
|
||||
let len = self.udp.recv(&mut buf)?;
|
||||
Ok(String::from_utf8(buf[..len].to_vec()).unwrap())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Info {
|
||||
pub name: String,
|
||||
pub virtual_ip: String,
|
||||
pub virtual_gateway: String,
|
||||
pub virtual_netmask: String,
|
||||
pub connect_status: String,
|
||||
pub relay_server: String,
|
||||
pub nat_type: String,
|
||||
pub public_ips: String,
|
||||
pub local_addr: String,
|
||||
pub ipv6_addr: String,
|
||||
pub port_mapping_list: Vec<(bool, SocketAddr, String)>,
|
||||
pub in_ips: Vec<(u32, u32, Ipv4Addr)>,
|
||||
pub out_ips: Vec<(u32, u32)>,
|
||||
pub udp_listen_addr: Vec<String>,
|
||||
pub tcp_listen_addr: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct RouteItem {
|
||||
pub destination: String,
|
||||
pub next_hop: String,
|
||||
pub metric: String,
|
||||
pub rt: String,
|
||||
pub interface: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct DeviceItem {
|
||||
pub name: String,
|
||||
pub virtual_ip: String,
|
||||
pub nat_type: String,
|
||||
pub public_ips: String,
|
||||
pub local_ip: String,
|
||||
pub ipv6: String,
|
||||
pub nat_traversal_type: String,
|
||||
pub rt: String,
|
||||
pub status: String,
|
||||
pub client_secret: bool,
|
||||
pub client_secret_hash: Vec<u8>,
|
||||
pub current_client_secret: bool,
|
||||
pub current_client_secret_hash: Vec<u8>,
|
||||
pub wire_guard: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
pub struct ChartA {
|
||||
pub disable_stats: bool,
|
||||
pub up_total: u64,
|
||||
pub down_total: u64,
|
||||
pub up_map: HashMap<Ipv4Addr, u64>,
|
||||
pub down_map: HashMap<Ipv4Addr, u64>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
pub struct ChartB {
|
||||
pub disable_stats: bool,
|
||||
pub ip: Option<Ipv4Addr>,
|
||||
pub up_total: u64,
|
||||
pub up_list: Vec<usize>,
|
||||
pub down_total: u64,
|
||||
pub down_list: Vec<usize>,
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
use std::collections::HashSet;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use vnt::channel::ConnectProtocol;
|
||||
use vnt::core::Vnt;
|
||||
|
||||
use crate::command::entity::{ChartA, ChartB, DeviceItem, Info, RouteItem};
|
||||
use crate::console_out;
|
||||
|
||||
pub mod client;
|
||||
pub mod entity;
|
||||
pub mod server;
|
||||
|
||||
pub enum CommandEnum {
|
||||
Route,
|
||||
List,
|
||||
All,
|
||||
Info,
|
||||
ChartA,
|
||||
ChartB(String),
|
||||
Stop,
|
||||
}
|
||||
|
||||
pub fn command_str(cmd: &str, vnt: &Vnt) -> bool {
|
||||
if cmd.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let cmd = cmd.to_lowercase();
|
||||
let cmd = cmd.trim();
|
||||
match cmd {
|
||||
"list" => {
|
||||
let list = command_list(&vnt);
|
||||
console_out::console_device_list(list);
|
||||
}
|
||||
"info" => {
|
||||
let info = command_info(&vnt);
|
||||
console_out::console_info(info);
|
||||
}
|
||||
"route" => {
|
||||
let route = command_route(&vnt);
|
||||
console_out::console_route_table(route);
|
||||
}
|
||||
"all" => {
|
||||
let list = command_list(&vnt);
|
||||
console_out::console_device_list_all(list);
|
||||
}
|
||||
"chart_a" => {
|
||||
let chart = command_chart_a(&vnt);
|
||||
console_out::console_chart_a(chart);
|
||||
}
|
||||
"stop" => {
|
||||
let _ = vnt.stop();
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some(ip) = cmd.strip_prefix("chart_b") {
|
||||
let chart = if ip.is_empty() {
|
||||
command_chart_b(&vnt, &vnt.current_device().virtual_gateway.to_string())
|
||||
} else {
|
||||
command_chart_b(&vnt, &ip[1..])
|
||||
};
|
||||
console_out::console_chart_b(chart);
|
||||
}
|
||||
println!();
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn command(cmd: CommandEnum) {
|
||||
if let Err(e) = command_(cmd) {
|
||||
println!("cmd: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn command_(cmd: CommandEnum) -> io::Result<()> {
|
||||
let mut command_client = client::CommandClient::new()?;
|
||||
match cmd {
|
||||
CommandEnum::Route => {
|
||||
let list = command_client.route()?;
|
||||
console_out::console_route_table(list);
|
||||
}
|
||||
CommandEnum::List => {
|
||||
let list = command_client.list()?;
|
||||
console_out::console_device_list(list);
|
||||
}
|
||||
CommandEnum::All => {
|
||||
let list = command_client.list()?;
|
||||
console_out::console_device_list_all(list);
|
||||
}
|
||||
CommandEnum::Info => {
|
||||
let info = command_client.info()?;
|
||||
console_out::console_info(info);
|
||||
}
|
||||
CommandEnum::ChartA => {
|
||||
let chart = command_client.chart_a()?;
|
||||
console_out::console_chart_a(chart);
|
||||
}
|
||||
CommandEnum::ChartB(input) => {
|
||||
let chart = command_client.chart_b(&input)?;
|
||||
console_out::console_chart_b(chart);
|
||||
}
|
||||
CommandEnum::Stop => {
|
||||
command_client.stop()?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn command_route(vnt: &Vnt) -> Vec<RouteItem> {
|
||||
let route_table = vnt.route_table();
|
||||
let server_addr = vnt.config().server_address_str.clone();
|
||||
let mut route_list = Vec::with_capacity(route_table.len());
|
||||
for (destination, routes) in route_table {
|
||||
for route in routes {
|
||||
let next_hop = vnt
|
||||
.route_key(&route.route_key())
|
||||
.map_or(String::new(), |v| v.to_string());
|
||||
let metric = route.metric.to_string();
|
||||
let rt = if route.rt < 0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
route.rt.to_string()
|
||||
};
|
||||
let interface = match route.protocol {
|
||||
ConnectProtocol::UDP => route.addr.to_string(),
|
||||
ConnectProtocol::TCP => {
|
||||
format!("tcp@{}", route.addr)
|
||||
}
|
||||
ConnectProtocol::WS | ConnectProtocol::WSS => server_addr.clone(),
|
||||
};
|
||||
|
||||
let item = RouteItem {
|
||||
destination: destination.to_string(),
|
||||
next_hop,
|
||||
metric,
|
||||
rt,
|
||||
interface,
|
||||
};
|
||||
route_list.push(item);
|
||||
}
|
||||
}
|
||||
route_list
|
||||
}
|
||||
|
||||
pub fn command_list(vnt: &Vnt) -> Vec<DeviceItem> {
|
||||
let info = vnt.current_device();
|
||||
let device_list = vnt.device_list();
|
||||
let mut list = Vec::new();
|
||||
let current_client_secret = vnt.client_encrypt();
|
||||
let client_encrypt_hash = vnt.client_encrypt_hash().unwrap_or(&[]);
|
||||
for peer in device_list {
|
||||
let name = peer.name;
|
||||
let virtual_ip = peer.virtual_ip.to_string();
|
||||
let (nat_type, public_ips, local_ip, ipv6) =
|
||||
if let Some(nat_info) = vnt.peer_nat_info(&peer.virtual_ip) {
|
||||
let nat_type = format!("{:?}", nat_info.nat_type);
|
||||
let public_ips: Vec<String> =
|
||||
nat_info.public_ips.iter().map(|v| v.to_string()).collect();
|
||||
let public_ips = public_ips.join(",");
|
||||
let local_ip = nat_info
|
||||
.local_ipv4()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or("None".to_string());
|
||||
let ipv6 = nat_info
|
||||
.ipv6()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or("None".to_string());
|
||||
(nat_type, public_ips, local_ip, ipv6)
|
||||
} else {
|
||||
(
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
"".to_string(),
|
||||
)
|
||||
};
|
||||
let (nat_traversal_type, rt) = if let Some(route) = vnt.route(&peer.virtual_ip) {
|
||||
let nat_traversal_type = if route.metric == 1 {
|
||||
if route.protocol.is_base_tcp() {
|
||||
"tcp-p2p"
|
||||
} else {
|
||||
"p2p"
|
||||
}
|
||||
} else {
|
||||
let next_hop = vnt.route_key(&route.route_key());
|
||||
if let Some(next_hop) = next_hop {
|
||||
if info.is_gateway(&next_hop) {
|
||||
"server-relay"
|
||||
} else {
|
||||
"client-relay"
|
||||
}
|
||||
} else {
|
||||
"server-relay"
|
||||
}
|
||||
}
|
||||
.to_string();
|
||||
let rt = if route.rt < 0 {
|
||||
"".to_string()
|
||||
} else {
|
||||
route.rt.to_string()
|
||||
};
|
||||
(nat_traversal_type, rt)
|
||||
} else {
|
||||
("relay".to_string(), "".to_string())
|
||||
};
|
||||
let status = format!("{:?}", peer.status);
|
||||
let client_secret = peer.client_secret;
|
||||
let item = DeviceItem {
|
||||
name,
|
||||
virtual_ip,
|
||||
nat_type,
|
||||
public_ips,
|
||||
local_ip,
|
||||
ipv6,
|
||||
nat_traversal_type,
|
||||
rt,
|
||||
status,
|
||||
client_secret,
|
||||
client_secret_hash: peer.client_secret_hash,
|
||||
current_client_secret,
|
||||
current_client_secret_hash: client_encrypt_hash.to_vec(),
|
||||
wire_guard: peer.wireguard,
|
||||
};
|
||||
list.push(item);
|
||||
}
|
||||
list
|
||||
}
|
||||
|
||||
pub fn command_info(vnt: &Vnt) -> Info {
|
||||
let config = vnt.config();
|
||||
let current_device = vnt.current_device();
|
||||
let nat_info = vnt.nat_info();
|
||||
let name = vnt.name().to_string();
|
||||
let virtual_ip = current_device.virtual_ip().to_string();
|
||||
let virtual_gateway = current_device.virtual_gateway().to_string();
|
||||
let virtual_netmask = current_device.virtual_netmask.to_string();
|
||||
let connect_status = format!("{:?}", vnt.connection_status());
|
||||
let relay_server = if current_device.connect_server.port() == 0 {
|
||||
config.server_address_str.clone()
|
||||
} else {
|
||||
current_device.connect_server.to_string()
|
||||
};
|
||||
let nat_type = format!("{:?}", nat_info.nat_type);
|
||||
let public_ips: Vec<String> = nat_info.public_ips.iter().map(|v| v.to_string()).collect();
|
||||
let public_ips = public_ips.join(",");
|
||||
let local_addr = nat_info
|
||||
.local_ipv4()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or("None".to_string());
|
||||
let ipv6_addr = nat_info
|
||||
.ipv6()
|
||||
.map(|v| v.to_string())
|
||||
.unwrap_or("None".to_string());
|
||||
#[cfg(feature = "port_mapping")]
|
||||
let port_mapping_list = vnt.config().port_mapping_list.clone();
|
||||
#[cfg(not(feature = "port_mapping"))]
|
||||
let port_mapping_list = vec![];
|
||||
let in_ips = vnt.config().in_ips.clone();
|
||||
let out_ips = vnt.config().out_ips.clone();
|
||||
let udp_listen_addr = nat_info
|
||||
.udp_ports
|
||||
.iter()
|
||||
.map(|port| format!("0.0.0.0:{}", port))
|
||||
.collect();
|
||||
let tcp_listen_addr = format!("0.0.0.0:{}", nat_info.tcp_port);
|
||||
Info {
|
||||
name,
|
||||
virtual_ip,
|
||||
virtual_gateway,
|
||||
virtual_netmask,
|
||||
connect_status,
|
||||
relay_server,
|
||||
nat_type,
|
||||
public_ips,
|
||||
local_addr,
|
||||
ipv6_addr,
|
||||
port_mapping_list,
|
||||
in_ips,
|
||||
out_ips,
|
||||
udp_listen_addr,
|
||||
tcp_listen_addr,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command_chart_a(vnt: &Vnt) -> ChartA {
|
||||
let disable_stats = !vnt.config().enable_traffic;
|
||||
if disable_stats {
|
||||
let mut chart = ChartA::default();
|
||||
chart.disable_stats = true;
|
||||
return chart;
|
||||
}
|
||||
let (up_total, up_map) = vnt.up_stream_all().unwrap_or_default();
|
||||
let (down_total, down_map) = vnt.down_stream_all().unwrap_or_default();
|
||||
ChartA {
|
||||
disable_stats,
|
||||
up_total,
|
||||
down_total,
|
||||
up_map,
|
||||
down_map,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn command_chart_b(vnt: &Vnt, input_str: &str) -> ChartB {
|
||||
let disable_stats = !vnt.config().enable_traffic;
|
||||
if disable_stats {
|
||||
let mut chart = ChartB::default();
|
||||
chart.disable_stats = true;
|
||||
return chart;
|
||||
}
|
||||
let (_, up_map) = vnt.up_stream_history().unwrap_or_default();
|
||||
let (_, down_map) = vnt.down_stream_history().unwrap_or_default();
|
||||
let up_keys: HashSet<_> = up_map.keys().cloned().collect();
|
||||
let down_keys: HashSet<_> = down_map.keys().cloned().collect();
|
||||
let mut keys: Vec<Ipv4Addr> = up_keys.union(&down_keys).cloned().collect();
|
||||
keys.sort();
|
||||
if let Some(ip) = find_matching_ipv4_address(input_str, &keys) {
|
||||
let (up_total, up_list) = up_map.get(&ip).cloned().unwrap_or_default();
|
||||
let (down_total, down_list) = down_map.get(&ip).cloned().unwrap_or_default();
|
||||
ChartB {
|
||||
disable_stats,
|
||||
ip: Some(ip),
|
||||
up_total,
|
||||
up_list,
|
||||
down_total,
|
||||
down_list,
|
||||
}
|
||||
} else {
|
||||
ChartB::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn match_from_end(input_str: &str, ip: &str) -> bool {
|
||||
let mut input_chars = input_str.chars().rev();
|
||||
let mut ip_chars = ip.chars().rev();
|
||||
|
||||
while let (Some(ic), Some(pc)) = (input_chars.next(), ip_chars.next()) {
|
||||
if ic != pc {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
input_chars.next().is_none() // Ensure all input characters matched
|
||||
}
|
||||
|
||||
fn find_matching_ipv4_address(input_str: &str, ip_addresses: &[Ipv4Addr]) -> Option<Ipv4Addr> {
|
||||
for &ip in ip_addresses {
|
||||
let ip_str = ip.to_string();
|
||||
if match_from_end(input_str, &ip_str) {
|
||||
return Some(ip);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use crate::command::command_chart_b;
|
||||
use std::io;
|
||||
use std::io::Write;
|
||||
use std::net::UdpSocket;
|
||||
use vnt::core::Vnt;
|
||||
|
||||
pub struct CommandServer {}
|
||||
|
||||
impl CommandServer {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandServer {
|
||||
pub fn start(self, vnt: Vnt) -> io::Result<()> {
|
||||
let udp = if let Ok(udp) = UdpSocket::bind("127.0.0.1:39271") {
|
||||
udp
|
||||
} else {
|
||||
UdpSocket::bind("127.0.0.1:0")?
|
||||
};
|
||||
let addr = udp.local_addr()?;
|
||||
log::info!("启动后台cmd:{:?}", addr);
|
||||
if let Err(e) = save_port(addr.port()) {
|
||||
log::warn!("保存后台命令端口失败:{:?}", e);
|
||||
}
|
||||
|
||||
let mut buf = [0u8; 64];
|
||||
loop {
|
||||
let (len, addr) = udp.recv_from(&mut buf)?;
|
||||
match std::str::from_utf8(&buf[..len]) {
|
||||
Ok(cmd) => {
|
||||
if let Ok(out) = command(cmd, &vnt) {
|
||||
if let Err(e) = udp.send_to(out.as_bytes(), addr) {
|
||||
log::warn!("cmd={},err={:?}", cmd, e);
|
||||
}
|
||||
if "stopped" == &out {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
fn save_port(port: u16) -> io::Result<()> {
|
||||
let path_buf = crate::cli::app_home()?.join("command-port");
|
||||
let mut file = std::fs::File::create(path_buf)?;
|
||||
file.write_all(port.to_string().as_bytes())?;
|
||||
file.sync_all()
|
||||
}
|
||||
|
||||
fn command(cmd: &str, vnt: &Vnt) -> io::Result<String> {
|
||||
let cmd = cmd.trim();
|
||||
let out_str = match cmd {
|
||||
"route" => serde_yaml::to_string(&crate::command::command_route(vnt))
|
||||
.unwrap_or_else(|e| format!("error {:?}", e)),
|
||||
"list" => serde_yaml::to_string(&crate::command::command_list(vnt))
|
||||
.unwrap_or_else(|e| format!("error {:?}", e)),
|
||||
"info" => serde_yaml::to_string(&crate::command::command_info(vnt))
|
||||
.unwrap_or_else(|e| format!("error {:?}", e)),
|
||||
"chart_a" => serde_yaml::to_string(&crate::command::command_chart_a(vnt))
|
||||
.unwrap_or_else(|e| format!("error {:?}", e)),
|
||||
"stop" => {
|
||||
vnt.stop();
|
||||
"stopped".to_string()
|
||||
}
|
||||
_ => {
|
||||
if let Some(ip) = cmd.strip_prefix("chart_b") {
|
||||
let chart = if ip.is_empty() {
|
||||
command_chart_b(&vnt, &vnt.current_device().virtual_gateway.to_string())
|
||||
} else {
|
||||
command_chart_b(&vnt, &ip[1..])
|
||||
};
|
||||
serde_yaml::to_string(&chart).unwrap_or_else(|e| format!("error {:?}", e))
|
||||
} else {
|
||||
format!(
|
||||
"command '{}' not found. Try to enter: 'route'/'list'/'stop' \n",
|
||||
cmd
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(out_str)
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
use anyhow::anyhow;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::config::get_device_id;
|
||||
use crate::{args_parse, config};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vnt::channel::punch::PunchModel;
|
||||
use vnt::channel::UseChannelType;
|
||||
use vnt::cipher::CipherModel;
|
||||
use vnt::compression::Compressor;
|
||||
use vnt::core::Config;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(default)]
|
||||
pub struct FileConfig {
|
||||
#[cfg(target_os = "windows")]
|
||||
pub tap: bool,
|
||||
pub token: String,
|
||||
pub device_id: String,
|
||||
pub name: String,
|
||||
pub server_address: String,
|
||||
pub stun_server: Vec<String>,
|
||||
pub dns: Vec<String>,
|
||||
pub in_ips: Vec<String>,
|
||||
pub out_ips: Vec<String>,
|
||||
pub password: Option<String>,
|
||||
pub mtu: Option<u32>,
|
||||
pub tcp: bool,
|
||||
pub ip: Option<String>,
|
||||
pub use_channel: String,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
pub no_proxy: bool,
|
||||
pub server_encrypt: bool,
|
||||
pub cipher_model: Option<String>,
|
||||
pub finger: bool,
|
||||
pub punch_model: String,
|
||||
pub ports: Option<Vec<u16>>,
|
||||
pub cmd: bool,
|
||||
pub first_latency: bool,
|
||||
pub device_name: Option<String>,
|
||||
pub packet_loss: Option<f64>,
|
||||
pub packet_delay: u32,
|
||||
#[cfg(feature = "port_mapping")]
|
||||
pub mapping: Vec<String>,
|
||||
pub compressor: Option<String>,
|
||||
pub vnt_mapping: Vec<String>,
|
||||
pub disable_stats: bool,
|
||||
// 允许传递wg流量
|
||||
pub allow_wire_guard: bool,
|
||||
pub local_dev: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for FileConfig {
|
||||
fn default() -> Self {
|
||||
let mut stun_server = Vec::new();
|
||||
for x in config::PUB_STUN {
|
||||
stun_server.push(x.to_string());
|
||||
}
|
||||
Self {
|
||||
#[cfg(target_os = "windows")]
|
||||
tap: false,
|
||||
token: "".to_string(),
|
||||
device_id: get_device_id(),
|
||||
name: gethostname::gethostname()
|
||||
.to_str()
|
||||
.unwrap_or("UnknownName")
|
||||
.to_string(),
|
||||
server_address: "nat1.wherewego.top:29872".to_string(),
|
||||
stun_server,
|
||||
dns: vec![],
|
||||
in_ips: vec![],
|
||||
out_ips: vec![],
|
||||
password: None,
|
||||
mtu: None,
|
||||
tcp: false,
|
||||
ip: None,
|
||||
use_channel: "all".to_string(),
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
no_proxy: false,
|
||||
server_encrypt: false,
|
||||
cipher_model: None,
|
||||
finger: false,
|
||||
punch_model: "all".to_string(),
|
||||
ports: None,
|
||||
cmd: false,
|
||||
first_latency: false,
|
||||
device_name: None,
|
||||
packet_loss: None,
|
||||
packet_delay: 0,
|
||||
#[cfg(feature = "port_mapping")]
|
||||
mapping: vec![],
|
||||
compressor: None,
|
||||
vnt_mapping: vec![],
|
||||
disable_stats: false,
|
||||
allow_wire_guard: false,
|
||||
local_dev: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_config(file_path: &str) -> anyhow::Result<(Config, Vec<String>, bool)> {
|
||||
let conf = std::fs::read_to_string(file_path)?;
|
||||
let file_conf = match serde_yaml::from_str::<FileConfig>(&conf) {
|
||||
Ok(val) => val,
|
||||
Err(e) => {
|
||||
log::error!("serde_yaml::from_str {:?}", e);
|
||||
return Err(anyhow!("serde_yaml::from_str {:?}", e));
|
||||
}
|
||||
};
|
||||
if file_conf.token.is_empty() {
|
||||
return Err(anyhow!("token is_empty"));
|
||||
}
|
||||
|
||||
let in_ips = match args_parse::ips_parse(&file_conf.in_ips) {
|
||||
Ok(in_ips) => in_ips,
|
||||
Err(e) => {
|
||||
return Err(anyhow!("in_ips {:?} error:{}", &file_conf.in_ips, e));
|
||||
}
|
||||
};
|
||||
let out_ips = match args_parse::out_ips_parse(&file_conf.out_ips) {
|
||||
Ok(out_ips) => out_ips,
|
||||
Err(e) => {
|
||||
return Err(anyhow!("out_ips {:?} error:{}", &file_conf.out_ips, e));
|
||||
}
|
||||
};
|
||||
let virtual_ip = match file_conf.ip.clone().map(|v| Ipv4Addr::from_str(&v)) {
|
||||
None => None,
|
||||
Some(r) => Some(r.map_err(|e| anyhow!("ip {:?} error:{}", &file_conf.ip, e))?),
|
||||
};
|
||||
let cipher_model = if let Some(v) = file_conf.cipher_model {
|
||||
CipherModel::from_str(&v).map_err(|e| anyhow!("{}", e))?
|
||||
} else {
|
||||
#[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))]
|
||||
if file_conf.password.is_some() {
|
||||
Err(anyhow!("cipher_model undefined"))?
|
||||
} else {
|
||||
CipherModel::None
|
||||
}
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
CipherModel::AesGcm
|
||||
};
|
||||
|
||||
let punch_model = PunchModel::from_str(&file_conf.punch_model).map_err(|e| anyhow!("{}", e))?;
|
||||
let use_channel_type =
|
||||
UseChannelType::from_str(&file_conf.use_channel).map_err(|e| anyhow!("{}", e))?;
|
||||
let compressor = if let Some(compressor) = file_conf.compressor.as_ref() {
|
||||
Compressor::from_str(compressor).map_err(|e| anyhow!("{}", e))?
|
||||
} else {
|
||||
Compressor::None
|
||||
};
|
||||
let config = Config::new(
|
||||
#[cfg(target_os = "windows")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
file_conf.tap,
|
||||
file_conf.token,
|
||||
file_conf.device_id,
|
||||
file_conf.name,
|
||||
file_conf.server_address,
|
||||
file_conf.dns,
|
||||
file_conf.stun_server,
|
||||
in_ips,
|
||||
out_ips,
|
||||
file_conf.password,
|
||||
file_conf.mtu,
|
||||
virtual_ip,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
file_conf.no_proxy,
|
||||
file_conf.server_encrypt,
|
||||
cipher_model,
|
||||
file_conf.finger,
|
||||
punch_model,
|
||||
file_conf.ports,
|
||||
file_conf.first_latency,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
file_conf.device_name,
|
||||
use_channel_type,
|
||||
file_conf.packet_loss,
|
||||
file_conf.packet_delay,
|
||||
#[cfg(feature = "port_mapping")]
|
||||
file_conf.mapping,
|
||||
compressor,
|
||||
!file_conf.disable_stats,
|
||||
file_conf.allow_wire_guard,
|
||||
file_conf.local_dev,
|
||||
)?;
|
||||
|
||||
Ok((config, file_conf.vnt_mapping, file_conf.cmd))
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
pub const PUB_STUN: [&'static str; 4] = [
|
||||
"stun.miwifi.com",
|
||||
"stun.chat.bilibili.com",
|
||||
"stun.hitv.com",
|
||||
"stun.cdnbye.com",
|
||||
];
|
||||
#[cfg(feature = "file_config")]
|
||||
mod file_config;
|
||||
|
||||
use crate::identifier;
|
||||
#[cfg(feature = "file_config")]
|
||||
pub use file_config::read_config;
|
||||
|
||||
#[cfg(not(feature = "file_config"))]
|
||||
pub fn read_config(_file_path: &str) -> anyhow::Result<(vnt::core::Config, Vec<String>, bool)> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub fn get_device_id() -> String {
|
||||
if let Some(id) = identifier::get_unique_identifier() {
|
||||
id
|
||||
} else {
|
||||
let path_buf = match crate::cli::app_home() {
|
||||
Ok(path_buf) => path_buf.join("device-id"),
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
if let Ok(id) = std::fs::read_to_string(path_buf.as_path()) {
|
||||
id
|
||||
} else {
|
||||
let id = uuid::Uuid::new_v4().to_string();
|
||||
let _ = std::fs::write(path_buf, &id);
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
use console::{style, Style};
|
||||
use std::collections::HashSet;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::command::entity::{ChartA, ChartB, DeviceItem, Info, RouteItem};
|
||||
|
||||
pub mod table;
|
||||
|
||||
pub fn console_info(status: Info) {
|
||||
println!("Name: {}", style(status.name).green());
|
||||
println!("Virtual ip: {}", style(status.virtual_ip).green());
|
||||
println!("Virtual gateway: {}", style(status.virtual_gateway).green());
|
||||
println!("Virtual netmask: {}", style(status.virtual_netmask).green());
|
||||
if status.connect_status.eq_ignore_ascii_case("Connected") {
|
||||
println!(
|
||||
"Connection status: {}",
|
||||
style(status.connect_status).green()
|
||||
);
|
||||
} else {
|
||||
println!("Connection status: {}", style(status.connect_status).red());
|
||||
}
|
||||
|
||||
println!("NAT type: {}", style(status.nat_type).green());
|
||||
println!("Relay server: {}", style(status.relay_server).green());
|
||||
println!(
|
||||
"Udp listen: {}",
|
||||
style(status.udp_listen_addr.join(", ")).green()
|
||||
);
|
||||
println!("Tcp listen: {}", style(status.tcp_listen_addr).green());
|
||||
println!("Public ips: {}", style(status.public_ips).green());
|
||||
println!("Local addr: {}", style(status.local_addr).green());
|
||||
println!("IPv6: {}", style(status.ipv6_addr).green());
|
||||
|
||||
if !status.port_mapping_list.is_empty() {
|
||||
println!("------------------------------------------");
|
||||
println!("Port mapping {}", status.port_mapping_list.len());
|
||||
for (is_tcp, addr, dest) in status.port_mapping_list {
|
||||
if is_tcp {
|
||||
println!(" TCP: {} -> {}", addr, dest)
|
||||
} else {
|
||||
println!(" UDP: {} -> {}", addr, dest)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !status.in_ips.is_empty() || !status.out_ips.is_empty() {
|
||||
println!("------------------------------------------");
|
||||
}
|
||||
if !status.in_ips.is_empty() {
|
||||
println!("IP forwarding {}", status.in_ips.len());
|
||||
for (dest, mask, ip) in status.in_ips {
|
||||
println!(
|
||||
" -- {} --> {}/{}",
|
||||
ip,
|
||||
Ipv4Addr::from(dest),
|
||||
mask.count_ones()
|
||||
)
|
||||
}
|
||||
}
|
||||
if !status.out_ips.is_empty() {
|
||||
println!("Allows network {}", status.out_ips.len());
|
||||
for (dest, mask) in status.out_ips {
|
||||
println!(" {}/{}", Ipv4Addr::from(dest), mask.count_ones())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert(num: u64) -> String {
|
||||
let gigabytes = num / (1024 * 1024 * 1024);
|
||||
let remaining_bytes = num % (1024 * 1024 * 1024);
|
||||
let megabytes = remaining_bytes / (1024 * 1024);
|
||||
let remaining_bytes = remaining_bytes % (1024 * 1024);
|
||||
let kilobytes = remaining_bytes / 1024;
|
||||
let remaining_bytes = remaining_bytes % 1024;
|
||||
let mut s = String::new();
|
||||
if gigabytes > 0 {
|
||||
s.push_str(&format!("{} GB ", gigabytes));
|
||||
}
|
||||
if megabytes > 0 {
|
||||
s.push_str(&format!("{} MB ", megabytes));
|
||||
}
|
||||
if kilobytes > 0 {
|
||||
s.push_str(&format!("{} KB ", kilobytes));
|
||||
}
|
||||
if remaining_bytes > 0 {
|
||||
s.push_str(&format!("{} bytes", remaining_bytes));
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
pub fn console_route_table(mut list: Vec<RouteItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No route found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.destination.cmp(&t2.destination));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
|
||||
out_list.push(vec![
|
||||
("Destination".to_string(), Style::new()),
|
||||
("Next Hop".to_string(), Style::new()),
|
||||
("Metric".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new()),
|
||||
("Interface".to_string(), Style::new()),
|
||||
]);
|
||||
for item in list {
|
||||
out_list.push(vec![
|
||||
(item.destination, Style::new().green()),
|
||||
(item.next_hop, Style::new().green()),
|
||||
(item.metric, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
(item.interface, Style::new().green()),
|
||||
]);
|
||||
}
|
||||
|
||||
table::println_table(out_list)
|
||||
}
|
||||
|
||||
pub fn console_device_list(mut list: Vec<DeviceItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
|
||||
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
//表头
|
||||
out_list.push(vec![
|
||||
("Name".to_string(), Style::new()),
|
||||
("Virtual Ip".to_string(), Style::new()),
|
||||
("Status".to_string(), Style::new()),
|
||||
("P2P/Relay".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new()),
|
||||
]);
|
||||
for item in list {
|
||||
let name = if item.wire_guard {
|
||||
format!("{}(wg)", item.name)
|
||||
} else {
|
||||
item.name
|
||||
};
|
||||
if &item.status == "Online" {
|
||||
if !item.wire_guard
|
||||
&& (item.client_secret != item.current_client_secret
|
||||
|| (!item.current_client_secret_hash.is_empty()
|
||||
&& !item.client_secret_hash.is_empty()
|
||||
&& item.current_client_secret_hash != item.client_secret_hash))
|
||||
{
|
||||
//加密状态不一致,无法通信的
|
||||
out_list.push(vec![
|
||||
(name, Style::new().red()),
|
||||
(item.virtual_ip, Style::new().red()),
|
||||
(item.status, Style::new().red()),
|
||||
("Mismatch".to_string(), Style::new().red()),
|
||||
("".to_string(), Style::new().red()),
|
||||
]);
|
||||
} else {
|
||||
if item.nat_traversal_type.contains("p2p") {
|
||||
out_list.push(vec![
|
||||
(name, Style::new().green()),
|
||||
(item.virtual_ip, Style::new().green()),
|
||||
(item.status, Style::new().green()),
|
||||
(item.nat_traversal_type, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
]);
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(name, Style::new().yellow()),
|
||||
(item.virtual_ip, Style::new().yellow()),
|
||||
(item.status, Style::new().yellow()),
|
||||
(item.nat_traversal_type, Style::new().yellow()),
|
||||
(item.rt, Style::new().yellow()),
|
||||
]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(name, Style::new().color256(102)),
|
||||
(item.virtual_ip, Style::new().color256(102)),
|
||||
(item.status, Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
table::println_table(out_list)
|
||||
}
|
||||
|
||||
pub fn console_device_list_all(mut list: Vec<DeviceItem>) {
|
||||
if list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
list.sort_by(|t1, t2| t1.virtual_ip.cmp(&t2.virtual_ip));
|
||||
list.sort_by(|t1, t2| t1.status.cmp(&t2.status));
|
||||
let mut out_list = Vec::with_capacity(list.len());
|
||||
//表头
|
||||
out_list.push(vec![
|
||||
("Name".to_string(), Style::new()),
|
||||
("Virtual Ip".to_string(), Style::new()),
|
||||
("Status".to_string(), Style::new()),
|
||||
("P2P/Relay".to_string(), Style::new()),
|
||||
("Rt".to_string(), Style::new()),
|
||||
("NAT Type".to_string(), Style::new()),
|
||||
("Public Ips".to_string(), Style::new()),
|
||||
("Local Ip".to_string(), Style::new()),
|
||||
("IPv6".to_string(), Style::new()),
|
||||
]);
|
||||
for item in list {
|
||||
if &item.status == "Online" {
|
||||
if &item.nat_traversal_type == "p2p" {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().green()),
|
||||
(item.virtual_ip, Style::new().green()),
|
||||
(item.status, Style::new().green()),
|
||||
(item.nat_traversal_type, Style::new().green()),
|
||||
(item.rt, Style::new().green()),
|
||||
(item.nat_type, Style::new().green()),
|
||||
(item.public_ips, Style::new().green()),
|
||||
(item.local_ip, Style::new().green()),
|
||||
(item.ipv6, Style::new().green()),
|
||||
]);
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().yellow()),
|
||||
(item.virtual_ip, Style::new().yellow()),
|
||||
(item.status, Style::new().yellow()),
|
||||
(item.nat_traversal_type, Style::new().yellow()),
|
||||
(item.rt, Style::new().yellow()),
|
||||
(item.nat_type, Style::new().yellow()),
|
||||
(item.public_ips, Style::new().yellow()),
|
||||
(item.local_ip, Style::new().yellow()),
|
||||
(item.ipv6, Style::new().yellow()),
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
out_list.push(vec![
|
||||
(item.name, Style::new().color256(102)),
|
||||
(item.virtual_ip, Style::new().color256(102)),
|
||||
(item.status, Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
("".to_string(), Style::new().color256(102)),
|
||||
]);
|
||||
}
|
||||
}
|
||||
table::println_table(out_list)
|
||||
}
|
||||
|
||||
pub fn console_chart_a(chart_a: ChartA) {
|
||||
if chart_a.disable_stats {
|
||||
println!("Traffic statistics not enabled");
|
||||
return;
|
||||
}
|
||||
println!();
|
||||
println!("-----------------------------------------------------------------");
|
||||
println!(
|
||||
"Upload total = {}",
|
||||
style(convert(chart_a.up_total)).green()
|
||||
);
|
||||
println!(
|
||||
"Download total = {}",
|
||||
style(convert(chart_a.down_total)).green()
|
||||
);
|
||||
println!("-----------------------------------------------------------------");
|
||||
let up_keys: HashSet<_> = chart_a.up_map.keys().cloned().collect();
|
||||
let down_keys: HashSet<_> = chart_a.down_map.keys().cloned().collect();
|
||||
let mut keys: Vec<Ipv4Addr> = up_keys.union(&down_keys).cloned().collect();
|
||||
// 排序
|
||||
keys.sort();
|
||||
|
||||
// 找到最大的值,用于缩放条形图长度
|
||||
let up_max_value = *chart_a.up_map.values().max().unwrap_or(&0);
|
||||
let down_max_value = *chart_a.down_map.values().max().unwrap_or(&0);
|
||||
let max_value = up_max_value.max(down_max_value);
|
||||
let max_value = max_value.max(1);
|
||||
let max_height = 50;
|
||||
// 打印条形图
|
||||
for key in &keys {
|
||||
if let Some(&value) = chart_a.up_map.get(key) {
|
||||
let bar = "█".repeat(((value as f64 / max_value as f64) * max_height as f64) as usize);
|
||||
println!(
|
||||
"{:<10} | {} upload {}",
|
||||
key,
|
||||
bar,
|
||||
style(convert(value)).green()
|
||||
);
|
||||
}
|
||||
if let Some(&value) = chart_a.down_map.get(key) {
|
||||
let bar = "█".repeat(((value as f64 / max_value as f64) * max_height as f64) as usize);
|
||||
println!(
|
||||
"{:<10} | {} download {}",
|
||||
key,
|
||||
bar,
|
||||
style(convert(value)).green()
|
||||
);
|
||||
}
|
||||
println!("-");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn console_chart_b(chart_b: ChartB) {
|
||||
if chart_b.disable_stats {
|
||||
println!("Traffic statistics not enabled");
|
||||
return;
|
||||
}
|
||||
let ip = if let Some(ip) = chart_b.ip {
|
||||
ip
|
||||
} else {
|
||||
println!("Ip: None");
|
||||
return;
|
||||
};
|
||||
println!("---------------------------- upload ----------------------------");
|
||||
println!("IP: {}", ip);
|
||||
println!("Upload total: {}", style(convert(chart_b.up_total)).green());
|
||||
println!(
|
||||
"Max: {}",
|
||||
style(convert(
|
||||
chart_b
|
||||
.up_list
|
||||
.iter()
|
||||
.max()
|
||||
.cloned()
|
||||
.map_or(0, |v| v as u64)
|
||||
))
|
||||
.green()
|
||||
);
|
||||
console_chart_b_list(chart_b.up_list);
|
||||
println!("---------------------------- download ----------------------------");
|
||||
println!("IP: {}", ip);
|
||||
println!(
|
||||
"Download total: {}",
|
||||
style(convert(chart_b.down_total)).green()
|
||||
);
|
||||
println!(
|
||||
"Max: {}",
|
||||
style(convert(
|
||||
chart_b
|
||||
.down_list
|
||||
.iter()
|
||||
.max()
|
||||
.cloned()
|
||||
.map_or(0, |v| v as u64)
|
||||
))
|
||||
.green()
|
||||
);
|
||||
console_chart_b_list(chart_b.down_list);
|
||||
}
|
||||
fn console_chart_b_list(list: Vec<usize>) {
|
||||
let max_value = *list.iter().max().unwrap_or(&0);
|
||||
let max_value = max_value.max(1);
|
||||
let max_height = max_value.min(20);
|
||||
// 遍历从最大高度到0
|
||||
for i in (0..=max_height).rev() {
|
||||
for &value in &list {
|
||||
let scaled_value = (value as f64 / max_value as f64 * max_height as f64) as usize;
|
||||
if scaled_value >= i {
|
||||
print!("█");
|
||||
} else {
|
||||
print!(" ");
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
use console::Style;
|
||||
|
||||
pub fn println_table(table: Vec<Vec<(String, Style)>>) {
|
||||
if table.is_empty() {
|
||||
return;
|
||||
}
|
||||
let mut width_list = vec![0; table[0].len()];
|
||||
for in_list in table.iter() {
|
||||
for (index, (item, _)) in in_list.iter().enumerate() {
|
||||
let width = console::measure_text_width(item) + 4;
|
||||
if width_list[index] < width {
|
||||
width_list[index] = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
for in_list in table {
|
||||
for (col, (item, style)) in in_list.iter().enumerate() {
|
||||
let str = format!("{:1$}", item, width_list[col]);
|
||||
print!("{}", style.apply_to(str));
|
||||
}
|
||||
println!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn get_unique_identifier() -> Option<String> {
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::process::Command;
|
||||
let output = match Command::new("wmic")
|
||||
.creation_flags(0x08000000)
|
||||
.args(&["csproduct", "get", "UUID"])
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let result = String::from_utf8_lossy(&output.stdout);
|
||||
let identifier = result.lines().nth(1).unwrap_or("").trim();
|
||||
if identifier.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(identifier.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn get_unique_identifier() -> Option<String> {
|
||||
use std::process::Command;
|
||||
let output = match Command::new("ioreg")
|
||||
.args(&["-rd1", "-c", "IOPlatformExpertDevice"])
|
||||
.output()
|
||||
{
|
||||
Ok(output) => output,
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let result = String::from_utf8_lossy(&output.stdout);
|
||||
let identifier = result
|
||||
.lines()
|
||||
.find(|line| line.contains("IOPlatformUUID"))
|
||||
.unwrap_or("")
|
||||
.trim();
|
||||
if identifier.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(identifier.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn get_unique_identifier() -> Option<String> {
|
||||
use std::process::Command;
|
||||
|
||||
// Try to execute 'dmidecode' command to get the system identifier first.
|
||||
if let Ok(output) = Command::new("dmidecode")
|
||||
.arg("-s")
|
||||
.arg("system-uuid")
|
||||
.output()
|
||||
{
|
||||
let identifier = String::from_utf8_lossy(&output.stdout).trim().to_owned();
|
||||
if !identifier.is_empty() {
|
||||
return Some(identifier.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// Try to read file /etc/machine-id if 'dmidecode' command cannot be executed or get nothing.
|
||||
// 对 linux 或 wsl 来说,读取 /etc/machine-id 即可获取当前操作系统的
|
||||
// 唯一标识,而且某些环境没有预装`dmidecode`命令
|
||||
if let Ok(identifier) = std::fs::read_to_string("/etc/machine-id") {
|
||||
let identifier = identifier.trim();
|
||||
if !identifier.is_empty() {
|
||||
return Some(identifier.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
pub mod args_parse;
|
||||
#[cfg(feature = "command")]
|
||||
pub mod command;
|
||||
pub mod config;
|
||||
#[cfg(feature = "command")]
|
||||
mod console_out;
|
||||
pub mod identifier;
|
||||
|
||||
pub mod cli;
|
||||
mod generated_serial_number;
|
||||
|
||||
pub mod callback;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
@@ -1,25 +0,0 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("the buffer is too small")]
|
||||
SmallBuffer,
|
||||
|
||||
#[error("the packet is invalid")]
|
||||
InvalidPacket,
|
||||
#[error("Unimplemented")]
|
||||
Unimplemented,
|
||||
// #[error("the vaue is invalid for the field")]
|
||||
// InvalidValue,
|
||||
//
|
||||
// #[error("the value has already been defined")]
|
||||
// AlreadyDefined,
|
||||
//
|
||||
// #[error(transparent)]
|
||||
// Io(#[from] io::Error),
|
||||
//
|
||||
// #[error(transparent)]
|
||||
// Nul(#[from] ffi::NulError),
|
||||
}
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
@@ -1,40 +0,0 @@
|
||||
syntax = "proto3";
|
||||
message RegistrationRequest{
|
||||
string token = 1;
|
||||
string mac_address = 2;
|
||||
}
|
||||
|
||||
message RegistrationResponse{
|
||||
fixed32 virtual_ip = 1;
|
||||
fixed32 virtual_gateway = 2;
|
||||
fixed32 virtual_netmask = 3;
|
||||
uint32 epoch = 4;
|
||||
repeated fixed32 virtual_ip_list = 5;
|
||||
fixed32 public_ip = 6;
|
||||
uint32 public_port = 7;
|
||||
}
|
||||
|
||||
message DeviceList{
|
||||
uint32 epoch = 1;
|
||||
repeated fixed32 virtual_ip_list = 2;
|
||||
}
|
||||
|
||||
message Punch{
|
||||
fixed32 virtual_ip = 1;
|
||||
repeated fixed32 public_ip_list = 2;
|
||||
uint32 public_port = 3;
|
||||
uint32 public_port_range = 4;
|
||||
NatType nat_type = 5;
|
||||
bool reply = 6;
|
||||
Step step = 7;
|
||||
}
|
||||
enum NatType{
|
||||
Symmetric = 0;
|
||||
Cone = 1;
|
||||
}
|
||||
enum Step{
|
||||
Step1 = 0;
|
||||
Step2 = 1;
|
||||
Step3 = 2;
|
||||
Step4 = 3;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
[package]
|
||||
name = "tun"
|
||||
version = "0.5.4"
|
||||
edition = "2018"
|
||||
|
||||
authors = ["meh. <[email protected]>"]
|
||||
license = "WTFPL"
|
||||
|
||||
description = "TUN device creation and handling."
|
||||
repository = "https://github.com/meh/rust-tun"
|
||||
keywords = ["tun", "network", "tunnel", "bindings"]
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
thiserror = "1"
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "ios", target_os = "android"))'.dependencies]
|
||||
tokio = { version = "1", features = ["net", "macros"], optional = true }
|
||||
tokio-util = { version = "0.6", features = ["codec"], optional = true }
|
||||
bytes = { version = "1", optional = true }
|
||||
byteorder = { version = "1", optional = true }
|
||||
# This is only for the `ready` macro.
|
||||
futures-core = { version = "0.3", optional = true }
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies]
|
||||
ioctl = { version = "0.6", package = "ioctl-sys" }
|
||||
|
||||
[dev-dependencies]
|
||||
packet = "0.1"
|
||||
futures = "0.3"
|
||||
|
||||
[features]
|
||||
async = ["tokio", "tokio-util", "bytes", "byteorder", "futures-core"]
|
||||
|
||||
[[example]]
|
||||
name = "read-async"
|
||||
required-features = [ "async", "tokio/rt-multi-thread" ]
|
||||
|
||||
[[example]]
|
||||
name = "read-async-codec"
|
||||
required-features = [ "async", "tokio/rt-multi-thread" ]
|
||||
|
||||
[[example]]
|
||||
name = "ping-tun"
|
||||
required-features = [ "async", "tokio/rt-multi-thread" ]
|
||||
@@ -1,106 +0,0 @@
|
||||
TUN interfaces [](https://crates.io/crates/tun)  
|
||||
==============
|
||||
This crate allows the creation and usage of TUN interfaces, the aim is to make this cross-platform.
|
||||
|
||||
Usage
|
||||
-----
|
||||
First, add the following to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tun = "0.5"
|
||||
```
|
||||
|
||||
Next, add this to your crate root:
|
||||
|
||||
```rust
|
||||
extern crate tun;
|
||||
```
|
||||
|
||||
If you want to use the TUN interface with mio/tokio, you need to enable the `async` feature:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tun = { version = "0.5", features = ["async"] }
|
||||
```
|
||||
|
||||
Example
|
||||
-------
|
||||
The following example creates and configures a TUN interface and starts reading
|
||||
packets from it.
|
||||
|
||||
```rust
|
||||
use std::io::Read;
|
||||
|
||||
extern crate tun;
|
||||
|
||||
fn main() {
|
||||
let mut config = tun::Configuration::default();
|
||||
config.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let mut dev = tun::create(&config).unwrap();
|
||||
let mut buf = [0; 4096];
|
||||
|
||||
loop {
|
||||
let amount = dev.read(&mut buf).unwrap();
|
||||
println!("{:?}", &buf[0 .. amount]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Platforms
|
||||
=========
|
||||
Not every platform is supported.
|
||||
|
||||
Linux
|
||||
-----
|
||||
You will need the `tun` module to be loaded and root is required to create
|
||||
interfaces.
|
||||
|
||||
macOS
|
||||
-----
|
||||
It just werks, but you have to set up routing manually.
|
||||
|
||||
iOS
|
||||
----
|
||||
You can pass the file descriptor of the TUN device to `rust-tun` to create the interface.
|
||||
|
||||
Here is an example to create the TUN device on iOS and pass the `fd` to `rust-tun`:
|
||||
```swift
|
||||
// Swift
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||
let tunnelNetworkSettings = createTunnelSettings() // Configure TUN address, DNS, mtu, routing...
|
||||
setTunnelNetworkSettings(tunnelNetworkSettings) { [weak self] error in
|
||||
let tunFd = self?.packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
|
||||
DispatchQueue.global(qos: .default).async {
|
||||
start_tun(tunFd)
|
||||
}
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
#[no_mangle]
|
||||
pub extern "C" fn start_tun(fd: std::os::raw::c_int) {
|
||||
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on(async {
|
||||
let mut cfg = tun::Configuration::default();
|
||||
cfg.raw_fd(fd);
|
||||
let mut tun = tun::create_as_async(&cfg).unwrap();
|
||||
let mut framed = tun.into_framed();
|
||||
while let Some(packet) = framed.next().await {
|
||||
...
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -1,78 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use packet::{builder::Builder, icmp, ip, Packet};
|
||||
use tun::{self, Configuration, TunPacket};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let mut config = Configuration::default();
|
||||
|
||||
config
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let dev = tun::create_as_async(&config).unwrap();
|
||||
|
||||
let mut framed = dev.into_framed();
|
||||
|
||||
while let Some(packet) = framed.next().await {
|
||||
match packet {
|
||||
Ok(pkt) => match ip::Packet::new(pkt.get_bytes()) {
|
||||
Ok(ip::Packet::V4(pkt)) => match icmp::Packet::new(pkt.payload()) {
|
||||
Ok(icmp) => match icmp.echo() {
|
||||
Ok(icmp) => {
|
||||
let reply = ip::v4::Builder::default()
|
||||
.id(0x42)
|
||||
.unwrap()
|
||||
.ttl(64)
|
||||
.unwrap()
|
||||
.source(pkt.destination())
|
||||
.unwrap()
|
||||
.destination(pkt.source())
|
||||
.unwrap()
|
||||
.icmp()
|
||||
.unwrap()
|
||||
.echo()
|
||||
.unwrap()
|
||||
.reply()
|
||||
.unwrap()
|
||||
.identifier(icmp.identifier())
|
||||
.unwrap()
|
||||
.sequence(icmp.sequence())
|
||||
.unwrap()
|
||||
.payload(icmp.payload())
|
||||
.unwrap()
|
||||
.build()
|
||||
.unwrap();
|
||||
framed.send(TunPacket::new(reply)).await.unwrap();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
},
|
||||
Err(err) => println!("Received an invalid packet: {:?}", err),
|
||||
_ => {}
|
||||
},
|
||||
Err(err) => panic!("Error: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use bytes::BytesMut;
|
||||
use futures::StreamExt;
|
||||
use packet::{ip::Packet, Error};
|
||||
use tokio_util::codec::{Decoder, FramedRead};
|
||||
|
||||
pub struct IPPacketCodec;
|
||||
|
||||
impl Decoder for IPPacketCodec {
|
||||
type Item = Packet<BytesMut>;
|
||||
type Error = Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
if buf.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let buf = buf.split_to(buf.len());
|
||||
Ok(match Packet::no_payload(buf) {
|
||||
Ok(pkt) => Some(pkt),
|
||||
Err(err) => {
|
||||
println!("error {:?}", err);
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
let dev = tun::create_as_async(&config).unwrap();
|
||||
|
||||
let mut stream = FramedRead::new(dev, IPPacketCodec);
|
||||
|
||||
while let Some(packet) = stream.next().await {
|
||||
match packet {
|
||||
Ok(pkt) => println!("pkt: {:#?}", pkt),
|
||||
Err(err) => panic!("Error: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use futures::StreamExt;
|
||||
use packet::ip::Packet;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let dev = tun::create_as_async(&config).unwrap();
|
||||
|
||||
let mut stream = dev.into_framed();
|
||||
|
||||
while let Some(packet) = stream.next().await {
|
||||
match packet {
|
||||
Ok(pkt) => println!("pkt: {:#?}", Packet::unchecked(pkt.get_bytes())),
|
||||
Err(err) => panic!("Error: {:?}", err),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io::Read;
|
||||
|
||||
fn main() {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.address((10, 0, 0, 1))
|
||||
.netmask((255, 255, 255, 0))
|
||||
.up();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let mut dev = tun::create(&config).unwrap();
|
||||
let mut buf = [0; 4096];
|
||||
|
||||
loop {
|
||||
let amount = dev.read(&mut buf).unwrap();
|
||||
println!("{:?}", &buf[0..amount]);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::net::{SocketAddr, SocketAddrV4};
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
/// Helper trait to convert things into IPv4 addresses.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
pub trait IntoAddress {
|
||||
/// Convert the type to an `Ipv4Addr`.
|
||||
fn into_address(&self) -> Result<Ipv4Addr>;
|
||||
}
|
||||
|
||||
impl IntoAddress for u32 {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
Ok(Ipv4Addr::new(
|
||||
((*self) & 0xff) as u8,
|
||||
((*self >> 8) & 0xff) as u8,
|
||||
((*self >> 16) & 0xff) as u8,
|
||||
((*self >> 24) & 0xff) as u8,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for i32 {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(*self as u32).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for (u8, u8, u8, u8) {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
Ok(Ipv4Addr::new(self.0, self.1, self.2, self.3))
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for str {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
self.parse().map_err(|_| Error::InvalidAddress)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a str {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(*self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for String {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a String {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for Ipv4Addr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
Ok(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a Ipv4Addr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for IpAddr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
match *self {
|
||||
IpAddr::V4(ref value) => Ok(*value),
|
||||
|
||||
IpAddr::V6(_) => Err(Error::InvalidAddress),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a IpAddr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for SocketAddrV4 {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
Ok(*self.ip())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a SocketAddrV4 {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAddress for SocketAddr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
match *self {
|
||||
SocketAddr::V4(ref value) => Ok(*value.ip()),
|
||||
|
||||
SocketAddr::V6(_) => Err(Error::InvalidAddress),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoAddress for &'a SocketAddr {
|
||||
fn into_address(&self) -> Result<Ipv4Addr> {
|
||||
(&**self).into_address()
|
||||
}
|
||||
}
|
||||
@@ -1,149 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io;
|
||||
|
||||
use byteorder::{NativeEndian, NetworkEndian, WriteBytesExt};
|
||||
use bytes::{BufMut, Bytes, BytesMut};
|
||||
use tokio_util::codec::{Decoder, Encoder};
|
||||
|
||||
/// A packet protocol IP version
|
||||
#[derive(Debug)]
|
||||
enum PacketProtocol {
|
||||
IPv4,
|
||||
IPv6,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
// Note: the protocol in the packet information header is platform dependent.
|
||||
impl PacketProtocol {
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn into_pi_field(&self) -> Result<u16, io::Error> {
|
||||
match self {
|
||||
PacketProtocol::IPv4 => Ok(libc::ETH_P_IP as u16),
|
||||
PacketProtocol::IPv6 => Ok(libc::ETH_P_IPV6 as u16),
|
||||
PacketProtocol::Other(_) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"neither an IPv4 or IPv6 packet",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
fn into_pi_field(&self) -> Result<u16, io::Error> {
|
||||
match self {
|
||||
PacketProtocol::IPv4 => Ok(libc::PF_INET as u16),
|
||||
PacketProtocol::IPv6 => Ok(libc::PF_INET6 as u16),
|
||||
PacketProtocol::Other(_) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"neither an IPv4 or IPv6 packet",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Tun Packet to be sent or received on the TUN interface.
|
||||
#[derive(Debug)]
|
||||
pub struct TunPacket(PacketProtocol, Bytes);
|
||||
|
||||
/// Infer the protocol based on the first nibble in the packet buffer.
|
||||
fn infer_proto(buf: &[u8]) -> PacketProtocol {
|
||||
match buf[0] >> 4 {
|
||||
4 => PacketProtocol::IPv4,
|
||||
6 => PacketProtocol::IPv6,
|
||||
p => PacketProtocol::Other(p),
|
||||
}
|
||||
}
|
||||
|
||||
impl TunPacket {
|
||||
/// Create a new `TunPacket` based on a byte slice.
|
||||
pub fn new(bytes: Vec<u8>) -> TunPacket {
|
||||
let proto = infer_proto(&bytes);
|
||||
TunPacket(proto, Bytes::from(bytes))
|
||||
}
|
||||
|
||||
/// Return this packet's bytes.
|
||||
pub fn get_bytes(&self) -> &[u8] {
|
||||
&self.1
|
||||
}
|
||||
|
||||
pub fn into_bytes(self) -> Bytes {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
/// A TunPacket Encoder/Decoder.
|
||||
pub struct TunPacketCodec(bool, i32);
|
||||
|
||||
impl TunPacketCodec {
|
||||
/// Create a new `TunPacketCodec` specifying whether the underlying
|
||||
/// tunnel Device has enabled the packet information header.
|
||||
pub fn new(pi: bool, mtu: i32) -> TunPacketCodec {
|
||||
TunPacketCodec(pi, mtu)
|
||||
}
|
||||
}
|
||||
|
||||
impl Decoder for TunPacketCodec {
|
||||
type Item = TunPacket;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
||||
if buf.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut pkt = buf.split_to(buf.len());
|
||||
|
||||
// reserve enough space for the next packet
|
||||
if self.0 {
|
||||
buf.reserve(self.1 as usize + 4);
|
||||
} else {
|
||||
buf.reserve(self.1 as usize);
|
||||
}
|
||||
|
||||
// if the packet information is enabled we have to ignore the first 4 bytes
|
||||
if self.0 {
|
||||
let _ = pkt.split_to(4);
|
||||
}
|
||||
|
||||
let proto = infer_proto(pkt.as_ref());
|
||||
Ok(Some(TunPacket(proto, pkt.freeze())))
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder<TunPacket> for TunPacketCodec {
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, item: TunPacket, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
||||
dst.reserve(item.get_bytes().len() + 4);
|
||||
match item {
|
||||
TunPacket(proto, bytes) if self.0 => {
|
||||
// build the packet information header comprising of 2 u16
|
||||
// fields: flags and protocol.
|
||||
let mut buf = Vec::<u8>::with_capacity(4);
|
||||
|
||||
// flags is always 0
|
||||
buf.write_u16::<NativeEndian>(0).unwrap();
|
||||
// write the protocol as network byte order
|
||||
buf.write_u16::<NetworkEndian>(proto.into_pi_field()?)
|
||||
.unwrap();
|
||||
|
||||
dst.put_slice(&buf);
|
||||
dst.put(bytes);
|
||||
}
|
||||
TunPacket(_, bytes) => dst.put(bytes),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io;
|
||||
use std::io::{IoSlice, Read, Write};
|
||||
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use futures_core::ready;
|
||||
use tokio::io::unix::AsyncFd;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tokio_util::codec::Framed;
|
||||
|
||||
use crate::device::Device as D;
|
||||
use crate::platform::{Device, Queue};
|
||||
use crate::r#async::codec::*;
|
||||
|
||||
/// An async TUN device wrapper around a TUN device.
|
||||
pub struct AsyncDevice {
|
||||
inner: AsyncFd<Device>,
|
||||
}
|
||||
|
||||
impl AsyncDevice {
|
||||
/// Create a new `AsyncDevice` wrapping around a `Device`.
|
||||
pub fn new(device: Device) -> io::Result<AsyncDevice> {
|
||||
device.set_nonblock()?;
|
||||
Ok(AsyncDevice {
|
||||
inner: AsyncFd::new(device)?,
|
||||
})
|
||||
}
|
||||
/// Returns a shared reference to the underlying Device object
|
||||
pub fn get_ref(&self) -> &Device {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying Device object
|
||||
pub fn get_mut(&mut self) -> &mut Device {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Consumes this AsyncDevice and return a Framed object (unified Stream and Sink interface)
|
||||
pub fn into_framed(mut self) -> Framed<Self, TunPacketCodec> {
|
||||
let pi = self.get_mut().has_packet_information();
|
||||
let codec = TunPacketCodec::new(pi, self.inner.get_ref().mtu().unwrap_or(1504));
|
||||
Framed::new(self, codec)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for AsyncDevice {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf,
|
||||
) -> Poll<io::Result<()>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_read_ready_mut(cx))?;
|
||||
let rbuf = buf.initialize_unfilled();
|
||||
match guard.try_io(|inner| inner.get_mut().read(rbuf)) {
|
||||
Ok(res) => return Poll::Ready(res.map(|n| buf.advance(n))),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for AsyncDevice {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().write(buf)) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().flush()) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().write_vectored(bufs)) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// An async TUN device queue wrapper around a TUN device queue.
|
||||
pub struct AsyncQueue {
|
||||
inner: AsyncFd<Queue>,
|
||||
}
|
||||
|
||||
impl AsyncQueue {
|
||||
/// Create a new `AsyncQueue` wrapping around a `Queue`.
|
||||
pub fn new(queue: Queue) -> io::Result<AsyncQueue> {
|
||||
queue.set_nonblock()?;
|
||||
Ok(AsyncQueue {
|
||||
inner: AsyncFd::new(queue)?,
|
||||
})
|
||||
}
|
||||
/// Returns a shared reference to the underlying Queue object
|
||||
pub fn get_ref(&self) -> &Queue {
|
||||
self.inner.get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying Queue object
|
||||
pub fn get_mut(&mut self) -> &mut Queue {
|
||||
self.inner.get_mut()
|
||||
}
|
||||
|
||||
/// Consumes this AsyncQueue and return a Framed object (unified Stream and Sink interface)
|
||||
pub fn into_framed(mut self) -> Framed<Self, TunPacketCodec> {
|
||||
let pi = self.get_mut().has_packet_information();
|
||||
let codec = TunPacketCodec::new(pi, 1504);
|
||||
Framed::new(self, codec)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for AsyncQueue {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf,
|
||||
) -> Poll<io::Result<()>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_read_ready_mut(cx))?;
|
||||
let rbuf = buf.initialize_unfilled();
|
||||
match guard.try_io(|inner| inner.get_mut().read(rbuf)) {
|
||||
Ok(res) => return Poll::Ready(res.map(|n| buf.advance(n))),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for AsyncQueue {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().write(buf)) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
loop {
|
||||
let mut guard = ready!(self.inner.poll_write_ready_mut(cx))?;
|
||||
match guard.try_io(|inner| inner.get_mut().flush()) {
|
||||
Ok(res) => return Poll::Ready(res),
|
||||
Err(_wb) => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Async specific modules.
|
||||
|
||||
use crate::error;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::platform::create;
|
||||
|
||||
mod device;
|
||||
pub use self::device::{AsyncDevice, AsyncQueue};
|
||||
|
||||
mod codec;
|
||||
pub use self::codec::{TunPacket, TunPacketCodec};
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create_as_async(configuration: &Configuration) -> Result<AsyncDevice, error::Error> {
|
||||
let device = create(&configuration)?;
|
||||
AsyncDevice::new(device).map_err(|err| err.into())
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use crate::address::IntoAddress;
|
||||
use crate::platform;
|
||||
|
||||
/// TUN interface OSI layer of operation.
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Layer {
|
||||
L2,
|
||||
L3,
|
||||
}
|
||||
|
||||
impl Default for Layer {
|
||||
fn default() -> Self {
|
||||
Layer::L3
|
||||
}
|
||||
}
|
||||
|
||||
/// Configuration builder for a TUN interface.
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct Configuration {
|
||||
pub(crate) name: Option<String>,
|
||||
pub(crate) platform: platform::Configuration,
|
||||
|
||||
pub(crate) address: Option<Ipv4Addr>,
|
||||
pub(crate) destination: Option<Ipv4Addr>,
|
||||
pub(crate) broadcast: Option<Ipv4Addr>,
|
||||
pub(crate) netmask: Option<Ipv4Addr>,
|
||||
pub(crate) mtu: Option<i32>,
|
||||
pub(crate) enabled: Option<bool>,
|
||||
pub(crate) layer: Option<Layer>,
|
||||
pub(crate) queues: Option<usize>,
|
||||
pub(crate) raw_fd: Option<RawFd>,
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
/// Access the platform dependant configuration.
|
||||
pub fn platform<F>(&mut self, f: F) -> &mut Self
|
||||
where
|
||||
F: FnOnce(&mut platform::Configuration),
|
||||
{
|
||||
f(&mut self.platform);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the name.
|
||||
pub fn name<S: AsRef<str>>(&mut self, name: S) -> &mut Self {
|
||||
self.name = Some(name.as_ref().into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the address.
|
||||
pub fn address<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||
self.address = Some(value.into_address().unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the destination address.
|
||||
pub fn destination<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||
self.destination = Some(value.into_address().unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the broadcast address.
|
||||
pub fn broadcast<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||
self.broadcast = Some(value.into_address().unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the netmask.
|
||||
pub fn netmask<A: IntoAddress>(&mut self, value: A) -> &mut Self {
|
||||
self.netmask = Some(value.into_address().unwrap());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the MTU.
|
||||
pub fn mtu(&mut self, value: i32) -> &mut Self {
|
||||
self.mtu = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the interface to be enabled once created.
|
||||
pub fn up(&mut self) -> &mut Self {
|
||||
self.enabled = Some(true);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the interface to be disabled once created.
|
||||
pub fn down(&mut self) -> &mut Self {
|
||||
self.enabled = Some(false);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the OSI layer of operation.
|
||||
pub fn layer(&mut self, value: Layer) -> &mut Self {
|
||||
self.layer = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the number of queues.
|
||||
pub fn queues(&mut self, value: usize) -> &mut Self {
|
||||
self.queues = Some(value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the raw fd.
|
||||
pub fn raw_fd(&mut self, fd: RawFd) -> &mut Self {
|
||||
self.raw_fd = Some(fd);
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::error::*;
|
||||
|
||||
/// A TUN device.
|
||||
pub trait Device: Read + Write {
|
||||
type Queue: Read + Write;
|
||||
|
||||
/// Reconfigure the device.
|
||||
fn configure(&mut self, config: &Configuration) -> Result<()> {
|
||||
if let Some(ip) = config.address {
|
||||
self.set_address(ip)?;
|
||||
}
|
||||
|
||||
if let Some(ip) = config.destination {
|
||||
self.set_destination(ip)?;
|
||||
}
|
||||
|
||||
if let Some(ip) = config.broadcast {
|
||||
self.set_broadcast(ip)?;
|
||||
}
|
||||
|
||||
if let Some(ip) = config.netmask {
|
||||
self.set_netmask(ip)?;
|
||||
}
|
||||
|
||||
if let Some(mtu) = config.mtu {
|
||||
self.set_mtu(mtu)?;
|
||||
}
|
||||
|
||||
if let Some(enabled) = config.enabled {
|
||||
self.enabled(enabled)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the device name.
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Set the device name.
|
||||
fn set_name(&mut self, name: &str) -> Result<()>;
|
||||
|
||||
/// Turn on or off the interface.
|
||||
fn enabled(&mut self, value: bool) -> Result<()>;
|
||||
|
||||
/// Get the address.
|
||||
fn address(&self) -> Result<Ipv4Addr>;
|
||||
|
||||
/// Set the address.
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Get the destination address.
|
||||
fn destination(&self) -> Result<Ipv4Addr>;
|
||||
|
||||
/// Set the destination address.
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Get the broadcast address.
|
||||
fn broadcast(&self) -> Result<Ipv4Addr>;
|
||||
|
||||
/// Set the broadcast address.
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Get the netmask.
|
||||
fn netmask(&self) -> Result<Ipv4Addr>;
|
||||
|
||||
/// Set the netmask.
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()>;
|
||||
|
||||
/// Get the MTU.
|
||||
fn mtu(&self) -> Result<i32>;
|
||||
|
||||
/// Set the MTU.
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()>;
|
||||
|
||||
/// Get a device queue.
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue>;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::{ffi, io, num};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("invalid configuration")]
|
||||
InvalidConfig,
|
||||
|
||||
#[error("not implementated")]
|
||||
NotImplemented,
|
||||
|
||||
#[error("device name too long")]
|
||||
NameTooLong,
|
||||
|
||||
#[error("invalid device name")]
|
||||
InvalidName,
|
||||
|
||||
#[error("invalid address")]
|
||||
InvalidAddress,
|
||||
|
||||
#[error("invalid file descriptor")]
|
||||
InvalidDescriptor,
|
||||
|
||||
#[error("unsuported network layer of operation")]
|
||||
UnsupportedLayer,
|
||||
|
||||
#[error("invalid queues number")]
|
||||
InvalidQueuesNumber,
|
||||
|
||||
#[error(transparent)]
|
||||
Io(#[from] io::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Nul(#[from] ffi::NulError),
|
||||
|
||||
#[error(transparent)]
|
||||
ParseNum(#[from] num::ParseIntError),
|
||||
}
|
||||
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
@@ -1,53 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
mod error;
|
||||
pub use crate::error::*;
|
||||
|
||||
mod address;
|
||||
pub use crate::address::IntoAddress;
|
||||
|
||||
mod device;
|
||||
pub use crate::device::Device;
|
||||
|
||||
mod configuration;
|
||||
pub use crate::configuration::{Configuration, Layer};
|
||||
|
||||
pub mod platform;
|
||||
pub use crate::platform::create;
|
||||
|
||||
#[cfg(all(
|
||||
feature = "async",
|
||||
any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "android"
|
||||
)
|
||||
))]
|
||||
pub mod r#async;
|
||||
#[cfg(all(
|
||||
feature = "async",
|
||||
any(
|
||||
target_os = "linux",
|
||||
target_os = "macos",
|
||||
target_os = "ios",
|
||||
target_os = "android"
|
||||
)
|
||||
))]
|
||||
pub use r#async::*;
|
||||
|
||||
pub fn configure() -> Configuration {
|
||||
Configuration::default()
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::device::Device as D;
|
||||
use crate::error::*;
|
||||
use crate::platform::posix::{self, Fd};
|
||||
|
||||
/// A TUN device for Android.
|
||||
pub struct Device {
|
||||
queue: Queue,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Create a new `Device` for the given `Configuration`.
|
||||
pub fn new(config: &Configuration) -> Result<Self> {
|
||||
let fd = match config.raw_fd {
|
||||
Some(raw_fd) => raw_fd,
|
||||
_ => return Err(Error::InvalidConfig),
|
||||
};
|
||||
let device = {
|
||||
let tun = Fd::new(fd).map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
Device {
|
||||
queue: Queue { tun: tun },
|
||||
}
|
||||
};
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
/// Split the interface into a `Reader` and `Writer`.
|
||||
pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||
let fd = Arc::new(self.queue.tun);
|
||||
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
}
|
||||
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.queue.has_packet_information()
|
||||
}
|
||||
|
||||
/// Set non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.queue.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.queue.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.queue.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.queue.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
return "";
|
||||
}
|
||||
|
||||
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn address(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destination(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mtu(&self) -> Result<i32> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||
if index > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(&mut self.queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.queue.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Device {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.queue.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Queue {
|
||||
tun: Fd,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
// on Android this is always the case
|
||||
false
|
||||
}
|
||||
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Queue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.tun.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Queue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.tun.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Queue {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Queue {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Android specific functionality.
|
||||
|
||||
mod device;
|
||||
pub use self::device::{Device, Queue};
|
||||
|
||||
use crate::configuration::Configuration as C;
|
||||
use crate::error::*;
|
||||
|
||||
/// Android-only interface configuration.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct Configuration {}
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create(configuration: &C) -> Result<Device> {
|
||||
Device::new(&configuration)
|
||||
}
|
||||
@@ -1,214 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::configuration::Configuration;
|
||||
use crate::device::Device as D;
|
||||
use crate::error::*;
|
||||
use crate::platform::posix::{self, Fd};
|
||||
|
||||
/// A TUN device for iOS.
|
||||
pub struct Device {
|
||||
queue: Queue,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Create a new `Device` for the given `Configuration`.
|
||||
pub fn new(config: &Configuration) -> Result<Self> {
|
||||
let fd = match config.raw_fd {
|
||||
Some(raw_fd) => raw_fd,
|
||||
_ => return Err(Error::InvalidConfig),
|
||||
};
|
||||
let mut device = unsafe {
|
||||
let tun = Fd::new(fd).map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
Device {
|
||||
queue: Queue { tun: tun },
|
||||
}
|
||||
};
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
/// Split the interface into a `Reader` and `Writer`.
|
||||
pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||
let fd = Arc::new(self.queue.tun);
|
||||
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
}
|
||||
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.queue.has_packet_information()
|
||||
}
|
||||
|
||||
/// Set non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.queue.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.queue.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.queue.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.queue.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
return "";
|
||||
}
|
||||
|
||||
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn address(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn destination(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn mtu(&self) -> Result<i32> {
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||
if index > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(&mut self.queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.queue.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Device {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.queue.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Queue {
|
||||
tun: Fd,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
// on ios this is always the case
|
||||
true
|
||||
}
|
||||
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Queue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.tun.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Queue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.tun.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Queue {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Queue {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! iOS specific functionality.
|
||||
|
||||
mod device;
|
||||
pub use self::device::{Device, Queue};
|
||||
|
||||
use crate::configuration::Configuration as C;
|
||||
use crate::error::*;
|
||||
|
||||
/// iOS-only interface configuration.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct Configuration {}
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create(configuration: &C) -> Result<Device> {
|
||||
Device::new(&configuration)
|
||||
}
|
||||
@@ -1,457 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::mem;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
use std::vec::Vec;
|
||||
|
||||
use libc;
|
||||
use libc::{c_char, c_short};
|
||||
use libc::{AF_INET, O_RDWR, SOCK_DGRAM};
|
||||
|
||||
use crate::configuration::{Configuration, Layer};
|
||||
use crate::device::Device as D;
|
||||
use crate::error::*;
|
||||
use crate::platform::linux::sys::*;
|
||||
use crate::platform::posix::{self, Fd, SockAddr};
|
||||
|
||||
/// A TUN device using the TUN/TAP Linux driver.
|
||||
pub struct Device {
|
||||
name: String,
|
||||
queues: Vec<Queue>,
|
||||
ctl: Fd,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Create a new `Device` for the given `Configuration`.
|
||||
pub fn new(config: &Configuration) -> Result<Self> {
|
||||
let mut device = unsafe {
|
||||
let dev = match config.name.as_ref() {
|
||||
Some(name) => {
|
||||
let name = CString::new(name.clone())?;
|
||||
|
||||
if name.as_bytes_with_nul().len() > IFNAMSIZ {
|
||||
return Err(Error::NameTooLong);
|
||||
}
|
||||
|
||||
Some(name)
|
||||
}
|
||||
|
||||
None => None,
|
||||
};
|
||||
|
||||
let mut queues = Vec::new();
|
||||
|
||||
let mut req: ifreq = mem::zeroed();
|
||||
|
||||
if let Some(dev) = dev.as_ref() {
|
||||
ptr::copy_nonoverlapping(
|
||||
dev.as_ptr() as *const c_char,
|
||||
req.ifrn.name.as_mut_ptr(),
|
||||
dev.as_bytes().len(),
|
||||
);
|
||||
}
|
||||
|
||||
let device_type: c_short = config.layer.unwrap_or(Layer::L3).into();
|
||||
|
||||
let queues_num = config.queues.unwrap_or(1);
|
||||
if queues_num < 1 {
|
||||
return Err(Error::InvalidQueuesNumber);
|
||||
}
|
||||
|
||||
req.ifru.flags = device_type
|
||||
| if config.platform.packet_information {
|
||||
0
|
||||
} else {
|
||||
IFF_NO_PI
|
||||
}
|
||||
| if queues_num > 1 { IFF_MULTI_QUEUE } else { 0 };
|
||||
|
||||
for _ in 0..queues_num {
|
||||
let tun = Fd::new(libc::open(b"/dev/net/tun\0".as_ptr() as *const _, O_RDWR))
|
||||
.map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
if tunsetiff(tun.0, &mut req as *mut _ as *mut _) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
queues.push(Queue {
|
||||
tun,
|
||||
pi_enabled: config.platform.packet_information,
|
||||
});
|
||||
}
|
||||
|
||||
let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))
|
||||
.map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
Device {
|
||||
name: CStr::from_ptr(req.ifrn.name.as_ptr())
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
queues,
|
||||
ctl,
|
||||
}
|
||||
};
|
||||
|
||||
device.configure(config)?;
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
/// Prepare a new request.
|
||||
unsafe fn request(&self) -> ifreq {
|
||||
let mut req: ifreq = mem::zeroed();
|
||||
ptr::copy_nonoverlapping(
|
||||
self.name.as_ptr() as *const c_char,
|
||||
req.ifrn.name.as_mut_ptr(),
|
||||
self.name.len(),
|
||||
);
|
||||
|
||||
req
|
||||
}
|
||||
|
||||
/// Make the device persistent.
|
||||
pub fn persist(&mut self) -> Result<()> {
|
||||
unsafe {
|
||||
if tunsetpersist(self.as_raw_fd(), &1) < 0 {
|
||||
Err(io::Error::last_os_error().into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the owner of the device.
|
||||
pub fn user(&mut self, value: i32) -> Result<()> {
|
||||
unsafe {
|
||||
if tunsetowner(self.as_raw_fd(), &value) < 0 {
|
||||
Err(io::Error::last_os_error().into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the group of the device.
|
||||
pub fn group(&mut self, value: i32) -> Result<()> {
|
||||
unsafe {
|
||||
if tunsetgroup(self.as_raw_fd(), &value) < 0 {
|
||||
Err(io::Error::last_os_error().into())
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn split(mut self) -> (posix::Reader, posix::Writer) {
|
||||
let queue = self.queues.swap_remove(0);
|
||||
let fd = Arc::new(queue.tun);
|
||||
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
}
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&mut self) -> bool {
|
||||
self.queues[0].has_packet_information()
|
||||
}
|
||||
|
||||
/// Set non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.queues[0].set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.queues[0].read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.queues[0].read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.queues[0].write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.queues[0].flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.queues[0].write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||
unsafe {
|
||||
let name = CString::new(value)?;
|
||||
|
||||
if name.as_bytes_with_nul().len() > IFNAMSIZ {
|
||||
return Err(Error::NameTooLong);
|
||||
}
|
||||
|
||||
let mut req = self.request();
|
||||
ptr::copy_nonoverlapping(
|
||||
name.as_ptr() as *const c_char,
|
||||
req.ifru.newname.as_mut_ptr(),
|
||||
value.len(),
|
||||
);
|
||||
|
||||
if siocsifname(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
self.name = value.into();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
if value {
|
||||
req.ifru.flags |= IFF_UP | IFF_RUNNING;
|
||||
} else {
|
||||
req.ifru.flags &= !IFF_UP;
|
||||
}
|
||||
|
||||
if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn address(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.addr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.addr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn destination(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.dstaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.dstaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.broadaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.broadaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.netmask).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.netmask = SockAddr::from(value).into();
|
||||
|
||||
if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn mtu(&self) -> Result<i32> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(req.ifru.mtu)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.mtu = value;
|
||||
|
||||
if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||
self.queues.get_mut(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.queues[0].as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Device {
|
||||
fn into_raw_fd(mut self) -> RawFd {
|
||||
// It is Ok to swap the first queue with the last one, because the self will be dropped afterwards
|
||||
let queue = self.queues.swap_remove(0);
|
||||
queue.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Queue {
|
||||
tun: Fd,
|
||||
pi_enabled: bool,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn has_packet_information(&mut self) -> bool {
|
||||
self.pi_enabled
|
||||
}
|
||||
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Queue {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Queue {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Queue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.tun.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Queue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.tun.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Layer> for c_short {
|
||||
fn from(layer: Layer) -> Self {
|
||||
match layer {
|
||||
Layer::L2 => IFF_TAP,
|
||||
Layer::L3 => IFF_TUN,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Linux specific functionality.
|
||||
|
||||
pub mod sys;
|
||||
|
||||
mod device;
|
||||
pub use self::device::{Device, Queue};
|
||||
|
||||
use crate::configuration::Configuration as C;
|
||||
use crate::error::*;
|
||||
|
||||
/// Linux-only interface configuration.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct Configuration {
|
||||
pub(crate) packet_information: bool,
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
/// Enable or disable packet information, when enabled the first 4 bytes of
|
||||
/// each packet is a header with flags and protocol type.
|
||||
pub fn packet_information(&mut self, value: bool) -> &mut Self {
|
||||
self.packet_information = value;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create(configuration: &C) -> Result<Device> {
|
||||
Device::new(configuration)
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Bindings to internal Linux stuff.
|
||||
|
||||
use ioctl::*;
|
||||
use libc::sockaddr;
|
||||
use libc::{c_char, c_int, c_short, c_uchar, c_uint, c_ulong, c_ushort, c_void};
|
||||
|
||||
pub const IFNAMSIZ: usize = 16;
|
||||
|
||||
pub const IFF_UP: c_short = 0x1;
|
||||
pub const IFF_RUNNING: c_short = 0x40;
|
||||
|
||||
pub const IFF_TUN: c_short = 0x0001;
|
||||
pub const IFF_TAP: c_short = 0x0002;
|
||||
pub const IFF_NO_PI: c_short = 0x1000;
|
||||
pub const IFF_MULTI_QUEUE: c_short = 0x0100;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifmap {
|
||||
pub mem_start: c_ulong,
|
||||
pub mem_end: c_ulong,
|
||||
pub base_addr: c_ushort,
|
||||
pub irq: c_uchar,
|
||||
pub dma: c_uchar,
|
||||
pub port: c_uchar,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union ifsu {
|
||||
pub raw_hdlc_proto: *mut c_void,
|
||||
pub cisco: *mut c_void,
|
||||
pub fr: *mut c_void,
|
||||
pub fr_pvc: *mut c_void,
|
||||
pub fr_pvc_info: *mut c_void,
|
||||
pub sync: *mut c_void,
|
||||
pub te1: *mut c_void,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct if_settings {
|
||||
pub type_: c_uint,
|
||||
pub size: c_uint,
|
||||
pub ifsu: ifsu,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union ifrn {
|
||||
pub name: [c_char; IFNAMSIZ],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union ifru {
|
||||
pub addr: sockaddr,
|
||||
pub dstaddr: sockaddr,
|
||||
pub broadaddr: sockaddr,
|
||||
pub netmask: sockaddr,
|
||||
pub hwaddr: sockaddr,
|
||||
|
||||
pub flags: c_short,
|
||||
pub ivalue: c_int,
|
||||
pub mtu: c_int,
|
||||
pub map: ifmap,
|
||||
pub slave: [c_char; IFNAMSIZ],
|
||||
pub newname: [c_char; IFNAMSIZ],
|
||||
pub data: *mut c_void,
|
||||
pub settings: if_settings,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifreq {
|
||||
pub ifrn: ifrn,
|
||||
pub ifru: ifru,
|
||||
}
|
||||
|
||||
ioctl!(bad read siocgifflags with 0x8913; ifreq);
|
||||
ioctl!(bad write siocsifflags with 0x8914; ifreq);
|
||||
ioctl!(bad read siocgifaddr with 0x8915; ifreq);
|
||||
ioctl!(bad write siocsifaddr with 0x8916; ifreq);
|
||||
ioctl!(bad read siocgifdstaddr with 0x8917; ifreq);
|
||||
ioctl!(bad write siocsifdstaddr with 0x8918; ifreq);
|
||||
ioctl!(bad read siocgifbrdaddr with 0x8919; ifreq);
|
||||
ioctl!(bad write siocsifbrdaddr with 0x891a; ifreq);
|
||||
ioctl!(bad read siocgifnetmask with 0x891b; ifreq);
|
||||
ioctl!(bad write siocsifnetmask with 0x891c; ifreq);
|
||||
ioctl!(bad read siocgifmtu with 0x8921; ifreq);
|
||||
ioctl!(bad write siocsifmtu with 0x8922; ifreq);
|
||||
ioctl!(bad write siocsifname with 0x8923; ifreq);
|
||||
|
||||
ioctl!(write tunsetiff with b'T', 202; c_int);
|
||||
ioctl!(write tunsetpersist with b'T', 203; c_int);
|
||||
ioctl!(write tunsetowner with b'T', 204; c_int);
|
||||
ioctl!(write tunsetgroup with b'T', 206; c_int);
|
||||
@@ -1,438 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
#![allow(unused_variables)]
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::mem;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
use std::ptr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libc;
|
||||
use libc::{c_char, c_uint, c_void, sockaddr, socklen_t, AF_INET, SOCK_DGRAM};
|
||||
|
||||
use crate::configuration::{Configuration, Layer};
|
||||
use crate::device::Device as D;
|
||||
use crate::error::*;
|
||||
use crate::platform::macos::sys::*;
|
||||
use crate::platform::posix::{self, Fd, SockAddr};
|
||||
|
||||
/// A TUN device using the TUN macOS driver.
|
||||
pub struct Device {
|
||||
name: String,
|
||||
queue: Queue,
|
||||
ctl: Fd,
|
||||
}
|
||||
|
||||
impl Device {
|
||||
/// Create a new `Device` for the given `Configuration`.
|
||||
pub fn new(config: &Configuration) -> Result<Self> {
|
||||
let id = if let Some(name) = config.name.as_ref() {
|
||||
if name.len() > IFNAMSIZ {
|
||||
return Err(Error::NameTooLong);
|
||||
}
|
||||
|
||||
if !name.starts_with("utun") {
|
||||
return Err(Error::InvalidName);
|
||||
}
|
||||
|
||||
name[4..].parse()?
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
if config.layer.filter(|l| *l != Layer::L3).is_some() {
|
||||
return Err(Error::UnsupportedLayer);
|
||||
}
|
||||
|
||||
let queues_number = config.queues.unwrap_or(1);
|
||||
if queues_number != 1 {
|
||||
return Err(Error::InvalidQueuesNumber);
|
||||
}
|
||||
|
||||
let mut device = unsafe {
|
||||
let tun = Fd::new(libc::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL))
|
||||
.map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
let mut info = ctl_info {
|
||||
ctl_id: 0,
|
||||
ctl_name: {
|
||||
let mut buffer = [0; 96];
|
||||
for (i, o) in UTUN_CONTROL_NAME.as_bytes().iter().zip(buffer.iter_mut()) {
|
||||
*o = *i as _;
|
||||
}
|
||||
buffer
|
||||
},
|
||||
};
|
||||
|
||||
if ctliocginfo(tun.0, &mut info as *mut _ as *mut _) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
let addr = sockaddr_ctl {
|
||||
sc_id: info.ctl_id,
|
||||
sc_len: mem::size_of::<sockaddr_ctl>() as _,
|
||||
sc_family: AF_SYSTEM,
|
||||
ss_sysaddr: AF_SYS_CONTROL,
|
||||
sc_unit: id as c_uint,
|
||||
sc_reserved: [0; 5],
|
||||
};
|
||||
|
||||
if libc::connect(
|
||||
tun.0,
|
||||
&addr as *const sockaddr_ctl as *const sockaddr,
|
||||
mem::size_of_val(&addr) as socklen_t,
|
||||
) < 0
|
||||
{
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
let mut name = [0u8; 64];
|
||||
let mut name_len: socklen_t = 64;
|
||||
|
||||
if libc::getsockopt(
|
||||
tun.0,
|
||||
SYSPROTO_CONTROL,
|
||||
UTUN_OPT_IFNAME,
|
||||
&mut name as *mut _ as *mut c_void,
|
||||
&mut name_len as *mut socklen_t,
|
||||
) < 0
|
||||
{
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
let ctl = Fd::new(libc::socket(AF_INET, SOCK_DGRAM, 0))
|
||||
.map_err(|_| io::Error::last_os_error())?;
|
||||
|
||||
Device {
|
||||
name: CStr::from_ptr(name.as_ptr() as *const c_char)
|
||||
.to_string_lossy()
|
||||
.into(),
|
||||
queue: Queue { tun: tun },
|
||||
ctl: ctl,
|
||||
}
|
||||
};
|
||||
|
||||
device.configure(&config)?;
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
/// Prepare a new request.
|
||||
pub unsafe fn request(&self) -> ifreq {
|
||||
let mut req: ifreq = mem::zeroed();
|
||||
ptr::copy_nonoverlapping(
|
||||
self.name.as_ptr() as *const c_char,
|
||||
req.ifrn.name.as_mut_ptr(),
|
||||
self.name.len(),
|
||||
);
|
||||
|
||||
req
|
||||
}
|
||||
|
||||
/// Set the IPv4 alias of the device.
|
||||
pub fn set_alias(&mut self, addr: Ipv4Addr, broadaddr: Ipv4Addr, mask: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req: ifaliasreq = mem::zeroed();
|
||||
ptr::copy_nonoverlapping(
|
||||
self.name.as_ptr() as *const c_char,
|
||||
req.ifran.as_mut_ptr(),
|
||||
self.name.len(),
|
||||
);
|
||||
|
||||
req.addr = SockAddr::from(addr).into();
|
||||
req.broadaddr = SockAddr::from(broadaddr).into();
|
||||
req.mask = SockAddr::from(mask).into();
|
||||
|
||||
if siocaifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Split the interface into a `Reader` and `Writer`.
|
||||
pub fn split(self) -> (posix::Reader, posix::Writer) {
|
||||
let fd = Arc::new(self.queue.tun);
|
||||
(posix::Reader(fd.clone()), posix::Writer(fd.clone()))
|
||||
}
|
||||
|
||||
/// Return whether the device has packet information
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
self.queue.has_packet_information()
|
||||
}
|
||||
|
||||
/// Set non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.queue.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Device {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.queue.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Device {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.queue.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.queue.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.queue.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl D for Device {
|
||||
type Queue = Queue;
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
// XXX: Cannot set interface name on Darwin.
|
||||
fn set_name(&mut self, value: &str) -> Result<()> {
|
||||
Err(Error::InvalidName)
|
||||
}
|
||||
|
||||
fn enabled(&mut self, value: bool) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifflags(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
if value {
|
||||
req.ifru.flags |= IFF_UP | IFF_RUNNING;
|
||||
} else {
|
||||
req.ifru.flags &= !IFF_UP;
|
||||
}
|
||||
|
||||
if siocsifflags(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn address(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.addr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_address(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.addr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn destination(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifdstaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.dstaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_destination(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.dstaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifdstaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn broadcast(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifbrdaddr(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::new(&req.ifru.broadaddr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_broadcast(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.broadaddr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifbrdaddr(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn netmask(&self) -> Result<Ipv4Addr> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifnetmask(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
SockAddr::unchecked(&req.ifru.addr).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_netmask(&mut self, value: Ipv4Addr) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.addr = SockAddr::from(value).into();
|
||||
|
||||
if siocsifnetmask(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn mtu(&self) -> Result<i32> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
|
||||
if siocgifmtu(self.ctl.as_raw_fd(), &mut req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(req.ifru.mtu)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_mtu(&mut self, value: i32) -> Result<()> {
|
||||
unsafe {
|
||||
let mut req = self.request();
|
||||
req.ifru.mtu = value;
|
||||
|
||||
if siocsifmtu(self.ctl.as_raw_fd(), &req) < 0 {
|
||||
return Err(io::Error::last_os_error().into());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn queue(&mut self, index: usize) -> Option<&mut Self::Queue> {
|
||||
if index > 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(&mut self.queue)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Device {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.queue.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Device {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.queue.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Queue {
|
||||
tun: Fd,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
pub fn has_packet_information(&self) -> bool {
|
||||
// on macos this is always the case
|
||||
true
|
||||
}
|
||||
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
self.tun.set_nonblock()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Queue {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.tun.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Queue {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.tun.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Queue {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.tun.read(buf)
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.tun.read_vectored(bufs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Queue {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.tun.write(buf)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.tun.flush()
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.tun.write_vectored(bufs)
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! macOS specific functionality.
|
||||
|
||||
pub mod sys;
|
||||
|
||||
mod device;
|
||||
pub use self::device::{Device, Queue};
|
||||
|
||||
use crate::configuration::Configuration as C;
|
||||
use crate::error::*;
|
||||
|
||||
/// macOS-only interface configuration.
|
||||
#[derive(Copy, Clone, Default, Debug)]
|
||||
pub struct Configuration {}
|
||||
|
||||
/// Create a TUN device with the given name.
|
||||
pub fn create(configuration: &C) -> Result<Device> {
|
||||
Device::new(&configuration)
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Bindings to internal macOS stuff.
|
||||
|
||||
use ioctl::*;
|
||||
use libc::sockaddr;
|
||||
use libc::{c_char, c_int, c_short, c_uint, c_ushort, c_void};
|
||||
|
||||
pub const IFNAMSIZ: usize = 16;
|
||||
|
||||
pub const IFF_UP: c_short = 0x1;
|
||||
pub const IFF_RUNNING: c_short = 0x40;
|
||||
|
||||
pub const AF_SYS_CONTROL: c_ushort = 2;
|
||||
pub const AF_SYSTEM: c_char = 32;
|
||||
pub const PF_SYSTEM: c_int = AF_SYSTEM as c_int;
|
||||
pub const SYSPROTO_CONTROL: c_int = 2;
|
||||
pub const UTUN_OPT_IFNAME: c_int = 2;
|
||||
pub const UTUN_CONTROL_NAME: &str = "com.apple.net.utun_control";
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ctl_info {
|
||||
pub ctl_id: c_uint,
|
||||
pub ctl_name: [c_char; 96],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct sockaddr_ctl {
|
||||
pub sc_len: c_char,
|
||||
pub sc_family: c_char,
|
||||
pub ss_sysaddr: c_ushort,
|
||||
pub sc_id: c_uint,
|
||||
pub sc_unit: c_uint,
|
||||
pub sc_reserved: [c_uint; 5],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union ifrn {
|
||||
pub name: [c_char; IFNAMSIZ],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifdevmtu {
|
||||
pub current: c_int,
|
||||
pub min: c_int,
|
||||
pub max: c_int,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union ifku {
|
||||
pub ptr: *mut c_void,
|
||||
pub value: c_int,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifkpi {
|
||||
pub module_id: c_uint,
|
||||
pub type_: c_uint,
|
||||
pub ifku: ifku,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub union ifru {
|
||||
pub addr: sockaddr,
|
||||
pub dstaddr: sockaddr,
|
||||
pub broadaddr: sockaddr,
|
||||
|
||||
pub flags: c_short,
|
||||
pub metric: c_int,
|
||||
pub mtu: c_int,
|
||||
pub phys: c_int,
|
||||
pub media: c_int,
|
||||
pub intval: c_int,
|
||||
pub data: *mut c_void,
|
||||
pub devmtu: ifdevmtu,
|
||||
pub wake_flags: c_uint,
|
||||
pub route_refcnt: c_uint,
|
||||
pub cap: [c_int; 2],
|
||||
pub functional_type: c_uint,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifreq {
|
||||
pub ifrn: ifrn,
|
||||
pub ifru: ifru,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ifaliasreq {
|
||||
pub ifran: [c_char; IFNAMSIZ],
|
||||
pub addr: sockaddr,
|
||||
pub broadaddr: sockaddr,
|
||||
pub mask: sockaddr,
|
||||
}
|
||||
|
||||
ioctl!(readwrite ctliocginfo with 'N', 3; ctl_info);
|
||||
|
||||
ioctl!(write siocsifflags with 'i', 16; ifreq);
|
||||
ioctl!(readwrite siocgifflags with 'i', 17; ifreq);
|
||||
|
||||
ioctl!(write siocsifaddr with 'i', 12; ifreq);
|
||||
ioctl!(readwrite siocgifaddr with 'i', 33; ifreq);
|
||||
|
||||
ioctl!(write siocsifdstaddr with 'i', 14; ifreq);
|
||||
ioctl!(readwrite siocgifdstaddr with 'i', 34; ifreq);
|
||||
|
||||
ioctl!(write siocsifbrdaddr with 'i', 19; ifreq);
|
||||
ioctl!(readwrite siocgifbrdaddr with 'i', 35; ifreq);
|
||||
|
||||
ioctl!(write siocsifnetmask with 'i', 22; ifreq);
|
||||
ioctl!(readwrite siocgifnetmask with 'i', 37; ifreq);
|
||||
|
||||
ioctl!(write siocsifmtu with 'i', 52; ifreq);
|
||||
ioctl!(readwrite siocgifmtu with 'i', 51; ifreq);
|
||||
|
||||
ioctl!(write siocaifaddr with 'i', 26; ifaliasreq);
|
||||
ioctl!(write siocdifaddr with 'i', 25; ifreq);
|
||||
@@ -1,70 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! Platform specific modules.
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod posix;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod linux;
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use self::linux::{create, Configuration, Device, Queue};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod macos;
|
||||
#[cfg(target_os = "macos")]
|
||||
pub use self::macos::{create, Configuration, Device, Queue};
|
||||
|
||||
#[cfg(target_os = "ios")]
|
||||
pub mod ios;
|
||||
#[cfg(target_os = "ios")]
|
||||
pub use self::ios::{create, Configuration, Device, Queue};
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub mod android;
|
||||
#[cfg(target_os = "android")]
|
||||
pub use self::android::{create, Configuration, Device, Queue};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::configuration::Configuration;
|
||||
use crate::device::Device;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[test]
|
||||
fn create() {
|
||||
let dev = super::create(
|
||||
Configuration::default()
|
||||
.name("utun6")
|
||||
.address("192.168.50.1")
|
||||
.netmask("255.255.0.0")
|
||||
.mtu(1400)
|
||||
.up(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
"192.168.50.1".parse::<Ipv4Addr>().unwrap(),
|
||||
dev.address().unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
"255.255.0.0".parse::<Ipv4Addr>().unwrap(),
|
||||
dev.netmask().unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(1400, dev.mtu().unwrap());
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
|
||||
|
||||
use crate::error::*;
|
||||
use libc::{self, fcntl, F_GETFL, F_SETFL, O_NONBLOCK};
|
||||
|
||||
/// POSIX file descriptor support for `io` traits.
|
||||
pub struct Fd(pub RawFd);
|
||||
|
||||
impl Fd {
|
||||
pub fn new(value: RawFd) -> Result<Self> {
|
||||
if value < 0 {
|
||||
return Err(Error::InvalidDescriptor);
|
||||
}
|
||||
|
||||
Ok(Fd(value))
|
||||
}
|
||||
|
||||
/// Enable non-blocking mode
|
||||
pub fn set_nonblock(&self) -> io::Result<()> {
|
||||
match unsafe { fcntl(self.0, F_SETFL, fcntl(self.0, F_GETFL) | O_NONBLOCK) } {
|
||||
0 => Ok(()),
|
||||
_ => Err(io::Error::last_os_error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Fd {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::read(self.0, buf.as_mut_ptr() as *mut _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let iov = bufs.as_ptr().cast();
|
||||
let iovcnt = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||
|
||||
let n = libc::readv(self.0, iov, iovcnt);
|
||||
if n < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Fd {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::write(self.0, buf.as_ptr() as *const _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let iov = bufs.as_ptr().cast();
|
||||
let iovcnt = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||
|
||||
let n = libc::writev(self.0, iov, iovcnt);
|
||||
if n < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Fd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for Fd {
|
||||
fn into_raw_fd(mut self) -> RawFd {
|
||||
let fd = self.0;
|
||||
self.0 = -1;
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Fd {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
if self.0 >= 0 {
|
||||
libc::close(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
//! POSIX compliant support.
|
||||
|
||||
mod sockaddr;
|
||||
pub use self::sockaddr::SockAddr;
|
||||
|
||||
mod fd;
|
||||
pub use self::fd::Fd;
|
||||
|
||||
mod split;
|
||||
pub use self::split::{Reader, Writer};
|
||||
@@ -1,96 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::mem;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::ptr;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
use libc::c_uchar;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use libc::c_ushort;
|
||||
|
||||
use libc::AF_INET as _AF_INET;
|
||||
use libc::{in_addr, sockaddr, sockaddr_in};
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
/// A wrapper for `sockaddr_in`.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct SockAddr(sockaddr_in);
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
const AF_INET: c_ushort = _AF_INET as c_ushort;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
const AF_INET: c_uchar = _AF_INET as c_uchar;
|
||||
|
||||
impl SockAddr {
|
||||
/// Create a new `SockAddr` from a generic `sockaddr`.
|
||||
pub fn new(value: &sockaddr) -> Result<Self> {
|
||||
if value.sa_family != AF_INET {
|
||||
return Err(Error::InvalidAddress);
|
||||
}
|
||||
|
||||
unsafe { Self::unchecked(value) }
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// Create a new `SockAddr` and not check the source.
|
||||
pub unsafe fn unchecked(value: &sockaddr) -> Result<Self> {
|
||||
Ok(SockAddr(ptr::read(value as *const _ as *const _)))
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// Get a generic pointer to the `SockAddr`.
|
||||
pub unsafe fn as_ptr(&self) -> *const sockaddr {
|
||||
&self.0 as *const _ as *const sockaddr
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ipv4Addr> for SockAddr {
|
||||
fn from(ip: Ipv4Addr) -> SockAddr {
|
||||
let octets = ip.octets();
|
||||
let mut addr = unsafe { mem::zeroed::<sockaddr_in>() };
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = 0;
|
||||
addr.sin_addr = in_addr {
|
||||
s_addr: u32::from_ne_bytes(octets),
|
||||
};
|
||||
|
||||
SockAddr(addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SockAddr> for Ipv4Addr {
|
||||
fn from(addr: SockAddr) -> Ipv4Addr {
|
||||
let ip = addr.0.sin_addr.s_addr;
|
||||
let [a, b, c, d] = ip.to_ne_bytes();
|
||||
|
||||
Ipv4Addr::new(a, b, c, d)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SockAddr> for sockaddr {
|
||||
fn from(addr: SockAddr) -> sockaddr {
|
||||
unsafe { mem::transmute(addr.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SockAddr> for sockaddr_in {
|
||||
fn from(addr: SockAddr) -> sockaddr_in {
|
||||
addr.0
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// Version 2, December 2004
|
||||
//
|
||||
// Copyleft (ↄ) meh. <[email protected]> | http://meh.schizofreni.co
|
||||
//
|
||||
// Everyone is permitted to copy and distribute verbatim or modified
|
||||
// copies of this license document, and changing it is allowed as long
|
||||
// as the name is changed.
|
||||
//
|
||||
// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
// TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
//
|
||||
// 0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::mem;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::platform::posix::Fd;
|
||||
use libc;
|
||||
|
||||
/// Read-only end for a file descriptor.
|
||||
pub struct Reader(pub(crate) Arc<Fd>);
|
||||
|
||||
/// Write-only end for a file descriptor.
|
||||
pub struct Writer(pub(crate) Arc<Fd>);
|
||||
|
||||
impl Read for Reader {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::read(self.0.as_raw_fd(), buf.as_mut_ptr() as *mut _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let mut msg: libc::msghdr = mem::zeroed();
|
||||
// msg.msg_name: NULL
|
||||
// msg.msg_namelen: 0
|
||||
msg.msg_iov = bufs.as_mut_ptr().cast();
|
||||
msg.msg_iovlen = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||
|
||||
let n = libc::recvmsg(self.0.as_raw_fd(), &mut msg, 0);
|
||||
if n < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Writer {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let amount = libc::write(self.0.as_raw_fd(), buf.as_ptr() as *const _, buf.len());
|
||||
|
||||
if amount < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(amount as usize)
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
unsafe {
|
||||
let mut msg: libc::msghdr = mem::zeroed();
|
||||
// msg.msg_name = NULL
|
||||
// msg.msg_namelen = 0
|
||||
msg.msg_iov = bufs.as_ptr() as *mut _;
|
||||
msg.msg_iovlen = bufs.len().min(libc::c_int::MAX as usize) as _;
|
||||
|
||||
let n = libc::sendmsg(self.0.as_raw_fd(), &msg, 0);
|
||||
if n < 0 {
|
||||
return Err(io::Error::last_os_error());
|
||||
}
|
||||
|
||||
Ok(n as usize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Reader {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for Writer {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.0.as_raw_fd()
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
use std::io;
|
||||
|
||||
use crossbeam::channel::RecvError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("packet error")]
|
||||
PacketError(#[from] packet::error::Error),
|
||||
#[error("Io error")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("Channel error")]
|
||||
Channel(#[from] RecvError),
|
||||
#[error("Protobuf error")]
|
||||
Protobuf(#[from] protobuf::Error),
|
||||
#[error("Invalid packet")]
|
||||
InvalidPacket,
|
||||
#[error("Not support")]
|
||||
NotSupport,
|
||||
#[error("Stop")]
|
||||
Stop(String),
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -1,45 +0,0 @@
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Local;
|
||||
|
||||
use crate::DEVICE_LIST;
|
||||
use crate::error::*;
|
||||
use crate::handle::DIRECT_ROUTE_TABLE;
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, Version};
|
||||
use crate::protocol::control_packet::PingPacket;
|
||||
|
||||
pub fn handle_loop(udp: UdpSocket, server_addr: SocketAddr) -> Result<()> {
|
||||
const INTERVAL: u64 = 3000;
|
||||
const MAX_INTERVAL: i64 = 3000 * 3;
|
||||
let mut buf = [0u8; (4 + 8 + 4)];
|
||||
let mut net_packet = NetPacket::new(&mut buf)?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Control);
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Ping.into());
|
||||
net_packet.set_ttl(255);
|
||||
loop {
|
||||
let current_time = Local::now().timestamp_millis();
|
||||
{
|
||||
let mut ping = PingPacket::new(net_packet.payload_mut())?;
|
||||
ping.set_time(current_time);
|
||||
let epoch = { DEVICE_LIST.lock().0 };
|
||||
ping.set_epoch(epoch);
|
||||
}
|
||||
let _ = udp.send_to(net_packet.buffer(), server_addr);
|
||||
for x in DIRECT_ROUTE_TABLE.iter() {
|
||||
let virtual_ip = x.key().clone();
|
||||
let route = x.value().clone();
|
||||
drop(x);
|
||||
if current_time - route.recv_time <= MAX_INTERVAL {
|
||||
let _ = udp.send_to(net_packet.buffer(), route.address);
|
||||
} else {
|
||||
DIRECT_ROUTE_TABLE.remove_if(&virtual_ip, |_, route| {
|
||||
current_time - route.recv_time <= MAX_INTERVAL
|
||||
});
|
||||
}
|
||||
}
|
||||
thread::sleep(Duration::from_millis(INTERVAL));
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
use std::net::{ Ipv4Addr, SocketAddr};
|
||||
use std::sync::atomic::AtomicI64;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Local;
|
||||
use dashmap::DashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use moka::sync::Cache;
|
||||
use parking_lot::{const_mutex, Mutex};
|
||||
|
||||
use crate::proto::message::NatType;
|
||||
|
||||
pub mod heartbeat_handler;
|
||||
pub mod punch_handler;
|
||||
pub mod registration_handler;
|
||||
pub mod tun_handler;
|
||||
pub mod udp_recv_handler;
|
||||
lazy_static! {
|
||||
/// 0. 机器纪元,每一次上线或者下线都会增1,由服务端维护,用于感知网络中机器变化
|
||||
/// 服务端和客户端的不一致,则服务端会推送新的设备列表
|
||||
/// 1. 网络中的虚拟ip列表
|
||||
pub static ref DEVICE_LIST:Mutex<(u32,Vec<Ipv4Addr>)> = const_mutex((0,Vec::new()));
|
||||
/// 服务器延迟
|
||||
pub static ref SERVER_RT:AtomicI64 = AtomicI64::new(-1);
|
||||
/// id
|
||||
pub static ref ID:AtomicI64 = AtomicI64::new(0);
|
||||
/// 直连路由表
|
||||
pub static ref DIRECT_ROUTE_TABLE:DashMap<Ipv4Addr,Route> = DashMap::new();
|
||||
/// 地址映射
|
||||
pub static ref ADDR_TABLE:Cache<SocketAddr,Ipv4Addr> = Cache::builder()
|
||||
.time_to_idle(Duration::from_secs(60*5)).build();
|
||||
/// 当前设备的nat信息
|
||||
pub static ref NAT_INFO:Mutex<Option<NatInfo>> = const_mutex(None);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NatInfo {
|
||||
public_ips: Vec<u32>,
|
||||
public_port: u16,
|
||||
public_port_range: u16,
|
||||
nat_type: NatType,
|
||||
}
|
||||
|
||||
impl NatInfo {
|
||||
pub fn new(public_ips: Vec<u32>,
|
||||
public_port: u16,
|
||||
public_port_range: u16,
|
||||
nat_type: NatType, ) -> Self {
|
||||
Self {
|
||||
public_ips,
|
||||
public_port,
|
||||
public_port_range,
|
||||
nat_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 初始化nat信息
|
||||
pub fn init_nat_info(public_ip: u32, public_port: u16) {
|
||||
match crate::nat::check::public_ip_list() {
|
||||
Ok((nat_type, ips, port_range)) => {
|
||||
let mut public_ips = Vec::new();
|
||||
public_ips.push(public_ip);
|
||||
for ip in ips {
|
||||
let ip = u32::from_be_bytes(ip.octets());
|
||||
if ip != public_ip {
|
||||
public_ips.push(ip);
|
||||
}
|
||||
}
|
||||
let nat_info = NatInfo::new(public_ips,
|
||||
public_port,
|
||||
port_range, nat_type);
|
||||
// println!("nat信息:{:?}",nat_info);
|
||||
let mut nat_info_lock = NAT_INFO.lock();
|
||||
nat_info_lock.replace(nat_info);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("获取nat数据失败,将无法进行udp打洞:{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CurrentDeviceInfo {
|
||||
pub(crate) virtual_ip: Ipv4Addr,
|
||||
pub(crate) virtual_gateway: Ipv4Addr,
|
||||
pub(crate) virtual_netmask: Ipv4Addr,
|
||||
//网络地址
|
||||
pub(crate) virtual_network: Ipv4Addr,
|
||||
//直接广播地址
|
||||
pub(crate) broadcast_address: Ipv4Addr,
|
||||
//链接的服务器地址
|
||||
pub(crate) connect_server: SocketAddr,
|
||||
}
|
||||
|
||||
impl CurrentDeviceInfo {
|
||||
pub fn new(virtual_ip: Ipv4Addr, virtual_gateway: Ipv4Addr, virtual_netmask: Ipv4Addr, connect_server: SocketAddr) -> Self {
|
||||
let broadcast_address = (!u32::from_be_bytes(virtual_netmask.octets()))
|
||||
| u32::from_be_bytes(virtual_gateway.octets());
|
||||
let broadcast_address = Ipv4Addr::from(broadcast_address);
|
||||
let virtual_network = u32::from_be_bytes(virtual_netmask.octets())
|
||||
& u32::from_be_bytes(virtual_gateway.octets());
|
||||
let virtual_network = Ipv4Addr::from(virtual_network);
|
||||
Self {
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
virtual_network,
|
||||
broadcast_address,
|
||||
connect_server,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Debug)]
|
||||
pub struct Route {
|
||||
pub(crate) address: SocketAddr,
|
||||
//用心跳探测延迟,收包时更新
|
||||
pub(crate) delay: i64,
|
||||
//收包时更新,如果太久没有收到消息则剔除
|
||||
pub(crate) recv_time: i64,
|
||||
}
|
||||
|
||||
impl Route {
|
||||
pub fn new(address: SocketAddr) -> Self {
|
||||
Self {
|
||||
address,
|
||||
delay: -1,
|
||||
recv_time: Local::now().timestamp_millis(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam::channel::{Receiver, RecvTimeoutError, Sender, SendError, TrySendError};
|
||||
use dashmap::DashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::{CurrentDeviceInfo, DEVICE_LIST, NAT_INFO, NatInfo};
|
||||
use crate::error::*;
|
||||
use crate::handle::DIRECT_ROUTE_TABLE;
|
||||
use crate::proto::message::{NatType, Punch, Step};
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, turn_packet, Version};
|
||||
use crate::protocol::control_packet::PunchRequestPacket;
|
||||
use crate::protocol::turn_packet::TurnPacket;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref STEP_MAP:DashMap<Ipv4Addr,Step> = DashMap::new();
|
||||
}
|
||||
/// 每一种类型一个通道,减少相互干扰
|
||||
pub fn bounded() -> (PunchSender, ConeReceiver, ReqSymmetricReceiver, ResSymmetricReceiver) {
|
||||
let (cone_sender, cone_receiver) = crossbeam::channel::bounded(3);
|
||||
let (req_symmetric_sender, req_symmetric_receiver) = crossbeam::channel::bounded(1);
|
||||
let (res_symmetric_sender, res_symmetric_receiver) = crossbeam::channel::bounded(1);
|
||||
(PunchSender::new(cone_sender, req_symmetric_sender, res_symmetric_sender),
|
||||
ConeReceiver(cone_receiver), ReqSymmetricReceiver(req_symmetric_receiver),
|
||||
ResSymmetricReceiver(res_symmetric_receiver))
|
||||
}
|
||||
|
||||
pub struct ConeReceiver(Receiver<Punch>);
|
||||
|
||||
pub struct ReqSymmetricReceiver(Receiver<Punch>);
|
||||
|
||||
pub struct ResSymmetricReceiver(Receiver<Punch>);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PunchSender {
|
||||
cone_sender: Sender<Punch>,
|
||||
req_symmetric_sender: Sender<Punch>,
|
||||
res_symmetric_sender: Sender<Punch>,
|
||||
}
|
||||
|
||||
impl PunchSender {
|
||||
pub fn new(cone_sender: Sender<Punch>,
|
||||
req_symmetric_sender: Sender<Punch>,
|
||||
res_symmetric_sender: Sender<Punch>, ) -> Self {
|
||||
Self {
|
||||
cone_sender,
|
||||
req_symmetric_sender,
|
||||
res_symmetric_sender,
|
||||
}
|
||||
}
|
||||
pub fn send(&self, punch: Punch) -> std::result::Result<(), SendError<Punch>> {
|
||||
match punch.nat_type.enum_value_or_default() {
|
||||
NatType::Symmetric => {
|
||||
if punch.reply {
|
||||
// 为true表示回应,也就是主动发起的打洞操作
|
||||
self.res_symmetric_sender.send(punch)
|
||||
} else {
|
||||
self.req_symmetric_sender.send(punch)
|
||||
}
|
||||
}
|
||||
NatType::Cone => {
|
||||
self.cone_sender.send(punch)
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn try_send(&self, punch: Punch) -> std::result::Result<(), TrySendError<Punch>> {
|
||||
match punch.nat_type.enum_value_or_default() {
|
||||
NatType::Symmetric => {
|
||||
if punch.reply {
|
||||
// 为true表示回应,也就是主动发起的打洞操作
|
||||
self.res_symmetric_sender.try_send(punch)
|
||||
} else {
|
||||
self.req_symmetric_sender.try_send(punch)
|
||||
}
|
||||
}
|
||||
NatType::Cone => {
|
||||
self.cone_sender.try_send(punch)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle(udp: &UdpSocket, punch_list: Vec<Punch>, buf: &[u8]) -> Result<()> {
|
||||
let mut counter = 0u64;
|
||||
for punch in punch_list {
|
||||
let dest = Ipv4Addr::from(punch.virtual_ip);
|
||||
if DIRECT_ROUTE_TABLE.contains_key(&dest) {
|
||||
continue;
|
||||
}
|
||||
// println!("punch {:?}", punch);
|
||||
match punch.nat_type.enum_value_or_default() {
|
||||
NatType::Symmetric => {
|
||||
match punch.step.enum_value_or_default() {
|
||||
Step::Step1 | Step::Step2 | Step::Step3 => {
|
||||
//预测范围发送
|
||||
for pub_ip in punch.public_ip_list {
|
||||
let pub_ip = Ipv4Addr::from(pub_ip);
|
||||
for range in 0..punch.public_port_range + 1 {
|
||||
let right_port = ((punch.public_port + range) & 0xFFFF) as u16;
|
||||
let left_port = ((0xFFFF + punch.public_port - range) & 0xFFFF) as u16;
|
||||
if right_port != 0 {
|
||||
// println!("{:?}", SocketAddr::V4(SocketAddrV4::new(pub_ip, right_port)));
|
||||
udp.send_to(
|
||||
buf,
|
||||
SocketAddr::V4(SocketAddrV4::new(pub_ip, right_port)),
|
||||
)?;
|
||||
select_sleep(&mut counter);
|
||||
}
|
||||
if left_port != 0 && range != 0 {
|
||||
// println!("{:?}", SocketAddr::V4(SocketAddrV4::new(pub_ip, right_port)));
|
||||
if left_port == right_port {
|
||||
break;
|
||||
}
|
||||
udp.send_to(
|
||||
buf,
|
||||
SocketAddr::V4(SocketAddrV4::new(pub_ip, left_port)),
|
||||
)?;
|
||||
select_sleep(&mut counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Step::Step4 => {
|
||||
//全范围发送
|
||||
for pub_ip in punch.public_ip_list {
|
||||
let pub_ip = Ipv4Addr::from(pub_ip);
|
||||
for port in 1..0xFFFF {
|
||||
udp.send_to(
|
||||
buf,
|
||||
SocketAddr::V4(SocketAddrV4::new(pub_ip, port)),
|
||||
)?;
|
||||
select_sleep(&mut counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NatType::Cone => {
|
||||
for pub_ip in punch.public_ip_list {
|
||||
udp.send_to(
|
||||
buf,
|
||||
SocketAddr::V4(SocketAddrV4::new(
|
||||
Ipv4Addr::from(pub_ip),
|
||||
punch.public_port as u16,
|
||||
)),
|
||||
)?;
|
||||
select_sleep(&mut counter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 给对称nat发送打洞数据包
|
||||
pub fn req_symmetric_handle_loop(
|
||||
receiver: ReqSymmetricReceiver,
|
||||
udp: UdpSocket,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let receiver = receiver.0;
|
||||
handle_loop(receiver, udp, cur_info)
|
||||
}
|
||||
|
||||
/// 给对称nat发送打洞数据包,处理主动发起的打洞操作
|
||||
pub fn res_symmetric_handle_loop(
|
||||
receiver: ResSymmetricReceiver,
|
||||
udp: UdpSocket,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let receiver = receiver.0;
|
||||
let mut buf = [0u8; 12];
|
||||
let mut packet = NetPacket::new(&mut buf)?;
|
||||
packet.set_version(Version::V1);
|
||||
packet.set_ttl(255);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
|
||||
{
|
||||
let mut punch_packet = PunchRequestPacket::new(packet.payload_mut())?;
|
||||
punch_packet.set_source(cur_info.virtual_ip);
|
||||
}
|
||||
loop {
|
||||
match receiver.recv_timeout(Duration::from_secs(30)) {
|
||||
Ok(punch) => {
|
||||
let mut list = Vec::new();
|
||||
list.push(punch);
|
||||
loop {
|
||||
match receiver.try_recv() {
|
||||
Ok(punch) => {
|
||||
list.push(punch);
|
||||
}
|
||||
Err(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for punch in &list {
|
||||
let dest = Ipv4Addr::from(punch.virtual_ip);
|
||||
match punch.step.enum_value_or_default() {
|
||||
Step::Step1 => {
|
||||
STEP_MAP.insert(dest, Step::Step2);
|
||||
}
|
||||
Step::Step2 => {
|
||||
STEP_MAP.insert(dest, Step::Step3);
|
||||
}
|
||||
Step::Step3 => {
|
||||
STEP_MAP.insert(dest, Step::Step4);
|
||||
}
|
||||
Step::Step4 => {
|
||||
STEP_MAP.insert(dest, Step::Step1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(e) = handle(&udp, list, packet.buffer()) {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
}
|
||||
Err(RecvTimeoutError::Timeout) => {
|
||||
punch_request_handle(&udp, &cur_info)?;
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(Error::Stop("打洞线程通道关闭".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 给锥形nat发送打洞数据包
|
||||
pub fn cone_handle_loop(
|
||||
receiver: ConeReceiver,
|
||||
udp: UdpSocket,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let receiver = receiver.0;
|
||||
handle_loop(receiver, udp, cur_info)
|
||||
}
|
||||
|
||||
pub fn handle_loop(
|
||||
receiver: Receiver<Punch>,
|
||||
udp: UdpSocket,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let mut buf = [0u8; 12];
|
||||
let mut packet = NetPacket::new(&mut buf)?;
|
||||
packet.set_version(Version::V1);
|
||||
packet.set_ttl(255);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
|
||||
{
|
||||
let mut punch_packet = PunchRequestPacket::new(packet.payload_mut())?;
|
||||
punch_packet.set_source(cur_info.virtual_ip);
|
||||
}
|
||||
loop {
|
||||
match receiver.recv() {
|
||||
Ok(punch) => {
|
||||
let mut list = Vec::new();
|
||||
list.push(punch);
|
||||
loop {
|
||||
match receiver.try_recv() {
|
||||
Ok(punch) => {
|
||||
list.push(punch);
|
||||
}
|
||||
Err(_) => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(e) = handle(&udp, list, packet.buffer()) {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return Err(Error::Stop("打洞线程通道关闭".to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_sleep(counter: &mut u64) {
|
||||
*counter += 1;
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
// if *counter > 1 {
|
||||
// if cone_nat {
|
||||
// thread::sleep(Duration::from_millis(2));
|
||||
// } else {
|
||||
// if (*counter) & 10 == 10 {
|
||||
// thread::sleep(Duration::from_millis(1));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
fn punch_request_handle(udp: &UdpSocket, cur_info: &CurrentDeviceInfo) -> Result<()> {
|
||||
let nat_info_lock = NAT_INFO.lock();
|
||||
let nat_info = nat_info_lock.clone();
|
||||
drop(nat_info_lock);
|
||||
if let Some(nat_info) = nat_info {
|
||||
if let Err(e) = send_punch(&udp,
|
||||
&cur_info,
|
||||
nat_info) {
|
||||
println!("发送打洞数据失败 :{:?}", e);
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Stop("未初始化nat信息".to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn send_punch(udp: &UdpSocket, cur_info: &CurrentDeviceInfo, nat_info: NatInfo) -> Result<()> {
|
||||
let lock = DEVICE_LIST.lock();
|
||||
let list = lock.1.clone();
|
||||
drop(lock);
|
||||
for ip in list {
|
||||
//只向ip比自己大的发起打洞,避免双方同时发起打洞浪费流量
|
||||
if ip > cur_info.virtual_ip && !DIRECT_ROUTE_TABLE.contains_key(&ip) {
|
||||
let step = if let Some(step) = STEP_MAP.get(&ip) {
|
||||
*step
|
||||
} else {
|
||||
Step::Step1
|
||||
};
|
||||
let bytes = punch_packet(cur_info.virtual_ip,
|
||||
nat_info.clone(), ip, step)?;
|
||||
udp.send_to(&bytes, cur_info.connect_server)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn punch_packet(virtual_ip: Ipv4Addr, nat_info: NatInfo, dest: Ipv4Addr, step: Step) -> Result<Vec<u8>> {
|
||||
let mut punch_reply = Punch::new();
|
||||
punch_reply.reply = false;
|
||||
punch_reply.virtual_ip = u32::from_be_bytes(virtual_ip.octets());
|
||||
punch_reply.step = protobuf::EnumOrUnknown::new(step);
|
||||
punch_reply.public_ip_list = nat_info.public_ips;
|
||||
punch_reply.public_port = nat_info.public_port as u32;
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.nat_type = protobuf::EnumOrUnknown::new(nat_info.nat_type);
|
||||
let bytes = punch_reply.write_to_bytes()?;
|
||||
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + bytes.len()])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::OtherTurn);
|
||||
net_packet.set_transport_protocol(turn_packet::Protocol::Punch.into());
|
||||
net_packet.set_ttl(255);
|
||||
let mut turn_packet = TurnPacket::new(net_packet.payload_mut())?;
|
||||
turn_packet.set_source(virtual_ip);
|
||||
turn_packet.set_destination(dest);
|
||||
turn_packet.set_payload(&bytes);
|
||||
Ok(net_packet.into_buffer())
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::Local;
|
||||
use parking_lot::RwLock;
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::error::*;
|
||||
use crate::proto::message::{RegistrationRequest, RegistrationResponse};
|
||||
use crate::protocol::{error_packet, NetPacket, Protocol, service_packet, Version};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref REQUEST:RwLock<Option<(String,String)>> = parking_lot::const_rwlock(None);
|
||||
static ref REGISTRATION_TIME:AtomicI64=AtomicI64::new(0);
|
||||
}
|
||||
|
||||
///向中继服务器注册,token标识一个虚拟网关,mac_address防止多次注册时得到的ip不一致
|
||||
pub fn registration(
|
||||
udp: &UdpSocket,
|
||||
server_address: SocketAddr,
|
||||
token: String,
|
||||
mac_address: String,
|
||||
) -> Result<RegistrationResponse> {
|
||||
// todo 和服务器通信加密
|
||||
let request_packet = registration_request_packet(token.clone(), mac_address.clone())?;
|
||||
let buf = request_packet.buffer();
|
||||
let mut counter = 0;
|
||||
let mut recv_buf = [0u8; 10240];
|
||||
udp.set_read_timeout(Some(Duration::from_millis(500)))?;
|
||||
loop {
|
||||
counter += 1;
|
||||
if counter & 10 == 10 {
|
||||
return Err(Error::Stop("注册请求超时".to_string()));
|
||||
}
|
||||
udp.send_to(buf, server_address)?;
|
||||
let (len, addr) = match udp.recv_from(&mut recv_buf) {
|
||||
Ok(ok) => ok,
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock || e.kind() == io::ErrorKind::TimedOut {
|
||||
continue;
|
||||
}
|
||||
return Err(Error::Io(e));
|
||||
}
|
||||
};
|
||||
if server_address != addr {
|
||||
continue;
|
||||
}
|
||||
let net_packet = NetPacket::new(&recv_buf[..len])?;
|
||||
match net_packet.protocol() {
|
||||
Protocol::Service => {
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
let response =
|
||||
RegistrationResponse::parse_from_bytes(net_packet.payload())?;
|
||||
let _ = REQUEST.write().replace((token, mac_address));
|
||||
udp.set_read_timeout(None)?;
|
||||
return Ok(response);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Protocol::Error => {
|
||||
match error_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
error_packet::Protocol::TokenError => {
|
||||
return Err(Error::Stop("token错误".to_string()));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn registration_request_packet(token: String, mac_address: String) -> Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = RegistrationRequest::new();
|
||||
request.token = token;
|
||||
request.mac_address = mac_address;
|
||||
let bytes = request.write_to_bytes()?;
|
||||
let buf = vec![0u8; 4 + bytes.len()];
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into());
|
||||
net_packet.set_ttl(255);
|
||||
net_packet.set_payload(&bytes);
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
pub fn fast_registration(udp: &UdpSocket, server_address: SocketAddr) -> Result<()> {
|
||||
let last = REGISTRATION_TIME.load(Ordering::Relaxed);
|
||||
let new = Local::now().timestamp_millis();
|
||||
if new - last < 2000
|
||||
|| REGISTRATION_TIME
|
||||
.compare_exchange(last, new, Ordering::Relaxed, Ordering::Relaxed)
|
||||
.is_err()
|
||||
{
|
||||
//短时间不重复注册
|
||||
return Ok(());
|
||||
}
|
||||
let lock = REQUEST.read();
|
||||
let option = lock.clone();
|
||||
drop(lock);
|
||||
if let Some((token, mac_address)) = option {
|
||||
let request_packet = registration_request_packet(token, mac_address)?;
|
||||
udp.send_to(request_packet.buffer(), server_address)?;
|
||||
REGISTRATION_TIME.store(Local::now().timestamp_millis(), Ordering::Relaxed);
|
||||
return Ok(());
|
||||
}
|
||||
return Err(Error::Stop("注册信息不存在".to_string()));
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
/// 接收tun数据,并且转发到udp上
|
||||
use std::net::{IpAddr, Ipv4Addr, UdpSocket};
|
||||
|
||||
use chrono::Local;
|
||||
use packet::icmp::icmp::IcmpPacket;
|
||||
use packet::icmp::Kind;
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::error::*;
|
||||
use crate::handle::{CurrentDeviceInfo, DIRECT_ROUTE_TABLE};
|
||||
use crate::protocol::{NetPacket, Protocol, Version};
|
||||
use crate::protocol::turn_packet::TurnPacket;
|
||||
use crate::tun_device::TunReader;
|
||||
|
||||
/// 是否在一个网段
|
||||
fn check_dest(dest: Ipv4Addr, cur_info: &CurrentDeviceInfo) -> bool {
|
||||
u32::from_be_bytes(dest.octets()) & u32::from_be_bytes(cur_info.virtual_netmask.octets())
|
||||
== u32::from_be_bytes(cur_info.virtual_network.octets())
|
||||
}
|
||||
|
||||
fn icmp(udp: &UdpSocket, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> Result<()> {
|
||||
if ipv4_packet.protocol() == ipv4::protocol::Protocol::Icmp {
|
||||
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
|
||||
if icmp.kind() == Kind::EchoRequest {
|
||||
icmp.set_kind(Kind::EchoReply);
|
||||
icmp.update_checksum();
|
||||
let src = ipv4_packet.source_ip();
|
||||
ipv4_packet.set_source_ip(ipv4_packet.destination_ip());
|
||||
ipv4_packet.set_destination_ip(src);
|
||||
ipv4_packet.update_checksum();
|
||||
let mut addr = udp.local_addr()?;
|
||||
addr.set_ip(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
|
||||
udp.send_to(ipv4_packet.buffer, addr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn handle(
|
||||
udp: &UdpSocket,
|
||||
data: &mut [u8],
|
||||
cur_info: &CurrentDeviceInfo,
|
||||
net_packet: &mut NetPacket<Vec<u8>>,
|
||||
) -> Result<()> {
|
||||
let data_len = data.len();
|
||||
let ipv4_packet = match IpV4Packet::new(data) {
|
||||
Ok(ipv4_packet) => ipv4_packet,
|
||||
Err(packet::error::Error::Unimplemented) => {
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => Err(e)?,
|
||||
};
|
||||
let src_ip = ipv4_packet.source_ip();
|
||||
let dest_ip = ipv4_packet.destination_ip();
|
||||
// if dest_ip == cur_info.broadcast_address {
|
||||
// // 启动服务后会收到对137端口的广播
|
||||
// // 137端口是在局域网中提供计算机的名字或IP地址查询服务
|
||||
// return Ok(());
|
||||
// }
|
||||
if src_ip != cur_info.virtual_ip || !check_dest(dest_ip, &cur_info) {
|
||||
return Ok(());
|
||||
}
|
||||
if src_ip == dest_ip {
|
||||
return icmp(&udp, ipv4_packet);
|
||||
}
|
||||
let mut ipv4_turn_packet = TurnPacket::new(net_packet.payload_mut())?;
|
||||
ipv4_turn_packet.set_source(src_ip);
|
||||
ipv4_turn_packet.set_destination(dest_ip);
|
||||
ipv4_turn_packet.set_payload(ipv4_packet.buffer);
|
||||
//优先发到直连到地址
|
||||
if let Some(route) = DIRECT_ROUTE_TABLE.get(&dest_ip) {
|
||||
let current_time = Local::now().timestamp_millis();
|
||||
if current_time - route.recv_time < 3_000 {
|
||||
udp.send_to(&net_packet.buffer()[..(4 + 8 + data_len)], route.address)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
udp.send_to(&net_packet.buffer()[..(4 + 8 + data_len)], cur_info.connect_server)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn handle_loop(
|
||||
udp: UdpSocket,
|
||||
tun_reader: TunReader,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Ipv4Turn);
|
||||
net_packet.set_transport_protocol(ipv4::protocol::Protocol::Ipv4.into());
|
||||
net_packet.set_ttl(255);
|
||||
loop {
|
||||
let mut data = tun_reader.next()?;
|
||||
match handle(&udp, data.bytes_mut(), &cur_info, &mut net_packet) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(unix))]
|
||||
pub fn handle_loop(
|
||||
udp: UdpSocket,
|
||||
mut tun_reader: TunReader,
|
||||
cur_info: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + 1500])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::Ipv4Turn);
|
||||
net_packet.set_transport_protocol(0);
|
||||
net_packet.set_ttl(255);
|
||||
let mut buf = [0u8; 1500];
|
||||
loop {
|
||||
let data = tun_reader.read(&mut buf)?;
|
||||
match handle(&udp, data, &cur_info, &mut net_packet) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
println!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr, UdpSocket};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use chrono::Local;
|
||||
use crossbeam::channel::{Receiver, Sender, TrySendError};
|
||||
use packet::icmp::{icmp, Kind};
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::CurrentDeviceInfo;
|
||||
use crate::error::*;
|
||||
use crate::handle::{ADDR_TABLE, DEVICE_LIST, DIRECT_ROUTE_TABLE, NAT_INFO, Route, SERVER_RT};
|
||||
use crate::handle::punch_handler::PunchSender;
|
||||
use crate::handle::registration_handler::fast_registration;
|
||||
use crate::proto::message::{DeviceList, Punch, RegistrationResponse};
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol, service_packet, turn_packet, Version};
|
||||
use crate::protocol::control_packet::{ControlPacket, PunchResponsePacket};
|
||||
use crate::protocol::error_packet::InErrorPacket;
|
||||
use crate::protocol::turn_packet::TurnPacket;
|
||||
use crate::tun_device::TunWriter;
|
||||
|
||||
pub fn recv_loop(
|
||||
udp: UdpSocket,
|
||||
server_addr: SocketAddr,
|
||||
other_sender: Sender<(SocketAddr, Vec<u8>)>,
|
||||
mut tun_writer: TunWriter,
|
||||
current_device: CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let mut buf = [0u8; 65536];
|
||||
let mut local_addr = udp.local_addr()?;
|
||||
local_addr.set_ip(std::net::IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
|
||||
loop {
|
||||
match udp.recv_from(&mut buf) {
|
||||
Ok((len, addr)) => {
|
||||
if addr == local_addr {
|
||||
//本地的包直接再发到网卡,这个主要用于处理当前虚拟ip的icmp ping
|
||||
if let Ok(ip) = IpV4Packet::new(&buf[..len]) {
|
||||
if ip.destination_ip() == current_device.virtual_ip {
|
||||
let _ = tun_writer.write(&buf[..len]);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
match recv_handle(
|
||||
&udp,
|
||||
addr,
|
||||
&mut buf[..len],
|
||||
&server_addr,
|
||||
&other_sender,
|
||||
&mut tun_writer,
|
||||
¤t_device,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(Error::Stop(str)) => {
|
||||
return Err(Error::Stop(str));
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{:?}", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_handle(
|
||||
udp: &UdpSocket,
|
||||
recv_addr: SocketAddr,
|
||||
buf: &mut [u8],
|
||||
_server_addr: &SocketAddr,
|
||||
other_sender: &Sender<(SocketAddr, Vec<u8>)>,
|
||||
tun_writer: &mut TunWriter,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> Result<()> {
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
match net_packet.protocol() {
|
||||
Protocol::Ipv4Turn => {
|
||||
let mut ipv4_turn_packet = TurnPacket::new(net_packet.payload_mut())?;
|
||||
let source = ipv4_turn_packet.source();
|
||||
let destination = ipv4_turn_packet.destination();
|
||||
let mut ipv4 = IpV4Packet::new(ipv4_turn_packet.payload_mut())?;
|
||||
if ipv4.source_ip() == source
|
||||
&& ipv4.destination_ip() == destination
|
||||
&& current_device.virtual_ip == ipv4.destination_ip()
|
||||
{
|
||||
if ipv4.protocol() == ipv4::protocol::Protocol::Icmp {
|
||||
let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?;
|
||||
if icmp_packet.kind() == Kind::EchoRequest {
|
||||
//开启ping
|
||||
icmp_packet.set_kind(Kind::EchoReply);
|
||||
icmp_packet.update_checksum();
|
||||
ipv4.set_source_ip(destination);
|
||||
ipv4.set_destination_ip(source);
|
||||
ipv4.update_checksum();
|
||||
ipv4_turn_packet.set_source(destination);
|
||||
ipv4_turn_packet.set_destination(source);
|
||||
udp.send_to(net_packet.buffer(), recv_addr)?;
|
||||
} else {
|
||||
tun_writer.write(ipv4_turn_packet.payload())?;
|
||||
}
|
||||
} else {
|
||||
tun_writer.write(ipv4_turn_packet.payload())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Protocol::UnKnow(_) => {}
|
||||
_ => {
|
||||
//发送到子线程处理
|
||||
let v = net_packet.buffer().to_vec();
|
||||
match other_sender.try_send((recv_addr, v)) {
|
||||
Ok(_) => {}
|
||||
Err(TrySendError::Disconnected(_)) => {
|
||||
return Err(Error::Stop("处理线程停止".to_string()));
|
||||
}
|
||||
Err(e) => {
|
||||
println!("子线程处理 :{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn other_loop(
|
||||
udp: UdpSocket,
|
||||
receiver: Receiver<(SocketAddr, Vec<u8>)>,
|
||||
current_device: CurrentDeviceInfo,
|
||||
sender: PunchSender,
|
||||
) -> Result<()> {
|
||||
loop {
|
||||
let (peer_addr, buf) = receiver.recv()?;
|
||||
match other_handle(&udp, buf, peer_addr, ¤t_device, &sender) {
|
||||
Ok(_) => {}
|
||||
Err(Error::Stop(str)) => {
|
||||
return Err(Error::Stop(str));
|
||||
}
|
||||
Err(e) => {
|
||||
println!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn other_handle(
|
||||
udp: &UdpSocket,
|
||||
buf: Vec<u8>,
|
||||
peer_addr: SocketAddr,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
sender: &PunchSender,
|
||||
) -> Result<()> {
|
||||
let server_addr = current_device.connect_server;
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
match net_packet.protocol() {
|
||||
Protocol::Service => {
|
||||
if peer_addr != current_device.connect_server {
|
||||
return Ok(());
|
||||
}
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationRequest => {}
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
let response = RegistrationResponse::parse_from_bytes(net_packet.payload())?;
|
||||
crate::handle::init_nat_info(response.public_ip, response.public_port as u16);
|
||||
//todo 重连之后ip可能会发生改变(目前2分钟内未重连则会释放ip),需要更新本地ip(或者保证重连ip不变)
|
||||
}
|
||||
service_packet::Protocol::UpdateDeviceList => {
|
||||
let device_list = DeviceList::parse_from_bytes(net_packet.payload())?;
|
||||
let ip_list: Vec<Ipv4Addr> = device_list
|
||||
.virtual_ip_list
|
||||
.iter()
|
||||
.map(|ip| Ipv4Addr::from(*ip))
|
||||
.collect();
|
||||
let mut dev = DEVICE_LIST.lock();
|
||||
if dev.0 < device_list.epoch || device_list.epoch - dev.0 > u32::MAX >> 2 {
|
||||
dev.0 = device_list.epoch;
|
||||
dev.1 = ip_list;
|
||||
}
|
||||
}
|
||||
service_packet::Protocol::UnKnow(_) => {}
|
||||
}
|
||||
}
|
||||
Protocol::Error => {
|
||||
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
InErrorPacket::TokenError => {
|
||||
if server_addr == peer_addr {
|
||||
//停止整个应用
|
||||
return Err(Error::Stop("token无效".to_string()));
|
||||
}
|
||||
}
|
||||
InErrorPacket::Disconnect => {
|
||||
if server_addr == peer_addr {
|
||||
fast_registration(&udp, server_addr)?;
|
||||
}
|
||||
}
|
||||
InErrorPacket::OtherError(e) => {
|
||||
println!("{:?}", e.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
Protocol::Control => {
|
||||
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
ControlPacket::PingPacket(ping) => {
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Pong.into());
|
||||
udp.send_to(&net_packet.buffer()[..12], peer_addr)?;
|
||||
}
|
||||
ControlPacket::PongPacket(pong_packet) => {
|
||||
let current_time = Local::now().timestamp_millis();
|
||||
let rt = current_time - pong_packet.time();
|
||||
if rt >= 0 {
|
||||
if peer_addr == server_addr {
|
||||
SERVER_RT.store(rt, Ordering::Relaxed)
|
||||
} else {
|
||||
//其他设备
|
||||
if let Some(virtual_ip) = ADDR_TABLE.get(&peer_addr) {
|
||||
if let Some(mut info) = DIRECT_ROUTE_TABLE.get_mut(&virtual_ip) {
|
||||
info.delay = rt;
|
||||
info.recv_time = current_time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ControlPacket::PunchRequest(punch_request) => {
|
||||
// println!("打洞请求:{:?}", punch_request);
|
||||
let src = punch_request.source();
|
||||
drop(punch_request);
|
||||
//回应
|
||||
let mut punch_response = PunchResponsePacket::new(net_packet.payload_mut())?;
|
||||
punch_response.set_source(current_device.virtual_ip);
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
|
||||
udp.send_to(net_packet.buffer(), peer_addr)?;
|
||||
let route = Route::new(peer_addr);
|
||||
DIRECT_ROUTE_TABLE.insert(src, route);
|
||||
ADDR_TABLE.insert(peer_addr, src);
|
||||
}
|
||||
ControlPacket::PunchResponse(punch_response) => {
|
||||
// println!("打洞响应:{:?}", punch_response);
|
||||
let route = Route::new(peer_addr);
|
||||
DIRECT_ROUTE_TABLE.insert(punch_response.source(), route);
|
||||
ADDR_TABLE.insert(peer_addr, punch_response.source());
|
||||
}
|
||||
}
|
||||
}
|
||||
Protocol::Ipv4Turn => {}
|
||||
Protocol::OtherTurn => {
|
||||
let turn_packet = TurnPacket::new(net_packet.payload())?;
|
||||
// println!("{:?}",turn_packet);
|
||||
let src = turn_packet.source();
|
||||
let dest = turn_packet.destination();
|
||||
if dest == current_device.virtual_ip {
|
||||
match turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
turn_packet::Protocol::Punch => {
|
||||
let punch = Punch::parse_from_bytes(turn_packet.payload())?;
|
||||
if punch.virtual_ip.to_be_bytes() == src.octets() {
|
||||
if !punch.reply {
|
||||
let mut punch_reply = Punch::new();
|
||||
punch_reply.reply = true;
|
||||
punch_reply.virtual_ip = u32::from_be_bytes(current_device.virtual_ip.octets());
|
||||
punch_reply.step = punch.step;
|
||||
if let Err(_) = sender.try_send(punch) {
|
||||
return Ok(());
|
||||
}
|
||||
let nat_info = NAT_INFO.lock();
|
||||
if let Some(info) = nat_info.as_ref() {
|
||||
punch_reply.public_ip_list = info.public_ips.clone();
|
||||
punch_reply.public_port = info.public_port as u32;
|
||||
punch_reply.public_port_range = info.public_port_range as u32;
|
||||
punch_reply.nat_type = protobuf::EnumOrUnknown::new(info.nat_type);
|
||||
drop(nat_info);
|
||||
let bytes = punch_reply.write_to_bytes()?;
|
||||
let mut net_packet = NetPacket::new(vec![0u8; 4 + 8 + bytes.len()])?;
|
||||
net_packet.set_version(Version::V1);
|
||||
net_packet.set_protocol(Protocol::OtherTurn);
|
||||
net_packet.set_transport_protocol(turn_packet::Protocol::Punch.into());
|
||||
net_packet.set_ttl(255);
|
||||
let mut turn_packet = TurnPacket::new(net_packet.payload_mut())?;
|
||||
turn_packet.set_source(current_device.virtual_ip);
|
||||
turn_packet.set_destination(src);
|
||||
turn_packet.set_payload(&bytes);
|
||||
udp.send_to(net_packet.buffer(), peer_addr)?;
|
||||
}
|
||||
} else {
|
||||
let _ = sender.try_send(punch);
|
||||
}
|
||||
}
|
||||
}
|
||||
turn_packet::Protocol::UnKnow(_) => {}
|
||||
}
|
||||
} else {
|
||||
panic!("ip")
|
||||
}
|
||||
}
|
||||
Protocol::UnKnow(p) => {
|
||||
println!("未知协议:{}", p)
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
-248
@@ -1,248 +0,0 @@
|
||||
use std::{io, thread};
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use clap::Parser;
|
||||
use console::style;
|
||||
|
||||
use crate::handle::{CurrentDeviceInfo, DEVICE_LIST, DIRECT_ROUTE_TABLE, NAT_INFO, NatInfo, SERVER_RT};
|
||||
use crate::handle::registration_handler::registration;
|
||||
use crate::tun_device::create_tun;
|
||||
|
||||
pub mod tun_device;
|
||||
pub mod nat;
|
||||
pub mod error;
|
||||
pub mod handle;
|
||||
pub mod proto;
|
||||
pub mod protocol;
|
||||
#[cfg(windows)]
|
||||
pub mod admin_check;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author = "Lu Beilin", version, about = "一个虚拟网络工具,启动后会获取一个ip,相同token下的设备之间可以用ip直接通信")]
|
||||
struct Args {
|
||||
/// 32位字符
|
||||
/// 相同token的设备之间才能通信。
|
||||
/// 建议使用uuid保证唯一性。
|
||||
/// 32-bit characters.
|
||||
/// Only devices with the same token can communicate with each other.
|
||||
/// It is recommended to use uuid to ensure uniqueness
|
||||
#[arg(short, long)]
|
||||
token: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
#[cfg(windows)]
|
||||
if !admin_check::is_app_elevated() {
|
||||
let args: Vec<_> = std::env::args().collect();
|
||||
println!("{}", style("正在启动管理员权限执行...").red());
|
||||
if let Some(absolute_path) = std::env::current_exe()
|
||||
.ok()
|
||||
.and_then(|p| p.to_str().map(|p| p.to_string()))
|
||||
{
|
||||
let _ = runas::Command::new(&absolute_path).args(&args[1..]).status()
|
||||
.expect("failed to execute");
|
||||
} else {
|
||||
panic!("failed to execute")
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(any(unix))]
|
||||
if sudo::RunningAs::Root != sudo::check() {
|
||||
println!("{}", style("需要使用root权限执行...").red());
|
||||
sudo::escalate_if_needed().unwrap();
|
||||
}
|
||||
|
||||
println!("{}", style("启动服务...").green());
|
||||
|
||||
let token = args.token;
|
||||
// let d = Local::now().timestamp().to_string();
|
||||
let mac_address = mac_address::get_mac_address().unwrap().unwrap().to_string();
|
||||
let server_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(43, 139, 56, 10)), 29876);
|
||||
// let server_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), 29876);
|
||||
let mut port = 101 as u16;
|
||||
let udp = loop {
|
||||
match UdpSocket::bind(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(0), port))) {
|
||||
Ok(udp) => {
|
||||
break udp;
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::AddrInUse {
|
||||
port += 1;
|
||||
} else {
|
||||
println!("创建udp失败:{:?}", e);
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
//注册
|
||||
let response = registration(&udp, server_address, token, mac_address).unwrap();
|
||||
{
|
||||
let ip_list = response
|
||||
.virtual_ip_list
|
||||
.iter()
|
||||
.map(|ip| Ipv4Addr::from(*ip))
|
||||
.collect();
|
||||
let mut dev = DEVICE_LIST.lock();
|
||||
dev.0 = response.epoch;
|
||||
dev.1 = ip_list;
|
||||
}
|
||||
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
|
||||
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
|
||||
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
|
||||
println!("virtual_gateway:{:?}", virtual_gateway);
|
||||
println!("virtual_netmask:{:?}", virtual_netmask);
|
||||
println!("当前设备ip(virtual_ip):{}", style(virtual_ip).green());
|
||||
//心跳线程
|
||||
{
|
||||
let udp = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
if let Err(e) = handle::heartbeat_handler::handle_loop(udp, server_address) {
|
||||
println!("心跳线程停止:{:?}", e);
|
||||
}
|
||||
std::process::exit(1);
|
||||
});
|
||||
}
|
||||
//初始化nat数据
|
||||
handle::init_nat_info(response.public_ip, response.public_port as u16);
|
||||
// tun服务
|
||||
let (tun_writer, tun_reader) =
|
||||
create_tun(virtual_ip, virtual_netmask, virtual_gateway).unwrap();
|
||||
// 打洞数据通道
|
||||
let (punch_sender, cone_receiver, req_symmetric_receiver, res_symmetric_receiver) = handle::punch_handler::bounded();
|
||||
//udp数据处理
|
||||
{
|
||||
// 低优先级的udp数据通道
|
||||
let (sender, receiver) = crossbeam::channel::bounded(100);
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::udp_recv_handler::recv_loop(
|
||||
udp1,
|
||||
server_address,
|
||||
sender,
|
||||
tun_writer,
|
||||
current_device,
|
||||
) {
|
||||
println!("udp数据处理线程停止:{:?}", e);
|
||||
}
|
||||
std::process::exit(1);
|
||||
});
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::udp_recv_handler::other_loop(udp1, receiver, current_device, punch_sender) {
|
||||
println!("udp数据处理线程停止:{:?}", e);
|
||||
}
|
||||
std::process::exit(1);
|
||||
});
|
||||
}
|
||||
//打洞处理
|
||||
{
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::punch_handler::cone_handle_loop(cone_receiver, udp1, current_device) {
|
||||
println!("打洞响应线程停止:{:?}", e);
|
||||
}
|
||||
});
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::punch_handler::req_symmetric_handle_loop(req_symmetric_receiver, udp1, current_device) {
|
||||
println!("打洞触发线程停止:{:?}", e);
|
||||
}
|
||||
});
|
||||
let udp1 = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::punch_handler::res_symmetric_handle_loop(res_symmetric_receiver, udp1, current_device) {
|
||||
println!("打洞触发线程停止:{:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
//tun数据处理
|
||||
{
|
||||
let udp = udp.try_clone().unwrap();
|
||||
let _ = thread::spawn(move || {
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
if let Err(e) = handle::tun_handler::handle_loop(udp, tun_reader, current_device) {
|
||||
println!("tun数据处理线程停止:{:?}", e);
|
||||
}
|
||||
std::process::exit(1);
|
||||
});
|
||||
}
|
||||
use console::Term;
|
||||
let term = Term::stdout();
|
||||
let current_device = CurrentDeviceInfo::new(virtual_ip, virtual_gateway, virtual_netmask, server_address);
|
||||
loop {
|
||||
println!("{}", style("Please enter the command (Usage: list,status,exit,help):").color256(102));
|
||||
match term.read_line() {
|
||||
Ok(cmd) => {
|
||||
command(cmd.trim(), ¤t_device);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("read_line:{:?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn command(cmd: &str, current_device: &CurrentDeviceInfo) {
|
||||
match cmd {
|
||||
"list" => {
|
||||
let server_delay = SERVER_RT.load(Ordering::Relaxed);
|
||||
let device_list_lock = DEVICE_LIST.lock();
|
||||
let (_epoch, device_list) = device_list_lock.clone();
|
||||
drop(device_list_lock);
|
||||
if device_list.is_empty() {
|
||||
println!("No other devices found");
|
||||
return;
|
||||
}
|
||||
for ip in device_list {
|
||||
if let Some(route_ref) = DIRECT_ROUTE_TABLE.get(&ip) {
|
||||
let str = if route_ref.value().delay >= 0 {
|
||||
format!("{}(p2p delay:{}ms)", ip, route_ref.value().delay)
|
||||
} else {
|
||||
format!("{}(p2p)", ip)
|
||||
};
|
||||
drop(route_ref);
|
||||
println!("{}", style(str).green());
|
||||
} else {
|
||||
let str = if server_delay >= 0 {
|
||||
format!("{}(relay delay:{}ms)", ip, server_delay * 2)
|
||||
} else {
|
||||
format!("{}(relay)", ip)
|
||||
};
|
||||
println!("{}", style(str).blue());
|
||||
}
|
||||
}
|
||||
}
|
||||
"status" => {
|
||||
let server_delay = SERVER_RT.load(Ordering::Relaxed);
|
||||
println!("Virtual ip:{}", style(current_device.virtual_ip).green());
|
||||
println!("Virtual gateway:{}", style(current_device.virtual_gateway).green());
|
||||
println!("Relay server :{}", style(current_device.connect_server).green());
|
||||
if server_delay >= 0 {
|
||||
println!("Delay of relay server :{}", style(server_delay).green());
|
||||
}
|
||||
}
|
||||
"help" | "h" => {
|
||||
println!("Options: ");
|
||||
println!("{} , Query the virtual IP of other devices", style("list").green());
|
||||
println!("{} , View current device status", style("status").green());
|
||||
println!("{} , Exit the program", style("exit").green());
|
||||
}
|
||||
"exit" => {
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => {
|
||||
println!("command {} not fount. ", style(cmd).red());
|
||||
println!("Try to enter: '{}'", style("help").green());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
use std::{io, thread};
|
||||
use std::collections::HashSet;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::proto::message::NatType;
|
||||
|
||||
// #[derive(Debug, Copy, Clone, PartialEq)]
|
||||
// pub enum NatType {
|
||||
// Symmetric,
|
||||
// Cone,
|
||||
// }
|
||||
//
|
||||
// impl Into<u8> for NatType {
|
||||
// fn into(self) -> u8 {
|
||||
// match self {
|
||||
// NatType::Symmetric => 0,
|
||||
// NatType::Cone => 1,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// 返回所有公网ip和端口变化范围
|
||||
pub fn public_ip_list() -> io::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let mut hash_set = HashSet::new();
|
||||
let mut max_port_range = 0;
|
||||
let mut nat_type = NatType::Cone;
|
||||
let mut port = 88;
|
||||
for _ in 0..3 {
|
||||
let udp = loop {
|
||||
match UdpSocket::bind(SocketAddr::new(IpAddr::from(Ipv4Addr::from(0)), port)) {
|
||||
Ok(udp) => {
|
||||
break udp;
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::AddrInUse {
|
||||
port += 1;
|
||||
continue;
|
||||
}
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
let (set, min_port, max_port) = public_ip_list_(&udp)?;
|
||||
drop(udp);
|
||||
let port_range = max_port - min_port;
|
||||
//有多个ip或者端口有变化,说明是对称nat
|
||||
if nat_type == NatType::Cone && (set.len() > 1 || port_range != 0) {
|
||||
nat_type = NatType::Symmetric;
|
||||
}
|
||||
if max_port_range < port_range {
|
||||
max_port_range = port_range;
|
||||
}
|
||||
for x in set {
|
||||
hash_set.insert(x);
|
||||
}
|
||||
thread::sleep(Duration::from_micros(5));
|
||||
}
|
||||
Ok((nat_type, hash_set.into_iter().collect(), max_port_range))
|
||||
}
|
||||
|
||||
/// 测试样本较少,可能不对
|
||||
///
|
||||
/// - 移动宽带:锥形网络、一个ip、端口和局域网端口不相同
|
||||
/// - 电信宽带:锥形网络、一个ip,端口和局域网端口不相同
|
||||
/// - 联调宽带:对称网络、端口不变ip轮流用
|
||||
/// - 移动4g:对称网络、ip端口都变 使用小的端口变化量小
|
||||
/// - 联通4g:对称网络、只有一个ip 端口变化大
|
||||
/// - 电信4g:对称网络只有一个ip 公网端口比较连续
|
||||
/// - 综上:客户端使用小端口,针对对称网络 尝试所有ip 公网端口+-变化量的范围
|
||||
/// - 打通概率 移动宽带=电信宽带>联调宽带>电信4g>移动4g>>联调4g
|
||||
pub fn public_ip_list_(udp: &UdpSocket) -> io::Result<(HashSet<Ipv4Addr>, u16, u16)> {
|
||||
// println!("local port {:?}", udp.local_addr().unwrap().port());
|
||||
udp.set_read_timeout(Some(Duration::from_millis(300)))?;
|
||||
let mut buf = [0u8; 128];
|
||||
let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35061")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35062")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35061")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35062")?;
|
||||
let mut hash_set = HashSet::new();
|
||||
let mut count = 0;
|
||||
let mut min_port = 65535;
|
||||
let mut max_port = 0;
|
||||
for _ in 0..4 {
|
||||
if let Ok(len) = udp.recv(&mut buf) {
|
||||
if len != 16 || &buf[..10] != &b"NatType213"[..] {
|
||||
continue;
|
||||
}
|
||||
let port = u16::from_be_bytes([buf[14], buf[15]]);
|
||||
if min_port > port {
|
||||
min_port = port;
|
||||
}
|
||||
if max_port < port {
|
||||
max_port = port;
|
||||
}
|
||||
let ip = Ipv4Addr::new(buf[10], buf[11], buf[12], buf[13]);
|
||||
// println!("pub {:?}:{}", ip, port);
|
||||
hash_set.insert(ip);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
if count <= 1 {
|
||||
return Err(io::Error::from(io::ErrorKind::TimedOut));
|
||||
}
|
||||
Ok((hash_set, min_port, max_port))
|
||||
}
|
||||
|
||||
/// 返回nat类型
|
||||
pub fn nat_test() -> io::Result<NatType> {
|
||||
for _ in 0..3 {
|
||||
if NatType::Symmetric == nat_test_()? {
|
||||
return Ok(NatType::Symmetric);
|
||||
}
|
||||
thread::sleep(Duration::from_micros(5));
|
||||
}
|
||||
Ok(NatType::Cone)
|
||||
}
|
||||
|
||||
pub fn nat_test_() -> io::Result<NatType> {
|
||||
let udp = UdpSocket::bind("0.0.0.0:0")?;
|
||||
udp.set_read_timeout(Some(Duration::from_millis(300)))?;
|
||||
let mut buf = [0u8; 128];
|
||||
let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35061")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat1.wherewego.top:35062")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35061")?;
|
||||
let _ = udp.send_to(b"NatTest", "nat2.wherewego.top:35062")?;
|
||||
let mut tmp_ip_port: Option<[u8; 6]> = None;
|
||||
let mut count = 0;
|
||||
for _ in 0..4 {
|
||||
if let Ok(len) = udp.recv(&mut buf) {
|
||||
if len != 16 || &buf[..10] != &b"NatType213"[..] {
|
||||
continue;
|
||||
}
|
||||
count += 1;
|
||||
let mut ip_port = [0u8; 6];
|
||||
ip_port.copy_from_slice(&buf[10..16]);
|
||||
if let Some(tmp_ip_port) = &tmp_ip_port {
|
||||
if tmp_ip_port != &ip_port {
|
||||
return Ok(NatType::Symmetric);
|
||||
}
|
||||
} else {
|
||||
tmp_ip_port = Some(ip_port);
|
||||
}
|
||||
}
|
||||
}
|
||||
if count <= 1 {
|
||||
return Err(io::Error::from(io::ErrorKind::TimedOut));
|
||||
}
|
||||
Ok(NatType::Cone)
|
||||
}
|
||||
#[test]
|
||||
fn nat_test_run(){
|
||||
let udp = UdpSocket::bind("0.0.0.0:101").unwrap();
|
||||
let print = public_ip_list_(&udp).unwrap();
|
||||
println!("{:?}",print);
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
pub mod check;
|
||||
@@ -1,941 +0,0 @@
|
||||
// This file is generated by rust-protobuf 3.2.0. Do not edit
|
||||
// .proto file is parsed by pure
|
||||
// @generated
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/issues/702
|
||||
#![allow(unknown_lints)]
|
||||
#![allow(clippy::all)]
|
||||
|
||||
#![allow(unused_attributes)]
|
||||
#![cfg_attr(rustfmt, rustfmt::skip)]
|
||||
|
||||
#![allow(box_pointers)]
|
||||
#![allow(dead_code)]
|
||||
#![allow(missing_docs)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(trivial_casts)]
|
||||
#![allow(unused_results)]
|
||||
#![allow(unused_mut)]
|
||||
|
||||
//! Generated file from `message.proto`
|
||||
|
||||
/// Generated files are compatible only with the same version
|
||||
/// of protobuf runtime.
|
||||
const _PROTOBUF_VERSION_CHECK: () = ::protobuf::VERSION_3_2_0;
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:RegistrationRequest)
|
||||
pub struct RegistrationRequest {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.token)
|
||||
pub token: ::std::string::String,
|
||||
// @@protoc_insertion_point(field:RegistrationRequest.mac_address)
|
||||
pub mac_address: ::std::string::String,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:RegistrationRequest.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a RegistrationRequest {
|
||||
fn default() -> &'a RegistrationRequest {
|
||||
<RegistrationRequest as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistrationRequest {
|
||||
pub fn new() -> RegistrationRequest {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(2);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"token",
|
||||
|m: &RegistrationRequest| { &m.token },
|
||||
|m: &mut RegistrationRequest| { &mut m.token },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"mac_address",
|
||||
|m: &RegistrationRequest| { &m.mac_address },
|
||||
|m: &mut RegistrationRequest| { &mut m.mac_address },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RegistrationRequest>(
|
||||
"RegistrationRequest",
|
||||
fields,
|
||||
oneofs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for RegistrationRequest {
|
||||
const NAME: &'static str = "RegistrationRequest";
|
||||
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
|
||||
while let Some(tag) = is.read_raw_tag_or_eof()? {
|
||||
match tag {
|
||||
10 => {
|
||||
self.token = is.read_string()?;
|
||||
},
|
||||
18 => {
|
||||
self.mac_address = is.read_string()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u64 {
|
||||
let mut my_size = 0;
|
||||
if !self.token.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(1, &self.token);
|
||||
}
|
||||
if !self.mac_address.is_empty() {
|
||||
my_size += ::protobuf::rt::string_size(2, &self.mac_address);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
|
||||
if !self.token.is_empty() {
|
||||
os.write_string(1, &self.token)?;
|
||||
}
|
||||
if !self.mac_address.is_empty() {
|
||||
os.write_string(2, &self.mac_address)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn special_fields(&self) -> &::protobuf::SpecialFields {
|
||||
&self.special_fields
|
||||
}
|
||||
|
||||
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
|
||||
&mut self.special_fields
|
||||
}
|
||||
|
||||
fn new() -> RegistrationRequest {
|
||||
RegistrationRequest::new()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.token.clear();
|
||||
self.mac_address.clear();
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static RegistrationRequest {
|
||||
static instance: RegistrationRequest = RegistrationRequest {
|
||||
token: ::std::string::String::new(),
|
||||
mac_address: ::std::string::String::new(),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::MessageFull for RegistrationRequest {
|
||||
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("RegistrationRequest").unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for RegistrationRequest {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for RegistrationRequest {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:RegistrationResponse)
|
||||
pub struct RegistrationResponse {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.virtual_ip)
|
||||
pub virtual_ip: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.virtual_gateway)
|
||||
pub virtual_gateway: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.virtual_netmask)
|
||||
pub virtual_netmask: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.epoch)
|
||||
pub epoch: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.virtual_ip_list)
|
||||
pub virtual_ip_list: ::std::vec::Vec<u32>,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.public_ip)
|
||||
pub public_ip: u32,
|
||||
// @@protoc_insertion_point(field:RegistrationResponse.public_port)
|
||||
pub public_port: u32,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:RegistrationResponse.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a RegistrationResponse {
|
||||
fn default() -> &'a RegistrationResponse {
|
||||
<RegistrationResponse as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl RegistrationResponse {
|
||||
pub fn new() -> RegistrationResponse {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(7);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"virtual_ip",
|
||||
|m: &RegistrationResponse| { &m.virtual_ip },
|
||||
|m: &mut RegistrationResponse| { &mut m.virtual_ip },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"virtual_gateway",
|
||||
|m: &RegistrationResponse| { &m.virtual_gateway },
|
||||
|m: &mut RegistrationResponse| { &mut m.virtual_gateway },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"virtual_netmask",
|
||||
|m: &RegistrationResponse| { &m.virtual_netmask },
|
||||
|m: &mut RegistrationResponse| { &mut m.virtual_netmask },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"epoch",
|
||||
|m: &RegistrationResponse| { &m.epoch },
|
||||
|m: &mut RegistrationResponse| { &mut m.epoch },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"virtual_ip_list",
|
||||
|m: &RegistrationResponse| { &m.virtual_ip_list },
|
||||
|m: &mut RegistrationResponse| { &mut m.virtual_ip_list },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_ip",
|
||||
|m: &RegistrationResponse| { &m.public_ip },
|
||||
|m: &mut RegistrationResponse| { &mut m.public_ip },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_port",
|
||||
|m: &RegistrationResponse| { &m.public_port },
|
||||
|m: &mut RegistrationResponse| { &mut m.public_port },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<RegistrationResponse>(
|
||||
"RegistrationResponse",
|
||||
fields,
|
||||
oneofs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for RegistrationResponse {
|
||||
const NAME: &'static str = "RegistrationResponse";
|
||||
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
|
||||
while let Some(tag) = is.read_raw_tag_or_eof()? {
|
||||
match tag {
|
||||
13 => {
|
||||
self.virtual_ip = is.read_fixed32()?;
|
||||
},
|
||||
21 => {
|
||||
self.virtual_gateway = is.read_fixed32()?;
|
||||
},
|
||||
29 => {
|
||||
self.virtual_netmask = is.read_fixed32()?;
|
||||
},
|
||||
32 => {
|
||||
self.epoch = is.read_uint32()?;
|
||||
},
|
||||
42 => {
|
||||
is.read_repeated_packed_fixed32_into(&mut self.virtual_ip_list)?;
|
||||
},
|
||||
45 => {
|
||||
self.virtual_ip_list.push(is.read_fixed32()?);
|
||||
},
|
||||
53 => {
|
||||
self.public_ip = is.read_fixed32()?;
|
||||
},
|
||||
56 => {
|
||||
self.public_port = is.read_uint32()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u64 {
|
||||
let mut my_size = 0;
|
||||
if self.virtual_ip != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
if self.virtual_gateway != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
if self.virtual_netmask != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
if self.epoch != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(4, self.epoch);
|
||||
}
|
||||
my_size += 5 * self.virtual_ip_list.len() as u64;
|
||||
if self.public_ip != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
if self.public_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(7, self.public_port);
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
|
||||
if self.virtual_ip != 0 {
|
||||
os.write_fixed32(1, self.virtual_ip)?;
|
||||
}
|
||||
if self.virtual_gateway != 0 {
|
||||
os.write_fixed32(2, self.virtual_gateway)?;
|
||||
}
|
||||
if self.virtual_netmask != 0 {
|
||||
os.write_fixed32(3, self.virtual_netmask)?;
|
||||
}
|
||||
if self.epoch != 0 {
|
||||
os.write_uint32(4, self.epoch)?;
|
||||
}
|
||||
for v in &self.virtual_ip_list {
|
||||
os.write_fixed32(5, *v)?;
|
||||
};
|
||||
if self.public_ip != 0 {
|
||||
os.write_fixed32(6, self.public_ip)?;
|
||||
}
|
||||
if self.public_port != 0 {
|
||||
os.write_uint32(7, self.public_port)?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn special_fields(&self) -> &::protobuf::SpecialFields {
|
||||
&self.special_fields
|
||||
}
|
||||
|
||||
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
|
||||
&mut self.special_fields
|
||||
}
|
||||
|
||||
fn new() -> RegistrationResponse {
|
||||
RegistrationResponse::new()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.virtual_ip = 0;
|
||||
self.virtual_gateway = 0;
|
||||
self.virtual_netmask = 0;
|
||||
self.epoch = 0;
|
||||
self.virtual_ip_list.clear();
|
||||
self.public_ip = 0;
|
||||
self.public_port = 0;
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static RegistrationResponse {
|
||||
static instance: RegistrationResponse = RegistrationResponse {
|
||||
virtual_ip: 0,
|
||||
virtual_gateway: 0,
|
||||
virtual_netmask: 0,
|
||||
epoch: 0,
|
||||
virtual_ip_list: ::std::vec::Vec::new(),
|
||||
public_ip: 0,
|
||||
public_port: 0,
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::MessageFull for RegistrationResponse {
|
||||
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("RegistrationResponse").unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for RegistrationResponse {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for RegistrationResponse {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:DeviceList)
|
||||
pub struct DeviceList {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:DeviceList.epoch)
|
||||
pub epoch: u32,
|
||||
// @@protoc_insertion_point(field:DeviceList.virtual_ip_list)
|
||||
pub virtual_ip_list: ::std::vec::Vec<u32>,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:DeviceList.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a DeviceList {
|
||||
fn default() -> &'a DeviceList {
|
||||
<DeviceList as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceList {
|
||||
pub fn new() -> DeviceList {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(2);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"epoch",
|
||||
|m: &DeviceList| { &m.epoch },
|
||||
|m: &mut DeviceList| { &mut m.epoch },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"virtual_ip_list",
|
||||
|m: &DeviceList| { &m.virtual_ip_list },
|
||||
|m: &mut DeviceList| { &mut m.virtual_ip_list },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<DeviceList>(
|
||||
"DeviceList",
|
||||
fields,
|
||||
oneofs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for DeviceList {
|
||||
const NAME: &'static str = "DeviceList";
|
||||
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
|
||||
while let Some(tag) = is.read_raw_tag_or_eof()? {
|
||||
match tag {
|
||||
8 => {
|
||||
self.epoch = is.read_uint32()?;
|
||||
},
|
||||
18 => {
|
||||
is.read_repeated_packed_fixed32_into(&mut self.virtual_ip_list)?;
|
||||
},
|
||||
21 => {
|
||||
self.virtual_ip_list.push(is.read_fixed32()?);
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u64 {
|
||||
let mut my_size = 0;
|
||||
if self.epoch != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(1, self.epoch);
|
||||
}
|
||||
my_size += 5 * self.virtual_ip_list.len() as u64;
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
|
||||
if self.epoch != 0 {
|
||||
os.write_uint32(1, self.epoch)?;
|
||||
}
|
||||
for v in &self.virtual_ip_list {
|
||||
os.write_fixed32(2, *v)?;
|
||||
};
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn special_fields(&self) -> &::protobuf::SpecialFields {
|
||||
&self.special_fields
|
||||
}
|
||||
|
||||
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
|
||||
&mut self.special_fields
|
||||
}
|
||||
|
||||
fn new() -> DeviceList {
|
||||
DeviceList::new()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.epoch = 0;
|
||||
self.virtual_ip_list.clear();
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static DeviceList {
|
||||
static instance: DeviceList = DeviceList {
|
||||
epoch: 0,
|
||||
virtual_ip_list: ::std::vec::Vec::new(),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::MessageFull for DeviceList {
|
||||
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("DeviceList").unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for DeviceList {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for DeviceList {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(PartialEq,Clone,Default,Debug)]
|
||||
// @@protoc_insertion_point(message:Punch)
|
||||
pub struct Punch {
|
||||
// message fields
|
||||
// @@protoc_insertion_point(field:Punch.virtual_ip)
|
||||
pub virtual_ip: u32,
|
||||
// @@protoc_insertion_point(field:Punch.public_ip_list)
|
||||
pub public_ip_list: ::std::vec::Vec<u32>,
|
||||
// @@protoc_insertion_point(field:Punch.public_port)
|
||||
pub public_port: u32,
|
||||
// @@protoc_insertion_point(field:Punch.public_port_range)
|
||||
pub public_port_range: u32,
|
||||
// @@protoc_insertion_point(field:Punch.nat_type)
|
||||
pub nat_type: ::protobuf::EnumOrUnknown<NatType>,
|
||||
// @@protoc_insertion_point(field:Punch.reply)
|
||||
pub reply: bool,
|
||||
// @@protoc_insertion_point(field:Punch.step)
|
||||
pub step: ::protobuf::EnumOrUnknown<Step>,
|
||||
// special fields
|
||||
// @@protoc_insertion_point(special_field:Punch.special_fields)
|
||||
pub special_fields: ::protobuf::SpecialFields,
|
||||
}
|
||||
|
||||
impl<'a> ::std::default::Default for &'a Punch {
|
||||
fn default() -> &'a Punch {
|
||||
<Punch as ::protobuf::Message>::default_instance()
|
||||
}
|
||||
}
|
||||
|
||||
impl Punch {
|
||||
pub fn new() -> Punch {
|
||||
::std::default::Default::default()
|
||||
}
|
||||
|
||||
fn generated_message_descriptor_data() -> ::protobuf::reflect::GeneratedMessageDescriptorData {
|
||||
let mut fields = ::std::vec::Vec::with_capacity(7);
|
||||
let mut oneofs = ::std::vec::Vec::with_capacity(0);
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"virtual_ip",
|
||||
|m: &Punch| { &m.virtual_ip },
|
||||
|m: &mut Punch| { &mut m.virtual_ip },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_vec_simpler_accessor::<_, _>(
|
||||
"public_ip_list",
|
||||
|m: &Punch| { &m.public_ip_list },
|
||||
|m: &mut Punch| { &mut m.public_ip_list },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_port",
|
||||
|m: &Punch| { &m.public_port },
|
||||
|m: &mut Punch| { &mut m.public_port },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"public_port_range",
|
||||
|m: &Punch| { &m.public_port_range },
|
||||
|m: &mut Punch| { &mut m.public_port_range },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"nat_type",
|
||||
|m: &Punch| { &m.nat_type },
|
||||
|m: &mut Punch| { &mut m.nat_type },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"reply",
|
||||
|m: &Punch| { &m.reply },
|
||||
|m: &mut Punch| { &mut m.reply },
|
||||
));
|
||||
fields.push(::protobuf::reflect::rt::v2::make_simpler_field_accessor::<_, _>(
|
||||
"step",
|
||||
|m: &Punch| { &m.step },
|
||||
|m: &mut Punch| { &mut m.step },
|
||||
));
|
||||
::protobuf::reflect::GeneratedMessageDescriptorData::new_2::<Punch>(
|
||||
"Punch",
|
||||
fields,
|
||||
oneofs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::Message for Punch {
|
||||
const NAME: &'static str = "Punch";
|
||||
|
||||
fn is_initialized(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn merge_from(&mut self, is: &mut ::protobuf::CodedInputStream<'_>) -> ::protobuf::Result<()> {
|
||||
while let Some(tag) = is.read_raw_tag_or_eof()? {
|
||||
match tag {
|
||||
13 => {
|
||||
self.virtual_ip = is.read_fixed32()?;
|
||||
},
|
||||
18 => {
|
||||
is.read_repeated_packed_fixed32_into(&mut self.public_ip_list)?;
|
||||
},
|
||||
21 => {
|
||||
self.public_ip_list.push(is.read_fixed32()?);
|
||||
},
|
||||
24 => {
|
||||
self.public_port = is.read_uint32()?;
|
||||
},
|
||||
32 => {
|
||||
self.public_port_range = is.read_uint32()?;
|
||||
},
|
||||
40 => {
|
||||
self.nat_type = is.read_enum_or_unknown()?;
|
||||
},
|
||||
48 => {
|
||||
self.reply = is.read_bool()?;
|
||||
},
|
||||
56 => {
|
||||
self.step = is.read_enum_or_unknown()?;
|
||||
},
|
||||
tag => {
|
||||
::protobuf::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;
|
||||
},
|
||||
};
|
||||
}
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
// Compute sizes of nested messages
|
||||
#[allow(unused_variables)]
|
||||
fn compute_size(&self) -> u64 {
|
||||
let mut my_size = 0;
|
||||
if self.virtual_ip != 0 {
|
||||
my_size += 1 + 4;
|
||||
}
|
||||
my_size += 5 * self.public_ip_list.len() as u64;
|
||||
if self.public_port != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(3, self.public_port);
|
||||
}
|
||||
if self.public_port_range != 0 {
|
||||
my_size += ::protobuf::rt::uint32_size(4, self.public_port_range);
|
||||
}
|
||||
if self.nat_type != ::protobuf::EnumOrUnknown::new(NatType::Symmetric) {
|
||||
my_size += ::protobuf::rt::int32_size(5, self.nat_type.value());
|
||||
}
|
||||
if self.reply != false {
|
||||
my_size += 1 + 1;
|
||||
}
|
||||
if self.step != ::protobuf::EnumOrUnknown::new(Step::Step1) {
|
||||
my_size += ::protobuf::rt::int32_size(7, self.step.value());
|
||||
}
|
||||
my_size += ::protobuf::rt::unknown_fields_size(self.special_fields.unknown_fields());
|
||||
self.special_fields.cached_size().set(my_size as u32);
|
||||
my_size
|
||||
}
|
||||
|
||||
fn write_to_with_cached_sizes(&self, os: &mut ::protobuf::CodedOutputStream<'_>) -> ::protobuf::Result<()> {
|
||||
if self.virtual_ip != 0 {
|
||||
os.write_fixed32(1, self.virtual_ip)?;
|
||||
}
|
||||
for v in &self.public_ip_list {
|
||||
os.write_fixed32(2, *v)?;
|
||||
};
|
||||
if self.public_port != 0 {
|
||||
os.write_uint32(3, self.public_port)?;
|
||||
}
|
||||
if self.public_port_range != 0 {
|
||||
os.write_uint32(4, self.public_port_range)?;
|
||||
}
|
||||
if self.nat_type != ::protobuf::EnumOrUnknown::new(NatType::Symmetric) {
|
||||
os.write_enum(5, ::protobuf::EnumOrUnknown::value(&self.nat_type))?;
|
||||
}
|
||||
if self.reply != false {
|
||||
os.write_bool(6, self.reply)?;
|
||||
}
|
||||
if self.step != ::protobuf::EnumOrUnknown::new(Step::Step1) {
|
||||
os.write_enum(7, ::protobuf::EnumOrUnknown::value(&self.step))?;
|
||||
}
|
||||
os.write_unknown_fields(self.special_fields.unknown_fields())?;
|
||||
::std::result::Result::Ok(())
|
||||
}
|
||||
|
||||
fn special_fields(&self) -> &::protobuf::SpecialFields {
|
||||
&self.special_fields
|
||||
}
|
||||
|
||||
fn mut_special_fields(&mut self) -> &mut ::protobuf::SpecialFields {
|
||||
&mut self.special_fields
|
||||
}
|
||||
|
||||
fn new() -> Punch {
|
||||
Punch::new()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
self.virtual_ip = 0;
|
||||
self.public_ip_list.clear();
|
||||
self.public_port = 0;
|
||||
self.public_port_range = 0;
|
||||
self.nat_type = ::protobuf::EnumOrUnknown::new(NatType::Symmetric);
|
||||
self.reply = false;
|
||||
self.step = ::protobuf::EnumOrUnknown::new(Step::Step1);
|
||||
self.special_fields.clear();
|
||||
}
|
||||
|
||||
fn default_instance() -> &'static Punch {
|
||||
static instance: Punch = Punch {
|
||||
virtual_ip: 0,
|
||||
public_ip_list: ::std::vec::Vec::new(),
|
||||
public_port: 0,
|
||||
public_port_range: 0,
|
||||
nat_type: ::protobuf::EnumOrUnknown::from_i32(0),
|
||||
reply: false,
|
||||
step: ::protobuf::EnumOrUnknown::from_i32(0),
|
||||
special_fields: ::protobuf::SpecialFields::new(),
|
||||
};
|
||||
&instance
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::MessageFull for Punch {
|
||||
fn descriptor() -> ::protobuf::reflect::MessageDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::MessageDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().message_by_package_relative_name("Punch").unwrap()).clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::fmt::Display for Punch {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
|
||||
::protobuf::text_format::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::protobuf::reflect::ProtobufValue for Punch {
|
||||
type RuntimeType = ::protobuf::reflect::rt::RuntimeTypeMessage<Self>;
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
|
||||
// @@protoc_insertion_point(enum:NatType)
|
||||
pub enum NatType {
|
||||
// @@protoc_insertion_point(enum_value:NatType.Symmetric)
|
||||
Symmetric = 0,
|
||||
// @@protoc_insertion_point(enum_value:NatType.Cone)
|
||||
Cone = 1,
|
||||
}
|
||||
|
||||
impl ::protobuf::Enum for NatType {
|
||||
const NAME: &'static str = "NatType";
|
||||
|
||||
fn value(&self) -> i32 {
|
||||
*self as i32
|
||||
}
|
||||
|
||||
fn from_i32(value: i32) -> ::std::option::Option<NatType> {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(NatType::Symmetric),
|
||||
1 => ::std::option::Option::Some(NatType::Cone),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
|
||||
const VALUES: &'static [NatType] = &[
|
||||
NatType::Symmetric,
|
||||
NatType::Cone,
|
||||
];
|
||||
}
|
||||
|
||||
impl ::protobuf::EnumFull for NatType {
|
||||
fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().enum_by_package_relative_name("NatType").unwrap()).clone()
|
||||
}
|
||||
|
||||
fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor {
|
||||
let index = *self as usize;
|
||||
Self::enum_descriptor().value_by_index(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::default::Default for NatType {
|
||||
fn default() -> Self {
|
||||
NatType::Symmetric
|
||||
}
|
||||
}
|
||||
|
||||
impl NatType {
|
||||
fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData {
|
||||
::protobuf::reflect::GeneratedEnumDescriptorData::new::<NatType>("NatType")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone,Copy,PartialEq,Eq,Debug,Hash)]
|
||||
// @@protoc_insertion_point(enum:Step)
|
||||
pub enum Step {
|
||||
// @@protoc_insertion_point(enum_value:Step.Step1)
|
||||
Step1 = 0,
|
||||
// @@protoc_insertion_point(enum_value:Step.Step2)
|
||||
Step2 = 1,
|
||||
// @@protoc_insertion_point(enum_value:Step.Step3)
|
||||
Step3 = 2,
|
||||
// @@protoc_insertion_point(enum_value:Step.Step4)
|
||||
Step4 = 3,
|
||||
}
|
||||
|
||||
impl ::protobuf::Enum for Step {
|
||||
const NAME: &'static str = "Step";
|
||||
|
||||
fn value(&self) -> i32 {
|
||||
*self as i32
|
||||
}
|
||||
|
||||
fn from_i32(value: i32) -> ::std::option::Option<Step> {
|
||||
match value {
|
||||
0 => ::std::option::Option::Some(Step::Step1),
|
||||
1 => ::std::option::Option::Some(Step::Step2),
|
||||
2 => ::std::option::Option::Some(Step::Step3),
|
||||
3 => ::std::option::Option::Some(Step::Step4),
|
||||
_ => ::std::option::Option::None
|
||||
}
|
||||
}
|
||||
|
||||
const VALUES: &'static [Step] = &[
|
||||
Step::Step1,
|
||||
Step::Step2,
|
||||
Step::Step3,
|
||||
Step::Step4,
|
||||
];
|
||||
}
|
||||
|
||||
impl ::protobuf::EnumFull for Step {
|
||||
fn enum_descriptor() -> ::protobuf::reflect::EnumDescriptor {
|
||||
static descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::EnumDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
descriptor.get(|| file_descriptor().enum_by_package_relative_name("Step").unwrap()).clone()
|
||||
}
|
||||
|
||||
fn descriptor(&self) -> ::protobuf::reflect::EnumValueDescriptor {
|
||||
let index = *self as usize;
|
||||
Self::enum_descriptor().value_by_index(index)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::std::default::Default for Step {
|
||||
fn default() -> Self {
|
||||
Step::Step1
|
||||
}
|
||||
}
|
||||
|
||||
impl Step {
|
||||
fn generated_enum_descriptor_data() -> ::protobuf::reflect::GeneratedEnumDescriptorData {
|
||||
::protobuf::reflect::GeneratedEnumDescriptorData::new::<Step>("Step")
|
||||
}
|
||||
}
|
||||
|
||||
static file_descriptor_proto_data: &'static [u8] = b"\
|
||||
\n\rmessage.proto\"L\n\x13RegistrationRequest\x12\x14\n\x05token\x18\x01\
|
||||
\x20\x01(\tR\x05token\x12\x1f\n\x0bmac_address\x18\x02\x20\x01(\tR\nmacA\
|
||||
ddress\"\x83\x02\n\x14RegistrationResponse\x12\x1d\n\nvirtual_ip\x18\x01\
|
||||
\x20\x01(\x07R\tvirtualIp\x12'\n\x0fvirtual_gateway\x18\x02\x20\x01(\x07\
|
||||
R\x0evirtualGateway\x12'\n\x0fvirtual_netmask\x18\x03\x20\x01(\x07R\x0ev\
|
||||
irtualNetmask\x12\x14\n\x05epoch\x18\x04\x20\x01(\rR\x05epoch\x12&\n\x0f\
|
||||
virtual_ip_list\x18\x05\x20\x03(\x07R\rvirtualIpList\x12\x1b\n\tpublic_i\
|
||||
p\x18\x06\x20\x01(\x07R\x08publicIp\x12\x1f\n\x0bpublic_port\x18\x07\x20\
|
||||
\x01(\rR\npublicPort\"J\n\nDeviceList\x12\x14\n\x05epoch\x18\x01\x20\x01\
|
||||
(\rR\x05epoch\x12&\n\x0fvirtual_ip_list\x18\x02\x20\x03(\x07R\rvirtualIp\
|
||||
List\"\xef\x01\n\x05Punch\x12\x1d\n\nvirtual_ip\x18\x01\x20\x01(\x07R\tv\
|
||||
irtualIp\x12$\n\x0epublic_ip_list\x18\x02\x20\x03(\x07R\x0cpublicIpList\
|
||||
\x12\x1f\n\x0bpublic_port\x18\x03\x20\x01(\rR\npublicPort\x12*\n\x11publ\
|
||||
ic_port_range\x18\x04\x20\x01(\rR\x0fpublicPortRange\x12#\n\x08nat_type\
|
||||
\x18\x05\x20\x01(\x0e2\x08.NatTypeR\x07natType\x12\x14\n\x05reply\x18\
|
||||
\x06\x20\x01(\x08R\x05reply\x12\x19\n\x04step\x18\x07\x20\x01(\x0e2\x05.\
|
||||
StepR\x04step*\"\n\x07NatType\x12\r\n\tSymmetric\x10\0\x12\x08\n\x04Cone\
|
||||
\x10\x01*2\n\x04Step\x12\t\n\x05Step1\x10\0\x12\t\n\x05Step2\x10\x01\x12\
|
||||
\t\n\x05Step3\x10\x02\x12\t\n\x05Step4\x10\x03b\x06proto3\
|
||||
";
|
||||
|
||||
/// `FileDescriptorProto` object which was a source for this generated file
|
||||
fn file_descriptor_proto() -> &'static ::protobuf::descriptor::FileDescriptorProto {
|
||||
static file_descriptor_proto_lazy: ::protobuf::rt::Lazy<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::Lazy::new();
|
||||
file_descriptor_proto_lazy.get(|| {
|
||||
::protobuf::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
/// `FileDescriptor` object which allows dynamic access to files
|
||||
pub fn file_descriptor() -> &'static ::protobuf::reflect::FileDescriptor {
|
||||
static generated_file_descriptor_lazy: ::protobuf::rt::Lazy<::protobuf::reflect::GeneratedFileDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
static file_descriptor: ::protobuf::rt::Lazy<::protobuf::reflect::FileDescriptor> = ::protobuf::rt::Lazy::new();
|
||||
file_descriptor.get(|| {
|
||||
let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {
|
||||
let mut deps = ::std::vec::Vec::with_capacity(0);
|
||||
let mut messages = ::std::vec::Vec::with_capacity(4);
|
||||
messages.push(RegistrationRequest::generated_message_descriptor_data());
|
||||
messages.push(RegistrationResponse::generated_message_descriptor_data());
|
||||
messages.push(DeviceList::generated_message_descriptor_data());
|
||||
messages.push(Punch::generated_message_descriptor_data());
|
||||
let mut enums = ::std::vec::Vec::with_capacity(2);
|
||||
enums.push(NatType::generated_enum_descriptor_data());
|
||||
enums.push(Step::generated_enum_descriptor_data());
|
||||
::protobuf::reflect::GeneratedFileDescriptor::new_generated(
|
||||
file_descriptor_proto(),
|
||||
deps,
|
||||
messages,
|
||||
enums,
|
||||
)
|
||||
});
|
||||
::protobuf::reflect::FileDescriptor::new_generated_2(generated_file_descriptor)
|
||||
})
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
// @generated
|
||||
|
||||
pub mod message;
|
||||
@@ -1,223 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
Ping,
|
||||
Pong,
|
||||
PunchRequest,
|
||||
PunchResponse,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Ping,
|
||||
2 => Protocol::Pong,
|
||||
3 => Protocol::PunchRequest,
|
||||
4 => Protocol::PunchResponse,
|
||||
val => Protocol::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Ping => 1,
|
||||
Protocol::Pong => 2,
|
||||
Protocol::PunchRequest => 3,
|
||||
Protocol::PunchResponse => 4,
|
||||
Protocol::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ControlPacket<B> {
|
||||
PingPacket(PingPacket<B>),
|
||||
PongPacket(PongPacket<B>),
|
||||
PunchRequest(PunchRequestPacket<B>),
|
||||
PunchResponse(PunchResponsePacket<B>),
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ControlPacket<B> {
|
||||
pub fn new(protocol: u8, buffer: B) -> Result<ControlPacket<B>> {
|
||||
match Protocol::from(protocol) {
|
||||
Protocol::Ping => Ok(ControlPacket::PingPacket(PingPacket::new(buffer)?)),
|
||||
Protocol::Pong => Ok(ControlPacket::PongPacket(PongPacket::new(buffer)?)),
|
||||
Protocol::PunchRequest => Ok(ControlPacket::PunchRequest(PunchRequestPacket::new(
|
||||
buffer,
|
||||
)?)),
|
||||
Protocol::PunchResponse => Ok(ControlPacket::PunchResponse(PunchResponsePacket::new(
|
||||
buffer,
|
||||
)?)),
|
||||
Protocol::UnKnow(_) => Err(Error::NotSupport),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 网络探针
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PingPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PongPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
|
||||
impl<B: AsRef<[u8]>> PingPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<PingPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 8 + 4 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(PingPacket { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PingPacket<B> {
|
||||
pub fn time(&self) -> i64 {
|
||||
i64::from_be_bytes(self.buffer.as_ref()[..8].try_into().unwrap())
|
||||
}
|
||||
pub fn epoch(&self) -> u32 {
|
||||
u32::from_be_bytes(self.buffer.as_ref()[8..12].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> PingPacket<B> {
|
||||
pub fn set_time(&mut self, time: i64) {
|
||||
self.buffer.as_mut()[..8].copy_from_slice(&time.to_be_bytes())
|
||||
}
|
||||
pub fn set_epoch(&mut self, epoch: u32) {
|
||||
self.buffer.as_mut()[8..12].copy_from_slice(&epoch.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for PingPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PingPacket")
|
||||
.field("time", &self.time())
|
||||
.field("epoch", &self.epoch())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PongPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<PongPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 8 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(PongPacket { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PongPacket<B> {
|
||||
pub fn time(&self) -> i64 {
|
||||
i64::from_be_bytes(self.buffer.as_ref()[..8].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> PongPacket<B> {
|
||||
pub fn set_time(&mut self, time: i64) {
|
||||
self.buffer.as_mut()[..8].copy_from_slice(&time.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for PongPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PongPacket")
|
||||
.field("time", &self.time())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub type TurnPongPacket<B> = TurnPingPacket<B>;
|
||||
|
||||
/// 探测目标延迟
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TurnPingPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TurnPingPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<TurnPingPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 16 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(TurnPingPacket { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TurnPingPacket<B> {
|
||||
// pub fn source(&self) -> Ipv4Addr {
|
||||
// let tmp:[u8;4] = self.buffer.as_ref()[..4].try_into().unwrap();
|
||||
// Ipv4Addr::from(tmp)
|
||||
// }
|
||||
// pub fn destination(&self) -> Ipv4Addr {
|
||||
// let tmp:[u8;4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
// Ipv4Addr::from(tmp)
|
||||
// }
|
||||
pub fn time(&self) -> i64 {
|
||||
i64::from_be_bytes(self.buffer.as_ref()[8..].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> TurnPingPacket<B> {
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[..4].copy_from_slice(&source.octets());
|
||||
}
|
||||
pub fn set_destination(&mut self, destination: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&destination.octets());
|
||||
}
|
||||
pub fn set_time(&mut self, time: i64) {
|
||||
self.buffer.as_mut()[8..].copy_from_slice(&time.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
pub type PunchResponsePacket<B> = PunchPacket<B>;
|
||||
pub type PunchRequestPacket<B> = PunchPacket<B>;
|
||||
|
||||
/// nat穿透
|
||||
#[derive(Clone)]
|
||||
pub struct PunchPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PunchPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<PunchPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len != 8 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(Self { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PunchPacket<B> {
|
||||
pub fn source(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[..4].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> PunchPacket<B> {
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[..4].copy_from_slice(&source.octets());
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for PunchPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PunchPacket")
|
||||
.field("source", &self.source())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,145 +0,0 @@
|
||||
use std::fmt;
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
pub mod control_packet;
|
||||
pub mod error_packet;
|
||||
pub mod service_packet;
|
||||
pub mod turn_packet;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Version {
|
||||
V1,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Version {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Version::V1,
|
||||
val => Version::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Version {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Version::V1 => 1,
|
||||
Version::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
/// 服务包 用于和服务端交互
|
||||
Service,
|
||||
/// 响应异常
|
||||
Error,
|
||||
/// 控制协议
|
||||
Control,
|
||||
/// 转发ipv4数据
|
||||
Ipv4Turn,
|
||||
OtherTurn,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Service,
|
||||
2 => Protocol::Error,
|
||||
3 => Protocol::Control,
|
||||
4 => Protocol::Ipv4Turn,
|
||||
5 => Protocol::OtherTurn,
|
||||
val => Protocol::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Service => 1,
|
||||
Protocol::Error => 2,
|
||||
Protocol::Control => 3,
|
||||
Protocol::Ipv4Turn => 4,
|
||||
Protocol::OtherTurn => 5,
|
||||
Protocol::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct NetPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<NetPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < 4 || len > 65535 - 20 - 8 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(NetPacket { buffer })
|
||||
}
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
pub fn into_buffer(self) -> B {
|
||||
self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
pub fn version(&self) -> Version {
|
||||
Version::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
pub fn protocol(&self) -> Protocol {
|
||||
Protocol::from(self.buffer.as_ref()[1])
|
||||
}
|
||||
pub fn transport_protocol(&self) -> u8 {
|
||||
self.buffer.as_ref()[2]
|
||||
}
|
||||
pub fn ttl(&self) -> u8 {
|
||||
self.buffer.as_ref()[3]
|
||||
}
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[4..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> NetPacket<B> {
|
||||
pub fn set_version(&mut self, version: Version) {
|
||||
self.buffer.as_mut()[0] = version.into();
|
||||
}
|
||||
pub fn set_protocol(&mut self, protocol: Protocol) {
|
||||
self.buffer.as_mut()[1] = protocol.into();
|
||||
}
|
||||
pub fn set_transport_protocol(&mut self, transport_protocol: u8) {
|
||||
self.buffer.as_mut()[2] = transport_protocol;
|
||||
}
|
||||
pub fn set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[3] = ttl;
|
||||
}
|
||||
pub fn set_payload(&mut self, payload: &[u8]) {
|
||||
self.buffer.as_mut()[4..payload.len() + 4].copy_from_slice(payload);
|
||||
}
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[4..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for NetPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("NetPacket")
|
||||
.field("version", &self.version())
|
||||
.field("protocol", &self.protocol())
|
||||
.field("transport_protocol", &self.transport_protocol())
|
||||
.field("ttl", &self.ttl())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
/// 注册请求
|
||||
RegistrationRequest,
|
||||
/// 注册响应
|
||||
RegistrationResponse,
|
||||
/// 更新设备列表
|
||||
UpdateDeviceList,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Self::RegistrationRequest,
|
||||
2 => Self::RegistrationResponse,
|
||||
3 => Self::UpdateDeviceList,
|
||||
val => Self::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Self::RegistrationRequest => 1,
|
||||
Self::RegistrationResponse => 2,
|
||||
Self::UpdateDeviceList => 3,
|
||||
Self::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Protocol {
|
||||
Punch,
|
||||
UnKnow(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Punch,
|
||||
val => Protocol::UnKnow(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Punch => 1,
|
||||
Protocol::UnKnow(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TurnPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TurnPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<TurnPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len <= 8 {
|
||||
return Err(Error::InvalidPacket);
|
||||
}
|
||||
Ok(Self { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TurnPacket<B> {
|
||||
pub fn source(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[..4].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn destination(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[8..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> TurnPacket<B> {
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[8..]
|
||||
}
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[..4].copy_from_slice(&source.octets());
|
||||
}
|
||||
pub fn set_destination(&mut self, destination: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&destination.octets());
|
||||
}
|
||||
pub fn set_payload(&mut self, payload: &[u8]) {
|
||||
self.buffer.as_mut()[8..payload.len() + 8].copy_from_slice(payload)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for TurnPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("TurnPacket")
|
||||
.field("source", &self.source())
|
||||
.field("destination", &self.destination())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
#[cfg(any(unix))]
|
||||
pub use unix::create_tun;
|
||||
#[cfg(any(unix))]
|
||||
pub use unix::{TunReader, TunWriter};
|
||||
|
||||
#[cfg(any(unix))]
|
||||
pub mod unix;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub mod windows;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use windows::create_tun;
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use windows::{TunReader, TunWriter};
|
||||
@@ -1,156 +0,0 @@
|
||||
use std::io;
|
||||
use std::io::{Error, Read, Write};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::os::unix::process::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
use bytes::BufMut;
|
||||
use tun::Device;
|
||||
use tun::platform::posix::{Reader, Writer};
|
||||
|
||||
pub fn create_tun(
|
||||
address: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
) -> crate::error::Result<(TunWriter, TunReader)> {
|
||||
let mut config = tun::Configuration::default();
|
||||
|
||||
config
|
||||
.destination(gateway)
|
||||
.address(address)
|
||||
.netmask(netmask)
|
||||
.mtu(1420)
|
||||
.up();
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
config.platform(|config| {
|
||||
config.packet_information(true);
|
||||
});
|
||||
|
||||
let dev = tun::create(&config).unwrap();
|
||||
|
||||
// let up_eth_str: String = format!("ifconfig utun3 {:?} {:?} up ", address, gateway);
|
||||
let route_add_str: String = format!(
|
||||
"sudo route -n add -net {:?} -netmask {:?} {:?}",
|
||||
address, netmask, gateway
|
||||
);
|
||||
//
|
||||
// let up_eth_out = Command::new("sh")
|
||||
// .arg("-c")
|
||||
// .arg(up_eth_str)
|
||||
// .output()
|
||||
// .expect("sh exec error!");
|
||||
// if !up_eth_out.status.success() {
|
||||
// return Err(crate::error::Error::Stop(format!("设置地址失败:{:?}", up_eth_out)));
|
||||
// }
|
||||
// println!("{:?}", up_eth_out);
|
||||
let if_config_out = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(route_add_str)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !if_config_out.status.success() {
|
||||
return Err(crate::error::Error::Stop(format!("设置路由失败:{:?}", if_config_out)));
|
||||
}
|
||||
// println!("{:?}", if_config_out);
|
||||
// let cmd_str: String = " ifconfig|grep flags=8051|awk -F ':' '{print $1}'|tail -1".to_string();
|
||||
//
|
||||
// let cmd_str_out = Command::new("sh")
|
||||
// .arg("-c")
|
||||
// .arg(cmd_str)
|
||||
// .output()
|
||||
// .expect("sh exec error!");
|
||||
// if !cmd_str_out.status.success(){
|
||||
// return Err(Error::Stop(format!("设置路由失败:{:?}", cmd_str_out)));
|
||||
// }
|
||||
// println!("{:?}", cmd_str_out);
|
||||
let packet_information = dev.has_packet_information();
|
||||
let (reader, writer) = dev.split();
|
||||
Ok((
|
||||
TunWriter(writer, packet_information),
|
||||
TunReader(reader, packet_information),
|
||||
))
|
||||
}
|
||||
|
||||
pub struct TunReader(Reader, bool);
|
||||
|
||||
impl TunReader {
|
||||
pub fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> io::Result<&mut [u8]> {
|
||||
let len = self.0.read(buf)?;
|
||||
if self.1 {
|
||||
Ok(&mut buf[4..len])
|
||||
} else {
|
||||
Ok(&mut buf[..len])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TunWriter(Writer, bool);
|
||||
|
||||
impl TunWriter {
|
||||
pub fn write(&mut self, packet: &[u8]) -> io::Result<()> {
|
||||
if self.1 {
|
||||
let mut buf = Vec::<u8>::with_capacity(4 + packet.len());
|
||||
buf.put_u16(0);
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
buf.put_u16(libc::PF_INET as u16);
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
buf.put_u16(libc::ETH_P_IP as u16);
|
||||
buf.extend_from_slice(packet);
|
||||
self.0.write_all(&buf)
|
||||
} else {
|
||||
self.0.write_all(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn main1() {
|
||||
// loop {
|
||||
// let len = reader.read(&mut buffer).unwrap();
|
||||
// println!("{:?}", &buffer[..len]);
|
||||
// match ip::Packet::new(&buffer[4..len]) {
|
||||
// Ok(ip::Packet::V4(pkt)) => {
|
||||
// match icmp::Packet::new(pkt.payload()) {
|
||||
// Ok(icmp) => {
|
||||
// match icmp.echo() {
|
||||
// Ok(icmp) => {
|
||||
// println!("{:?}", icmp);
|
||||
// let reply = ip::v4::Builder::default()
|
||||
// .id(0x42)
|
||||
// .unwrap()
|
||||
// .ttl(64)
|
||||
// .unwrap()
|
||||
// .source(pkt.destination())
|
||||
// .unwrap()
|
||||
// .destination(pkt.source())
|
||||
// .unwrap()
|
||||
// .icmp()
|
||||
// .unwrap()
|
||||
// .echo()
|
||||
// .unwrap()
|
||||
// .reply()
|
||||
// .unwrap()
|
||||
// .identifier(icmp.identifier())
|
||||
// .unwrap()
|
||||
// .sequence(icmp.sequence())
|
||||
// .unwrap()
|
||||
// .payload(icmp.payload())
|
||||
// .unwrap()
|
||||
// .build()
|
||||
// .unwrap();
|
||||
// let l = reply.len();
|
||||
// &mut buffer[4..(l + 4)].copy_from_slice(&reply);
|
||||
// // writer.write_all(&buffer[..4]).unwrap();
|
||||
// writer.write_all(&buffer[..(l + 4)]).unwrap();
|
||||
// }
|
||||
// Err(_) => {}
|
||||
// }
|
||||
// }
|
||||
// Err(_) => {}
|
||||
// }
|
||||
// }
|
||||
// _ => {}
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -1,132 +0,0 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use libloading::Library;
|
||||
use wintun::{Adapter, Packet, Session};
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
pub struct TunWriter(Arc<Session>);
|
||||
|
||||
impl TunWriter {
|
||||
pub fn write(&self, buf: &[u8]) -> io::Result<()> {
|
||||
match self.0.allocate_send_packet(buf.len() as u16) {
|
||||
Ok(mut packet) => {
|
||||
packet.bytes_mut().copy_from_slice(buf);
|
||||
self.0.send_packet(packet);
|
||||
return Ok(());
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "send err"));
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TunReader(Arc<Session>);
|
||||
|
||||
impl TunReader {
|
||||
pub fn next(&self) -> io::Result<Packet> {
|
||||
match self.0.receive_blocking() {
|
||||
Ok(packet) => {
|
||||
return Ok(packet);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "read err"));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_tun(
|
||||
address: Ipv4Addr,
|
||||
netmask: Ipv4Addr,
|
||||
gateway: Ipv4Addr,
|
||||
) -> Result<(TunWriter, TunReader)> {
|
||||
let win_tun = unsafe {
|
||||
match Library::new("wintun.dll") {
|
||||
Ok(library) => match wintun::load_from_library(library) {
|
||||
Ok(win_tun) => win_tun,
|
||||
Err(e) => {
|
||||
return Err(Error::Stop(format!("{:?}", e)));
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
println!("{}", console::style("wintun.dll not found").red());
|
||||
return Err(Error::Stop(format!("{:?}", e)));
|
||||
}
|
||||
}
|
||||
};
|
||||
let adapter = match Adapter::open(&win_tun, "Demo") {
|
||||
Ok(a) => a,
|
||||
Err(_) => match Adapter::create(&win_tun, "Example", "Demo", None) {
|
||||
Ok(adapter) => adapter,
|
||||
|
||||
Err(e) => return Err(Error::Stop(format!("{:?}", e))),
|
||||
},
|
||||
};
|
||||
let index = adapter.get_adapter_index().unwrap();
|
||||
let set_mtu = format!(
|
||||
"netsh interface ipv4 set subinterface {} mtu=1420 store=persistent",
|
||||
index
|
||||
);
|
||||
let set_metric = format!("netsh interface ip set interface {} metric=1", index);
|
||||
let set_address = format!(
|
||||
"netsh interface ip set address {} static {:?} {:?} ", // gateway={:?}
|
||||
index, address, netmask,
|
||||
);
|
||||
// println!("{}", set_mtu);
|
||||
// println!("{}", set_metric);
|
||||
// println!("{}", set_address);
|
||||
// 执行网卡初始化命令
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_mtu)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(Error::Stop(format!("设置mtu失败:{:?}", out)));
|
||||
}
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_metric)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(Error::Stop(format!("设置接口跃点失败:{:?}", out)));
|
||||
}
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_address)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(Error::Stop(format!("设置网络地址失败:{:?}", out)));
|
||||
}
|
||||
let dest = {
|
||||
let ip = address.octets();
|
||||
let mask = netmask.octets();
|
||||
Ipv4Addr::from([
|
||||
ip[0] & mask[0],
|
||||
ip[1] & mask[1],
|
||||
ip[2] & mask[2],
|
||||
ip[3] & mask[3],
|
||||
])
|
||||
};
|
||||
let set_route = format!(
|
||||
"route add {:?} mask {:?} {:?} if {}",
|
||||
dest, netmask, gateway, index
|
||||
);
|
||||
// println!("{}", set_route);
|
||||
// 执行添加路由命令
|
||||
let out = std::process::Command::new("cmd")
|
||||
.arg("/C")
|
||||
.arg(set_route)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !out.status.success() {
|
||||
return Err(Error::Stop(format!("添加路由失败:{:?}", out)));
|
||||
}
|
||||
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
|
||||
let reader_session = session.clone();
|
||||
Ok((TunWriter(session), TunReader(reader_session)))
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
[package]
|
||||
name = "vn-link-cli"
|
||||
version = "1.2.16"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
vn-link = { path = "../vn-link", default-features = false }
|
||||
common = { path = "../common", default-features = false }
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
log = "0.4.17"
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
thunk-rs = { version = "0.3.3", features = ["win7"] }
|
||||
|
||||
[features]
|
||||
default = ["default-feature"]
|
||||
default-feature = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "port_mapping", "log", "command", "file_config", "lz4", "ws"]
|
||||
|
||||
openssl = ["vn-link/openssl", "common/openssl"]
|
||||
openssl-vendored = ["vn-link/openssl-vendored", "common/openssl-vendored"]
|
||||
ring-cipher = ["vn-link/ring-cipher", "common/ring-cipher"]
|
||||
aes_cbc = ["vn-link/aes_cbc", "common/aes_cbc"]
|
||||
aes_ecb = ["vn-link/aes_ecb", "common/aes_ecb"]
|
||||
sm4_cbc = ["vn-link/sm4_cbc", "common/sm4_cbc"]
|
||||
aes_gcm = ["vn-link/aes_gcm", "common/aes_gcm"]
|
||||
chacha20_poly1305 = ["vn-link/chacha20_poly1305", "common/chacha20_poly1305"]
|
||||
server_encrypt = ["vn-link/server_encrypt", "common/server_encrypt"]
|
||||
port_mapping = ["vn-link/port_mapping", "common/port_mapping"]
|
||||
lz4 = ["vn-link/lz4_compress", "common/lz4"]
|
||||
zstd = ["vn-link/zstd_compress", "common/zstd"]
|
||||
upnp = ["vn-link/upnp", "common/upnp"]
|
||||
ws = ["vn-link/ws", "common/ws"]
|
||||
wss = ["vn-link/wss", "common/wss"]
|
||||
log = ["common/log"]
|
||||
command = ["common/command"]
|
||||
file_config = ["common/file_config"]
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# 端口映射模式
|
||||
|
||||
## 一、特点
|
||||
|
||||
1. 不需要tap/tun虚拟网卡
|
||||
2. 不需要管理员/root权限
|
||||
3. 不改变本地路由
|
||||
4. 使用端口映射来访问目标服务
|
||||
|
||||
## 二、作用
|
||||
|
||||
和vnt互补,能简单快速构建网络,外部依赖更少
|
||||
|
||||
## 三、使用方式
|
||||
|
||||
和vnt的使用方式一样,只是多了"--vnt-mapping"这个参数
|
||||
|
||||
### vn-link作为被访问端,不需要额外配置vnt-mapping
|
||||
|
||||
### vn-link访问vnt或者vn-link,需要加vnt-mapping
|
||||
|
||||
例如:
|
||||
|
||||
设备A 运行vnt(虚拟IP 10.26.0.A),设备B 运行vn-link(虚拟IP 10.26.0.B)。
|
||||
|
||||
如果要用B访问A上的tcp 80端口,则在设备B上需要加--vnt-mapping "tcp:port1-10.26.0.A:80"
|
||||
|
||||
这个参数的作用是将B上的***本地端口port1***转发到设备A的地址10.26.0.A:
|
||||
80,此时在设备B上可以访问本地port1端口从而间接访问10.26.0.A:80
|
||||
|
||||
## 四、vn-link的子网代理
|
||||
|
||||
vn-link也支持点对网参数。 还是接着上面的例子
|
||||
|
||||
假设 设备C在设备A的子网下,C的子网IP为192.168.1.C,A的子网IP为192.168.1.A,要在设备B上访问C
|
||||
|
||||
则在B上加这些参数
|
||||
|
||||
- --vnt-mapping "tcp:port2-192.168.1.C:80" (将本地port2端口映射到C的80端口)
|
||||
- -i 192.168.1.0/24,10.26.0.A (将目标192.168.1.0/24的数据发送到10.26.0.A,也就是A节点)
|
||||
|
||||
在A上加参数
|
||||
|
||||
- -o 0.0.0.0/0 (允许所有流量转发)
|
||||
|
||||
***再次说明,vn-link作为被访问端时和vnt使用方式一致,vn-link作为访问端时需要加--vnt-mapping映射端口***
|
||||
|
||||
***vn-link是基于端口映射的使用模式,不会改变本地路由***
|
||||
|
||||
## 五、参数介绍
|
||||
|
||||
--vnt-mapping支持udp/tcp,例如 --vnt-mapping "tcp:port1-remoteIp:remotePort"
|
||||
|
||||
- 第一部分为协议,支持使用udp/tcp
|
||||
- 第二部分是本地端口,注意不要和本地服务的端口冲突
|
||||
- 第三部分是目标机器的地址,一般是目标虚拟IP地址,如果配置了点对网参数(-i和-o)则也可以是目标子网地址
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
// 配置 thunk-rs 来链接 Windows 7 兼容库,并自动设置链接参数
|
||||
#[cfg(target_os = "windows")]
|
||||
thunk::thunk();
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
use common::callback;
|
||||
use vn_link::config::VnLinkConfig;
|
||||
use vn_link::vnt::core::Config;
|
||||
|
||||
fn main() {
|
||||
let (config, vnt_link_config, cmd) = match common::cli::parse_args_config() {
|
||||
Ok(rs) => {
|
||||
if let Some(rs) = rs {
|
||||
rs
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"parse error={:?} cmd={:?}",
|
||||
e,
|
||||
std::env::args().collect::<Vec<String>>()
|
||||
);
|
||||
println!("Error {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let vnt_link_config = VnLinkConfig::new(vn_link::config::convert(vnt_link_config).unwrap());
|
||||
main0(config, vnt_link_config, cmd)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main0(config: Config, vn_link_config: VnLinkConfig, _show_cmd: bool) {
|
||||
#[cfg(feature = "port_mapping")]
|
||||
for (is_tcp, addr, dest) in config.port_mapping_list.iter() {
|
||||
if *is_tcp {
|
||||
println!("TCP port mapping {}->{}", addr, dest)
|
||||
} else {
|
||||
println!("UDP port mapping {}->{}", addr, dest)
|
||||
}
|
||||
}
|
||||
for x in &vn_link_config.mapping {
|
||||
if x.protocol.is_tcp() {
|
||||
println!("TCP vnt addr mapping 127.0.0.1:{}->{}", x.src_port, x.dest)
|
||||
} else {
|
||||
println!("UDP vnt addr mapping 127.0.0.1:{}->{}", x.src_port, x.dest)
|
||||
}
|
||||
}
|
||||
|
||||
let vnt_util = match vn_link::VnLink::new(config, vn_link_config, callback::VntHandler {}).await
|
||||
{
|
||||
Ok(vnt) => vnt,
|
||||
Err(e) => {
|
||||
println!("error: {:?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(feature = "command")]
|
||||
{
|
||||
let vnt_c = vnt_util.as_vnt().clone();
|
||||
std::thread::Builder::new()
|
||||
.name("CommandServer".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = common::command::server::CommandServer::new().start(vnt_c) {
|
||||
log::warn!("cmd:{:?}", e);
|
||||
}
|
||||
})
|
||||
.expect("CommandServer");
|
||||
let vnt_c = vnt_util.as_vnt();
|
||||
if _show_cmd {
|
||||
use tokio::io::AsyncBufReadExt;
|
||||
let mut cmd = String::new();
|
||||
let mut reader = tokio::io::BufReader::new(tokio::io::stdin());
|
||||
loop {
|
||||
cmd.clear();
|
||||
println!("======== input:list,info,route,all,stop,chart_a,chart_b[:ip] ========");
|
||||
match reader.read_line(&mut cmd).await {
|
||||
Ok(len) => {
|
||||
if !common::command::command_str(&cmd[..len], vnt_c) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("input err:{}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vnt_util.wait().await
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
[package]
|
||||
name = "vn-link"
|
||||
version = "1.2.16"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
lwip-rs = { git = "https://github.com/vnt-dev/lwip-rs" }
|
||||
vnt = { path = "../vnt", package = "vnt", default-features = false }
|
||||
log = "0.4.17"
|
||||
anyhow = "1.0.82"
|
||||
parking_lot = "0.12.1"
|
||||
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
crossbeam-utils = "0.8"
|
||||
|
||||
[features]
|
||||
default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "port_mapping", "lz4_compress"]
|
||||
openssl = ["vnt/openssl"]
|
||||
openssl-vendored = ["vnt/openssl-vendored"]
|
||||
ring-cipher = ["vnt/ring-cipher"]
|
||||
aes_cbc = ["vnt/aes_cbc"]
|
||||
aes_ecb = ["vnt/aes_ecb"]
|
||||
sm4_cbc = ["vnt/sm4_cbc"]
|
||||
aes_gcm = ["vnt/aes_gcm"]
|
||||
chacha20_poly1305 = ["vnt/chacha20_poly1305"]
|
||||
server_encrypt = ["vnt/server_encrypt"]
|
||||
port_mapping = ["vnt/port_mapping"]
|
||||
lz4_compress = ["vnt/lz4_compress"]
|
||||
zstd_compress = ["vnt/zstd_compress"]
|
||||
upnp = ["vnt/upnp"]
|
||||
ws = ["vnt/ws"]
|
||||
wss = ["vnt/wss"]
|
||||
@@ -0,0 +1,85 @@
|
||||
use anyhow::Context;
|
||||
use std::net::SocketAddr;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VnLinkConfig {
|
||||
pub mapping: Vec<LinkItem>,
|
||||
}
|
||||
|
||||
impl VnLinkConfig {
|
||||
pub fn new(mapping: Vec<LinkItem>) -> Self {
|
||||
Self { mapping }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum LinkProtocol {
|
||||
Tcp,
|
||||
Udp,
|
||||
}
|
||||
|
||||
impl LinkProtocol {
|
||||
pub fn is_tcp(&self) -> bool {
|
||||
self == &LinkProtocol::Tcp
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct LinkItem {
|
||||
pub protocol: LinkProtocol,
|
||||
pub src_port: u16,
|
||||
pub dest: SocketAddr,
|
||||
}
|
||||
|
||||
impl LinkItem {
|
||||
pub fn new(protocol: LinkProtocol, src_port: u16, dest: SocketAddr) -> Self {
|
||||
Self {
|
||||
protocol,
|
||||
src_port,
|
||||
dest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn convert(vec: Vec<String>) -> anyhow::Result<Vec<LinkItem>> {
|
||||
let mut rs = Vec::with_capacity(vec.len());
|
||||
for x in vec {
|
||||
let string = x.trim().to_lowercase();
|
||||
if let Some(udp_mapping) = string.strip_prefix("udp:") {
|
||||
let mut split = udp_mapping.split("-");
|
||||
let bind_port = split
|
||||
.next()
|
||||
.with_context(|| format!("vnt-mapping error {:?},eg: udp:80-10.26.0.10:8080", x))?;
|
||||
let bind_port = u16::from_str(bind_port)
|
||||
.with_context(|| format!("udp_mapping error {}", bind_port))?;
|
||||
let dest = split
|
||||
.next()
|
||||
.with_context(|| format!("vnt-mapping error {:?},eg: udp:80-10.26.0.10:8080", x))?;
|
||||
let dest_addr = SocketAddr::from_str(dest)
|
||||
.with_context(|| format!("udp_mapping error {}", dest))?;
|
||||
rs.push(LinkItem::new(LinkProtocol::Udp, bind_port, dest_addr));
|
||||
continue;
|
||||
}
|
||||
if let Some(tcp_mapping) = string.strip_prefix("tcp:") {
|
||||
let mut split = tcp_mapping.split("-");
|
||||
let bind_port = split
|
||||
.next()
|
||||
.with_context(|| format!("vnt-mapping error {:?},eg: tcp:80-10.26.0.10:8080", x))?;
|
||||
let bind_port = u16::from_str(bind_port)
|
||||
.with_context(|| format!("tcp_mapping error {}", bind_port))?;
|
||||
let dest = split
|
||||
.next()
|
||||
.with_context(|| format!("vnt-mapping error {:?},eg: tcp:80-10.26.0.10:8080", x))?;
|
||||
let dest_addr = SocketAddr::from_str(dest)
|
||||
.with_context(|| format!("tcp_mapping error {}", dest))?;
|
||||
rs.push(LinkItem::new(LinkProtocol::Tcp, bind_port, dest_addr));
|
||||
continue;
|
||||
}
|
||||
Err(anyhow::anyhow!(
|
||||
"vnt-mapping error {:?},eg: tcp:80-10.26.0.10:8080",
|
||||
x
|
||||
))?;
|
||||
}
|
||||
Ok(rs)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod tcp;
|
||||
pub mod udp;
|
||||
@@ -0,0 +1,46 @@
|
||||
use crate::out_mapping::tcp::tcp_copy;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use lwip_rs::tcp_stream::TcpStream as LwIpTcpStream;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::net::TcpListener;
|
||||
use vnt::handle::CurrentDeviceInfo;
|
||||
|
||||
pub async fn tcp_mapping_listen(
|
||||
tcp_listener: TcpListener,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
dest: SocketAddr,
|
||||
) {
|
||||
loop {
|
||||
let (stream, addr) = match tcp_listener.accept().await {
|
||||
Ok((stream, addr)) => (stream, addr),
|
||||
Err(e) => {
|
||||
log::warn!("tcp_mapping_listen {:?} dest {}", e, dest);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let current_info = current_device.load();
|
||||
if current_info.virtual_ip.is_unspecified() {
|
||||
continue;
|
||||
}
|
||||
if let IpAddr::V4(ip) = dest.ip() {
|
||||
if ip == current_info.virtual_ip {
|
||||
//防止用错参数的
|
||||
log::warn!("目的地址不能是本地虚拟ip tcp->{}", dest);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let src = SocketAddr::new(IpAddr::V4(current_info.virtual_ip), addr.port());
|
||||
tokio::spawn(async move {
|
||||
match LwIpTcpStream::connect(src, dest, Duration::from_secs(5)).await {
|
||||
Ok(lw_tcp) => {
|
||||
tcp_copy(lw_tcp, stream);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("{} {}->{} {}", addr, src, dest, e);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use lwip_rs::udp::UdpSocketWrite;
|
||||
use vnt::handle::CurrentDeviceInfo;
|
||||
|
||||
pub async fn udp_mapping_start(
|
||||
udp: UdpSocket,
|
||||
lwip_udp_write: UdpSocketWrite,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
in_udp_map: &Arc<
|
||||
Mutex<
|
||||
HashMap<
|
||||
(SocketAddr, SocketAddr),
|
||||
(Arc<UdpSocket>, Option<SocketAddr>, Arc<AtomicCell<Instant>>),
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
|
||||
dest: SocketAddr,
|
||||
) {
|
||||
let udp = Arc::new(udp);
|
||||
let mut buf = [0u8; 65536];
|
||||
loop {
|
||||
let (len, addr) = match udp.recv_from(&mut buf).await {
|
||||
Ok(rs) => rs,
|
||||
Err(e) => {
|
||||
log::warn!("recv_from {} {}", dest, e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let current_info = current_device.load();
|
||||
if current_info.virtual_ip.is_unspecified() {
|
||||
continue;
|
||||
}
|
||||
if let IpAddr::V4(ip) = dest.ip() {
|
||||
if ip == current_info.virtual_ip {
|
||||
//防止用错参数的
|
||||
log::warn!("目的地址不能是本地虚拟ip udp->{}", dest);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let src = SocketAddr::new(IpAddr::V4(current_info.virtual_ip), addr.port());
|
||||
in_udp_map.lock().insert(
|
||||
(dest, src),
|
||||
(
|
||||
udp.clone(),
|
||||
Some(addr),
|
||||
Arc::new(AtomicCell::new(Instant::now())),
|
||||
),
|
||||
);
|
||||
|
||||
if let Err(e) = lwip_udp_write.send(&buf[..len], &src, &dest) {
|
||||
log::warn!("lwip_udp_write {}->{} {}", src, dest, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
pub mod config;
|
||||
mod in_mapping;
|
||||
mod out_mapping;
|
||||
mod vnt_link;
|
||||
|
||||
pub use vnt;
|
||||
|
||||
pub use vnt_link::*;
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod tcp;
|
||||
pub mod udp;
|
||||
@@ -0,0 +1,64 @@
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
use lwip_rs::tcp_listener::TcpListener;
|
||||
use lwip_rs::tcp_stream::TcpStream as LwIpTcpStream;
|
||||
use vnt::handle::CurrentDeviceInfo;
|
||||
|
||||
pub async fn tcp_mapping_listen(
|
||||
mut tcp_listener: TcpListener,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
) {
|
||||
loop {
|
||||
let stream = match tcp_listener.accept().await {
|
||||
Ok(stream) => stream,
|
||||
Err(e) => {
|
||||
log::warn!("tcp_mapping_listen err {:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
let device_info = current_device.load();
|
||||
tokio::spawn(async move {
|
||||
let dest = stream.dest_addr();
|
||||
let src = stream.src_addr();
|
||||
if let Err(e) = tcp_mapping_handle(stream, device_info).await {
|
||||
log::warn!("tcp_mapping_handle {}->{} {:?}", src, dest, e)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn tcp_mapping_handle(
|
||||
tcp_stream: LwIpTcpStream,
|
||||
device_info: CurrentDeviceInfo,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut dest = tcp_stream.dest_addr();
|
||||
// let src = tcp_stream.src_addr();
|
||||
if let IpAddr::V4(ip) = dest.ip() {
|
||||
if ip.is_unspecified()
|
||||
|| ip.is_broadcast()
|
||||
|| ip.is_multicast()
|
||||
|| ip == device_info.virtual_ip
|
||||
|| ip == device_info.broadcast_ip
|
||||
{
|
||||
//是自己
|
||||
dest.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
}
|
||||
}
|
||||
let peer_stream = TcpStream::connect(dest).await?;
|
||||
if dest.port() == peer_stream.local_addr()?.port() {
|
||||
return Err(anyhow::anyhow!("tcp port loop"));
|
||||
}
|
||||
tcp_copy(tcp_stream, peer_stream);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn tcp_copy(lw_tcp: LwIpTcpStream, tokio_tcp: TcpStream) {
|
||||
let (mut write, mut read) = lw_tcp.into_split();
|
||||
let (mut peer_read, mut peer_write) = tokio_tcp.into_split();
|
||||
tokio::spawn(async move { tokio::io::copy(&mut read, &mut peer_write).await });
|
||||
tokio::spawn(async move { tokio::io::copy(&mut peer_read, &mut write).await });
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use lwip_rs::udp::{UdpSocketRead, UdpSocketWrite};
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::net::UdpSocket;
|
||||
use vnt::handle::CurrentDeviceInfo;
|
||||
|
||||
pub async fn udp_mapping_start(
|
||||
lwip_udp_write: UdpSocketWrite,
|
||||
mut lwip_udp_read: UdpSocketRead,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
in_udp_map: Arc<
|
||||
Mutex<
|
||||
HashMap<
|
||||
(SocketAddr, SocketAddr),
|
||||
(Arc<UdpSocket>, Option<SocketAddr>, Arc<AtomicCell<Instant>>),
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
) {
|
||||
loop {
|
||||
let (buf, src, dest) = match lwip_udp_read.recv().await {
|
||||
Ok(rs) => rs,
|
||||
Err(e) => {
|
||||
log::warn!("udp_mapping err {:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
if let Err(e) = handle(
|
||||
¤t_device,
|
||||
&lwip_udp_write,
|
||||
&in_udp_map,
|
||||
buf,
|
||||
src,
|
||||
dest,
|
||||
)
|
||||
.await
|
||||
{
|
||||
log::warn!("udp_mapping err {}->{} {:?}", src, dest, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle(
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
lwip_udp_write: &UdpSocketWrite,
|
||||
map: &Arc<
|
||||
Mutex<
|
||||
HashMap<
|
||||
(SocketAddr, SocketAddr),
|
||||
(Arc<UdpSocket>, Option<SocketAddr>, Arc<AtomicCell<Instant>>),
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
buf: Vec<u8>,
|
||||
src: SocketAddr,
|
||||
dest: SocketAddr,
|
||||
) -> anyhow::Result<()> {
|
||||
let option = map.lock().get(&(src, dest)).cloned();
|
||||
|
||||
if let Some((dest_udp, addr, time)) = option {
|
||||
time.store(Instant::now());
|
||||
if let Some(addr) = addr {
|
||||
dest_udp.send_to(&buf, addr).await?;
|
||||
} else {
|
||||
dest_udp.send(&buf).await?;
|
||||
}
|
||||
} else {
|
||||
let mut real_dest = dest;
|
||||
let peer_udp_socket = match UdpSocket::bind(format!("0.0.0.0:{}", src.port())).await {
|
||||
Ok(udp) => udp,
|
||||
Err(_) => UdpSocket::bind("0.0.0.0:0").await?,
|
||||
};
|
||||
if let IpAddr::V4(ip) = dest.ip() {
|
||||
let device_info = current_device.load();
|
||||
if ip.is_unspecified()
|
||||
|| ip.is_broadcast()
|
||||
|| ip.is_multicast()
|
||||
|| ip == device_info.virtual_ip
|
||||
|| ip == device_info.broadcast_ip
|
||||
{
|
||||
//是自己
|
||||
real_dest.set_ip(IpAddr::V4(Ipv4Addr::LOCALHOST));
|
||||
}
|
||||
}
|
||||
peer_udp_socket.connect(real_dest).await?;
|
||||
peer_udp_socket.send(&buf).await?;
|
||||
let peer_udp_socket = Arc::new(peer_udp_socket);
|
||||
let time = Arc::new(AtomicCell::new(Instant::now()));
|
||||
let map = map.clone();
|
||||
map.lock()
|
||||
.insert((src, dest), (peer_udp_socket.clone(), None, time.clone()));
|
||||
let lwip_udp_write = lwip_udp_write.clone();
|
||||
tokio::spawn(async move {
|
||||
peer_udp_handle(peer_udp_socket, lwip_udp_write, src, dest, time).await;
|
||||
map.lock().remove(&(src, dest));
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn peer_udp_handle(
|
||||
peer_udp_socket: Arc<UdpSocket>,
|
||||
lwip_udp_write: UdpSocketWrite,
|
||||
src: SocketAddr,
|
||||
dest: SocketAddr,
|
||||
time: Arc<AtomicCell<Instant>>,
|
||||
) {
|
||||
let mut buf = [0u8; 65536];
|
||||
loop {
|
||||
match tokio::time::timeout(Duration::from_secs(600), peer_udp_socket.recv(&mut buf)).await {
|
||||
Ok(rs) => match rs {
|
||||
Ok(len) => match lwip_udp_write.send(&buf[..len], &dest, &src) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!("udp proxy {}->{} {:?}", dest, src, e);
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("udp proxy {}->{} {:?}", dest, src, e);
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
if time.load().elapsed() > Duration::from_secs(580) {
|
||||
//超时关闭
|
||||
log::warn!("udp proxy timeout {}->{}", dest, src,);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
use anyhow::Context;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use tokio::net::{TcpListener, UdpSocket};
|
||||
use tokio::sync::watch::{channel, Sender};
|
||||
|
||||
use lwip_rs::stack::{NetStack, NetStackWrite};
|
||||
use lwip_rs::tcp_listener::TcpListener as LwIPTcpListener;
|
||||
use lwip_rs::udp::{UdpSocket as LwIpUdpSocket, UdpSocketWrite};
|
||||
use vnt::channel::BUFFER_SIZE;
|
||||
use vnt::core::{Config, Vnt};
|
||||
use vnt::packet::ip::ipv4::packet::IpV4Packet;
|
||||
use vnt::protocol::HEAD_LEN;
|
||||
use vnt::vnt_device::DeviceWrite;
|
||||
use vnt::VntCallback;
|
||||
|
||||
use crate::config::{LinkItem, LinkProtocol, VnLinkConfig};
|
||||
use crate::{in_mapping, out_mapping};
|
||||
|
||||
pub struct VnLink {
|
||||
vnt: Vnt,
|
||||
in_udp_map: Arc<
|
||||
Mutex<
|
||||
HashMap<
|
||||
(SocketAddr, SocketAddr),
|
||||
(Arc<UdpSocket>, Option<SocketAddr>, Arc<AtomicCell<Instant>>),
|
||||
>,
|
||||
>,
|
||||
>,
|
||||
lwip_udp_write: UdpSocketWrite,
|
||||
shutdown_tx: Sender<bool>,
|
||||
}
|
||||
|
||||
impl VnLink {
|
||||
pub async fn new<Call: VntCallback>(
|
||||
vnt_config: Config,
|
||||
vn_link_config: VnLinkConfig,
|
||||
callback: Call,
|
||||
) -> anyhow::Result<Self> {
|
||||
let stack = NetStack::new(HEAD_LEN, 1024, vnt_config.mtu.unwrap_or(1420) as u16).await;
|
||||
let udp = LwIpUdpSocket::new()?;
|
||||
let tcp_listener = LwIPTcpListener::new()?;
|
||||
let (shutdown_tx, shutdown_rx) = channel(false);
|
||||
let (net_stack_write, mut net_stack_read) = stack.into_split();
|
||||
let vnt = Vnt::new_device(vnt_config, callback, VntDevice { net_stack_write })?;
|
||||
let shutdown_tx_ = shutdown_tx.clone();
|
||||
let w = vnt.add_stop_listener("vnt-link".into(), move || {
|
||||
let _ = shutdown_tx_.send(true);
|
||||
})?;
|
||||
let ip_sender = vnt.ipv4_packet_sender().unwrap();
|
||||
let mut shutdown_rx_ = shutdown_rx.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = shutdown_rx_.changed() => {
|
||||
break;
|
||||
}
|
||||
rs = net_stack_read.recv_ip() => {
|
||||
match rs{
|
||||
Ok((mut buf, start_index, len)) => {
|
||||
let ipv4_packet = if let Ok(packet) =
|
||||
IpV4Packet::new(&buf[start_index..len])
|
||||
{
|
||||
packet
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let destination_ip = ipv4_packet.destination_ip();
|
||||
let source_ip = ipv4_packet.source_ip();
|
||||
|
||||
if let Err(e) = ip_sender.send_ip(&mut buf, len, &mut extend, destination_ip) {
|
||||
log::warn!("{}->{},{}", source_ip, destination_ip, e);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("net_stack_read {:?}", e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
w.stop_all();
|
||||
});
|
||||
|
||||
let (lwip_udp_write, lwip_udp_read) = udp.into_split();
|
||||
let in_udp_map: Arc<
|
||||
Mutex<
|
||||
HashMap<
|
||||
(SocketAddr, SocketAddr),
|
||||
(Arc<UdpSocket>, Option<SocketAddr>, Arc<AtomicCell<Instant>>),
|
||||
>,
|
||||
>,
|
||||
> = Arc::new(Mutex::new(HashMap::new()));
|
||||
|
||||
let current_device_info = vnt.current_device_info();
|
||||
let in_udp_map_ = in_udp_map.clone();
|
||||
let lwip_udp_write_ = lwip_udp_write.clone();
|
||||
let vnt_ = vnt.clone();
|
||||
let mut shutdown_rx_ = shutdown_rx.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::select! {
|
||||
_ = shutdown_rx_.changed() => {}
|
||||
_ = out_mapping::udp::udp_mapping_start(
|
||||
lwip_udp_write_,
|
||||
lwip_udp_read,
|
||||
current_device_info,
|
||||
in_udp_map_,
|
||||
) => {}
|
||||
}
|
||||
|
||||
vnt_.stop();
|
||||
});
|
||||
let current_device_info = vnt.current_device_info();
|
||||
let vnt_ = vnt.clone();
|
||||
let mut shutdown_rx_ = shutdown_rx.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
tokio::select! {
|
||||
_ = shutdown_rx_.changed() => {}
|
||||
_ = out_mapping::tcp::tcp_mapping_listen(tcp_listener, current_device_info) => {}
|
||||
}
|
||||
vnt_.stop();
|
||||
});
|
||||
let link = Self {
|
||||
vnt,
|
||||
in_udp_map,
|
||||
lwip_udp_write,
|
||||
shutdown_tx,
|
||||
};
|
||||
link.add_mapping(vn_link_config.mapping).await?;
|
||||
Ok(link)
|
||||
}
|
||||
pub async fn add_mapping(&self, mapping: Vec<LinkItem>) -> anyhow::Result<()> {
|
||||
for item in mapping {
|
||||
let current_device_info = self.vnt.current_device_info();
|
||||
if item.dest.ip().is_unspecified() {
|
||||
Err(anyhow::anyhow!("dest_address {:?} is_unspecified", item))?
|
||||
}
|
||||
let mut shutdown_rx_ = self.shutdown_tx.subscribe();
|
||||
if *shutdown_rx_.borrow() {
|
||||
Err(anyhow::anyhow!("mapping stop"))?
|
||||
}
|
||||
if item.protocol == LinkProtocol::Udp {
|
||||
let lwip_udp_write = self.lwip_udp_write.clone();
|
||||
let in_udp_map = self.in_udp_map.clone();
|
||||
//只能本机访问,不然不同IP的相同来源端口会有问题
|
||||
let udp = UdpSocket::bind(format!("127.0.0.1:{}", item.src_port))
|
||||
.await
|
||||
.with_context(|| format!("udp bind failed {}", item.src_port))?;
|
||||
tokio::spawn(async move {
|
||||
tokio::select! {
|
||||
_ = shutdown_rx_.changed() => {}
|
||||
_ = in_mapping::udp::udp_mapping_start(
|
||||
udp,
|
||||
lwip_udp_write,
|
||||
current_device_info,
|
||||
&in_udp_map,
|
||||
item.dest,
|
||||
) => {}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
let listener = TcpListener::bind(format!("127.0.0.1:{}", item.src_port))
|
||||
.await
|
||||
.with_context(|| format!("tcp bind failed {}", item.src_port))?;
|
||||
tokio::spawn(async move {
|
||||
tokio::select! {
|
||||
_ = shutdown_rx_.changed() => {}
|
||||
_ = in_mapping::tcp::tcp_mapping_listen(
|
||||
listener,
|
||||
current_device_info,
|
||||
item.dest,
|
||||
) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn stop(&self) {
|
||||
self.as_vnt().stop()
|
||||
}
|
||||
pub async fn wait(&self) {
|
||||
loop {
|
||||
let mut receiver = self.shutdown_tx.subscribe();
|
||||
if *receiver.borrow() {
|
||||
return;
|
||||
}
|
||||
if receiver.changed().await.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vnt(&self) -> &Vnt {
|
||||
&self.vnt
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct VntDevice {
|
||||
net_stack_write: NetStackWrite,
|
||||
}
|
||||
|
||||
impl DeviceWrite for VntDevice {
|
||||
fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
self.net_stack_write.send_ip(buf)?;
|
||||
Ok(buf.len())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
[package]
|
||||
name = "vnt-cli"
|
||||
version = "1.2.16"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
vnt = { path = "../vnt", package = "vnt", default-features = false, features = ["integrated_tun"] }
|
||||
common = { path = "../common", default-features = false, features = ["integrated_tun"] }
|
||||
log = "0.4.17"
|
||||
anyhow = "1.0.82"
|
||||
console = "0.15.2"
|
||||
|
||||
[target.'cfg(any(target_os = "linux",target_os = "macos"))'.dependencies]
|
||||
sudo = "0.6.0"
|
||||
signal-hook = "0.3.17"
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winapi = { version = "0.3.9", features = ["handleapi", "processthreadsapi", "winnt", "securitybaseapi", "impl-default"] }
|
||||
|
||||
|
||||
[features]
|
||||
default = ["default-feature"]
|
||||
default-feature = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "log", "command", "file_config", "lz4", "ws"]
|
||||
|
||||
openssl = ["vnt/openssl", "common/openssl"]
|
||||
openssl-vendored = ["vnt/openssl-vendored", "common/openssl-vendored"]
|
||||
ring-cipher = ["vnt/ring-cipher", "common/ring-cipher"]
|
||||
aes_cbc = ["vnt/aes_cbc", "common/aes_cbc"]
|
||||
aes_ecb = ["vnt/aes_ecb", "common/aes_ecb"]
|
||||
sm4_cbc = ["vnt/sm4_cbc", "common/sm4_cbc"]
|
||||
aes_gcm = ["vnt/aes_gcm", "common/aes_gcm"]
|
||||
chacha20_poly1305 = ["vnt/chacha20_poly1305", "common/chacha20_poly1305"]
|
||||
server_encrypt = ["vnt/server_encrypt", "common/server_encrypt"]
|
||||
port_mapping = ["vnt/port_mapping", "common/port_mapping"]
|
||||
lz4 = ["vnt/lz4_compress", "common/lz4"]
|
||||
zstd = ["vnt/zstd_compress", "common/zstd"]
|
||||
ip_proxy = ["vnt/ip_proxy", "common/ip_proxy"]
|
||||
upnp = ["vnt/upnp", "common/upnp"]
|
||||
ws = ["vnt/ws", "common/ws"]
|
||||
wss = ["vnt/wss", "common/wss"]
|
||||
log = ["common/log"]
|
||||
command = ["common/command"]
|
||||
file_config = ["common/file_config"]
|
||||
[build-dependencies]
|
||||
rand = "0.8.5"
|
||||
chrono = "0.4.23"
|
||||
|
||||
[target.'cfg(windows)'.build-dependencies]
|
||||
thunk-rs = { version = "0.3.3", features = ["win7"] }
|
||||
@@ -0,0 +1,256 @@
|
||||
## 模块介绍
|
||||
|
||||
体积小,可以在服务器、路由器等环境使用
|
||||
|
||||
## 详细参数说明
|
||||
|
||||
### -k `<token>`
|
||||
|
||||
一个虚拟局域网的标识,在同一服务器下,相同token的设备会组建一个局域网
|
||||
|
||||
### -n `<name>`
|
||||
|
||||
设备名称,方便区分不同设备
|
||||
|
||||
### -d `<id>`
|
||||
|
||||
设备id,每台设备的唯一标识,注意不要重复
|
||||
|
||||
### -c
|
||||
|
||||
关闭控制台交互式命令,后台运行时可以加此参数
|
||||
|
||||
### -s `<server>`
|
||||
|
||||
注册和中继服务器地址,注册和转发数据,以'TXT:'开头表示解析TXT记录,TXT记录内容必须是'host:port'形式的服务器地址
|
||||
|
||||
### -e `<stun-server>`
|
||||
|
||||
使用stun服务探测客户端NAT类型,不同类型有不同的打洞策略
|
||||
|
||||
### -a
|
||||
|
||||
加了此参数表示使用tap网卡,默认使用tun网卡,tun网卡效率更高
|
||||
|
||||
注意:仅在windows上支持使用tap,用于兼容低版本windows系统(低版本windows不支持wintun)
|
||||
|
||||
使用tap模式需要手动创建tap网卡,使用--nic参数指定已经创建好的tap网卡名称
|
||||
|
||||
### --nic `<tun0>`
|
||||
|
||||
指定虚拟网卡名称,默认tun模式使用vnt-tun,tap模式使用vnt-tap
|
||||
|
||||
### -i `<in-ip>`、-o `<out-ip>`
|
||||
|
||||
配置点对网(IP代理)时使用,例如A(虚拟ip:10.26.0.2)通过B(虚拟ip:10.26.0.3,本地出口ip:192.168.0.10)访问C(
|
||||
目标网段192.168.0.0/24),
|
||||
|
||||
则在A配置 **'-i 192.168.0.0/24,10.26.0.3'** ,表示将192.168.0.0/24网段的数据都转发到10.26.0.3节点
|
||||
|
||||
在B配置 **'-o 192.168.0.0/24'** ,表示允许将数据转发到 192.168.0.0/24 ,允许转发所有网段可以使用 **'-o 0.0.0.0/0'**
|
||||
|
||||
-i和-o参数均可使用多次,来指定不同网段,例如 **'-o 192.168.1.0/24 -o 192.168.2.0/24'**
|
||||
表示允许转发目标为192.168.1.0/24或192.168.2.0/24这两个网段的数据
|
||||
|
||||
### -w `<password>`
|
||||
|
||||
提升通信安全性,使用该密码生成的密钥对客户端数据进行加密,并且服务端无法解密(包括中继数据)。使用相同密码的客户端才能通信
|
||||
|
||||
| 密码位数 | 加密算法 |
|
||||
|-------|------------|
|
||||
| 小于8 | AES128-GCM |
|
||||
| 大于等于8 | AES256-GCM |
|
||||
|
||||
### -W
|
||||
|
||||
开启和服务端通信的数据加密,采用rsa+aes256gcm加密客户端和服务端之间通信的数据,可以避免token泄漏、中间人攻击
|
||||
|
||||
注意:
|
||||
|
||||
1. -w `<password>`是用于客户端-客户端之间的加密,password不会传递到服务端,只添加这个参数不会加密客户端-服务端通信的数据
|
||||
2. -W 用于开启客户端-服务端之间的加密
|
||||
|
||||
### -u `<mtu>`
|
||||
|
||||
设置虚拟网卡的mtu值,大多数情况下使用默认值效率会更高,也可根据实际情况微调这个值,不加密默认为1450,加密默认为1410
|
||||
|
||||
### ~~--tcp~~
|
||||
|
||||
~~和服务端使用tcp通信。有些网络提供商对UDP限制比较大,这个时候可以选择使用TCP模式,提高稳定性。一般来说udp延迟和消耗更低~~
|
||||
|
||||
新版本使用 `-s tcp://`的形式使用tcp
|
||||
|
||||
### --ip `<IP>`
|
||||
|
||||
指定虚拟ip,指定的ip不能和其他设备重复,必须有效并且在服务端所属网段下,默认情况由服务端分配
|
||||
|
||||
### --par `<parallel>`
|
||||
|
||||
任务并行度(必须为正整数),默认值为1,该值表示处理网卡读写的任务数,组网设备数较多、处理延迟较大时可适当调大此值
|
||||
|
||||
### --model `<model>`
|
||||
|
||||
加密模式,可选值
|
||||
aes_gcm/aes_cbc/aes_ecb/sm4_cbc/chacha20_poly1305/chacha20/xor,默认使用aes_gcm,通常情况aes_gcm和chacha20_poly1305安全性高。
|
||||
各种加密模式的安全性和速度都不相同,请按需选取
|
||||
|
||||
特别说明:xor只是对数据进行简单异或,仅仅避免了明文传输,安全性很差,同时对性能影响也极小;
|
||||
|
||||
| 密码位数 | model | 加密算法 |
|
||||
|--------|-------------------|-------------------|
|
||||
| `< 8` | aes_gcm | AES128-GCM |
|
||||
| `>= 8` | aes_gcm | AES256-GCM |
|
||||
| `< 8` | aes_cbc | AES128-CBC |
|
||||
| `>= 8` | aes_cbc | AES256-CBC |
|
||||
| `< 8` | aes_ecb | AES128-ECB |
|
||||
| `>= 8` | aes_ecb | AES256-ECB |
|
||||
| `> 0` | sm4_cbc | SM4-CBC |
|
||||
| `> 0` | chacha20_poly1305 | ChaCha20-Poly1305 |
|
||||
| `> 0` | chacha20 | ChaCha20 |
|
||||
| `> 0` | xor | 简单异或混淆 |
|
||||
|
||||
### --finger
|
||||
|
||||
开启数据指纹校验,可增加安全性,如果服务端开启指纹校验,则客户端也必须开启,开启会损耗一部分性能
|
||||
|
||||
注意:默认情况下服务端不会对中转的数据做校验,如果要对中转的数据做校验,则需要客户端、服务端都开启此参数
|
||||
|
||||
### --punch `<punch>`
|
||||
|
||||
取值ipv4/ipv6,选择只使用ipv4打洞或者只使用ipv6打洞,默认两者都会使用
|
||||
|
||||
### --ports `<port1,port2>`
|
||||
|
||||
指定本地监听的端口组,多个端口使用逗号分隔,多个端口可以分摊流量,增加并发、减缓流量限制,tcp会监听端口组的第一个端口,用于tcp直连
|
||||
|
||||
- 例1:‘--ports 12345,12346,12347’ 表示udp监听12345、12346、12347这三个端口,tcp监听12345端口
|
||||
- 例2:‘--ports 0,0’ 表示udp监听两个未使用的端口,tcp监听一个未使用的端口
|
||||
|
||||
### --cmd
|
||||
|
||||
开启交互式命令,开启后可以直接在窗口下输入命令,如需后台运行请勿开启
|
||||
|
||||
### --first_latency
|
||||
|
||||
优先使用低延迟通道,默认情况下优先使用p2p通道,某些情况下可能p2p比客户端中继延迟更高,可使用此参数进行优化传输
|
||||
|
||||
### --no-proxy
|
||||
|
||||
关闭内置的ip代理,内置的代理较为简单,而且一般来说直接使用网卡NAT转发性能会更高,
|
||||
有需要可以自行配置NAT转发,[可参考‘编译’小节中的NAT配置](https://github.com/vnt-dev/vnt#%E7%BC%96%E8%AF%91)
|
||||
|
||||
### --dns `<223.5.5.5>`
|
||||
|
||||
设置域名解析服务器地址,可以设置多个。如果使用TXT记录的域名,则dns默认使用223.5.5.5和114.114.114.114,端口省略值为53
|
||||
|
||||
当地址解析失败时,会依次尝试后面的dns,直到有A记录、AAAA记录(或TXT记录)的解析结果
|
||||
|
||||
### --mapping `<udp:0.0.0.0:80-10.26.0.10:80>`
|
||||
|
||||
端口映射,可以设置多个映射地址,例如 '--mapping udp:0.0.0.0:80-10.26.0.10:80 --mapping tcp:0.0.0.0:80-10.26.0.11:81'
|
||||
表示将本地udp 80端口的数据转发到10.26.0.10:80,将本地tcp 80端口的数据转发到10.26.0.11:81,转发的目的地址可以使用域名+端口
|
||||
|
||||
### --compressor `<lz4>`
|
||||
|
||||
启用压缩,默认仅支持lz4压缩,开启压缩后,如果数据包长度大于等于128,则会使用压缩,否则还是会按原数据发送
|
||||
|
||||
也支持开启zstd压缩,但是需要自行编译,编译时加入参数--features zstd
|
||||
|
||||
如果宽度速度比较慢,可以考虑使用高级别的压缩
|
||||
|
||||
### -f `<conf>`
|
||||
|
||||
指定配置文件
|
||||
配置文件采用yaml格式,可参考:
|
||||
|
||||
```yaml
|
||||
# 全部参数
|
||||
tap: false #是否使用tap 仅在windows上支持使用tap
|
||||
token: xxx #组网token
|
||||
device_id: xxx #当前设备id
|
||||
name: windows 11 #当前设备名称
|
||||
server_address: ip:port #注册和中继服务器
|
||||
stun_server: #stun服务器
|
||||
- stun1.l.google.com:19302
|
||||
- stun2.l.google.com:19302
|
||||
in_ips: #代理ip入站
|
||||
- 192.168.1.0/24,10.26.0.3
|
||||
out_ips: #代理ip出站
|
||||
- 0.0.0.0/0
|
||||
password: xxx #密码
|
||||
mtu: 1420 #mtu
|
||||
tcp: false #tcp模式
|
||||
ip: 10.26.0.2 #指定虚拟ip
|
||||
use_channel: relay #relay:仅中继模式.p2p:仅直连模式
|
||||
server_encrypt: true #服务端加密
|
||||
parallel: 1 #任务并行度
|
||||
cipher_model: aes_gcm #客户端加密算法
|
||||
finger: false #关闭数据指纹
|
||||
punch_model: ipv4 #打洞模式,表示只使用ipv4地址打洞,默认会同时使用v6和v4
|
||||
ports:
|
||||
- 0 #使用随机端口,tcp监听此端口
|
||||
- 0
|
||||
cmd: false #关闭控制台输入
|
||||
no_proxy: false #是否关闭内置代理,true为关闭
|
||||
first_latency: false #是否优先低延迟通道,默认为false,表示优先使用p2p通道
|
||||
device_name: vnt-tun #网卡名称
|
||||
packet_loss: 0 #指定丢包率 取值0~1之间的数 用于模拟弱网
|
||||
packet_delay: 0 #指定延迟 单位毫秒 用于模拟弱网
|
||||
dns:
|
||||
- 223.5.5.5 # 首选dns
|
||||
- 8.8.8.8 # 备选dns
|
||||
mapping:
|
||||
- udp:0.0.0.0:80-10.26.0.10:80 # 映射udp数据
|
||||
- tcp:0.0.0.0:80-10.26.0.10:81 # 映射tcp数据
|
||||
- tcp:0.0.0.0:82-localhost:83 # 映射tcp数据
|
||||
disable_stats: false # 为true表示关闭统计
|
||||
allow_wire_guard: false # 为true则表示允许接入wg
|
||||
```
|
||||
|
||||
或者需要哪个配置就加哪个,当然token是必须的
|
||||
|
||||
```yaml
|
||||
# 部分参数
|
||||
token: xxx #组网token
|
||||
```
|
||||
|
||||
### --use-channel `<relay/p2p>`
|
||||
|
||||
- relay:仅中继模式,会禁止打洞/p2p直连,只使用服务器转发
|
||||
- p2p:仅直连模式,会禁止网络数据从服务器/客户端转发,只会使用服务器转发控制包
|
||||
|
||||
### --packet-loss `<0>`
|
||||
|
||||
模拟丢包,取值0~1之间的小数,程序会按设定的概率主动丢包。在模拟弱网环境时会有帮助。
|
||||
|
||||
### --packet-delay `<0>`
|
||||
|
||||
模拟延迟,整数,单位毫秒(ms),程序会按设定的值延迟发包,可用于模拟弱网
|
||||
|
||||
### --disable-stats
|
||||
|
||||
关闭流量统计
|
||||
|
||||
### --allow-wg
|
||||
|
||||
允许接入WireGuard客户端,和wg混用时必须开启此参数
|
||||
|
||||
### --list
|
||||
|
||||
在后台运行时,查看其他设备列表
|
||||
|
||||
### --all
|
||||
|
||||
在后台运行时,查看其他设备完整信息
|
||||
|
||||
### --info
|
||||
|
||||
在后台运行时,查看当前设备信息
|
||||
|
||||
### --route
|
||||
|
||||
在后台运行时,查看数据转发路径
|
||||
|
||||
### --stop
|
||||
|
||||
停止后台运行
|
||||
@@ -0,0 +1,5 @@
|
||||
fn main() {
|
||||
// 配置 thunk-rs 来链接 Windows 7 兼容库,并自动设置链接参数
|
||||
#[cfg(target_os = "windows")]
|
||||
thunk::thunk();
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
use common::callback;
|
||||
use console::style;
|
||||
use vnt::core::{Config, Vnt};
|
||||
mod root_check;
|
||||
fn main() {
|
||||
let (config, _vnt_link_config, cmd) = match common::cli::parse_args_config() {
|
||||
Ok(rs) => {
|
||||
if let Some(rs) = rs {
|
||||
rs
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"parse error={:?} cmd={:?}",
|
||||
e,
|
||||
std::env::args().collect::<Vec<String>>()
|
||||
);
|
||||
println!("{}", style(format!("Error {:?}", e)).red());
|
||||
return;
|
||||
}
|
||||
};
|
||||
main0(config, cmd)
|
||||
}
|
||||
fn main0(config: Config, _show_cmd: bool) {
|
||||
if !root_check::is_app_elevated() {
|
||||
println!("Please run it with administrator or root privileges");
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
sudo::escalate_if_needed().unwrap();
|
||||
return;
|
||||
}
|
||||
#[cfg(feature = "port_mapping")]
|
||||
for (is_tcp, addr, dest) in config.port_mapping_list.iter() {
|
||||
if *is_tcp {
|
||||
println!("TCP port mapping {}->{}", addr, dest)
|
||||
} else {
|
||||
println!("UDP port mapping {}->{}", addr, dest)
|
||||
}
|
||||
}
|
||||
let vnt_util = match Vnt::new(config, callback::VntHandler {}) {
|
||||
Ok(vnt) => vnt,
|
||||
Err(e) => {
|
||||
log::error!("vnt create error {:?}", e);
|
||||
println!("error: {:?}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
{
|
||||
let vnt_c = vnt_util.clone();
|
||||
let mut signals = signal_hook::iterator::Signals::new(&[
|
||||
signal_hook::consts::SIGINT,
|
||||
signal_hook::consts::SIGTERM,
|
||||
])
|
||||
.unwrap();
|
||||
let handle = signals.handle();
|
||||
std::thread::spawn(move || {
|
||||
for sig in signals.forever() {
|
||||
match sig {
|
||||
signal_hook::consts::SIGINT | signal_hook::consts::SIGTERM => {
|
||||
println!("Received SIGINT, {}", sig);
|
||||
vnt_c.stop();
|
||||
handle.close();
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
#[cfg(feature = "command")]
|
||||
{
|
||||
let vnt_c = vnt_util.clone();
|
||||
std::thread::Builder::new()
|
||||
.name("CommandServer".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = common::command::server::CommandServer::new().start(vnt_c) {
|
||||
log::warn!("cmd:{:?}", e);
|
||||
}
|
||||
})
|
||||
.expect("CommandServer");
|
||||
if _show_cmd {
|
||||
let mut cmd = String::new();
|
||||
loop {
|
||||
cmd.clear();
|
||||
println!("======== input:list,info,route,all,stop,chart_a,chart_b[:ip] ========");
|
||||
match std::io::stdin().read_line(&mut cmd) {
|
||||
Ok(len) => {
|
||||
if !common::command::command_str(&cmd[..len], &vnt_util) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("input err:{}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vnt_util.wait()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#[cfg(target_os = "windows")]
|
||||
mod windows;
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub use windows::is_app_elevated;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
mod unix;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
pub use unix::is_app_elevated;
|
||||
@@ -0,0 +1,3 @@
|
||||
pub fn is_app_elevated() -> bool {
|
||||
sudo::RunningAs::Root == sudo::check()
|
||||
}
|
||||
@@ -1,76 +1,76 @@
|
||||
/// 使用 https://github.com/spa5k/is_sudo/blob/main/src/window.rs
|
||||
use std::io::Error;
|
||||
use std::ptr;
|
||||
|
||||
use winapi::um::handleapi::CloseHandle;
|
||||
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
|
||||
use winapi::um::securitybaseapi::GetTokenInformation;
|
||||
use winapi::um::winnt::{HANDLE, TOKEN_ELEVATION, TOKEN_QUERY, TokenElevation};
|
||||
|
||||
// Use std::io::Error::last_os_error for errors.
|
||||
// NOTE: For this example I'm simple passing on the OS error.
|
||||
// However, customising the error could provide more context
|
||||
|
||||
/// Returns true if the current process has admin rights, otherwise false.
|
||||
pub fn is_app_elevated() -> bool {
|
||||
_is_app_elevated().unwrap_or(false)
|
||||
}
|
||||
|
||||
/// On success returns a bool indicating if the current process has admin rights.
|
||||
/// Otherwise returns an OS error.
|
||||
///
|
||||
/// This is unlikely to fail but if it does it's even more unlikely that you have admin permissions anyway.
|
||||
/// Therefore the public function above simply eats the error and returns a bool.
|
||||
fn _is_app_elevated() -> Result<bool, Error> {
|
||||
let token = QueryAccessToken::from_current_process()?;
|
||||
token.is_elevated()
|
||||
}
|
||||
|
||||
/// A safe wrapper around querying Windows access tokens.
|
||||
pub struct QueryAccessToken(HANDLE);
|
||||
|
||||
impl QueryAccessToken {
|
||||
pub fn from_current_process() -> Result<Self, Error> {
|
||||
unsafe {
|
||||
let mut handle: HANDLE = ptr::null_mut();
|
||||
let result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle);
|
||||
|
||||
if result != 0 {
|
||||
Ok(Self(handle))
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// On success returns a bool indicating if the access token has elevated privilidges.
|
||||
/// Otherwise returns an OS error.
|
||||
pub fn is_elevated(&self) -> Result<bool, Error> {
|
||||
unsafe {
|
||||
let mut elevation = TOKEN_ELEVATION::default();
|
||||
let size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
|
||||
let mut ret_size = size;
|
||||
// The weird looking repetition of `as *mut _` is casting the reference to a c_void pointer.
|
||||
if GetTokenInformation(
|
||||
self.0,
|
||||
TokenElevation,
|
||||
&mut elevation as *mut _ as *mut _,
|
||||
size,
|
||||
&mut ret_size,
|
||||
) != 0
|
||||
{
|
||||
Ok(elevation.TokenIsElevated != 0)
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for QueryAccessToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.0.is_null() {
|
||||
unsafe { CloseHandle(self.0) };
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 使用 https://github.com/spa5k/is_sudo/blob/main/src/window.rs
|
||||
use std::io::Error;
|
||||
use std::ptr;
|
||||
|
||||
use winapi::um::handleapi::CloseHandle;
|
||||
use winapi::um::processthreadsapi::{GetCurrentProcess, OpenProcessToken};
|
||||
use winapi::um::securitybaseapi::GetTokenInformation;
|
||||
use winapi::um::winnt::{TokenElevation, HANDLE, TOKEN_ELEVATION, TOKEN_QUERY};
|
||||
|
||||
// Use std::io::Error::last_os_error for errors.
|
||||
// NOTE: For this example I'm simple passing on the OS error.
|
||||
// However, customising the error could provide more context
|
||||
|
||||
/// Returns true if the current process has admin rights, otherwise false.
|
||||
pub fn is_app_elevated() -> bool {
|
||||
_is_app_elevated().unwrap_or(false)
|
||||
}
|
||||
|
||||
/// On success returns a bool indicating if the current process has admin rights.
|
||||
/// Otherwise returns an OS error.
|
||||
///
|
||||
/// This is unlikely to fail but if it does it's even more unlikely that you have admin permissions anyway.
|
||||
/// Therefore the public function above simply eats the error and returns a bool.
|
||||
fn _is_app_elevated() -> Result<bool, Error> {
|
||||
let token = QueryAccessToken::from_current_process()?;
|
||||
token.is_elevated()
|
||||
}
|
||||
|
||||
/// A safe wrapper around querying Windows access tokens.
|
||||
pub struct QueryAccessToken(HANDLE);
|
||||
|
||||
impl QueryAccessToken {
|
||||
pub fn from_current_process() -> Result<Self, Error> {
|
||||
unsafe {
|
||||
let mut handle: HANDLE = ptr::null_mut();
|
||||
let result = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle);
|
||||
|
||||
if result != 0 {
|
||||
Ok(Self(handle))
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// On success returns a bool indicating if the access token has elevated privilidges.
|
||||
/// Otherwise returns an OS error.
|
||||
pub fn is_elevated(&self) -> Result<bool, Error> {
|
||||
unsafe {
|
||||
let mut elevation = TOKEN_ELEVATION::default();
|
||||
let size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
|
||||
let mut ret_size = size;
|
||||
// The weird looking repetition of `as *mut _` is casting the reference to a c_void pointer.
|
||||
if GetTokenInformation(
|
||||
self.0,
|
||||
TokenElevation,
|
||||
&mut elevation as *mut _ as *mut _,
|
||||
size,
|
||||
&mut ret_size,
|
||||
) != 0
|
||||
{
|
||||
Ok(elevation.TokenIsElevated != 0)
|
||||
} else {
|
||||
Err(Error::last_os_error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for QueryAccessToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.0.is_null() {
|
||||
unsafe { CloseHandle(self.0) };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
[package]
|
||||
name = "vnt"
|
||||
version = "1.2.16"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tun-rs = { version = "2.5.0", optional = true, features = ["interruptible"] }
|
||||
packet = { path = "./packet" }
|
||||
bytes = "1.5.0"
|
||||
log = "0.4.17"
|
||||
libc = "0.2.137"
|
||||
crossbeam-utils = "0.8"
|
||||
crossbeam-epoch = "0.9.15"
|
||||
parking_lot = "0.12.1"
|
||||
rand = "0.8.5"
|
||||
sha2 = { version = "0.10.6", features = ["oid"] }
|
||||
thiserror = "1.0.37"
|
||||
protobuf = "=3.2.0"
|
||||
socket2 = { version = "0.5.7", features = ["all"] }
|
||||
aes-gcm = { version = "0.10.2", optional = true }
|
||||
ring = { version = "0.17.0", optional = true }
|
||||
cbc = { version = "0.1.2", optional = true }
|
||||
ecb = { version = "0.1.2", optional = true }
|
||||
chacha20poly1305 = { version = "0.10.1", optional = true }
|
||||
chacha20 = { version = "0.9.1", optional = true }
|
||||
aes = "0.8.3"
|
||||
stun-format = { version = "1.0.1", features = ["fmt", "rfc3489"] }
|
||||
rsa = { version = "0.9.2", features = [], optional = true }
|
||||
spki = { version = "0.7.2", features = ["fingerprint", "alloc", "base64"], optional = true }
|
||||
openssl-sys = { git = "https://github.com/vnt-dev/rust-openssl", optional = true }
|
||||
libsm = { git = "https://github.com/vnt-dev/libsm", optional = true }
|
||||
http_req = { git = "https://github.com/lmq8267/http_req.git", default-features = false, features = ["rust-tls"] }
|
||||
|
||||
mio = { version = "=0.8.11", features = ["os-poll", "net", "os-ext"] }
|
||||
crossbeam-queue = "0.3.11"
|
||||
anyhow = "1.0.82"
|
||||
dns-parser = "0.8.0"
|
||||
|
||||
tokio = { version = "1.46.1", features = ["full"] }
|
||||
|
||||
lz4_flex = { version = "0.11", default-features = false, optional = true }
|
||||
zstd = { version = "0.13.1", optional = true }
|
||||
|
||||
fnv = "1.0.7"
|
||||
igd = { version = "0.12.1", optional = true }
|
||||
tokio-tungstenite = { version = "0.23.1", optional = true }
|
||||
rustls = { version = "0.23.0", features = ["ring"], default-features = false, optional = true }
|
||||
|
||||
network-interface = "2.0.0"
|
||||
|
||||
futures-util = "0.3.30"
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
winreg = "0.55.0"
|
||||
libloading = "0.8.0"
|
||||
windows-sys = { version = "0.60.2", features = ["Win32_Foundation",
|
||||
"Win32_NetworkManagement",
|
||||
"Win32_NetworkManagement_IpHelper",
|
||||
"Win32_Networking_WinSock",
|
||||
"Win32_System_IO",
|
||||
"Win32_System_Threading",
|
||||
"Win32_System_WindowsProgramming", ] }
|
||||
|
||||
[build-dependencies]
|
||||
protobuf-codegen = "=3.2.0"
|
||||
protoc-bin-vendored = "3.0.0"
|
||||
cfg_aliases = "0.2.1"
|
||||
|
||||
[features]
|
||||
default = ["server_encrypt", "aes_gcm", "aes_cbc", "aes_ecb", "sm4_cbc", "chacha20_poly1305", "ip_proxy", "port_mapping", "lz4_compress", "zstd_compress", "integrated_tun"]
|
||||
openssl = ["openssl-sys"]
|
||||
# 从源码编译
|
||||
openssl-vendored = ["openssl-sys/vendored"]
|
||||
ring-cipher = ["ring"]
|
||||
aes_cbc = ["cbc"]
|
||||
aes_ecb = ["ecb"]
|
||||
sm4_cbc = ["libsm"]
|
||||
aes_gcm = ["aes-gcm"]
|
||||
chacha20_poly1305 = ["chacha20poly1305", "chacha20"]
|
||||
server_encrypt = ["aes-gcm", "rsa", "spki"]
|
||||
ip_proxy = []
|
||||
port_mapping = []
|
||||
lz4_compress = ["lz4_flex"]
|
||||
zstd_compress = ["zstd"]
|
||||
integrated_tun = ["tun-rs"]
|
||||
upnp = ["igd"]
|
||||
ws = ["tokio-tungstenite"]
|
||||
wss = ["ws", "tokio-tungstenite/rustls-tls-native-roots", "tokio-tungstenite/rustls-tls-webpki-roots", "rustls"]
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,4 +1,17 @@
|
||||
use cfg_aliases::cfg_aliases;
|
||||
|
||||
fn main() {
|
||||
cfg_aliases! {
|
||||
cipher: {
|
||||
any(feature = "aes_gcm",
|
||||
feature = "chacha20_poly1305",
|
||||
feature = "server_encrypt",
|
||||
feature = "aes_cbc",
|
||||
feature = "aes_ecb",
|
||||
feature = "sm4_cbc"
|
||||
)},
|
||||
}
|
||||
|
||||
std::fs::create_dir_all("src/proto").unwrap();
|
||||
protobuf_codegen::Codegen::new()
|
||||
.pure()
|
||||
@@ -7,4 +7,3 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.4.3"
|
||||
thiserror = "1.0.37"
|
||||
@@ -0,0 +1,122 @@
|
||||
use std::{fmt, io};
|
||||
|
||||
/// 地址解析协议,由IP地址找到MAC地址
|
||||
/// https://www.ietf.org/rfc/rfc6747.txt
|
||||
/*
|
||||
0 2 4 5 6 8 10 (字节)
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 硬件类型|协议类型|硬件地址长度|协议地址长度|操作类型|
|
||||
| 源MAC地址 | 源ip地址 |
|
||||
| 目的MAC地址 | 目的ip地址 |
|
||||
*/
|
||||
|
||||
pub struct ArpPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ArpPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() != 28 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ArpPacket<B> {
|
||||
/// 硬件类型 以太网类型为1
|
||||
pub fn hardware_type(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
|
||||
}
|
||||
/// 上层协议类型,ipv4是0x0800
|
||||
pub fn protocol_type(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
/// 如果是MAC地址 则长度为6
|
||||
pub fn hardware_size(&self) -> u8 {
|
||||
self.buffer.as_ref()[4]
|
||||
}
|
||||
/// 如果是IPv4 则长度为4
|
||||
pub fn protocol_size(&self) -> u8 {
|
||||
self.buffer.as_ref()[5]
|
||||
}
|
||||
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应
|
||||
pub fn op_code(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
|
||||
}
|
||||
/// 发送端硬件地址,仅支持以太网
|
||||
pub fn sender_hardware_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[8..14]
|
||||
}
|
||||
/// 发送端协议地址,仅支持IPv4
|
||||
pub fn sender_protocol_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[14..18]
|
||||
}
|
||||
/// 接收端硬件地址,仅支持以太网
|
||||
pub fn target_hardware_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[18..24]
|
||||
}
|
||||
/// 接收端协议地址,仅支持IPv4
|
||||
pub fn target_protocol_addr(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[24..28]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> ArpPacket<B> {
|
||||
/// 硬件类型 以太网类型为1
|
||||
pub fn set_hardware_type(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 上层协议类型,ipv4是0x0800
|
||||
pub fn set_protocol_type(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 如果是MAC地址 则长度为6
|
||||
pub fn set_hardware_size(&mut self, value: u8) {
|
||||
self.buffer.as_mut()[4] = value
|
||||
}
|
||||
/// 如果是IPv4 则长度为4
|
||||
pub fn set_protocol_size(&mut self, value: u8) {
|
||||
self.buffer.as_mut()[5] = value
|
||||
}
|
||||
/// 操作类型,请求和响应 1:ARP请求,2:ARP响应,3:RARP请求,4:RARP响应
|
||||
pub fn set_op_code(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 发送端硬件地址,仅支持以太网
|
||||
pub fn set_sender_hardware_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[8..14].copy_from_slice(buf)
|
||||
}
|
||||
/// 发送端协议地址,仅支持IPv4
|
||||
pub fn set_sender_protocol_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[14..18].copy_from_slice(buf)
|
||||
}
|
||||
/// 接收端硬件地址,仅支持以太网
|
||||
pub fn set_target_hardware_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[18..24].copy_from_slice(buf)
|
||||
}
|
||||
/// 接收端协议地址,仅支持IPv4
|
||||
pub fn set_target_protocol_addr(&mut self, buf: &[u8]) {
|
||||
self.buffer.as_mut()[24..28].copy_from_slice(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for ArpPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ArpPacket")
|
||||
.field("hardware_type", &self.hardware_type())
|
||||
.field("protocol_type", &self.protocol_type())
|
||||
.field("hardware_size", &self.hardware_size())
|
||||
.field("protocol_size", &self.protocol_size())
|
||||
.field("op_code", &self.op_code())
|
||||
.field("sender_hardware_addr", &self.sender_hardware_addr())
|
||||
.field("sender_protocol_addr", &self.sender_protocol_addr())
|
||||
.field("target_hardware_addr", &self.target_hardware_addr())
|
||||
.field("target_protocol_addr", &self.target_protocol_addr())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod arp;
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::ethernet::protocol::Protocol;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// 以太网帧协议
|
||||
/// https://www.ietf.org/rfc/rfc894.txt
|
||||
/*
|
||||
0 6 12 14 (字节)
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 目的地址 | 源地址 | 类型 |
|
||||
*/
|
||||
pub struct EthernetPacket<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> EthernetPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> EthernetPacket<B> {
|
||||
EthernetPacket { buffer }
|
||||
}
|
||||
|
||||
pub fn new(buffer: B) -> io::Result<EthernetPacket<B>> {
|
||||
let packet = EthernetPacket::unchecked(buffer);
|
||||
//头部固定14位
|
||||
if packet.buffer.as_ref().len() < 14 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> EthernetPacket<B> {
|
||||
/// 目的MAC地址
|
||||
pub fn destination(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[0..6]
|
||||
}
|
||||
/// 源MAC地址
|
||||
pub fn source(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[6..12]
|
||||
}
|
||||
/// 3层协议
|
||||
pub fn protocol(&self) -> Protocol {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[12..14].try_into().unwrap()).into()
|
||||
}
|
||||
/// 载荷
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[14..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> EthernetPacket<B> {
|
||||
pub fn set_destination(&mut self, value: &[u8]) {
|
||||
self.buffer.as_mut()[0..6].copy_from_slice(value);
|
||||
}
|
||||
|
||||
pub fn set_source(&mut self, value: &[u8]) {
|
||||
self.buffer.as_mut()[6..12].copy_from_slice(value);
|
||||
}
|
||||
|
||||
pub fn set_protocol(&mut self, value: Protocol) {
|
||||
let p: u16 = value.into();
|
||||
self.buffer.as_mut()[12..14].copy_from_slice(&p.to_be_bytes())
|
||||
}
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[14..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for EthernetPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("EthernetPacket")
|
||||
.field("destination", &self.destination())
|
||||
.field("source", &self.source())
|
||||
.field("protocol", &self.protocol())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/// 以太网帧协议
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
///
|
||||
Ipv4,
|
||||
|
||||
///
|
||||
Arp,
|
||||
|
||||
///
|
||||
WakeOnLan,
|
||||
|
||||
///
|
||||
Trill,
|
||||
|
||||
///
|
||||
DecNet,
|
||||
|
||||
///
|
||||
Rarp,
|
||||
|
||||
///
|
||||
AppleTalk,
|
||||
|
||||
///
|
||||
Aarp,
|
||||
|
||||
///
|
||||
Ipx,
|
||||
|
||||
///
|
||||
Qnx,
|
||||
|
||||
///
|
||||
Ipv6,
|
||||
|
||||
///
|
||||
FlowControl,
|
||||
|
||||
///
|
||||
CobraNet,
|
||||
|
||||
///
|
||||
Mpls,
|
||||
|
||||
///
|
||||
MplsMulticast,
|
||||
|
||||
///
|
||||
PppoeDiscovery,
|
||||
|
||||
///
|
||||
PppoeSession,
|
||||
|
||||
///
|
||||
Vlan,
|
||||
|
||||
///
|
||||
PBridge,
|
||||
|
||||
///
|
||||
Lldp,
|
||||
|
||||
///
|
||||
Ptp,
|
||||
|
||||
///
|
||||
Cfm,
|
||||
|
||||
///
|
||||
QinQ,
|
||||
|
||||
///
|
||||
Unknown(u16),
|
||||
}
|
||||
|
||||
impl From<u16> for Protocol {
|
||||
fn from(value: u16) -> Protocol {
|
||||
use self::Protocol::*;
|
||||
|
||||
match value {
|
||||
0x0800 => Ipv4,
|
||||
0x0806 => Arp,
|
||||
0x0842 => WakeOnLan,
|
||||
0x22f3 => Trill,
|
||||
0x6003 => DecNet,
|
||||
0x8035 => Rarp,
|
||||
0x809b => AppleTalk,
|
||||
0x80f3 => Aarp,
|
||||
0x8137 => Ipx,
|
||||
0x8204 => Qnx,
|
||||
0x86dd => Ipv6,
|
||||
0x8808 => FlowControl,
|
||||
0x8819 => CobraNet,
|
||||
0x8847 => Mpls,
|
||||
0x8848 => MplsMulticast,
|
||||
0x8863 => PppoeDiscovery,
|
||||
0x8864 => PppoeSession,
|
||||
0x8100 => Vlan,
|
||||
0x88a8 => PBridge,
|
||||
0x88cc => Lldp,
|
||||
0x88f7 => Ptp,
|
||||
0x8902 => Cfm,
|
||||
0x9100 => QinQ,
|
||||
n => Unknown(n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u16> for Protocol {
|
||||
fn into(self) -> u16 {
|
||||
use self::Protocol::*;
|
||||
|
||||
match self {
|
||||
Ipv4 => 0x0800,
|
||||
Arp => 0x0806,
|
||||
WakeOnLan => 0x0842,
|
||||
Trill => 0x22f3,
|
||||
DecNet => 0x6003,
|
||||
Rarp => 0x8035,
|
||||
AppleTalk => 0x809b,
|
||||
Aarp => 0x80f3,
|
||||
Ipx => 0x8137,
|
||||
Qnx => 0x8204,
|
||||
Ipv6 => 0x86dd,
|
||||
FlowControl => 0x8808,
|
||||
CobraNet => 0x8819,
|
||||
Mpls => 0x8847,
|
||||
MplsMulticast => 0x8848,
|
||||
PppoeDiscovery => 0x8863,
|
||||
PppoeSession => 0x8864,
|
||||
Vlan => 0x8100,
|
||||
PBridge => 0x88a8,
|
||||
Lldp => 0x88cc,
|
||||
Ptp => 0x88f7,
|
||||
Cfm => 0x8902,
|
||||
QinQ => 0x9100,
|
||||
Unknown(n) => n,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::fmt;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
use crate::cal_checksum;
|
||||
use crate::error::*;
|
||||
use crate::icmp::{Code, Kind};
|
||||
use crate::ip::ipv4::packet::IpV4Packet;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use std::{fmt, io};
|
||||
|
||||
/// icmp 协议
|
||||
/* https://www.rfc-editor.org/rfc/rfc792
|
||||
0 1 2 3
|
||||
@@ -17,20 +17,18 @@ use crate::error::*;
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
*/
|
||||
use crate::icmp::{Code, Kind};
|
||||
use crate::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
pub struct IcmpPacket<B> {
|
||||
buffer: B,
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IcmpPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> Result<Self> {
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 8 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
@@ -56,9 +54,7 @@ impl<B: AsRef<[u8]>> IcmpPacket<B> {
|
||||
Code::from(self.kind(), self.buffer.as_ref()[1])
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[2..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
@@ -71,12 +67,8 @@ impl<B: AsRef<[u8]>> IcmpPacket<B> {
|
||||
| Kind::TimestampReply
|
||||
| Kind::InformationRequest
|
||||
| Kind::InformationReply => {
|
||||
let ide = (&self.buffer.as_ref()[4..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap();
|
||||
let seq = (&self.buffer.as_ref()[6..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap();
|
||||
let ide = u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap());
|
||||
let seq = u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap());
|
||||
HeaderOther::Identifier(ide, seq)
|
||||
}
|
||||
Kind::DestinationUnreachable | Kind::TimeExceeded | Kind::SourceQuench => {
|
||||
@@ -110,6 +102,7 @@ impl<B: AsRef<[u8]>> IcmpPacket<B> {
|
||||
},
|
||||
Kind::TimestampRequest | Kind::TimestampReply => {
|
||||
let mut buffer = Cursor::new(self.payload());
|
||||
|
||||
Description::Timestamp(
|
||||
buffer.read_u32::<BigEndian>().unwrap(),
|
||||
buffer.read_u32::<BigEndian>().unwrap(),
|
||||
@@ -0,0 +1,117 @@
|
||||
use crate::cal_checksum;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// igmp v1
|
||||
/* https://datatracker.ietf.org/doc/html/rfc1112
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|Version| Type | Unused | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Group Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
/// v1版本的报文
|
||||
pub struct IgmpV1Packet<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpV1Type {
|
||||
/// 0x11 所有组224.0.0.1或者特定组
|
||||
Query,
|
||||
/// 0x12
|
||||
ReportV1,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpV1Type {
|
||||
fn from(value: u8) -> IgmpV1Type {
|
||||
use self::IgmpV1Type::*;
|
||||
|
||||
match value {
|
||||
0x11 => Query,
|
||||
0x12 => ReportV1,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpV1Type {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
IgmpV1Type::Query => 0x11,
|
||||
IgmpV1Type::ReportV1 => 0x12,
|
||||
IgmpV1Type::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV1Packet<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() != 8 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV1Packet<B> {
|
||||
pub fn version(&self) -> u8 {
|
||||
self.buffer.as_ref()[0] >> 4
|
||||
}
|
||||
pub fn igmp_type(&self) -> IgmpV1Type {
|
||||
IgmpV1Type::from(self.buffer.as_ref()[0] & 0x0F)
|
||||
}
|
||||
pub fn unused(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
}
|
||||
pub fn group_address(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> IgmpV1Packet<B> {
|
||||
pub fn set_version(&mut self, version: u8) {
|
||||
self.buffer.as_mut()[0] = (version << 4) | 0x0F & self.buffer.as_mut()[0]
|
||||
}
|
||||
pub fn set_type(&mut self, igmp_type: IgmpV1Type) {
|
||||
let t: u8 = igmp_type.into();
|
||||
self.buffer.as_mut()[0] = self.buffer.as_mut()[0] & 0xF0 | t
|
||||
}
|
||||
pub fn set_checksum(&mut self, checksum: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes());
|
||||
}
|
||||
pub fn update_checksum(&mut self) {
|
||||
self.set_checksum(0);
|
||||
self.set_checksum(cal_checksum(self.buffer.as_ref()));
|
||||
}
|
||||
pub fn set_group_address(&mut self, group_address: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets());
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV1Packet<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V1")
|
||||
.field("version", &self.version())
|
||||
.field("type", &self.igmp_type())
|
||||
.field("checksum", &self.checksum())
|
||||
.field("is_valid", &self.is_valid())
|
||||
.field("group_address", &self.group_address())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
use crate::cal_checksum;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// igmp v2
|
||||
/* https://www.rfc-editor.org/rfc/rfc2236.html
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Max Resp Time | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Group Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
/// v2版本的报文
|
||||
pub struct IgmpV2Packet<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpV2Type {
|
||||
/// 0x11 所有组224.0.0.1或者特定组
|
||||
Query,
|
||||
/// 0x16
|
||||
ReportV2,
|
||||
LeaveV2,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpV2Type {
|
||||
fn from(value: u8) -> IgmpV2Type {
|
||||
use self::IgmpV2Type::*;
|
||||
|
||||
match value {
|
||||
0x11 => Query,
|
||||
0x16 => ReportV2,
|
||||
0x17 => LeaveV2,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpV2Type {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
IgmpV2Type::Query => 0x11,
|
||||
IgmpV2Type::ReportV2 => 0x16,
|
||||
IgmpV2Type::LeaveV2 => 0x17,
|
||||
IgmpV2Type::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV2Packet<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() != 8 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV2Packet<B> {
|
||||
pub fn igmp_type(&self) -> IgmpV2Type {
|
||||
IgmpV2Type::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
pub fn max_resp_time(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
}
|
||||
pub fn group_address(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> IgmpV2Packet<B> {
|
||||
pub fn set_type(&mut self, igmp_type: IgmpV2Type) {
|
||||
self.buffer.as_mut()[0] = igmp_type.into()
|
||||
}
|
||||
pub fn set_max_resp_time(&mut self, resp: u8) {
|
||||
self.buffer.as_mut()[1] = resp
|
||||
}
|
||||
pub fn set_checksum(&mut self, checksum: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes());
|
||||
}
|
||||
pub fn update_checksum(&mut self) {
|
||||
self.set_checksum(0);
|
||||
self.set_checksum(cal_checksum(self.buffer.as_ref()));
|
||||
}
|
||||
pub fn set_group_address(&mut self, group_address: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&group_address.octets());
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV2Packet<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V2")
|
||||
.field("type", &self.igmp_type())
|
||||
.field("max_resp_time", &self.max_resp_time())
|
||||
.field("checksum", &self.checksum())
|
||||
.field("is_valid", &self.is_valid())
|
||||
.field("group_address", &self.group_address())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,493 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
use crate::cal_checksum;
|
||||
|
||||
/// igmp v3
|
||||
/* https://www.rfc-editor.org/rfc/rfc3376
|
||||
Query:
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 0x11 | Max Resp Code | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Group Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Resv |S| QRV | QQIC | Number of Sources (N) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Source Address [1] |
|
||||
+- -+
|
||||
| Source Address [2] |
|
||||
+- . -+
|
||||
. . .
|
||||
. . .
|
||||
+- -+
|
||||
| Source Address [N] |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Report:
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type = 0x22 | Reserved | Checksum |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Reserved | Number of Group Records (M) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
. .
|
||||
. Group Record [1] .
|
||||
. .
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
. .
|
||||
. Group Record [2] .
|
||||
. .
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| . |
|
||||
. . .
|
||||
| . |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
. .
|
||||
. Group Record [M] .
|
||||
. .
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Group Record:
|
||||
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Record Type | Aux Data Len | Number of Sources (N) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Multicast Address |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Source Address [1] |
|
||||
+- -+
|
||||
| Source Address [2] |
|
||||
+- -+
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
+- -+
|
||||
| Source Address [N] |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| |
|
||||
. .
|
||||
. Auxiliary Data .
|
||||
. .
|
||||
| |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
Record Type:
|
||||
1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址
|
||||
2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址
|
||||
3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址
|
||||
4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址
|
||||
5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址
|
||||
6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址
|
||||
*/
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpV3Type {
|
||||
/// 0x11 所有组224.0.0.1或者特定组
|
||||
Query,
|
||||
/// 0x22
|
||||
ReportV3,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpV3Type {
|
||||
fn from(value: u8) -> IgmpV3Type {
|
||||
use self::IgmpV3Type::*;
|
||||
|
||||
match value {
|
||||
0x11 => Query,
|
||||
0x22 => ReportV3,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpV3Type {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
IgmpV3Type::Query => 0x11,
|
||||
IgmpV3Type::ReportV3 => 0x22,
|
||||
IgmpV3Type::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpV3RecordType {
|
||||
//1 MODE_IS_INCLUDE 表示主机希望加入指定组播组并指定了一个或多个源地址
|
||||
ModeIsInclude,
|
||||
//2 MODE_IS_EXCLUDE 表示主机希望加入指定组播组但排除了一个或多个源地址
|
||||
ModeIsExclude,
|
||||
//3 CHANGE_TO_INCLUDE_MODE 表示主机正在将组播组的过滤模式从排除切换为包括,指定了一个或多个源地址
|
||||
ChangeToIncludeMode,
|
||||
//4 CHANGE_TO_EXCLUDE_MODE 表示主机正在将组播组的过滤模式从包括切换为排除,指定了一个或多个源地址
|
||||
ChangeToExcludeMode,
|
||||
//5 ALLOW_NEW_SOURCES 表示主机希望在已有的源地址列表中添加新的源地址,指定了一个或多个源地址
|
||||
AllowNewSources,
|
||||
//6 BLOCK_OLD_SOURCES 表示主机希望在已有的源地址列表中删除旧的源地址,指定了一个或多个源地址
|
||||
BlockOldSources,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpV3RecordType {
|
||||
fn from(value: u8) -> IgmpV3RecordType {
|
||||
use self::IgmpV3RecordType::*;
|
||||
|
||||
match value {
|
||||
1 => ModeIsInclude,
|
||||
2 => ModeIsExclude,
|
||||
3 => ChangeToIncludeMode,
|
||||
4 => ChangeToExcludeMode,
|
||||
5 => AllowNewSources,
|
||||
6 => BlockOldSources,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpV3RecordType {
|
||||
fn into(self) -> u8 {
|
||||
use self::IgmpV3RecordType::*;
|
||||
|
||||
match self {
|
||||
ModeIsInclude => 1,
|
||||
ModeIsExclude => 2,
|
||||
ChangeToIncludeMode => 3,
|
||||
ChangeToExcludeMode => 4,
|
||||
AllowNewSources => 5,
|
||||
BlockOldSources => 6,
|
||||
Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// v3版本的query报文
|
||||
pub struct IgmpV3QueryPacket<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3QueryPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 12 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> IgmpV3QueryPacket<B> {
|
||||
pub fn set_igmp_type(&mut self) {
|
||||
self.buffer.as_mut()[0] = IgmpV3Type::Query.into();
|
||||
}
|
||||
pub fn set_max_resp_code(&mut self, code: u8) {
|
||||
self.buffer.as_mut()[1] = code;
|
||||
}
|
||||
pub fn set_group_address(&mut self, addr: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&addr.octets())
|
||||
}
|
||||
pub fn set_checksum(&mut self, checksum: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&checksum.to_be_bytes())
|
||||
}
|
||||
pub fn set_qrv(&mut self, qrv: u8) {
|
||||
self.buffer.as_mut()[8] = (self.buffer.as_ref()[8] & (!0x07)) | (qrv & 0x07)
|
||||
}
|
||||
pub fn set_qqic(&mut self, qqic: u8) {
|
||||
self.buffer.as_mut()[9] = qqic
|
||||
}
|
||||
|
||||
pub fn update_checksum(&mut self) {
|
||||
self.set_checksum(0);
|
||||
let checksum = cal_checksum(self.buffer.as_ref());
|
||||
self.set_checksum(checksum);
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3QueryPacket<B> {
|
||||
pub fn igmp_type(&self) -> IgmpV3Type {
|
||||
IgmpV3Type::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
pub fn max_resp_code(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
}
|
||||
pub fn group_address(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
/// 保留字段,设置为0
|
||||
pub fn resv(&self) -> u8 {
|
||||
self.buffer.as_ref()[8] >> 4
|
||||
}
|
||||
/// 标志位
|
||||
/// 该比特位为1时,所有收到此查询报文的其他路由器不启动定时器刷新过程,但是此查询报文并不抑制查询者选举过程和路由器的主机侧处理过程;默认未置位。
|
||||
pub fn s(&self) -> u8 {
|
||||
(self.buffer.as_ref()[8] & 0x0F) >> 3
|
||||
}
|
||||
/// 查询者向网络通告的健壮系数
|
||||
/// 此参数可使查询者使用自己的健壮系统同步其他组播路由器的健壮系数;
|
||||
/// 其他路由器接收到查询报文时,如果发现该字段非0,则将自己的健壮系数调整为该字段的值;如果发现该字段为0,则不做处理。默认健壮系数值为2。
|
||||
pub fn qrv(&self) -> u8 {
|
||||
self.buffer.as_ref()[8] & 0x07
|
||||
}
|
||||
/// IGMP查询者的查询间隔
|
||||
/// 非查询者收到查询报文时,如果发现该字段非0,则将自己的查询间隔参数调整为该字段的值:如果发现该字段为0,则不做处理。默认值为60。
|
||||
pub fn qqic(&self) -> u8 {
|
||||
self.buffer.as_ref()[9]
|
||||
}
|
||||
/// 报文中包含的组播源的数量
|
||||
/// 对于普遍组查询报文和特定组查询报文,该字段为0;对于特定源组查询报文,该字段非0
|
||||
pub fn source_number(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap())
|
||||
}
|
||||
pub fn source_addresses(&self) -> Option<Vec<Ipv4Addr>> {
|
||||
let num = self.source_number();
|
||||
if num == 0 {
|
||||
None
|
||||
} else {
|
||||
let num = num as usize;
|
||||
let mut list = Vec::with_capacity(num);
|
||||
let buf = self.buffer.as_ref();
|
||||
let len = buf.len();
|
||||
for index in 0..num {
|
||||
let start = (12 + index * 4) as usize;
|
||||
let end = start + 4;
|
||||
if end > len {
|
||||
return None;
|
||||
}
|
||||
let tmp: [u8; 4] = buf[start..end].try_into().unwrap();
|
||||
list.push(Ipv4Addr::from(tmp));
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
}
|
||||
pub fn source_address(&self, index: u16) -> Option<Ipv4Addr> {
|
||||
if self.source_number() >= index {
|
||||
None
|
||||
} else {
|
||||
let start = (12 + index * 4) as usize;
|
||||
let end = start + 4;
|
||||
let buf = self.buffer.as_ref();
|
||||
let len = buf.len();
|
||||
if end > len {
|
||||
return None;
|
||||
}
|
||||
let tmp: [u8; 4] = buf[start..end].try_into().unwrap();
|
||||
Some(Ipv4Addr::from(tmp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// v3版本的query报文
|
||||
pub struct IgmpV3ReportPacket<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3ReportPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 8 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3ReportPacket<B> {
|
||||
pub fn igmp_type(&self) -> IgmpV3Type {
|
||||
IgmpV3Type::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
pub fn reserved1(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
pub fn is_valid(&self) -> bool {
|
||||
self.checksum() == 0 || cal_checksum(self.buffer.as_ref()) == 0
|
||||
}
|
||||
pub fn reserved2(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
|
||||
}
|
||||
pub fn record_number(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
|
||||
}
|
||||
pub fn group_records(&self) -> Option<Vec<IgmpV3RecordPacket<&[u8]>>> {
|
||||
let num = self.record_number();
|
||||
if num == 0 {
|
||||
None
|
||||
} else {
|
||||
let num = num as usize;
|
||||
let mut list = Vec::with_capacity(num);
|
||||
let mut start = 8 as usize;
|
||||
let buf = self.buffer.as_ref();
|
||||
let len = buf.len();
|
||||
for _ in 0..num {
|
||||
if start >= len {
|
||||
return None;
|
||||
}
|
||||
if let Ok(record) = IgmpV3RecordPacket::new(&buf[start..]) {
|
||||
let end = start
|
||||
+ 8
|
||||
+ record.aux_data_len() as usize * 4
|
||||
+ record.source_number() as usize * 4;
|
||||
if end > len {
|
||||
return None;
|
||||
}
|
||||
list.push(IgmpV3RecordPacket::new(&buf[start..end]).unwrap());
|
||||
start = end;
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// group record
|
||||
pub struct IgmpV3RecordPacket<B> {
|
||||
pub buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3RecordPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 8 {
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))
|
||||
} else {
|
||||
let packet = Self::unchecked(buffer);
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IgmpV3RecordPacket<B> {
|
||||
pub fn record_type(&self) -> IgmpV3RecordType {
|
||||
IgmpV3RecordType::from(self.buffer.as_ref()[0])
|
||||
}
|
||||
/// 辅助数据长度 以4字节为单位
|
||||
pub fn aux_data_len(&self) -> u8 {
|
||||
self.buffer.as_ref()[1]
|
||||
}
|
||||
/// 源地址数
|
||||
pub fn source_number(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
///多播地址
|
||||
pub fn multicast_address(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn source_addresses(&self) -> Option<Vec<Ipv4Addr>> {
|
||||
let num = self.source_number();
|
||||
if num == 0 {
|
||||
None
|
||||
} else {
|
||||
let num = num as usize;
|
||||
let mut list = Vec::with_capacity(num);
|
||||
let buf = self.buffer.as_ref();
|
||||
let len = buf.len();
|
||||
for index in 0..num {
|
||||
let start = (8 + index * 4) as usize;
|
||||
let end = start + 4;
|
||||
if end > len {
|
||||
return None;
|
||||
}
|
||||
let tmp: [u8; 4] = buf[start..end].try_into().unwrap();
|
||||
list.push(Ipv4Addr::from(tmp));
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
}
|
||||
pub fn source_address(&self, index: u16) -> Option<Ipv4Addr> {
|
||||
if self.source_number() >= index {
|
||||
None
|
||||
} else {
|
||||
let start = (8 + index * 4) as usize;
|
||||
let end = start + 4;
|
||||
if end > self.buffer.as_ref().len() {
|
||||
return None;
|
||||
}
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[start..end].try_into().unwrap();
|
||||
Some(Ipv4Addr::from(tmp))
|
||||
}
|
||||
}
|
||||
/// 在文档中没有定义辅助数据的作用,通常应该是空的
|
||||
pub fn auxiliary_data(&self) -> &[u8] {
|
||||
let start = 8 + self.source_number() as usize * 4;
|
||||
let end = start + self.aux_data_len() as usize * 4;
|
||||
if end > self.buffer.as_ref().len() {
|
||||
return &[];
|
||||
}
|
||||
&self.buffer.as_ref()[start..end]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV3QueryPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V3Query")
|
||||
.field("type", &self.igmp_type())
|
||||
.field("max_resp_code", &self.max_resp_code())
|
||||
.field("checksum", &self.checksum())
|
||||
.field("is_valid", &self.is_valid())
|
||||
.field("group_address", &self.group_address())
|
||||
.field("s", &self.s())
|
||||
.field("qrv", &self.qrv())
|
||||
.field("qqic", &self.qqic())
|
||||
.field("number of sources", &self.source_number())
|
||||
.field("source_addresses", &self.source_addresses())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV3ReportPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V3Report")
|
||||
.field("type", &self.igmp_type())
|
||||
.field("reserved1", &self.reserved1())
|
||||
.field("checksum", &self.checksum())
|
||||
.field("is_valid", &self.is_valid())
|
||||
.field("reserved2", &self.reserved2())
|
||||
.field("record_number", &self.record_number())
|
||||
.field("group_records", &self.group_records())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for IgmpV3RecordPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("igmp::V3Record")
|
||||
.field("record_type", &self.record_type())
|
||||
.field("aux_data_len", &self.aux_data_len())
|
||||
.field("source_number", &self.source_number())
|
||||
.field("multicast_address", &self.multicast_address())
|
||||
.field("source_addresses", &self.source_addresses())
|
||||
.field("auxiliary_data", &self.auxiliary_data())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
pub mod igmp_v1;
|
||||
pub mod igmp_v2;
|
||||
pub mod igmp_v3;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum IgmpType {
|
||||
/// 0x11 所有组224.0.0.1或者特定组
|
||||
Query,
|
||||
/// 0x12
|
||||
ReportV1,
|
||||
/// 0x16
|
||||
ReportV2,
|
||||
/// 0x22
|
||||
ReportV3,
|
||||
/// 0x17 目标组固定是 224.0.0.2
|
||||
LeaveV2,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for IgmpType {
|
||||
fn from(value: u8) -> IgmpType {
|
||||
use self::IgmpType::*;
|
||||
|
||||
match value {
|
||||
0x11 => Query,
|
||||
0x12 => ReportV1,
|
||||
0x16 => ReportV2,
|
||||
0x22 => ReportV3,
|
||||
0x17 => LeaveV2,
|
||||
v => Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for IgmpType {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
IgmpType::Query => 0x11,
|
||||
IgmpType::ReportV1 => 0x12,
|
||||
IgmpType::ReportV2 => 0x16,
|
||||
IgmpType::ReportV3 => 0x22,
|
||||
IgmpType::LeaveV2 => 0x17,
|
||||
IgmpType::Unknown(v) => v,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod packet;
|
||||
pub mod protocol;
|
||||
@@ -1,10 +1,7 @@
|
||||
use std::fmt;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use std::{fmt, io};
|
||||
|
||||
use crate::cal_checksum;
|
||||
use crate::error::*;
|
||||
use crate::ip::ipv4::protocol::Protocol;
|
||||
|
||||
/// ip协议
|
||||
@@ -39,16 +36,16 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> Result<Self> {
|
||||
if buffer.as_ref()[0] >> 4 != 4 {
|
||||
Err(Error::Unimplemented)?
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
if buffer.as_ref().len() < 20 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "len < 20"))?;
|
||||
}
|
||||
if buffer.as_ref()[0] >> 4 != 4 {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "not ipv4"))?;
|
||||
}
|
||||
let packet = Self::unchecked(buffer);
|
||||
if packet.buffer.as_ref().len() < packet.header_len() as usize * 4 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "head_len err"))?;
|
||||
}
|
||||
Ok(packet)
|
||||
}
|
||||
@@ -60,17 +57,6 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
}
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[(self.header_len() as usize * 4)..]
|
||||
// match self.protocol() {
|
||||
// Protocol::Udp => {
|
||||
// let udp = UdpPacket::new(IpAddr::V4(self.source_ip()),
|
||||
// IpAddr::V4(self.destination_ip()),
|
||||
// &self.buffer.as_ref()[(self.header_len() as usize * 4)..])?;
|
||||
// Ok(crate::IpUpperLayer::UDP(udp))
|
||||
// }
|
||||
// _ => {
|
||||
// Ok(crate::IpUpperLayer::Unknown(self.buffer.as_ref()));
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,13 +69,21 @@ impl<B: AsRef<[u8]> + AsMut<[u8]>> IpV4Packet<B> {
|
||||
let len = self.header_len() as usize * 4;
|
||||
&mut self.buffer.as_mut()[len..]
|
||||
}
|
||||
|
||||
pub fn set_protocol(&mut self, value: Protocol) {
|
||||
self.header_mut()[9] = value.into();
|
||||
}
|
||||
pub fn set_source_ip(&mut self, value: Ipv4Addr) {
|
||||
self.header_mut()[12..16].copy_from_slice(&value.octets());
|
||||
}
|
||||
pub fn set_destination_ip(&mut self, value: Ipv4Addr) {
|
||||
self.header_mut()[16..20].copy_from_slice(&value.octets());
|
||||
}
|
||||
pub fn set_flags(&mut self, flags: u8) {
|
||||
self.buffer.as_mut()[6] = (self.buffer.as_ref()[6] & 0b11100000) | (flags << 5)
|
||||
}
|
||||
pub fn set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[8] = ttl
|
||||
}
|
||||
fn set_checksum(&mut self, value: u16) {
|
||||
self.header_mut()[10..12].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
@@ -141,16 +135,12 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
|
||||
/// ip报总字节数
|
||||
pub fn length(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[2..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 标识. ip报文在数据链路层可能会被拆分,同一报文的不同分组标识字段相同
|
||||
pub fn id(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[4..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 标志 3位.
|
||||
@@ -170,10 +160,7 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
/// 以字节为单位,用于指明分段起始点相对于包头起始点的偏移量
|
||||
/// 由于分段到达时可能错序,所以分段的偏移字段可以使接收者按照正确的顺序重组数据包
|
||||
pub fn offset(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[6..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
& 0x1fff
|
||||
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap()) & 0x1fff
|
||||
}
|
||||
|
||||
/// 生存时间.
|
||||
@@ -189,9 +176,7 @@ impl<B: AsRef<[u8]>> IpV4Packet<B> {
|
||||
|
||||
/// 首部校验和
|
||||
pub fn checksum(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[10..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[10..12].try_into().unwrap())
|
||||
}
|
||||
/// 验证校验和
|
||||
///
|
||||
@@ -1,4 +1,4 @@
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
///
|
||||
Hopopt,
|
||||
@@ -1,6 +1,5 @@
|
||||
use ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::error::*;
|
||||
use std::io;
|
||||
|
||||
pub mod ipv4;
|
||||
|
||||
@@ -9,10 +8,10 @@ pub enum IpPacket<B> {
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IpPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<Self> {
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
match buffer.as_ref()[0] >> 4 {
|
||||
4 => Ok(IpPacket::V4(IpV4Packet::new(buffer)?)),
|
||||
_ => Err(Error::InvalidPacket),
|
||||
_ => Err(io::Error::from(io::ErrorKind::InvalidData)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,13 @@ use std::net::Ipv4Addr;
|
||||
use byteorder::BigEndian;
|
||||
use byteorder::ReadBytesExt;
|
||||
|
||||
pub mod error;
|
||||
pub mod arp;
|
||||
pub mod ethernet;
|
||||
pub mod icmp;
|
||||
pub mod igmp;
|
||||
pub mod ip;
|
||||
pub mod tcp;
|
||||
pub mod udp;
|
||||
|
||||
// pub enum IpUpperLayer<B> {
|
||||
// UDP(UdpPacket<B>),
|
||||
// Unknown(B),
|
||||
@@ -70,14 +71,12 @@ unsigned short getChecksum(unsigned short * iphead, int count)
|
||||
pub fn cal_checksum(buffer: &[u8]) -> u16 {
|
||||
use std::io::Cursor;
|
||||
let mut sum = 0;
|
||||
let length = buffer.len();
|
||||
let mut buffer = Cursor::new(buffer);
|
||||
while let Ok(value) = buffer.read_u16::<BigEndian>() {
|
||||
sum += u32::from(value);
|
||||
}
|
||||
if length & 1 == 1 {
|
||||
//奇数,说明还有一位,不足的补0
|
||||
sum += u32c(buffer.read_u8().unwrap(), 0);
|
||||
if let Ok(l) = buffer.read_u8() {
|
||||
sum += u32c(l, 0);
|
||||
}
|
||||
while sum >> 16 != 0 {
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
@@ -102,9 +101,9 @@ pub fn ipv4_cal_checksum(
|
||||
src_ip: &Ipv4Addr,
|
||||
dest_ip: &Ipv4Addr,
|
||||
protocol: u8,
|
||||
length: u16,
|
||||
) -> u16 {
|
||||
use std::io::Cursor;
|
||||
let length = buffer.len();
|
||||
let mut sum = 0;
|
||||
let src_ip = src_ip.octets();
|
||||
sum += u32c(src_ip[0], src_ip[1]);
|
||||
@@ -118,9 +117,8 @@ pub fn ipv4_cal_checksum(
|
||||
while let Ok(value) = buffer.read_u16::<BigEndian>() {
|
||||
sum += u32::from(value);
|
||||
}
|
||||
if length & 1 == 1 {
|
||||
//奇数,说明还有一位
|
||||
sum += u32c(buffer.read_u8().unwrap(), 0);
|
||||
if let Ok(l) = buffer.read_u8() {
|
||||
sum += u32c(l, 0);
|
||||
}
|
||||
while sum >> 16 != 0 {
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
@@ -133,7 +131,6 @@ fn u32c(x: u8, y: u8) -> u32 {
|
||||
((x as u32) << 8) | y as u32
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -1,9 +1,6 @@
|
||||
use std::fmt;
|
||||
use std::net::IpAddr;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
use crate::error::*;
|
||||
use crate::tcp::Flags;
|
||||
|
||||
/// tcp
|
||||
@@ -48,59 +45,80 @@ use crate::tcp::Flags;
|
||||
Options+Padding:32位整数倍,最多40个字节
|
||||
*/
|
||||
pub struct TcpPacket<B> {
|
||||
source_ip: IpAddr,
|
||||
destination_ip: IpAddr,
|
||||
source_ip: Ipv4Addr,
|
||||
destination_ip: Ipv4Addr,
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TcpPacket<B> {
|
||||
pub fn unchecked(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> TcpPacket<B> {
|
||||
pub fn unchecked(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> TcpPacket<B> {
|
||||
TcpPacket {
|
||||
source_ip,
|
||||
destination_ip,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
pub fn new(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> Result<TcpPacket<B>> {
|
||||
pub fn new(
|
||||
source_ip: Ipv4Addr,
|
||||
destination_ip: Ipv4Addr,
|
||||
buffer: B,
|
||||
) -> io::Result<TcpPacket<B>> {
|
||||
let packet = TcpPacket::unchecked(source_ip, destination_ip, buffer);
|
||||
|
||||
if packet.buffer.as_ref().len() < 20 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
|
||||
if packet.buffer.as_ref().len() < packet.data_offset() as usize * 4 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> TcpPacket<B> {
|
||||
pub fn set_source_ip(&mut self, value: Ipv4Addr) {
|
||||
self.source_ip = value;
|
||||
}
|
||||
pub fn set_destination_ip(&mut self, value: Ipv4Addr) {
|
||||
self.destination_ip = value;
|
||||
}
|
||||
}
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> TcpPacket<B> {
|
||||
fn set_checksum(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[16..18].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
pub fn set_source_port(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
pub fn set_destination_port(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
/// 更新校验和
|
||||
pub fn update_checksum(&mut self) {
|
||||
//先将校验和置0
|
||||
self.set_checksum(0);
|
||||
self.set_checksum(self.cal_checksum())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> TcpPacket<B> {
|
||||
/// 源端口
|
||||
pub fn source_port(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[0..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 目标端口
|
||||
pub fn destination_port(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[2..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
/// 序列号
|
||||
pub fn sequence(&self) -> u32 {
|
||||
(&self.buffer.as_ref()[4..])
|
||||
.read_u32::<BigEndian>()
|
||||
.unwrap()
|
||||
u32::from_be_bytes(self.buffer.as_ref()[4..8].try_into().unwrap())
|
||||
}
|
||||
/// 确认号
|
||||
pub fn acknowledgment(&self) -> u32 {
|
||||
(&self.buffer.as_ref()[8..])
|
||||
.read_u32::<BigEndian>()
|
||||
.unwrap()
|
||||
u32::from_be_bytes(self.buffer.as_ref()[8..12].try_into().unwrap())
|
||||
}
|
||||
/// 数据偏移 4字节为单位
|
||||
pub fn data_offset(&self) -> u8 {
|
||||
@@ -110,14 +128,10 @@ impl<B: AsRef<[u8]>> TcpPacket<B> {
|
||||
Flags(self.buffer.as_ref()[13])
|
||||
}
|
||||
pub fn window(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[14..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[14..16].try_into().unwrap())
|
||||
}
|
||||
pub fn checksum(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[16..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[16..18].try_into().unwrap())
|
||||
}
|
||||
/// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0
|
||||
/// TCP/IP协议栈不会自己计算校验和,而是简单地将一个空的校验和字段(零或随机填充)交给网卡硬件。
|
||||
@@ -126,26 +140,15 @@ impl<B: AsRef<[u8]>> TcpPacket<B> {
|
||||
self.checksum() == 0 || self.cal_checksum() == 0
|
||||
}
|
||||
fn cal_checksum(&self) -> u16 {
|
||||
match self.source_ip {
|
||||
IpAddr::V4(src) => {
|
||||
if let IpAddr::V4(dest) = self.destination_ip {
|
||||
return crate::ipv4_cal_checksum(
|
||||
self.buffer.as_ref(),
|
||||
&src,
|
||||
&dest,
|
||||
6,
|
||||
self.buffer.as_ref().len() as u16,
|
||||
);
|
||||
}
|
||||
}
|
||||
IpAddr::V6(_src) => {}
|
||||
}
|
||||
unimplemented!()
|
||||
crate::ipv4_cal_checksum(
|
||||
self.buffer.as_ref(),
|
||||
&self.source_ip,
|
||||
&self.destination_ip,
|
||||
6,
|
||||
)
|
||||
}
|
||||
pub fn urgent_pointer(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[18..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[18..20].try_into().unwrap())
|
||||
}
|
||||
pub fn options(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[20..(self.data_offset() as usize * 4)]
|
||||
@@ -1 +1 @@
|
||||
pub mod udp;
|
||||
pub mod udp;
|
||||
@@ -1,11 +1,5 @@
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use byteorder::WriteBytesExt;
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
|
||||
use crate::error::*;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
/// udp协议
|
||||
///
|
||||
@@ -53,22 +47,26 @@ RFC 768 https://www.ietf.org/rfc/rfc768.txt
|
||||
*/
|
||||
|
||||
pub struct UdpPacket<B> {
|
||||
source_ip: IpAddr,
|
||||
destination_ip: IpAddr,
|
||||
source_ip: Ipv4Addr,
|
||||
destination_ip: Ipv4Addr,
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> UdpPacket<B> {
|
||||
pub fn unchecked(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> UdpPacket<B> {
|
||||
pub fn unchecked(source_ip: Ipv4Addr, destination_ip: Ipv4Addr, buffer: B) -> UdpPacket<B> {
|
||||
UdpPacket {
|
||||
source_ip,
|
||||
destination_ip,
|
||||
buffer,
|
||||
}
|
||||
}
|
||||
pub fn new(source_ip: IpAddr, destination_ip: IpAddr, buffer: B) -> Result<UdpPacket<B>> {
|
||||
pub fn new(
|
||||
source_ip: Ipv4Addr,
|
||||
destination_ip: Ipv4Addr,
|
||||
buffer: B,
|
||||
) -> io::Result<UdpPacket<B>> {
|
||||
if buffer.as_ref().len() < 8 {
|
||||
Err(Error::SmallBuffer)?
|
||||
Err(io::Error::from(io::ErrorKind::InvalidData))?;
|
||||
}
|
||||
let packet = Self::unchecked(source_ip, destination_ip, buffer);
|
||||
Ok(packet)
|
||||
@@ -78,30 +76,22 @@ impl<B: AsRef<[u8]>> UdpPacket<B> {
|
||||
impl<B: AsRef<[u8]>> UdpPacket<B> {
|
||||
/// 源端口
|
||||
pub fn source_port(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[0..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[0..2].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 目标端口
|
||||
pub fn destination_port(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[2..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// 总字节数
|
||||
pub fn length(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[4..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
|
||||
}
|
||||
|
||||
/// Checksum of the packet.
|
||||
pub fn checksum(&self) -> u16 {
|
||||
(&self.buffer.as_ref()[6..])
|
||||
.read_u16::<BigEndian>()
|
||||
.unwrap()
|
||||
u16::from_be_bytes(self.buffer.as_ref()[6..8].try_into().unwrap())
|
||||
}
|
||||
/// 验证校验和,ipv4中为0表示不使用校验和,ipv6校验和不能为0
|
||||
pub fn is_valid(&self) -> bool {
|
||||
@@ -111,55 +101,38 @@ impl<B: AsRef<[u8]>> UdpPacket<B> {
|
||||
&self.buffer.as_ref()[8..]
|
||||
}
|
||||
fn cal_checksum(&self) -> u16 {
|
||||
match self.source_ip {
|
||||
IpAddr::V4(src) => {
|
||||
if let IpAddr::V4(dest) = self.destination_ip {
|
||||
return crate::ipv4_cal_checksum(
|
||||
self.buffer.as_ref(),
|
||||
&src,
|
||||
&dest,
|
||||
17,
|
||||
self.length(),
|
||||
);
|
||||
}
|
||||
}
|
||||
IpAddr::V6(_src) => {}
|
||||
}
|
||||
unimplemented!()
|
||||
crate::ipv4_cal_checksum(
|
||||
self.buffer.as_ref(),
|
||||
&self.source_ip,
|
||||
&self.destination_ip,
|
||||
17,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
|
||||
fn header_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[..8]
|
||||
}
|
||||
}
|
||||
// impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
|
||||
// fn header_mut(&mut self) -> &mut [u8] {
|
||||
// &mut self.buffer.as_mut()[..8]
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> UdpPacket<B> {
|
||||
/// 设置源端口
|
||||
pub fn set_source_port(&mut self, value: u16) -> &mut Self {
|
||||
Cursor::new(&mut self.header_mut()[0..])
|
||||
.write_u16::<BigEndian>(value)
|
||||
.unwrap();
|
||||
self
|
||||
pub fn set_source_port(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[0..2].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
|
||||
/// 设置目的端口
|
||||
pub fn set_destination_port(&mut self, value: u16) -> &mut Self {
|
||||
Cursor::new(&mut self.header_mut()[2..])
|
||||
.write_u16::<BigEndian>(value)
|
||||
.unwrap();
|
||||
self
|
||||
pub fn set_destination_port(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
fn set_checknum(&mut self, value: u16) {
|
||||
Cursor::new(&mut self.header_mut()[6..])
|
||||
.write_u16::<BigEndian>(value)
|
||||
.unwrap();
|
||||
fn set_checksum(&mut self, value: u16) {
|
||||
self.buffer.as_mut()[6..8].copy_from_slice(&value.to_be_bytes())
|
||||
}
|
||||
pub fn update_checknum(&mut self) {
|
||||
pub fn update_checksum(&mut self) {
|
||||
//先写0
|
||||
self.set_checknum(0);
|
||||
self.set_checknum(self.cal_checksum());
|
||||
self.set_checksum(0);
|
||||
self.set_checksum(self.cal_checksum());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
syntax = "proto3";
|
||||
|
||||
message HandshakeRequest {
|
||||
string version = 1;
|
||||
bool secret = 2;
|
||||
string key_finger = 3;
|
||||
}
|
||||
message HandshakeResponse {
|
||||
string version = 1;
|
||||
bool secret = 2;
|
||||
bytes public_key = 3;
|
||||
string key_finger = 4;
|
||||
}
|
||||
message SecretHandshakeRequest {
|
||||
string token = 1;
|
||||
bytes key = 2;
|
||||
}
|
||||
message RegistrationRequest {
|
||||
string token = 1;
|
||||
string device_id = 2;
|
||||
string name = 3;
|
||||
bool is_fast = 4;
|
||||
string version = 5;
|
||||
fixed32 virtual_ip = 6;
|
||||
bool allow_ip_change = 7;
|
||||
bool client_secret = 8;
|
||||
bytes client_secret_hash = 9;
|
||||
}
|
||||
|
||||
message RegistrationResponse {
|
||||
fixed32 virtual_ip = 1;
|
||||
fixed32 virtual_gateway = 2;
|
||||
fixed32 virtual_netmask = 3;
|
||||
uint32 epoch = 4;
|
||||
repeated DeviceInfo device_info_list = 5;
|
||||
fixed32 public_ip = 6;
|
||||
uint32 public_port = 7;
|
||||
bytes public_ipv6 = 8;
|
||||
}
|
||||
message DeviceInfo {
|
||||
string name = 1;
|
||||
fixed32 virtual_ip = 2;
|
||||
uint32 device_status = 3;
|
||||
bool client_secret = 4;
|
||||
bytes client_secret_hash = 5;
|
||||
bool wireguard = 6;
|
||||
}
|
||||
|
||||
message DeviceList {
|
||||
uint32 epoch = 1;
|
||||
repeated DeviceInfo device_info_list = 2;
|
||||
}
|
||||
|
||||
message PunchInfo {
|
||||
repeated fixed32 public_ip_list = 2;
|
||||
uint32 public_port = 3;
|
||||
uint32 public_port_range = 4;
|
||||
PunchNatType nat_type = 5;
|
||||
bool reply = 6;
|
||||
fixed32 local_ip = 7;
|
||||
uint32 local_port = 8;
|
||||
bytes ipv6 = 9;
|
||||
uint32 ipv6_port = 10;
|
||||
uint32 tcp_port = 11;
|
||||
repeated uint32 udp_ports = 12;
|
||||
repeated uint32 public_ports = 13;
|
||||
uint32 public_tcp_port = 14;
|
||||
PunchNatModel punch_model = 15;
|
||||
}
|
||||
enum PunchNatType {
|
||||
Symmetric = 0;
|
||||
Cone = 1;
|
||||
}
|
||||
enum PunchNatModel {
|
||||
All = 0;
|
||||
IPv4 = 1;
|
||||
IPv6 = 2;
|
||||
IPv4Tcp = 3;
|
||||
IPv4Udp = 4;
|
||||
IPv6Tcp = 5;
|
||||
IPv6Udp = 6;
|
||||
}
|
||||
|
||||
/// 向服务器上报客户端状态信息
|
||||
message ClientStatusInfo {
|
||||
fixed32 source = 1;
|
||||
repeated RouteItem p2p_list = 2;
|
||||
uint64 up_stream = 3;
|
||||
uint64 down_stream = 4;
|
||||
PunchNatType nat_type = 5;
|
||||
}
|
||||
message RouteItem {
|
||||
fixed32 next_ip = 1;
|
||||
}
|
||||
@@ -0,0 +1,559 @@
|
||||
use fnv::FnvHashMap;
|
||||
use std::net::{Ipv4Addr, SocketAddr, UdpSocket};
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::RwLock;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::channel::punch::NatType;
|
||||
use crate::channel::sender::{AcceptSocketSender, PacketSender};
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use crate::channel::{ConnectProtocol, Route, RouteKey, UseChannelType, DEFAULT_RT};
|
||||
use crate::protocol::NetPacket;
|
||||
use crate::util::limit::TrafficMeterMultiAddress;
|
||||
|
||||
/// 传输通道上下文,持有udp socket、tcp socket和路由信息
|
||||
#[derive(Clone)]
|
||||
pub struct ChannelContext {
|
||||
inner: Arc<ContextInner>,
|
||||
}
|
||||
|
||||
impl ChannelContext {
|
||||
pub fn new(
|
||||
main_udp_socket: Vec<UdpSocket>,
|
||||
v4_len: usize,
|
||||
use_channel_type: UseChannelType,
|
||||
first_latency: bool,
|
||||
protocol: ConnectProtocol,
|
||||
packet_loss_rate: Option<f64>,
|
||||
packet_delay: u32,
|
||||
up_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
down_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
default_interface: LocalInterface,
|
||||
) -> Self {
|
||||
let channel_num = v4_len;
|
||||
assert_ne!(channel_num, 0, "not channel");
|
||||
let packet_loss_rate = packet_loss_rate
|
||||
.map(|v| {
|
||||
let v = (v * PACKET_LOSS_RATE_DENOMINATOR as f64) as u32;
|
||||
if v > PACKET_LOSS_RATE_DENOMINATOR {
|
||||
PACKET_LOSS_RATE_DENOMINATOR
|
||||
} else {
|
||||
v
|
||||
}
|
||||
})
|
||||
.unwrap_or(0);
|
||||
let inner = ContextInner {
|
||||
main_udp_socket,
|
||||
v4_len,
|
||||
sub_udp_socket: RwLock::new(Vec::new()),
|
||||
packet_map: RwLock::new(FnvHashMap::default()),
|
||||
route_table: RouteTable::new(use_channel_type, first_latency, channel_num),
|
||||
protocol,
|
||||
packet_loss_rate,
|
||||
packet_delay,
|
||||
up_traffic_meter,
|
||||
down_traffic_meter,
|
||||
default_interface,
|
||||
default_route_key: AtomicCell::default(),
|
||||
};
|
||||
Self {
|
||||
inner: Arc::new(inner),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ChannelContext {
|
||||
type Target = ContextInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
/// 对称网络增加的udp socket数目,有助于增加打洞成功率
|
||||
pub const SYMMETRIC_CHANNEL_NUM: usize = 84;
|
||||
const PACKET_LOSS_RATE_DENOMINATOR: u32 = 100_0000;
|
||||
|
||||
pub struct ContextInner {
|
||||
// 核心udp socket
|
||||
pub(crate) main_udp_socket: Vec<UdpSocket>,
|
||||
v4_len: usize,
|
||||
// 对称网络增加的udp socket
|
||||
sub_udp_socket: RwLock<Vec<UdpSocket>>,
|
||||
// tcp数据发送器
|
||||
pub(crate) packet_map: RwLock<FnvHashMap<RouteKey, PacketSender>>,
|
||||
// 路由信息
|
||||
pub route_table: RouteTable,
|
||||
// 使用什么协议连接服务器
|
||||
protocol: ConnectProtocol,
|
||||
//控制丢包率,取值v=[0,100_0000] 丢包率r=v/100_0000
|
||||
packet_loss_rate: u32,
|
||||
//控制延迟
|
||||
packet_delay: u32,
|
||||
pub(crate) up_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
pub(crate) down_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
default_interface: LocalInterface,
|
||||
default_route_key: AtomicCell<Option<RouteKey>>,
|
||||
}
|
||||
|
||||
impl ContextInner {
|
||||
pub fn use_channel_type(&self) -> UseChannelType {
|
||||
self.route_table.use_channel_type
|
||||
}
|
||||
pub fn default_interface(&self) -> &LocalInterface {
|
||||
&self.default_interface
|
||||
}
|
||||
pub fn set_default_route_key(&self, route_key: RouteKey) {
|
||||
self.default_route_key.store(Some(route_key));
|
||||
}
|
||||
/// 通过sub_udp_socket是否为空来判断是否为锥形网络
|
||||
pub fn is_cone(&self) -> bool {
|
||||
self.sub_udp_socket.read().is_empty()
|
||||
}
|
||||
pub fn main_protocol(&self) -> ConnectProtocol {
|
||||
self.protocol
|
||||
}
|
||||
pub fn is_udp_main(&self, route_key: &RouteKey) -> bool {
|
||||
route_key.protocol().is_udp() && route_key.index < self.main_udp_socket.len()
|
||||
}
|
||||
pub fn first_latency(&self) -> bool {
|
||||
self.route_table.first_latency
|
||||
}
|
||||
/// 切换NAT类型,不同的nat打洞模式会有不同
|
||||
pub fn switch(
|
||||
&self,
|
||||
nat_type: NatType,
|
||||
udp_socket_sender: &AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut write_guard = self.sub_udp_socket.write();
|
||||
match nat_type {
|
||||
NatType::Symmetric => {
|
||||
if !write_guard.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut vec = Vec::with_capacity(SYMMETRIC_CHANNEL_NUM);
|
||||
for _ in 0..SYMMETRIC_CHANNEL_NUM {
|
||||
let udp = crate::channel::socket::bind_udp(
|
||||
"0.0.0.0:0".parse().unwrap(),
|
||||
&self.default_interface,
|
||||
)?;
|
||||
let udp: UdpSocket = udp.into();
|
||||
vec.push(udp);
|
||||
}
|
||||
let mut mio_vec = Vec::with_capacity(SYMMETRIC_CHANNEL_NUM);
|
||||
for udp in vec.iter() {
|
||||
let udp_socket = mio::net::UdpSocket::from_std(udp.try_clone()?);
|
||||
mio_vec.push(udp_socket);
|
||||
}
|
||||
udp_socket_sender.try_add_socket(Some(mio_vec))?;
|
||||
*write_guard = vec;
|
||||
}
|
||||
NatType::Cone => {
|
||||
if write_guard.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
udp_socket_sender.try_add_socket(None)?;
|
||||
*write_guard = Vec::new();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
#[inline]
|
||||
pub fn channel_num(&self) -> usize {
|
||||
self.v4_len
|
||||
}
|
||||
#[inline]
|
||||
pub fn main_len(&self) -> usize {
|
||||
self.main_udp_socket.len()
|
||||
}
|
||||
/// 获取核心udp监听的端口,用于其他客户端连接
|
||||
pub fn main_local_udp_port(&self) -> io::Result<Vec<u16>> {
|
||||
let mut ports = Vec::new();
|
||||
for udp in self.main_udp_socket[..self.v4_len].iter() {
|
||||
ports.push(udp.local_addr()?.port())
|
||||
}
|
||||
Ok(ports)
|
||||
}
|
||||
pub fn send_tcp(&self, buf: &[u8], route_key: &RouteKey) -> io::Result<()> {
|
||||
if let Some(tcp) = self.packet_map.read().get(route_key) {
|
||||
tcp.try_send(buf)
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("dest={:?}", route_key),
|
||||
))
|
||||
}
|
||||
}
|
||||
pub fn send_main_udp(&self, index: usize, buf: &[u8], addr: SocketAddr) -> io::Result<()> {
|
||||
if let Some(udp) = self.main_udp_socket.get(index) {
|
||||
udp.send_to(buf, addr)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::Other, "overflow"))
|
||||
}
|
||||
}
|
||||
/// 将数据发送到默认通道,一般发往服务器才用此方法
|
||||
pub fn send_default<B: AsRef<[u8]>>(
|
||||
&self,
|
||||
buf: &NetPacket<B>,
|
||||
addr: SocketAddr,
|
||||
) -> io::Result<()> {
|
||||
if self.protocol.is_udp() {
|
||||
if addr.is_ipv4() {
|
||||
self.send_main_udp(0, buf.buffer(), addr)?
|
||||
} else {
|
||||
self.send_main_udp(self.v4_len, buf.buffer(), addr)?
|
||||
}
|
||||
} else {
|
||||
if let Some(key) = self.default_route_key.load() {
|
||||
self.send_tcp(buf.buffer(), &key)?
|
||||
} else {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("dest={:?}", addr),
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some(up_traffic_meter) = &self.up_traffic_meter {
|
||||
up_traffic_meter.add_traffic(buf.destination(), buf.data_len());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 此方法仅用于对称网络打洞
|
||||
pub fn try_send_all(&self, buf: &[u8], addr: SocketAddr) {
|
||||
self.try_send_all_main(buf, addr);
|
||||
for udp in self.sub_udp_socket.read().iter() {
|
||||
if let Err(e) = udp.send_to(buf, addr) {
|
||||
log::warn!("{:?},add={:?}", e, addr);
|
||||
}
|
||||
thread::sleep(Duration::from_millis(3));
|
||||
}
|
||||
}
|
||||
pub fn try_send_all_main(&self, buf: &[u8], addr: SocketAddr) {
|
||||
for index in 0..self.channel_num() {
|
||||
if let Err(e) = self.send_main_udp(index, buf, addr) {
|
||||
log::warn!("{:?},add={:?}", e, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 发送网络数据
|
||||
pub fn send_ipv4_by_id<B: AsRef<[u8]>>(
|
||||
&self,
|
||||
buf: &NetPacket<B>,
|
||||
id: &Ipv4Addr,
|
||||
server_addr: SocketAddr,
|
||||
send_default: bool,
|
||||
) -> io::Result<()> {
|
||||
if self.packet_loss_rate > 0 {
|
||||
if rand::thread_rng().gen_ratio(self.packet_loss_rate, PACKET_LOSS_RATE_DENOMINATOR) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if self.packet_delay > 0 {
|
||||
thread::sleep(Duration::from_millis(self.packet_delay as _));
|
||||
}
|
||||
//优先发到直连到地址
|
||||
if let Err(e) = self.send_by_id(buf, id) {
|
||||
if e.kind() != io::ErrorKind::NotFound {
|
||||
log::warn!("{}:{:?}", id, e);
|
||||
}
|
||||
if !self.route_table.use_channel_type.is_only_p2p() && send_default {
|
||||
//符合条件再发到服务器转发
|
||||
self.send_default(buf, server_addr)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
/// 将数据发到指定id
|
||||
pub fn send_by_id<B: AsRef<[u8]>>(&self, buf: &NetPacket<B>, id: &Ipv4Addr) -> io::Result<()> {
|
||||
let mut c = 0;
|
||||
loop {
|
||||
let route = self.route_table.get_route_by_id(c, id)?;
|
||||
return if let Err(e) = self.send_by_key(buf, route.route_key()) {
|
||||
//降低发送速率
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
c += 1;
|
||||
if c < 10 {
|
||||
thread::sleep(Duration::from_micros(200));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(e)
|
||||
} else {
|
||||
Ok(())
|
||||
};
|
||||
}
|
||||
}
|
||||
/// 将数据发到指定路由
|
||||
pub fn send_by_key<B: AsRef<[u8]>>(
|
||||
&self,
|
||||
buf: &NetPacket<B>,
|
||||
route_key: RouteKey,
|
||||
) -> io::Result<()> {
|
||||
match route_key.protocol() {
|
||||
ConnectProtocol::UDP => {
|
||||
if let Some(main_udp) = self.main_udp_socket.get(route_key.index) {
|
||||
main_udp.send_to(buf.buffer(), route_key.addr)?;
|
||||
} else {
|
||||
if let Some(udp) = self
|
||||
.sub_udp_socket
|
||||
.read()
|
||||
.get(route_key.index - self.main_len())
|
||||
{
|
||||
udp.send_to(buf.buffer(), route_key.addr)?;
|
||||
} else {
|
||||
Err(io::Error::from(io::ErrorKind::NotFound))?
|
||||
}
|
||||
}
|
||||
}
|
||||
ConnectProtocol::TCP | ConnectProtocol::WS | ConnectProtocol::WSS => {
|
||||
self.send_tcp(buf.buffer(), &route_key)?
|
||||
}
|
||||
}
|
||||
if let Some(up_traffic_meter) = &self.up_traffic_meter {
|
||||
up_traffic_meter.add_traffic(buf.destination(), buf.data_len());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn remove_route(&self, ip: &Ipv4Addr, route_key: RouteKey) {
|
||||
self.route_table.remove_route(ip, route_key)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RouteTable {
|
||||
pub(crate) route_table:
|
||||
RwLock<FnvHashMap<Ipv4Addr, (AtomicUsize, Vec<(Route, AtomicCell<Instant>)>)>>,
|
||||
first_latency: bool,
|
||||
channel_num: usize,
|
||||
use_channel_type: UseChannelType,
|
||||
}
|
||||
|
||||
impl RouteTable {
|
||||
fn new(use_channel_type: UseChannelType, first_latency: bool, channel_num: usize) -> Self {
|
||||
Self {
|
||||
route_table: RwLock::new(FnvHashMap::with_capacity_and_hasher(64, Default::default())),
|
||||
use_channel_type,
|
||||
first_latency,
|
||||
channel_num,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RouteTable {
|
||||
fn get_route_by_id(&self, index: usize, id: &Ipv4Addr) -> io::Result<Route> {
|
||||
if let Some((_count, v)) = self.route_table.read().get(id) {
|
||||
if self.first_latency {
|
||||
if let Some((route, _)) = v.first() {
|
||||
return Ok(*route);
|
||||
}
|
||||
} else {
|
||||
let len = v.len();
|
||||
if len != 0 {
|
||||
let route = &v[index % len].0;
|
||||
// 跳过默认rt的路由(一般是刚加入的),这有助于提升稳定性
|
||||
if route.rt != DEFAULT_RT {
|
||||
return Ok(*route);
|
||||
}
|
||||
for (route, _) in v {
|
||||
if route.rt != DEFAULT_RT {
|
||||
return Ok(*route);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "route not found"))
|
||||
}
|
||||
pub fn add_route_if_absent(&self, id: Ipv4Addr, route: Route) -> bool {
|
||||
self.add_route_(id, route, true)
|
||||
}
|
||||
pub fn add_route(&self, id: Ipv4Addr, route: Route) -> bool {
|
||||
self.add_route_(id, route, false)
|
||||
}
|
||||
fn add_route_(&self, id: Ipv4Addr, route: Route, only_if_absent: bool) -> bool {
|
||||
// 限制通道类型
|
||||
match self.use_channel_type {
|
||||
UseChannelType::P2p => {
|
||||
if !route.is_p2p() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
let key = route.route_key();
|
||||
if only_if_absent {
|
||||
if let Some((_, list)) = self.route_table.read().get(&id) {
|
||||
for (x, _) in list {
|
||||
if x.route_key() == key {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut route_table = self.route_table.write();
|
||||
let (_, list) = route_table
|
||||
.entry(id)
|
||||
.or_insert_with(|| (AtomicUsize::new(0), Vec::with_capacity(4)));
|
||||
let mut exist = false;
|
||||
for (x, time) in list.iter_mut() {
|
||||
if x.metric < route.metric && !self.first_latency {
|
||||
//非优先延迟的情况下 不能比当前的路径更长
|
||||
return false;
|
||||
}
|
||||
if x.route_key() == key {
|
||||
if only_if_absent {
|
||||
return true;
|
||||
}
|
||||
x.metric = route.metric;
|
||||
x.rt = route.rt;
|
||||
exist = true;
|
||||
time.store(Instant::now());
|
||||
break;
|
||||
}
|
||||
}
|
||||
if exist {
|
||||
list.sort_by_key(|(k, _)| k.rt);
|
||||
} else {
|
||||
if !self.first_latency {
|
||||
if route.is_p2p() {
|
||||
//非优先延迟的情况下 添加了直连的则排除非直连的
|
||||
list.retain(|(k, _)| k.is_p2p());
|
||||
}
|
||||
};
|
||||
list.sort_by_key(|(k, _)| k.rt);
|
||||
list.push((route, AtomicCell::new(Instant::now())));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// 直接移除会导致通道不稳定,所以废弃这个方法,后面改用多余通道不发心跳包,从而让通道自动过期
|
||||
// fn truncate_(&self, list: &mut Vec<(Route, AtomicCell<Instant>)>, len: usize) {
|
||||
// if list.len() <= len {
|
||||
// return;
|
||||
// }
|
||||
// if self.first_latency {
|
||||
// //找到第一个p2p通道
|
||||
// if let Some(index) =
|
||||
// list.iter()
|
||||
// .enumerate()
|
||||
// .find_map(|(index, (route, _))| if route.is_p2p() { Some(index) } else { None })
|
||||
// {
|
||||
// if index >= len {
|
||||
// //保留第一个p2p通道
|
||||
// let route = list.remove(index);
|
||||
// list.truncate(len - 1);
|
||||
// list.push(route);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// list.truncate(len);
|
||||
// }
|
||||
pub fn route(&self, id: &Ipv4Addr) -> Option<Vec<Route>> {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
Some(v.iter().map(|(i, _)| *i).collect())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn route_one(&self, id: &Ipv4Addr) -> Option<Route> {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
v.first().map(|(i, _)| *i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn route_one_p2p(&self, id: &Ipv4Addr) -> Option<Route> {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
for (i, _) in v {
|
||||
if i.is_p2p() {
|
||||
return Some(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn route_to_id(&self, route_key: &RouteKey) -> Option<Ipv4Addr> {
|
||||
let table = self.route_table.read();
|
||||
for (k, (_, v)) in table.iter() {
|
||||
for (route, _) in v {
|
||||
if &route.route_key() == route_key && route.is_p2p() {
|
||||
return Some(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn no_need_punch(&self, id: &Ipv4Addr) -> bool {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
//p2p的通道数符合要求
|
||||
return v.iter().filter(|(k, _)| k.is_p2p()).count() >= self.channel_num;
|
||||
}
|
||||
false
|
||||
}
|
||||
pub fn p2p_num(&self, id: &Ipv4Addr) -> usize {
|
||||
if let Some((_, v)) = self.route_table.read().get(id) {
|
||||
v.iter().filter(|(k, _)| k.is_p2p()).count()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
/// 返回所有路由
|
||||
pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec<Route>)> {
|
||||
let table = self.route_table.read();
|
||||
table
|
||||
.iter()
|
||||
.map(|(k, (_, v))| (k.clone(), v.iter().map(|(i, _)| *i).collect()))
|
||||
.collect()
|
||||
}
|
||||
pub fn route_table_p2p(&self) -> Vec<(Ipv4Addr, Route)> {
|
||||
let table = self.route_table.read();
|
||||
let mut list = Vec::with_capacity(8);
|
||||
for (ip, (_, routes)) in table.iter() {
|
||||
for (route, _) in routes.iter() {
|
||||
if route.is_p2p() {
|
||||
list.push((*ip, *route));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
list
|
||||
}
|
||||
pub fn route_table_one(&self) -> Vec<(Ipv4Addr, Route)> {
|
||||
let mut list = Vec::with_capacity(8);
|
||||
let table = self.route_table.read();
|
||||
for (k, (_, v)) in table.iter() {
|
||||
if let Some((route, _)) = v.first() {
|
||||
list.push((*k, *route));
|
||||
}
|
||||
}
|
||||
list
|
||||
}
|
||||
pub fn remove_route(&self, id: &Ipv4Addr, route_key: RouteKey) {
|
||||
let mut write_guard = self.route_table.write();
|
||||
if let Some((_, routes)) = write_guard.get_mut(id) {
|
||||
routes.retain(|(x, _)| x.route_key() != route_key);
|
||||
if routes.is_empty() {
|
||||
write_guard.remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 更新路由入栈包的时刻,长时间没有收到数据的路由将会被剔除
|
||||
pub fn update_read_time(&self, id: &Ipv4Addr, route_key: &RouteKey) {
|
||||
if let Some((_, routes)) = self.route_table.read().get(id) {
|
||||
for (route, time) in routes {
|
||||
if &route.route_key() == route_key {
|
||||
time.store(Instant::now());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::RouteKey;
|
||||
|
||||
pub trait RecvChannelHandler: Clone + Send + Sync + 'static {
|
||||
fn handle(
|
||||
&self,
|
||||
buf: &mut [u8],
|
||||
extend: &mut [u8],
|
||||
route_key: RouteKey,
|
||||
context: &ChannelContext,
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::Route;
|
||||
|
||||
pub struct Idle {
|
||||
read_idle: Duration,
|
||||
context: ChannelContext,
|
||||
}
|
||||
|
||||
impl Idle {
|
||||
pub fn new(read_idle: Duration, context: ChannelContext) -> Self {
|
||||
Self { read_idle, context }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum IdleType {
|
||||
Timeout(Ipv4Addr, Route),
|
||||
Sleep(Duration),
|
||||
None,
|
||||
}
|
||||
|
||||
impl Idle {
|
||||
/// 获取空闲路由
|
||||
pub fn next_idle(&self) -> IdleType {
|
||||
let mut max = Duration::from_secs(0);
|
||||
let read_guard = self.context.route_table.route_table.read();
|
||||
if read_guard.is_empty() {
|
||||
return IdleType::None;
|
||||
}
|
||||
for (ip, (_, routes)) in read_guard.iter() {
|
||||
for (route, time) in routes {
|
||||
let last_read = time.load().elapsed();
|
||||
if last_read >= self.read_idle {
|
||||
return IdleType::Timeout(*ip, *route);
|
||||
} else if max < last_read {
|
||||
max = last_read;
|
||||
}
|
||||
}
|
||||
}
|
||||
let sleep_time = self.read_idle.checked_sub(max).unwrap_or_default();
|
||||
return IdleType::Sleep(sleep_time);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
use anyhow::Context;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::str::FromStr;
|
||||
use tokio::sync::mpsc::channel;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::sender::{AcceptSocketSender, ConnectUtil};
|
||||
use crate::channel::socket::{bind_udp, LocalInterface};
|
||||
use crate::channel::tcp_channel::tcp_listen;
|
||||
use crate::channel::udp_channel::udp_listen;
|
||||
#[cfg(feature = "ws")]
|
||||
use crate::channel::ws_channel::ws_connect_accept;
|
||||
use crate::util::limit::TrafficMeterMultiAddress;
|
||||
use crate::util::StopManager;
|
||||
|
||||
pub mod context;
|
||||
pub mod handler;
|
||||
pub mod idle;
|
||||
pub mod notify;
|
||||
pub mod punch;
|
||||
pub mod sender;
|
||||
pub mod socket;
|
||||
pub mod tcp_channel;
|
||||
pub mod udp_channel;
|
||||
#[cfg(feature = "ws")]
|
||||
pub mod ws_channel;
|
||||
|
||||
pub const BUFFER_SIZE: usize = 1024 * 64;
|
||||
// 这里留个坑,tcp是支持_TCP_MAX_PACKET_SIZE长度的,
|
||||
// 但是缓存只用BUFFER_SIZE,会导致多余的数据接收不了
|
||||
const TCP_MAX_PACKET_SIZE: usize = (1 << 24) - 1;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum UseChannelType {
|
||||
Relay,
|
||||
P2p,
|
||||
All,
|
||||
}
|
||||
|
||||
impl UseChannelType {
|
||||
pub fn is_only_relay(&self) -> bool {
|
||||
self == &UseChannelType::Relay
|
||||
}
|
||||
pub fn is_only_p2p(&self) -> bool {
|
||||
self == &UseChannelType::P2p
|
||||
}
|
||||
pub fn is_all(&self) -> bool {
|
||||
self == &UseChannelType::All
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for UseChannelType {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().trim() {
|
||||
"relay" => Ok(UseChannelType::Relay),
|
||||
"p2p" => Ok(UseChannelType::P2p),
|
||||
"all" => Ok(UseChannelType::All),
|
||||
_ => Err(format!("not match '{}', enum: relay/p2p/all", s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for UseChannelType {
|
||||
fn default() -> Self {
|
||||
UseChannelType::All
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub enum ConnectProtocol {
|
||||
UDP,
|
||||
TCP,
|
||||
WS,
|
||||
WSS,
|
||||
}
|
||||
|
||||
impl ConnectProtocol {
|
||||
#[inline]
|
||||
pub fn is_tcp(&self) -> bool {
|
||||
self == &ConnectProtocol::TCP
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_udp(&self) -> bool {
|
||||
self == &ConnectProtocol::UDP
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_ws(&self) -> bool {
|
||||
self == &ConnectProtocol::WS
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_wss(&self) -> bool {
|
||||
self == &ConnectProtocol::WSS
|
||||
}
|
||||
pub fn is_transport(&self) -> bool {
|
||||
self.is_tcp() || self.is_udp()
|
||||
}
|
||||
pub fn is_base_tcp(&self) -> bool {
|
||||
self.is_tcp() || self.is_ws() || self.is_wss()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Route {
|
||||
pub protocol: ConnectProtocol,
|
||||
index: usize,
|
||||
pub addr: SocketAddr,
|
||||
pub metric: u8,
|
||||
pub rt: i64,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct RouteSortKey {
|
||||
pub metric: u8,
|
||||
pub rt: i64,
|
||||
}
|
||||
|
||||
const DEFAULT_RT: i64 = 9999;
|
||||
|
||||
impl Route {
|
||||
pub fn new(
|
||||
protocol: ConnectProtocol,
|
||||
index: usize,
|
||||
addr: SocketAddr,
|
||||
metric: u8,
|
||||
rt: i64,
|
||||
) -> Self {
|
||||
Self {
|
||||
protocol,
|
||||
index,
|
||||
addr,
|
||||
metric,
|
||||
rt,
|
||||
}
|
||||
}
|
||||
pub fn from(route_key: RouteKey, metric: u8, rt: i64) -> Self {
|
||||
Self {
|
||||
protocol: route_key.protocol,
|
||||
index: route_key.index,
|
||||
addr: route_key.addr,
|
||||
metric,
|
||||
rt,
|
||||
}
|
||||
}
|
||||
pub fn from_default_rt(route_key: RouteKey, metric: u8) -> Self {
|
||||
Self {
|
||||
protocol: route_key.protocol,
|
||||
index: route_key.index,
|
||||
addr: route_key.addr,
|
||||
metric,
|
||||
rt: DEFAULT_RT,
|
||||
}
|
||||
}
|
||||
pub fn route_key(&self) -> RouteKey {
|
||||
RouteKey {
|
||||
protocol: self.protocol,
|
||||
index: self.index,
|
||||
addr: self.addr,
|
||||
}
|
||||
}
|
||||
pub fn sort_key(&self) -> RouteSortKey {
|
||||
RouteSortKey {
|
||||
metric: self.metric,
|
||||
rt: self.rt,
|
||||
}
|
||||
}
|
||||
pub fn is_p2p(&self) -> bool {
|
||||
self.metric == 1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct RouteKey {
|
||||
protocol: ConnectProtocol,
|
||||
index: usize,
|
||||
pub addr: SocketAddr,
|
||||
}
|
||||
|
||||
impl RouteKey {
|
||||
pub(crate) const fn new(protocol: ConnectProtocol, index: usize, addr: SocketAddr) -> Self {
|
||||
Self {
|
||||
protocol,
|
||||
index,
|
||||
addr,
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
pub fn protocol(&self) -> ConnectProtocol {
|
||||
self.protocol
|
||||
}
|
||||
#[inline]
|
||||
pub fn index(&self) -> usize {
|
||||
self.index
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init_context(
|
||||
ports: Vec<u16>,
|
||||
use_channel_type: UseChannelType,
|
||||
first_latency: bool,
|
||||
protocol: ConnectProtocol,
|
||||
packet_loss_rate: Option<f64>,
|
||||
packet_delay: u32,
|
||||
default_interface: LocalInterface,
|
||||
up_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
down_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
) -> anyhow::Result<(ChannelContext, std::net::TcpListener)> {
|
||||
assert!(!ports.is_empty(), "not channel");
|
||||
let mut main_udp_socket_v4 = Vec::with_capacity(ports.len());
|
||||
let mut main_udp_socket_v6 = Vec::with_capacity(ports.len());
|
||||
//检查系统是否支持ipv6
|
||||
let use_ipv6 = match socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::DGRAM, None) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
false
|
||||
}
|
||||
};
|
||||
for port in &ports {
|
||||
let addr_v4: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||
if use_ipv6 {
|
||||
let (main_channel_v4, main_channel_v6) = bind_udp_v4_and_v6(*port, &default_interface)?;
|
||||
main_udp_socket_v4.push(main_channel_v4);
|
||||
main_udp_socket_v6.push(main_channel_v6);
|
||||
} else {
|
||||
let socket = bind_udp(addr_v4, &default_interface)?;
|
||||
let main_channel_v4: UdpSocket = socket.into();
|
||||
main_udp_socket_v4.push(main_channel_v4);
|
||||
}
|
||||
}
|
||||
let mut main_udp_socket =
|
||||
Vec::with_capacity(main_udp_socket_v4.len() + main_udp_socket_v6.len());
|
||||
let v4_len = main_udp_socket_v4.len();
|
||||
main_udp_socket.append(&mut main_udp_socket_v4);
|
||||
main_udp_socket.append(&mut main_udp_socket_v6);
|
||||
let context = ChannelContext::new(
|
||||
main_udp_socket,
|
||||
v4_len,
|
||||
use_channel_type,
|
||||
first_latency,
|
||||
protocol,
|
||||
packet_loss_rate,
|
||||
packet_delay,
|
||||
up_traffic_meter,
|
||||
down_traffic_meter,
|
||||
default_interface,
|
||||
);
|
||||
|
||||
let port = context.main_local_udp_port()?[0];
|
||||
//监听v6+v4双栈,tcp通道使用异步io
|
||||
let (socket, address) = if use_ipv6 {
|
||||
let address: SocketAddr = format!("[::]:{}", port).parse().unwrap();
|
||||
let socket = socket2::Socket::new(socket2::Domain::IPV6, socket2::Type::STREAM, None)?;
|
||||
socket
|
||||
.set_only_v6(false)
|
||||
.with_context(|| format!("set_only_v6 failed: {}", &address))?;
|
||||
(socket, address)
|
||||
} else {
|
||||
let address: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||
let socket = socket2::Socket::new(socket2::Domain::IPV4, socket2::Type::STREAM, None)?;
|
||||
(socket, address)
|
||||
};
|
||||
socket
|
||||
.set_reuse_address(true)
|
||||
.context("set_reuse_address")?;
|
||||
#[cfg(unix)]
|
||||
if let Err(e) = socket.set_reuse_port(true) {
|
||||
log::warn!("set_reuse_port {:?}", e)
|
||||
}
|
||||
if let Err(e) = socket.bind(&address.into()) {
|
||||
if ports[0] == 0 {
|
||||
//端口可能冲突,则使用任意端口
|
||||
log::warn!("监听tcp端口失败 {:?},重试一次", address);
|
||||
let address: SocketAddr = if use_ipv6 {
|
||||
format!("[::]:{}", 0).parse().unwrap()
|
||||
} else {
|
||||
format!("0.0.0.0:{}", port).parse().unwrap()
|
||||
};
|
||||
socket
|
||||
.bind(&address.into())
|
||||
.with_context(|| format!("bind failed: {}", &address))?;
|
||||
} else {
|
||||
//手动指定的ip,直接报错
|
||||
Err(anyhow::anyhow!("{:?},bind failed: {}", e, address))?;
|
||||
}
|
||||
}
|
||||
socket.listen(128)?;
|
||||
socket.set_nonblocking(true)?;
|
||||
socket.set_nodelay(true)?;
|
||||
Ok((context, socket.into()))
|
||||
}
|
||||
fn bind_udp_v4_and_v6(
|
||||
port: u16,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<(UdpSocket, UdpSocket)> {
|
||||
let mut count = 0;
|
||||
loop {
|
||||
let addr_v4: SocketAddr = format!("0.0.0.0:{}", port).parse().unwrap();
|
||||
let socket = bind_udp(addr_v4, default_interface)?;
|
||||
if let Err(e) = socket.set_recv_buffer_size(2 * 1024 * 1024) {
|
||||
log::warn!("set_recv_buffer_size {:?}", e);
|
||||
}
|
||||
let main_channel_v4: UdpSocket = socket.into();
|
||||
let addr = main_channel_v4.local_addr()?;
|
||||
let addr_v6: SocketAddr = format!("[::]:{}", addr.port()).parse().unwrap();
|
||||
let socket = if port == 0 {
|
||||
match bind_udp(addr_v6, default_interface) {
|
||||
Ok(socket) => socket,
|
||||
Err(e) => {
|
||||
if count > 10 {
|
||||
return Err(e);
|
||||
}
|
||||
if let Some(e) = e.downcast_ref::<std::io::Error>() {
|
||||
if e.kind() == std::io::ErrorKind::AddrInUse {
|
||||
count += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(e)?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
bind_udp(addr_v6, default_interface)?
|
||||
};
|
||||
if let Err(e) = socket.set_recv_buffer_size(2 * 1024 * 1024) {
|
||||
log::warn!("set_recv_buffer_size {:?}", e);
|
||||
}
|
||||
let main_channel_v6: UdpSocket = socket.into();
|
||||
return Ok((main_channel_v4, main_channel_v6));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn init_channel<H>(
|
||||
tcp_listener: std::net::TcpListener,
|
||||
context: ChannelContext,
|
||||
stop_manager: StopManager,
|
||||
recv_handler: H,
|
||||
) -> anyhow::Result<(
|
||||
AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
|
||||
ConnectUtil,
|
||||
)>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let (tcp_connect_s, tcp_connect_r) = channel(16);
|
||||
let (ws_connect_s, _ws_connect_r) = channel(16);
|
||||
let connect_util = ConnectUtil::new(tcp_connect_s, ws_connect_s);
|
||||
// udp监听,udp_socket_sender 用于NAT类型切换
|
||||
let udp_socket_sender =
|
||||
udp_listen(stop_manager.clone(), recv_handler.clone(), context.clone())?;
|
||||
// 建立tcp监听,tcp_socket_sender 用于tcp 直连
|
||||
tcp_listen(
|
||||
tcp_listener,
|
||||
tcp_connect_r,
|
||||
recv_handler.clone(),
|
||||
context.clone(),
|
||||
stop_manager.clone(),
|
||||
)?;
|
||||
#[cfg(feature = "ws")]
|
||||
ws_connect_accept(_ws_connect_r, recv_handler, context.clone(), stop_manager)?;
|
||||
|
||||
Ok((udp_socket_sender, connect_util))
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use mio::{Token, Waker};
|
||||
use parking_lot::Mutex;
|
||||
use std::io;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WritableNotify {
|
||||
inner: Arc<WritableNotifyInner>,
|
||||
}
|
||||
|
||||
impl WritableNotify {
|
||||
pub fn new(waker: Waker) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(WritableNotifyInner {
|
||||
waker,
|
||||
state: AtomicUsize::new(0),
|
||||
tokens: Mutex::new(Vec::with_capacity(8)),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for WritableNotify {
|
||||
type Target = WritableNotifyInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WritableNotifyInner {
|
||||
waker: Waker,
|
||||
state: AtomicUsize,
|
||||
tokens: Mutex<Vec<(Token, bool)>>,
|
||||
}
|
||||
|
||||
impl WritableNotifyInner {
|
||||
pub fn notify(&self, token: Token, state: bool) -> io::Result<()> {
|
||||
{
|
||||
let mut guard = self.tokens.lock();
|
||||
if guard.is_empty() || !guard.contains(&(token, state)) {
|
||||
guard.push((token, state));
|
||||
}
|
||||
drop(guard);
|
||||
}
|
||||
self.need_write()
|
||||
}
|
||||
|
||||
pub fn stop(&self) -> io::Result<()> {
|
||||
self.state.store(0b001, Ordering::Release);
|
||||
self.waker.wake()
|
||||
}
|
||||
pub fn need_write(&self) -> io::Result<()> {
|
||||
self.state.fetch_or(0b010, Ordering::AcqRel);
|
||||
self.waker.wake()
|
||||
}
|
||||
pub fn add_socket(&self) -> io::Result<()> {
|
||||
self.state.fetch_or(0b100, Ordering::AcqRel);
|
||||
self.waker.wake()
|
||||
}
|
||||
pub fn take_all(&self) -> Option<Vec<(Token, bool)>> {
|
||||
let mut guard = self.tokens.lock();
|
||||
if guard.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(guard.drain(..).collect())
|
||||
}
|
||||
}
|
||||
pub fn is_stop(&self) -> bool {
|
||||
self.state.load(Ordering::Acquire) & 0b001 == 0b001
|
||||
}
|
||||
pub fn is_need_write(&self) -> bool {
|
||||
self.state.fetch_and(!0b010, Ordering::AcqRel) & 0b010 == 0b010
|
||||
}
|
||||
pub fn is_add_socket(&self) -> bool {
|
||||
self.state.fetch_and(!0b100, Ordering::AcqRel) & 0b100 == 0b100
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AcceptNotify {
|
||||
inner: Arc<AcceptNotifyInner>,
|
||||
}
|
||||
|
||||
impl AcceptNotify {
|
||||
pub fn new(waker: Waker) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(AcceptNotifyInner {
|
||||
waker,
|
||||
state: AtomicUsize::new(0),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for AcceptNotify {
|
||||
type Target = AcceptNotifyInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AcceptNotifyInner {
|
||||
waker: Waker,
|
||||
state: AtomicUsize,
|
||||
}
|
||||
|
||||
impl AcceptNotifyInner {
|
||||
pub fn is_stop(&self) -> bool {
|
||||
self.state.load(Ordering::Acquire) & 0b001 == 0b001
|
||||
}
|
||||
pub fn is_add_socket(&self) -> bool {
|
||||
self.state.fetch_and(!0b100, Ordering::AcqRel) & 0b100 == 0b100
|
||||
}
|
||||
pub fn stop(&self) -> io::Result<()> {
|
||||
self.state.store(0b001, Ordering::Release);
|
||||
self.waker.wake()
|
||||
}
|
||||
pub fn add_socket(&self) -> io::Result<()> {
|
||||
self.state.fetch_or(0b100, Ordering::AcqRel);
|
||||
self.waker.wake()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,508 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use std::ops::{Div, Mul};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::sender::ConnectUtil;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::nat::{is_ipv4_global, NatTest};
|
||||
use crate::proto::message::{PunchNatModel, PunchNatType};
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum PunchModel {
|
||||
All,
|
||||
IPv4,
|
||||
IPv6,
|
||||
IPv4Tcp,
|
||||
IPv4Udp,
|
||||
IPv6Tcp,
|
||||
IPv6Udp,
|
||||
}
|
||||
|
||||
impl PunchModel {
|
||||
pub fn use_tcp(&self) -> bool {
|
||||
self != &PunchModel::IPv4Udp && self != &PunchModel::IPv6Udp
|
||||
}
|
||||
pub fn use_udp(&self) -> bool {
|
||||
self != &PunchModel::IPv4Tcp && self != &PunchModel::IPv6Tcp
|
||||
}
|
||||
pub fn use_ipv6(&self) -> bool {
|
||||
self == &PunchModel::All
|
||||
|| self == &PunchModel::IPv6
|
||||
|| self == &PunchModel::IPv6Tcp
|
||||
|| self == &PunchModel::IPv6Udp
|
||||
}
|
||||
pub fn use_ipv4(&self) -> bool {
|
||||
self == &PunchModel::All
|
||||
|| self == &PunchModel::IPv4
|
||||
|| self == &PunchModel::IPv4Tcp
|
||||
|| self == &PunchModel::IPv4Udp
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PunchModel {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().trim() {
|
||||
"ipv4" => Ok(PunchModel::IPv4),
|
||||
"ipv6" => Ok(PunchModel::IPv6),
|
||||
"ipv4-tcp" => Ok(PunchModel::IPv4Tcp),
|
||||
"ipv4-udp" => Ok(PunchModel::IPv4Udp),
|
||||
"ipv6-tcp" => Ok(PunchModel::IPv6Tcp),
|
||||
"ipv6-udp" => Ok(PunchModel::IPv6Udp),
|
||||
"all" => Ok(PunchModel::All),
|
||||
_ => Err(format!(
|
||||
"not match '{}', enum: ipv4/ipv4-tcp/ipv4-udp/ipv6/ipv6-tcp/ipv6-udp/all",
|
||||
s
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PunchModel {
|
||||
fn default() -> Self {
|
||||
PunchModel::All
|
||||
}
|
||||
}
|
||||
impl From<PunchModel> for PunchNatModel {
|
||||
fn from(value: PunchModel) -> Self {
|
||||
match value {
|
||||
PunchModel::All => PunchNatModel::All,
|
||||
PunchModel::IPv4 => PunchNatModel::IPv4,
|
||||
PunchModel::IPv6 => PunchNatModel::IPv6,
|
||||
PunchModel::IPv4Tcp => PunchNatModel::IPv4Tcp,
|
||||
PunchModel::IPv4Udp => PunchNatModel::IPv4Udp,
|
||||
PunchModel::IPv6Tcp => PunchNatModel::IPv6Tcp,
|
||||
PunchModel::IPv6Udp => PunchNatModel::IPv6Udp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<PunchModel> for PunchNatModel {
|
||||
fn into(self) -> PunchModel {
|
||||
match self {
|
||||
PunchNatModel::All => PunchModel::All,
|
||||
PunchNatModel::IPv4 => PunchModel::IPv4,
|
||||
PunchNatModel::IPv6 => PunchModel::IPv6,
|
||||
PunchNatModel::IPv4Tcp => PunchModel::IPv4Tcp,
|
||||
PunchNatModel::IPv4Udp => PunchModel::IPv4Udp,
|
||||
PunchNatModel::IPv6Tcp => PunchModel::IPv6Tcp,
|
||||
PunchNatModel::IPv6Udp => PunchModel::IPv6Udp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct NatInfo {
|
||||
pub public_ips: Vec<Ipv4Addr>,
|
||||
pub public_ports: Vec<u16>,
|
||||
pub public_port_range: u16,
|
||||
pub nat_type: NatType,
|
||||
pub(crate) local_ipv4: Option<Ipv4Addr>,
|
||||
pub(crate) ipv6: Option<Ipv6Addr>,
|
||||
pub udp_ports: Vec<u16>,
|
||||
pub tcp_port: u16,
|
||||
pub public_tcp_port: u16,
|
||||
pub punch_model: PunchModel,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub enum NatType {
|
||||
Symmetric,
|
||||
Cone,
|
||||
}
|
||||
|
||||
impl NatType {
|
||||
pub fn is_cone(&self) -> bool {
|
||||
self == &NatType::Cone
|
||||
}
|
||||
}
|
||||
impl From<NatType> for PunchNatType {
|
||||
fn from(value: NatType) -> Self {
|
||||
match value {
|
||||
NatType::Symmetric => PunchNatType::Symmetric,
|
||||
NatType::Cone => PunchNatType::Cone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<NatType> for PunchNatType {
|
||||
fn into(self) -> NatType {
|
||||
match self {
|
||||
PunchNatType::Symmetric => NatType::Symmetric,
|
||||
PunchNatType::Cone => NatType::Cone,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NatInfo {
|
||||
pub fn new(
|
||||
mut public_ips: Vec<Ipv4Addr>,
|
||||
public_ports: Vec<u16>,
|
||||
public_port_range: u16,
|
||||
mut local_ipv4: Option<Ipv4Addr>,
|
||||
mut ipv6: Option<Ipv6Addr>,
|
||||
udp_ports: Vec<u16>,
|
||||
tcp_port: u16,
|
||||
public_tcp_port: u16,
|
||||
mut nat_type: NatType,
|
||||
punch_model: PunchModel,
|
||||
) -> Self {
|
||||
public_ips.retain(|ip| {
|
||||
!ip.is_multicast()
|
||||
&& !ip.is_broadcast()
|
||||
&& !ip.is_unspecified()
|
||||
&& !ip.is_loopback()
|
||||
&& !ip.is_private()
|
||||
});
|
||||
if public_ips.len() > 1 {
|
||||
nat_type = NatType::Symmetric;
|
||||
}
|
||||
if let Some(ip) = local_ipv4 {
|
||||
if ip.is_multicast() || ip.is_broadcast() || ip.is_unspecified() || ip.is_loopback() {
|
||||
local_ipv4 = None
|
||||
}
|
||||
}
|
||||
if let Some(ip) = ipv6 {
|
||||
if ip.is_multicast() || ip.is_unspecified() || ip.is_loopback() {
|
||||
ipv6 = None
|
||||
}
|
||||
}
|
||||
Self {
|
||||
public_ips,
|
||||
public_ports,
|
||||
public_port_range,
|
||||
local_ipv4,
|
||||
ipv6,
|
||||
udp_ports,
|
||||
tcp_port,
|
||||
public_tcp_port,
|
||||
nat_type,
|
||||
punch_model,
|
||||
}
|
||||
}
|
||||
pub fn update_addr(&mut self, index: usize, ip: Ipv4Addr, port: u16) -> bool {
|
||||
let mut updated = false;
|
||||
if port != 0 {
|
||||
if let Some(public_port) = self.public_ports.get_mut(index) {
|
||||
if *public_port != port {
|
||||
updated = true;
|
||||
log::info!("端口变化={}:{} index={}", ip, port, index)
|
||||
}
|
||||
*public_port = port;
|
||||
}
|
||||
}
|
||||
if is_ipv4_global(&ip) {
|
||||
if !self.public_ips.contains(&ip) {
|
||||
self.public_ips.push(ip);
|
||||
updated = true;
|
||||
log::info!("ip变化={},{:?}", ip, self.public_ips)
|
||||
}
|
||||
}
|
||||
updated
|
||||
}
|
||||
pub fn update_tcp_port(&mut self, port: u16) {
|
||||
self.public_tcp_port = port;
|
||||
}
|
||||
pub fn local_ipv4(&self) -> Option<Ipv4Addr> {
|
||||
self.local_ipv4
|
||||
}
|
||||
pub fn ipv6(&self) -> Option<Ipv6Addr> {
|
||||
self.ipv6
|
||||
}
|
||||
pub fn local_udp_ipv4addr(&self, index: usize) -> Option<SocketAddr> {
|
||||
let len = self.udp_ports.len();
|
||||
if len == 0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(local_ipv4) = self.local_ipv4 {
|
||||
Some(SocketAddr::V4(SocketAddrV4::new(
|
||||
local_ipv4,
|
||||
self.udp_ports[index % len],
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn local_udp_ipv6addr(&self, index: usize) -> Option<SocketAddr> {
|
||||
let len = self.udp_ports.len();
|
||||
if len == 0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(ipv6) = self.ipv6 {
|
||||
Some(SocketAddr::V6(SocketAddrV6::new(
|
||||
ipv6,
|
||||
self.udp_ports[index % len],
|
||||
0,
|
||||
0,
|
||||
)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_tcp_ipv6addr(&self) -> Option<SocketAddr> {
|
||||
if self.tcp_port == 0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(ipv6) = self.ipv6 {
|
||||
Some(SocketAddr::V6(SocketAddrV6::new(ipv6, self.tcp_port, 0, 0)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn local_tcp_ipv4addr(&self) -> Option<SocketAddr> {
|
||||
if self.tcp_port == 0 {
|
||||
return None;
|
||||
}
|
||||
if let Some(ipv4) = self.local_ipv4 {
|
||||
Some(SocketAddr::V4(SocketAddrV4::new(ipv4, self.tcp_port)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Punch {
|
||||
context: ChannelContext,
|
||||
port_vec: Vec<u16>,
|
||||
port_index: HashMap<Ipv4Addr, usize>,
|
||||
punch_model: PunchModel,
|
||||
connect_util: ConnectUtil,
|
||||
nat_test: NatTest,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
}
|
||||
|
||||
impl Punch {
|
||||
pub fn new(
|
||||
context: ChannelContext,
|
||||
punch_model: PunchModel,
|
||||
connect_util: ConnectUtil,
|
||||
nat_test: NatTest,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
) -> Self {
|
||||
let mut port_vec: Vec<u16> = (1..65535).collect();
|
||||
port_vec.push(65535);
|
||||
let mut rng = rand::thread_rng();
|
||||
port_vec.shuffle(&mut rng);
|
||||
Punch {
|
||||
context,
|
||||
port_vec,
|
||||
port_index: HashMap::new(),
|
||||
punch_model,
|
||||
connect_util,
|
||||
nat_test,
|
||||
current_device,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Punch {
|
||||
fn connect_tcp(&self, buf: &[u8], addr: SocketAddr) {
|
||||
if self.nat_test.is_local_address(true, addr) {
|
||||
return;
|
||||
}
|
||||
if addr.ip().is_unspecified() || addr.port() == 0 {
|
||||
return;
|
||||
}
|
||||
self.connect_util.try_connect_tcp_punch(buf.to_vec(), addr);
|
||||
}
|
||||
pub fn punch(
|
||||
&mut self,
|
||||
buf: &[u8],
|
||||
id: Ipv4Addr,
|
||||
mut nat_info: NatInfo,
|
||||
punch_tcp: bool,
|
||||
count: usize,
|
||||
) -> io::Result<()> {
|
||||
if self.context.route_table.no_need_punch(&id) {
|
||||
log::info!("已打洞成功,无需打洞:{:?}", id);
|
||||
return Ok(());
|
||||
}
|
||||
let device_info = self.current_device.load();
|
||||
|
||||
nat_info
|
||||
.public_ips
|
||||
.retain(|ip| is_ipv4_global(ip) && device_info.not_in_network(*ip));
|
||||
nat_info.public_ports.retain(|port| *port != 0);
|
||||
nat_info.udp_ports.retain(|port| *port != 0);
|
||||
|
||||
nat_info.local_ipv4 = nat_info
|
||||
.local_ipv4
|
||||
.filter(|ip| device_info.not_in_network(*ip));
|
||||
if punch_tcp && self.punch_model.use_tcp() && nat_info.punch_model.use_tcp() {
|
||||
//向tcp发起连接
|
||||
if self.punch_model.use_ipv6() && nat_info.punch_model.use_ipv6() {
|
||||
if let Some(ipv6_addr) = nat_info.local_tcp_ipv6addr() {
|
||||
self.connect_tcp(buf, ipv6_addr)
|
||||
}
|
||||
}
|
||||
if self.punch_model.use_ipv4() && nat_info.punch_model.use_ipv4() {
|
||||
if let Some(ipv4_addr) = nat_info.local_tcp_ipv4addr() {
|
||||
self.connect_tcp(buf, ipv4_addr)
|
||||
}
|
||||
for ip in &nat_info.public_ips {
|
||||
let addr = SocketAddr::V4(SocketAddrV4::new(*ip, nat_info.tcp_port));
|
||||
self.connect_tcp(buf, addr);
|
||||
}
|
||||
if nat_info.nat_type.is_cone() && nat_info.public_tcp_port != 0 {
|
||||
for ip in &nat_info.public_ips {
|
||||
let addr = SocketAddr::V4(SocketAddrV4::new(*ip, nat_info.public_tcp_port));
|
||||
self.connect_tcp(buf, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.punch_model.use_udp() || !nat_info.punch_model.use_udp() {
|
||||
return Ok(());
|
||||
}
|
||||
let channel_num = self.context.channel_num();
|
||||
let main_len = self.context.main_len();
|
||||
|
||||
if self.punch_model.use_ipv6() && nat_info.punch_model.use_ipv6() {
|
||||
for index in channel_num..main_len {
|
||||
if let Some(ipv6_addr) = nat_info.local_udp_ipv6addr(index) {
|
||||
if !self.nat_test.is_local_address(false, ipv6_addr) {
|
||||
let rs = self.context.send_main_udp(index, buf, ipv6_addr);
|
||||
log::info!("发送到ipv6地址:{:?},rs={:?} {}", ipv6_addr, rs, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !self.punch_model.use_ipv4() || !nat_info.punch_model.use_ipv4() {
|
||||
return Ok(());
|
||||
}
|
||||
for index in 0..channel_num {
|
||||
if let Some(ipv4_addr) = nat_info.local_udp_ipv4addr(index) {
|
||||
if !self.nat_test.is_local_address(false, ipv4_addr) {
|
||||
let _ = self.context.send_main_udp(index, buf, ipv4_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 可能是开放了端口的,需要打洞
|
||||
for index in 0..channel_num {
|
||||
for port in &nat_info.udp_ports {
|
||||
if *port == 0 {
|
||||
continue;
|
||||
}
|
||||
for ip in &nat_info.public_ips {
|
||||
if ip.is_unspecified() {
|
||||
continue;
|
||||
}
|
||||
let addr = SocketAddrV4::new(*ip, *port);
|
||||
let _ = self.context.send_main_udp(index, buf, addr.into());
|
||||
thread::sleep(Duration::from_millis(3));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match nat_info.nat_type {
|
||||
NatType::Symmetric => {
|
||||
// 假设对方绑定n个端口,通过NAT对外映射出n个 公网ip:公网端口,自己随机尝试k次的情况下
|
||||
// 猜中的概率 p = 1-((65535-n)/65535)*((65535-n-1)/(65535-1))*...*((65535-n-k+1)/(65535-k+1))
|
||||
// n取76,k取600,猜中的概率就超过50%了
|
||||
// 前提 自己是锥形网络,否则猜中了也通信不了
|
||||
|
||||
//预测范围内最多发送max_k1个包
|
||||
let max_k1 = 60;
|
||||
//全局最多发送max_k2个包
|
||||
let mut max_k2: usize = rand::thread_rng().gen_range(600..800);
|
||||
if count > 2 {
|
||||
//递减探测规模
|
||||
max_k2 = max_k2.mul(2).div(count).max(max_k1 as usize);
|
||||
}
|
||||
let port = nat_info.public_ports.get(0).map(|e| *e).unwrap_or(0);
|
||||
if nat_info.public_port_range < max_k1 * 3 {
|
||||
//端口变化不大时,在预测的范围内随机发送
|
||||
let min_port = if port > nat_info.public_port_range {
|
||||
port - nat_info.public_port_range
|
||||
} else {
|
||||
1
|
||||
};
|
||||
let (max_port, overflow) = port.overflowing_add(nat_info.public_port_range);
|
||||
let max_port = if overflow { 65535 } else { max_port };
|
||||
let k = if max_port - min_port + 1 > max_k1 {
|
||||
max_k1 as usize
|
||||
} else {
|
||||
(max_port - min_port + 1) as usize
|
||||
};
|
||||
let mut nums: Vec<u16> = (min_port..=max_port).collect();
|
||||
nums.shuffle(&mut rand::thread_rng());
|
||||
self.punch_symmetric(&nums[..k], buf, &nat_info.public_ips, max_k1 as usize)?;
|
||||
}
|
||||
let start = *self.port_index.entry(id.clone()).or_insert(0);
|
||||
let mut end = start + max_k2;
|
||||
if end > self.port_vec.len() {
|
||||
end = self.port_vec.len();
|
||||
}
|
||||
let mut index = start
|
||||
+ self.punch_symmetric(
|
||||
&self.port_vec[start..end],
|
||||
buf,
|
||||
&nat_info.public_ips,
|
||||
max_k2,
|
||||
)?;
|
||||
if index >= self.port_vec.len() {
|
||||
index = 0
|
||||
}
|
||||
self.port_index.insert(id, index);
|
||||
}
|
||||
NatType::Cone => {
|
||||
let is_cone = self.context.is_cone();
|
||||
'a: for index in 0..nat_info.public_ports.len().min(channel_num) {
|
||||
for ip in &nat_info.public_ips {
|
||||
let port = nat_info.public_ports[index];
|
||||
if port == 0 || ip.is_unspecified() {
|
||||
continue;
|
||||
}
|
||||
let addr = SocketAddr::V4(SocketAddrV4::new(*ip, port));
|
||||
if is_cone {
|
||||
self.context.send_main_udp(index, buf, addr)?;
|
||||
} else {
|
||||
//只有一方是对称,则对称方要使用全部端口发送数据,符合上述计算的概率
|
||||
self.context.try_send_all(buf, addr);
|
||||
}
|
||||
thread::sleep(Duration::from_millis(2));
|
||||
}
|
||||
if !is_cone {
|
||||
//对称网络数据只发一遍
|
||||
break 'a;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn punch_symmetric(
|
||||
&self,
|
||||
ports: &[u16],
|
||||
buf: &[u8],
|
||||
ips: &Vec<Ipv4Addr>,
|
||||
max: usize,
|
||||
) -> io::Result<usize> {
|
||||
let mut count = 0;
|
||||
for (index, port) in ports.iter().enumerate() {
|
||||
for pub_ip in ips {
|
||||
count += 1;
|
||||
if count == max {
|
||||
return Ok(index);
|
||||
}
|
||||
let addr = SocketAddr::V4(SocketAddrV4::new(*pub_ip, *port));
|
||||
self.context.send_main_udp(0, buf, addr)?;
|
||||
thread::sleep(Duration::from_millis(3));
|
||||
}
|
||||
}
|
||||
Ok(ports.len())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,272 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
use std::sync::mpsc::{SyncSender, TrySendError};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::notify::AcceptNotify;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::compression::Compressor;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::protocol;
|
||||
use crate::protocol::{ip_turn_packet, NetPacket};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IpPacketSender {
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
compressor: Compressor,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
ip_route: ExternalRoute,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
allow_wire_guard: bool,
|
||||
}
|
||||
|
||||
impl IpPacketSender {
|
||||
pub fn new(
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
compressor: Compressor,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
ip_route: ExternalRoute,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
allow_wire_guard: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
context,
|
||||
current_device,
|
||||
compressor,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
ip_route,
|
||||
device_map,
|
||||
allow_wire_guard,
|
||||
}
|
||||
}
|
||||
pub fn self_virtual_ip(&self) -> Ipv4Addr {
|
||||
self.current_device.load().virtual_ip
|
||||
}
|
||||
pub fn send_ip(
|
||||
&self,
|
||||
buf: &mut [u8],
|
||||
data_len: usize,
|
||||
auxiliary_buf: &mut [u8],
|
||||
mut dest_ip: Ipv4Addr,
|
||||
) -> anyhow::Result<()> {
|
||||
let device_info = self.current_device.load();
|
||||
let src_ip = device_info.virtual_ip;
|
||||
if src_ip.is_unspecified() {
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(v) = self.ip_route.route(&dest_ip) {
|
||||
dest_ip = v;
|
||||
}
|
||||
if dest_ip.is_multicast() {
|
||||
//广播
|
||||
dest_ip = Ipv4Addr::BROADCAST;
|
||||
}
|
||||
let mut net_packet = NetPacket::new0(data_len, buf)?;
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_protocol(protocol::Protocol::IpTurn);
|
||||
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into());
|
||||
net_packet.first_set_ttl(6);
|
||||
net_packet.set_source(src_ip);
|
||||
net_packet.set_destination(dest_ip);
|
||||
if self.allow_wire_guard {
|
||||
if dest_ip.is_broadcast() || dest_ip == device_info.broadcast_ip {
|
||||
let exists_wg = self
|
||||
.device_map
|
||||
.lock()
|
||||
.1
|
||||
.values()
|
||||
.any(|v| v.status.is_online() && v.wireguard);
|
||||
if exists_wg {
|
||||
send_to_wg_broadcast(
|
||||
&self.context,
|
||||
&net_packet,
|
||||
&self.server_cipher,
|
||||
&device_info,
|
||||
)?;
|
||||
}
|
||||
} else {
|
||||
let guard = self.device_map.lock();
|
||||
if let Some(peer_info) = guard.1.get(&dest_ip) {
|
||||
if peer_info.wireguard {
|
||||
if peer_info.status.is_offline() {
|
||||
return Ok(());
|
||||
}
|
||||
drop(guard);
|
||||
send_to_wg(
|
||||
&self.context,
|
||||
&mut net_packet,
|
||||
&self.server_cipher,
|
||||
&device_info,
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut auxiliary = NetPacket::new(auxiliary_buf)?;
|
||||
|
||||
let mut net_packet = if self.compressor.compress(&net_packet, &mut auxiliary)? {
|
||||
auxiliary.set_default_version();
|
||||
auxiliary.set_protocol(protocol::Protocol::IpTurn);
|
||||
auxiliary.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into());
|
||||
auxiliary.first_set_ttl(6);
|
||||
auxiliary.set_source(src_ip);
|
||||
auxiliary.set_destination(dest_ip);
|
||||
auxiliary
|
||||
} else {
|
||||
net_packet
|
||||
};
|
||||
self.client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
if dest_ip.is_broadcast() || dest_ip == device_info.broadcast_ip {
|
||||
//走服务端广播
|
||||
self.context
|
||||
.send_default(&net_packet, device_info.connect_server)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if device_info.not_in_network(dest_ip) {
|
||||
//不是一个网段的直接忽略
|
||||
return Ok(());
|
||||
}
|
||||
self.context.send_ipv4_by_id(
|
||||
&net_packet,
|
||||
&dest_ip,
|
||||
device_info.connect_server,
|
||||
device_info.status.online(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_to_wg_broadcast(
|
||||
sender: &ChannelContext,
|
||||
net_packet: &NetPacket<&mut [u8]>,
|
||||
server_cipher: &Cipher,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut copy_packet = NetPacket::new0(net_packet.data_len(), [0; 65536])?;
|
||||
copy_packet.set_default_version();
|
||||
copy_packet.set_protocol(protocol::Protocol::IpTurn);
|
||||
copy_packet.set_transport_protocol(ip_turn_packet::Protocol::WGIpv4.into());
|
||||
copy_packet.first_set_ttl(6);
|
||||
copy_packet.set_source(net_packet.source());
|
||||
copy_packet.set_destination(net_packet.destination());
|
||||
copy_packet.set_gateway_flag(true);
|
||||
copy_packet.set_payload(net_packet.payload())?;
|
||||
server_cipher.encrypt_ipv4(&mut copy_packet)?;
|
||||
sender.send_default(©_packet, current_device.connect_server)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
pub fn send_to_wg(
|
||||
sender: &ChannelContext,
|
||||
net_packet: &mut NetPacket<&mut [u8]>,
|
||||
server_cipher: &Cipher,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> anyhow::Result<()> {
|
||||
net_packet.set_transport_protocol(ip_turn_packet::Protocol::WGIpv4.into());
|
||||
net_packet.set_gateway_flag(true);
|
||||
server_cipher.encrypt_ipv4(net_packet)?;
|
||||
sender.send_default(&net_packet, current_device.connect_server)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct AcceptSocketSender<T> {
|
||||
sender: SyncSender<T>,
|
||||
notify: AcceptNotify,
|
||||
}
|
||||
|
||||
impl<T> Clone for AcceptSocketSender<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
sender: self.sender.clone(),
|
||||
notify: self.notify.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AcceptSocketSender<T> {
|
||||
pub fn new(notify: AcceptNotify, sender: SyncSender<T>) -> Self {
|
||||
Self { sender, notify }
|
||||
}
|
||||
pub fn try_add_socket(&self, t: T) -> io::Result<()> {
|
||||
match self.sender.try_send(t) {
|
||||
Ok(_) => self.notify.add_socket(),
|
||||
Err(e) => match e {
|
||||
TrySendError::Full(_) => Err(io::Error::from(io::ErrorKind::WouldBlock)),
|
||||
TrySendError::Disconnected(_) => Err(io::Error::from(io::ErrorKind::WriteZero)),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct PacketSender {
|
||||
sender: Sender<Vec<u8>>,
|
||||
}
|
||||
|
||||
impl PacketSender {
|
||||
pub fn new(sender: Sender<Vec<u8>>) -> Self {
|
||||
Self { sender }
|
||||
}
|
||||
pub fn try_send(&self, buf: &[u8]) -> io::Result<()> {
|
||||
match self.sender.try_send(buf.to_vec()) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(tokio::sync::mpsc::error::TrySendError::Full(_)) => Err(io::Error::new(
|
||||
io::ErrorKind::WouldBlock,
|
||||
"通道已满,发生丢包",
|
||||
)),
|
||||
Err(_) => Err(io::Error::new(
|
||||
io::ErrorKind::ConnectionRefused,
|
||||
"通道关闭,发生丢包",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConnectUtil {
|
||||
connect_tcp: Sender<(Vec<u8>, Option<u16>, SocketAddr)>,
|
||||
connect_ws: Sender<(Vec<u8>, String)>,
|
||||
}
|
||||
|
||||
impl ConnectUtil {
|
||||
pub fn new(
|
||||
connect_tcp: Sender<(Vec<u8>, Option<u16>, SocketAddr)>,
|
||||
connect_ws: Sender<(Vec<u8>, String)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
connect_tcp,
|
||||
connect_ws,
|
||||
}
|
||||
}
|
||||
pub fn try_connect_tcp(&self, buf: Vec<u8>, addr: SocketAddr) {
|
||||
if self.connect_tcp.try_send((buf, None, addr)).is_err() {
|
||||
log::warn!("try_connect_tcp failed {}", addr);
|
||||
}
|
||||
}
|
||||
pub fn try_connect_tcp_punch(&self, buf: Vec<u8>, addr: SocketAddr) {
|
||||
// 打洞的连接可以绑定随机端口
|
||||
if self.connect_tcp.try_send((buf, Some(0), addr)).is_err() {
|
||||
log::warn!("try_connect_tcp failed {}", addr);
|
||||
}
|
||||
}
|
||||
pub fn try_connect_ws(&self, buf: Vec<u8>, addr: String) {
|
||||
if self.connect_ws.try_send((buf, addr)).is_err() {
|
||||
log::warn!("try_connect_ws failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use network_interface::{NetworkInterface, NetworkInterfaceConfig};
|
||||
use socket2::Protocol;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix;
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
pub trait VntSocketTrait {
|
||||
fn set_ip_unicast_if(&self, _interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct LocalInterface {
|
||||
index: u32,
|
||||
#[cfg(unix)]
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn connect_tcp(
|
||||
addr: SocketAddr,
|
||||
bind_port: u16,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<tokio::net::TcpStream> {
|
||||
let socket = create_tcp0(addr.is_ipv4(), bind_port, default_interface)?;
|
||||
Ok(socket.connect(addr).await?)
|
||||
}
|
||||
pub fn create_tcp(
|
||||
v4: bool,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<tokio::net::TcpSocket> {
|
||||
create_tcp0(v4, 0, default_interface)
|
||||
}
|
||||
pub fn create_tcp0(
|
||||
v4: bool,
|
||||
bind_port: u16,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<tokio::net::TcpSocket> {
|
||||
let socket = if v4 {
|
||||
socket2::Socket::new(
|
||||
socket2::Domain::IPV4,
|
||||
socket2::Type::STREAM,
|
||||
Some(Protocol::TCP),
|
||||
)?
|
||||
} else {
|
||||
socket2::Socket::new(
|
||||
socket2::Domain::IPV6,
|
||||
socket2::Type::STREAM,
|
||||
Some(Protocol::TCP),
|
||||
)?
|
||||
};
|
||||
if v4 {
|
||||
if let Err(e) = socket.set_ip_unicast_if(default_interface) {
|
||||
log::warn!("set_ip_unicast_if {:?}", e)
|
||||
}
|
||||
}
|
||||
if bind_port != 0 {
|
||||
socket
|
||||
.set_reuse_address(true)
|
||||
.context("set_reuse_address")?;
|
||||
#[cfg(unix)]
|
||||
if let Err(e) = socket.set_reuse_port(true) {
|
||||
log::warn!("set_reuse_port {:?}", e)
|
||||
}
|
||||
if v4 {
|
||||
let addr: SocketAddr = format!("0.0.0.0:{}", bind_port).parse().unwrap();
|
||||
socket.bind(&addr.into())?;
|
||||
} else {
|
||||
socket.set_only_v6(true)?;
|
||||
let addr: SocketAddr = format!("[::]:{}", bind_port).parse().unwrap();
|
||||
socket.bind(&addr.into())?;
|
||||
}
|
||||
}
|
||||
socket.set_nonblocking(true)?;
|
||||
socket.set_nodelay(true)?;
|
||||
Ok(tokio::net::TcpSocket::from_std_stream(socket.into()))
|
||||
}
|
||||
pub fn bind_udp_ops(
|
||||
addr: SocketAddr,
|
||||
only_v6: bool,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<socket2::Socket> {
|
||||
let socket = if addr.is_ipv4() {
|
||||
let socket = socket2::Socket::new(
|
||||
socket2::Domain::IPV4,
|
||||
socket2::Type::DGRAM,
|
||||
Some(Protocol::UDP),
|
||||
)?;
|
||||
if let Err(e) = socket.set_ip_unicast_if(default_interface) {
|
||||
log::warn!("set_ip_unicast_if {:?}", e)
|
||||
}
|
||||
socket
|
||||
} else {
|
||||
let socket = socket2::Socket::new(
|
||||
socket2::Domain::IPV6,
|
||||
socket2::Type::DGRAM,
|
||||
Some(Protocol::UDP),
|
||||
)?;
|
||||
socket
|
||||
.set_only_v6(only_v6)
|
||||
.with_context(|| format!("set_only_v6 failed: {}", &addr))?;
|
||||
socket
|
||||
};
|
||||
socket.set_nonblocking(true)?;
|
||||
socket.bind(&addr.into())?;
|
||||
Ok(socket)
|
||||
}
|
||||
pub fn bind_udp(
|
||||
addr: SocketAddr,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<socket2::Socket> {
|
||||
bind_udp_ops(addr, true, default_interface).with_context(|| format!("{}", addr))
|
||||
}
|
||||
|
||||
pub fn get_interface(dest_name: String) -> anyhow::Result<(LocalInterface, Ipv4Addr)> {
|
||||
let network_interfaces = NetworkInterface::show()?;
|
||||
for iface in network_interfaces {
|
||||
if iface.name == dest_name {
|
||||
for addr in iface.addr {
|
||||
if let IpAddr::V4(ip) = addr.ip() {
|
||||
return Ok((
|
||||
LocalInterface {
|
||||
index: iface.index,
|
||||
#[cfg(unix)]
|
||||
name: Some(iface.name),
|
||||
},
|
||||
ip,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(anyhow!("No network card with name {} found", dest_name))
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
use crate::channel::socket::{LocalInterface, VntSocketTrait};
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
use anyhow::Context;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
impl VntSocketTrait for socket2::Socket {
|
||||
fn set_ip_unicast_if(&self, interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
if let Some(name) = &interface.name {
|
||||
self.bind_device(Some(name.as_bytes()))
|
||||
.context("bind_device")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "macos")]
|
||||
impl VntSocketTrait for socket2::Socket {
|
||||
fn set_ip_unicast_if(&self, interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
if interface.index != 0 {
|
||||
self.bind_device_by_index_v4(std::num::NonZeroU32::new(interface.index))
|
||||
.with_context(|| format!("bind_device_by_index_v4 {:?}", interface))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "android")]
|
||||
impl VntSocketTrait for socket2::Socket {
|
||||
fn set_ip_unicast_if(&self, _interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
// pub fn get_best_interface(dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
|
||||
// match get_interface(dest_ip) {
|
||||
// Ok(iface) => return Ok(iface),
|
||||
// Err(e) => {
|
||||
// log::warn!("not find interface e={:?},ip={}", e, dest_ip);
|
||||
// }
|
||||
// }
|
||||
// // 应该再查路由表找到默认路由的
|
||||
// Ok(LocalInterface::default())
|
||||
// }
|
||||
// #[cfg(target_os = "android")]
|
||||
// pub fn get_best_interface(_dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
|
||||
// Ok(LocalInterface::default())
|
||||
// }
|
||||
@@ -0,0 +1,56 @@
|
||||
use std::mem;
|
||||
use std::os::windows::io::AsRawSocket;
|
||||
|
||||
use windows_sys::core::PCSTR;
|
||||
use windows_sys::Win32::Networking::WinSock::{
|
||||
htonl, setsockopt, IPPROTO_IP, IP_UNICAST_IF, SOCKET_ERROR,
|
||||
};
|
||||
|
||||
use crate::channel::socket::{LocalInterface, VntSocketTrait};
|
||||
|
||||
impl VntSocketTrait for socket2::Socket {
|
||||
fn set_ip_unicast_if(&self, interface: &LocalInterface) -> anyhow::Result<()> {
|
||||
let index = interface.index;
|
||||
if index == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let raw_socket = self.as_raw_socket();
|
||||
let result = unsafe {
|
||||
let best_interface = htonl(index);
|
||||
setsockopt(
|
||||
raw_socket as usize,
|
||||
IPPROTO_IP,
|
||||
IP_UNICAST_IF,
|
||||
&best_interface as *const _ as PCSTR,
|
||||
mem::size_of_val(&best_interface) as i32,
|
||||
)
|
||||
};
|
||||
if result == SOCKET_ERROR {
|
||||
Err(anyhow::anyhow!(
|
||||
"Failed to set IP_UNICAST_IF: {:?} {}",
|
||||
std::io::Error::last_os_error(),
|
||||
index
|
||||
))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn get_best_interface(dest_ip: Ipv4Addr) -> anyhow::Result<LocalInterface> {
|
||||
// // 获取最佳接口
|
||||
// let index = unsafe {
|
||||
// let mut dest: SOCKADDR_IN = mem::zeroed();
|
||||
// dest.sin_family = AF_INET as u16;
|
||||
// dest.sin_addr.S_un.S_addr = u32::from_ne_bytes(dest_ip.octets());
|
||||
//
|
||||
// let mut index: u32 = 0;
|
||||
// if GetBestInterfaceEx(&dest as *const _ as *mut SOCKADDR, &mut index) != 0 {
|
||||
// Err(anyhow::anyhow!(
|
||||
// "Failed to GetBestInterfaceEx: {:?}",
|
||||
// std::io::Error::last_os_error()
|
||||
// ))?;
|
||||
// }
|
||||
// index
|
||||
// };
|
||||
// Ok(LocalInterface { index })
|
||||
// }
|
||||
@@ -0,0 +1,212 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use std::net::SocketAddr;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::io::AsRawSocket;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
use tokio::net::tcp::OwnedReadHalf;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::sync::mpsc::{channel, Receiver};
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::sender::PacketSender;
|
||||
use crate::channel::socket::create_tcp0;
|
||||
use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE, TCP_MAX_PACKET_SIZE};
|
||||
use crate::util::StopManager;
|
||||
|
||||
/// 监听tcp端口,等待客户端连接
|
||||
pub fn tcp_listen<H>(
|
||||
tcp_server: std::net::TcpListener,
|
||||
receiver: Receiver<(Vec<u8>, Option<u16>, SocketAddr)>,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
stop_manager: StopManager,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let (stop_sender, stop_receiver) = tokio::sync::oneshot::channel::<()>();
|
||||
let worker = stop_manager.add_listener("tcpChannel".into(), move || {
|
||||
let _ = stop_sender.send(());
|
||||
})?;
|
||||
let bind_port = tcp_server.local_addr()?.port();
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(2)
|
||||
.enable_all()
|
||||
.build()
|
||||
.context("tcp tokio runtime build failed")?;
|
||||
thread::Builder::new()
|
||||
.name("tcpChannel".into())
|
||||
.spawn(move || {
|
||||
runtime.spawn(async move {
|
||||
{
|
||||
let recv_handler = recv_handler.clone();
|
||||
let context = context.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = tcp_accept(tcp_server, recv_handler, context).await {
|
||||
log::warn!("tcp_listen {:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
tokio::spawn(async move {
|
||||
connect_tcp_handle(receiver, recv_handler, context, bind_port).await
|
||||
});
|
||||
});
|
||||
runtime.block_on(async {
|
||||
let _ = stop_receiver.await;
|
||||
});
|
||||
runtime.shutdown_background();
|
||||
worker.stop_all();
|
||||
})
|
||||
.context("tcp thread build failed")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn connect_tcp_handle<H>(
|
||||
mut receiver: Receiver<(Vec<u8>, Option<u16>, SocketAddr)>,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
listener_bind_port: u16,
|
||||
) where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
while let Some((data, bind_port, addr)) = receiver.recv().await {
|
||||
let recv_handler = recv_handler.clone();
|
||||
let context = context.clone();
|
||||
let bind_port = if let Some(bind_port) = bind_port {
|
||||
bind_port
|
||||
} else {
|
||||
listener_bind_port
|
||||
};
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = connect_tcp0(data, addr, recv_handler, context, bind_port).await {
|
||||
log::warn!("连接失败,链接终止:{:?},{:?}", addr, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn connect_tcp0<H>(
|
||||
data: Vec<u8>,
|
||||
addr: SocketAddr,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
bind_port: u16,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let socket = if bind_port != 0 {
|
||||
match create_tcp0(addr.is_ipv4(), bind_port, context.default_interface()) {
|
||||
Ok(socket) => socket,
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
create_tcp0(addr.is_ipv4(), 0, context.default_interface())?
|
||||
}
|
||||
}
|
||||
} else {
|
||||
create_tcp0(addr.is_ipv4(), 0, context.default_interface())?
|
||||
};
|
||||
let mut stream = tokio::time::timeout(Duration::from_secs(3), socket.connect(addr)).await??;
|
||||
tcp_write(&mut stream, &data).await?;
|
||||
|
||||
tcp_stream_handle(stream, addr, recv_handler, context).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn tcp_accept<H>(
|
||||
tcp_server: std::net::TcpListener,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let tcp_server = TcpListener::from_std(tcp_server)?;
|
||||
|
||||
loop {
|
||||
let (stream, addr) = tcp_server.accept().await?;
|
||||
|
||||
tcp_stream_handle(stream, addr, recv_handler.clone(), context.clone()).await;
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn tcp_stream_handle<H>(
|
||||
stream: TcpStream,
|
||||
addr: SocketAddr,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
) where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let _ = stream.set_nodelay(true);
|
||||
let local = stream.local_addr();
|
||||
#[cfg(windows)]
|
||||
let index = stream.as_raw_socket() as usize;
|
||||
#[cfg(unix)]
|
||||
let index = stream.as_raw_fd() as usize;
|
||||
let route_key = RouteKey::new(ConnectProtocol::TCP, index, addr);
|
||||
let (r, mut w) = stream.into_split();
|
||||
let (sender, mut receiver) = channel::<Vec<u8>>(100);
|
||||
context
|
||||
.packet_map
|
||||
.write()
|
||||
.insert(route_key, PacketSender::new(sender));
|
||||
tokio::spawn(async move {
|
||||
while let Some(data) = receiver.recv().await {
|
||||
if let Err(e) = tcp_write(&mut w, &data).await {
|
||||
log::info!("发送失败,tcp链接终止:{:?},{:?}", addr, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let _ = w.shutdown().await;
|
||||
});
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = tcp_read(r, addr, &context, recv_handler, route_key).await {
|
||||
log::warn!("tcp_read {:?} {local:?}-{addr}", e)
|
||||
}
|
||||
context.packet_map.write().remove(&route_key);
|
||||
});
|
||||
}
|
||||
|
||||
async fn tcp_write<W: AsyncWrite + Unpin>(w: &mut W, buf: &[u8]) -> anyhow::Result<()> {
|
||||
let len = buf.len();
|
||||
if len > TCP_MAX_PACKET_SIZE {
|
||||
return Err(anyhow!("超过了tcp的最大长度传输"));
|
||||
}
|
||||
w.write_all(&[0, (len >> 16) as u8, (len >> 8) as u8, len as u8])
|
||||
.await?;
|
||||
w.write_all(&buf).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn tcp_read<H>(
|
||||
mut read: OwnedReadHalf,
|
||||
addr: SocketAddr,
|
||||
context: &ChannelContext,
|
||||
recv_handler: H,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut head = [0; 4];
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
loop {
|
||||
read.read_exact(&mut head).await?;
|
||||
if head[0] != 0 {
|
||||
return Err(anyhow!("tcp数据流错误 {}", addr));
|
||||
}
|
||||
let len = ((head[1] as usize) << 16) | ((head[2] as usize) << 8) | head[3] as usize;
|
||||
if len < 12 || len > buf.len() {
|
||||
return Err(anyhow!("tcp数据长度无效 {}", addr));
|
||||
}
|
||||
read.read_exact(&mut buf[..len]).await?;
|
||||
recv_handler.handle(&mut buf[..len], &mut extend, route_key, context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
use std::sync::mpsc::{sync_channel, Receiver};
|
||||
use std::{io, thread};
|
||||
|
||||
use mio::event::Source;
|
||||
use mio::net::UdpSocket;
|
||||
use mio::{Events, Interest, Poll, Token, Waker};
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::notify::AcceptNotify;
|
||||
use crate::channel::sender::AcceptSocketSender;
|
||||
use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE};
|
||||
use crate::util::StopManager;
|
||||
|
||||
pub fn udp_listen<H>(
|
||||
stop_manager: StopManager,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
) -> anyhow::Result<AcceptSocketSender<Option<Vec<UdpSocket>>>>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
main_udp_listen(stop_manager.clone(), recv_handler.clone(), context.clone())?;
|
||||
sub_udp_listen(stop_manager, recv_handler, context)
|
||||
}
|
||||
|
||||
const NOTIFY: Token = Token(0);
|
||||
|
||||
fn sub_udp_listen<H>(
|
||||
stop_manager: StopManager,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
) -> anyhow::Result<AcceptSocketSender<Option<Vec<UdpSocket>>>>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let (udp_sender, udp_receiver) = sync_channel(64);
|
||||
let poll = Poll::new()?;
|
||||
let waker = AcceptNotify::new(Waker::new(poll.registry(), NOTIFY)?);
|
||||
let worker = {
|
||||
let waker = waker.clone();
|
||||
stop_manager.add_listener("sub_udp_listen".into(), move || {
|
||||
if let Err(e) = waker.stop() {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
})?
|
||||
};
|
||||
let accept = AcceptSocketSender::new(waker.clone(), udp_sender);
|
||||
thread::Builder::new()
|
||||
.name("subUdp".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = sub_udp_listen0(poll, recv_handler, context, waker, udp_receiver) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
worker.stop_all();
|
||||
})?;
|
||||
Ok(accept)
|
||||
}
|
||||
|
||||
fn sub_udp_listen0<H>(
|
||||
mut poll: Poll,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
accept_notify: AcceptNotify,
|
||||
accept_receiver: Receiver<Option<Vec<UdpSocket>>>,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut events = Events::with_capacity(1024);
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
let mut list: Vec<UdpSocket> = Vec::with_capacity(100);
|
||||
let main_len = context.main_len();
|
||||
loop {
|
||||
if let Err(e) = poll.poll(&mut events, None) {
|
||||
crate::ignore_io_interrupted(e)?;
|
||||
continue;
|
||||
}
|
||||
for event in events.iter() {
|
||||
match event.token() {
|
||||
NOTIFY => {
|
||||
if accept_notify.is_stop() {
|
||||
return Ok(());
|
||||
}
|
||||
if accept_notify.is_add_socket() {
|
||||
while let Ok(option) = accept_receiver.try_recv() {
|
||||
match option {
|
||||
None => {
|
||||
log::info!("切换成锥形模式");
|
||||
for mut udp_socket in list.drain(..) {
|
||||
if let Err(e) = udp_socket.deregister(poll.registry()) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(socket_list) => {
|
||||
for mut udp_socket in list.drain(..) {
|
||||
if let Err(e) = udp_socket.deregister(poll.registry()) {
|
||||
log::error!("deregister {:?}", e);
|
||||
}
|
||||
}
|
||||
log::info!("切换成对称模式 监听端口数:{}", socket_list.len());
|
||||
for (index, mut udp_socket) in
|
||||
socket_list.into_iter().enumerate()
|
||||
{
|
||||
poll.registry().register(
|
||||
&mut udp_socket,
|
||||
Token(index),
|
||||
Interest::READABLE,
|
||||
)?;
|
||||
list.push(udp_socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Token(index) => {
|
||||
if let Some(udp_socket) = list.get(index) {
|
||||
loop {
|
||||
match udp_socket.recv_from(&mut buf) {
|
||||
Ok((len, addr)) => {
|
||||
recv_handler.handle(
|
||||
&mut buf[..len],
|
||||
&mut extend,
|
||||
RouteKey::new(ConnectProtocol::UDP, index + main_len, addr),
|
||||
&context,
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// /// 阻塞
|
||||
// fn main_udp_listen<H>(
|
||||
// stop_manager: StopManager,
|
||||
// recv_handler: H,
|
||||
// context: Context,
|
||||
// ) -> io::Result<()>
|
||||
// where
|
||||
// H: RecvChannelHandler,
|
||||
// {
|
||||
// for index in 0..context.main_udp_socket.len() {
|
||||
// let stop_manager = stop_manager.clone();
|
||||
// let context = context.clone();
|
||||
// let recv_handler = recv_handler.clone();
|
||||
// thread::Builder::new()
|
||||
// .name(format!("mainUdp{}", index))
|
||||
// .spawn(move || {
|
||||
// if let Err(e) = main_udp_listen0(stop_manager, index, recv_handler, context) {
|
||||
// log::error!("{:?}", e);
|
||||
// }
|
||||
// })?;
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
//
|
||||
// pub fn main_udp_listen0<H>(
|
||||
// stop_manager: StopManager,
|
||||
// index: usize,
|
||||
// mut recv_handler: H,
|
||||
// context: Context,
|
||||
// ) -> io::Result<()>
|
||||
// where
|
||||
// H: RecvChannelHandler,
|
||||
// {
|
||||
// use std::time::Duration;
|
||||
// let udp_socket = &context.main_udp_socket[index];
|
||||
// udp_socket.set_read_timeout(Some(Duration::from_secs(5)))?;
|
||||
// udp_socket.set_write_timeout(Some(Duration::from_secs(1)))?;
|
||||
// let local_addr = udp_socket.local_addr()?;
|
||||
// let worker = stop_manager.add_listener(format!("main_udp_{}", index), move || {
|
||||
// if let Ok(udp) = std::net::UdpSocket::bind("0.0.0.0:0") {
|
||||
// let _ = udp.send_to(b"stop", format!("127.0.0.1:{}", local_addr.port()));
|
||||
// }
|
||||
// })?;
|
||||
//
|
||||
// let mut buf = [0; BUFFER_SIZE];
|
||||
// loop {
|
||||
// match udp_socket.recv_from(&mut buf) {
|
||||
// Ok((len, addr)) => {
|
||||
// if &buf[..len] == b"stop" {
|
||||
// if stop_manager.is_stop() {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// recv_handler.handle(&mut buf[..len], RouteKey::new(false, index, addr), &context);
|
||||
// }
|
||||
// Err(e) => {
|
||||
// if stop_manager.is_stop() {
|
||||
// break;
|
||||
// }
|
||||
// log::error!("index={},{:?},{}", index, udp_socket.local_addr(), e)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// worker.stop_all();
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
/// 非阻塞
|
||||
fn main_udp_listen<H>(
|
||||
stop_manager: StopManager,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
use std::sync::Arc;
|
||||
let poll = Poll::new()?;
|
||||
let waker = Arc::new(Waker::new(poll.registry(), NOTIFY)?);
|
||||
let _waker = waker.clone();
|
||||
let worker = stop_manager.add_listener("main_udp".into(), move || {
|
||||
if let Err(e) = waker.wake() {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
})?;
|
||||
thread::Builder::new()
|
||||
.name("mainUdp".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = main_udp_listen0(poll, recv_handler, context) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
drop(_waker);
|
||||
worker.stop_all();
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn main_udp_listen0<H>(
|
||||
mut poll: Poll,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut udps = Vec::with_capacity(context.main_udp_socket.len());
|
||||
|
||||
for (index, udp) in context.main_udp_socket.iter().enumerate() {
|
||||
let udp_socket = udp.try_clone()?;
|
||||
udp_socket.set_nonblocking(true)?;
|
||||
let mut mio_udp = UdpSocket::from_std(udp_socket);
|
||||
poll.registry()
|
||||
.register(&mut mio_udp, Token(index + 1), Interest::READABLE)?;
|
||||
udps.push(mio_udp);
|
||||
}
|
||||
|
||||
let mut events = Events::with_capacity(udps.len());
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
loop {
|
||||
if let Err(e) = poll.poll(&mut events, None) {
|
||||
crate::ignore_io_interrupted(e)?;
|
||||
continue;
|
||||
}
|
||||
for x in events.iter() {
|
||||
let index = match x.token() {
|
||||
NOTIFY => return Ok(()),
|
||||
// 0的位置留给NOTIFY了,这里要再减回去,因为路由是通过index来找到对应udp的
|
||||
Token(index) => index - 1,
|
||||
};
|
||||
let udp = if let Some(udp) = udps.get(index) {
|
||||
udp
|
||||
} else {
|
||||
log::error!("{:?}", x);
|
||||
continue;
|
||||
};
|
||||
loop {
|
||||
match udp.recv_from(&mut buf) {
|
||||
Ok((len, addr)) => {
|
||||
recv_handler.handle(
|
||||
&mut buf[..len],
|
||||
&mut extend,
|
||||
RouteKey::new(ConnectProtocol::UDP, index, addr),
|
||||
&context,
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::WouldBlock {
|
||||
break;
|
||||
}
|
||||
log::error!("main_udp_listen_{}={:?}", index, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// /// 用recvmmsg没什么帮助,这里记录下,以下是完整代码
|
||||
// #[cfg(unix)]
|
||||
// pub fn main_udp_listen0<H>(index: usize, mut recv_handler: H, context: Context) -> io::Result<()>
|
||||
// where
|
||||
// H: RecvChannelHandler,
|
||||
// {
|
||||
// use libc::{c_uint, mmsghdr, sockaddr_storage, socklen_t, timespec};
|
||||
// use std::os::fd::AsRawFd;
|
||||
//
|
||||
// let udp_socket = context.main_udp_socket[index].try_clone()?;
|
||||
// let fd = udp_socket.as_raw_fd();
|
||||
// const MAX_MESSAGES: usize = 16;
|
||||
// let mut iov: [libc::iovec; MAX_MESSAGES] = unsafe { std::mem::zeroed() };
|
||||
// let mut buf: [[u8; BUFFER_SIZE]; MAX_MESSAGES] = [[0; BUFFER_SIZE]; MAX_MESSAGES];
|
||||
// let mut msgs: [mmsghdr; MAX_MESSAGES] = unsafe { std::mem::zeroed() };
|
||||
// let mut addrs: [sockaddr_storage; MAX_MESSAGES] = unsafe { std::mem::zeroed() };
|
||||
// for i in 0..MAX_MESSAGES {
|
||||
// iov[i].iov_base = buf[i].as_mut_ptr() as *mut libc::c_void;
|
||||
// iov[i].iov_len = BUFFER_SIZE;
|
||||
// msgs[i].msg_hdr.msg_iov = &mut iov[i];
|
||||
// msgs[i].msg_hdr.msg_iovlen = 1;
|
||||
// msgs[i].msg_hdr.msg_name = &mut addrs[i] as *const _ as *mut libc::c_void;
|
||||
// msgs[i].msg_hdr.msg_namelen = std::mem::size_of::<sockaddr_storage>() as socklen_t;
|
||||
// }
|
||||
// let mut time: timespec = unsafe { std::mem::zeroed() };
|
||||
// loop {
|
||||
// if context.is_stop() {
|
||||
// return Ok(());
|
||||
// }
|
||||
// let res =
|
||||
// unsafe { libc::recvmmsg(fd, msgs.as_mut_ptr(), MAX_MESSAGES as c_uint, 0, &mut time) };
|
||||
// if res == -1 {
|
||||
// log::error!("main_udp_listen_{}={:?}", index, io::Error::last_os_error());
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// let nmsgs = res as usize;
|
||||
// for i in 0..nmsgs {
|
||||
// let msg = &mut buf[i][0..msgs[i].msg_len as usize];
|
||||
// let addr = sockaddr_to_socket_addr(&addrs[i], msgs[i].msg_hdr.msg_namelen);
|
||||
// if msg == b"stop" {
|
||||
// if context.is_stop() {
|
||||
// return Ok(());
|
||||
// }
|
||||
// }
|
||||
// recv_handler.handle(msg, RouteKey::new(false, index, addr), &context);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// #[cfg(unix)]
|
||||
// fn sockaddr_to_socket_addr(addr: &libc::sockaddr_storage, _len: libc::socklen_t) -> SocketAddr {
|
||||
// match addr.ss_family as libc::c_int {
|
||||
// libc::AF_INET => {
|
||||
// let addr_in = unsafe { *(addr as *const _ as *const libc::sockaddr_in) };
|
||||
// let ip = u32::from_be(addr_in.sin_addr.s_addr);
|
||||
// let port = u16::from_be(addr_in.sin_port);
|
||||
// SocketAddr::V4(std::net::SocketAddrV4::new(Ipv4Addr::from(ip), port))
|
||||
// }
|
||||
// libc::AF_INET6 => {
|
||||
// let addr_in6 = unsafe { *(addr as *const _ as *const libc::sockaddr_in6) };
|
||||
// let ip = std::net::Ipv6Addr::from(addr_in6.sin6_addr.s6_addr);
|
||||
// let port = u16::from_be(addr_in6.sin6_port);
|
||||
// SocketAddr::V6(std::net::SocketAddrV6::new(ip, port, 0, 0))
|
||||
// }
|
||||
// _ => panic!("Unsupported address family"),
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,173 @@
|
||||
use crate::channel::{ConnectProtocol, RouteKey, BUFFER_SIZE};
|
||||
use anyhow::Context;
|
||||
use futures_util::stream::SplitStream;
|
||||
use futures_util::{SinkExt, StreamExt};
|
||||
use std::convert::Into;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::mpsc::{channel, Receiver};
|
||||
use tokio_tungstenite::tungstenite::http::StatusCode;
|
||||
use tokio_tungstenite::tungstenite::{Error, Message};
|
||||
use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::sender::PacketSender;
|
||||
use crate::util::StopManager;
|
||||
|
||||
/// ws协议,
|
||||
/// 暂时只允许用ws连服务端,不能用ws打洞/连客户端
|
||||
pub fn ws_connect_accept<H>(
|
||||
receiver: Receiver<(Vec<u8>, String)>,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
stop_manager: StopManager,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let (stop_sender, stop_receiver) = tokio::sync::oneshot::channel::<()>();
|
||||
let worker = stop_manager.add_listener("wsChannel".into(), move || {
|
||||
let _ = stop_sender.send(());
|
||||
})?;
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(2)
|
||||
.enable_all()
|
||||
.build()
|
||||
.context("ws tokio runtime build failed")?;
|
||||
thread::Builder::new()
|
||||
.name("wsChannel".into())
|
||||
.spawn(move || {
|
||||
runtime.spawn(async move { connect_ws_handle(receiver, recv_handler, context).await });
|
||||
runtime.block_on(async {
|
||||
let _ = stop_receiver.await;
|
||||
});
|
||||
runtime.shutdown_background();
|
||||
worker.stop_all();
|
||||
})
|
||||
.context("ws thread build failed")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn connect_ws_handle<H>(
|
||||
mut receiver: Receiver<(Vec<u8>, String)>,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
) where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut index = 0;
|
||||
while let Some((data, url)) = receiver.recv().await {
|
||||
let recv_handler = recv_handler.clone();
|
||||
let context = context.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = connect_ws(data, url, recv_handler, context, index).await {
|
||||
log::warn!("发送失败,ws链接终止:{:?}", e);
|
||||
}
|
||||
});
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
const WS_ADDR: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0));
|
||||
|
||||
async fn connect_ws<H>(
|
||||
data: Vec<u8>,
|
||||
mut url: String,
|
||||
recv_handler: H,
|
||||
context: ChannelContext,
|
||||
index: usize,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut count = 0;
|
||||
log::info!("尝试建立连接 {:?}", url);
|
||||
let (mut ws, response) = loop {
|
||||
count += 1;
|
||||
if count > 3 {
|
||||
Err(anyhow::anyhow!("发生多次重定向,链接终止"))?
|
||||
}
|
||||
match tokio::time::timeout(Duration::from_secs(3), connect_async(url)).await? {
|
||||
Ok(rs) => break rs,
|
||||
Err(e) => {
|
||||
if let Error::Http(res) = &e {
|
||||
if res.status() == StatusCode::MOVED_PERMANENTLY
|
||||
|| res.status() == StatusCode::FOUND
|
||||
|| res.status() == StatusCode::SEE_OTHER
|
||||
|| res.status() == StatusCode::TEMPORARY_REDIRECT
|
||||
|| res.status() == StatusCode::PERMANENT_REDIRECT
|
||||
{
|
||||
if let Some(v) = res.headers().get("Location") {
|
||||
if let Ok(redirect) = v.to_str() {
|
||||
log::info!("url重定向响应头 {:?}", res.headers());
|
||||
log::info!("url重定向地址 {}", redirect);
|
||||
// 替换协议前缀
|
||||
if redirect.starts_with("http://") {
|
||||
url = redirect.replacen("http://", "ws://", 1);
|
||||
} else if redirect.starts_with("https://") {
|
||||
url = redirect.replacen("https://", "wss://", 1);
|
||||
} else {
|
||||
url = redirect.to_string();
|
||||
}
|
||||
println!("Location:{}", url);
|
||||
log::info!("修改后的重定向地址: {}", url);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Err(e)?;
|
||||
}
|
||||
}
|
||||
};
|
||||
log::info!("ws协议握手 {:?}", response);
|
||||
ws.send(Message::Binary(data)).await?;
|
||||
let (mut ws_write, ws_read) = ws.split();
|
||||
let (sender, mut receiver) = channel::<Vec<u8>>(100);
|
||||
let route_key = RouteKey::new(ConnectProtocol::WS, index, WS_ADDR);
|
||||
|
||||
context
|
||||
.packet_map
|
||||
.write()
|
||||
.insert(route_key, PacketSender::new(sender));
|
||||
tokio::spawn(async move {
|
||||
while let Some(data) = receiver.recv().await {
|
||||
if let Err(e) = ws_write.send(Message::Binary(data)).await {
|
||||
log::warn!("websocket err {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let _ = ws_write.close().await;
|
||||
});
|
||||
if let Err(e) = ws_read_handle(ws_read, recv_handler, &context, route_key).await {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
context.packet_map.write().remove(&route_key);
|
||||
Ok(())
|
||||
}
|
||||
async fn ws_read_handle<H>(
|
||||
mut ws_read: SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>,
|
||||
recv_handler: H,
|
||||
context: &ChannelContext,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()>
|
||||
where
|
||||
H: RecvChannelHandler,
|
||||
{
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
while let Some(msg) = ws_read.next().await {
|
||||
let msg = msg.context("Error during WebSocket ")?;
|
||||
match msg {
|
||||
Message::Text(txt) => log::info!("Received text message: {}", txt),
|
||||
Message::Binary(mut data) => {
|
||||
recv_handler.handle(&mut data, &mut extend, route_key, context);
|
||||
}
|
||||
Message::Ping(_) | Message::Pong(_) => (),
|
||||
Message::Close(_) => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod rs_aes_cbc;
|
||||
pub use rs_aes_cbc::*;
|
||||
@@ -0,0 +1,153 @@
|
||||
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit};
|
||||
use anyhow::anyhow;
|
||||
use rand::RngCore;
|
||||
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::body::AesCbcSecretBody;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
|
||||
type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>;
|
||||
type Aes128CbcDec = cbc::Decryptor<aes::Aes128>;
|
||||
type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
|
||||
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AesCbcCipher {
|
||||
pub(crate) cipher: AesCbcEnum,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AesCbcEnum {
|
||||
AES128CBC([u8; 16]),
|
||||
AES256CBC([u8; 32]),
|
||||
}
|
||||
|
||||
impl AesCbcCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
match &self.cipher {
|
||||
AesCbcEnum::AES128CBC(key) => key,
|
||||
AesCbcEnum::AES256CBC(key) => key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AesCbcCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
Self {
|
||||
cipher: AesCbcEnum::AES128CBC(key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
Self {
|
||||
cipher: AesCbcEnum::AES256CBC(key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(anyhow!("aes_cbc data err"));
|
||||
}
|
||||
let mut iv = [0; 16];
|
||||
iv[0..12].copy_from_slice(&net_packet.head_tag());
|
||||
if let Some(finger) = &self.finger {
|
||||
iv[12..16].copy_from_slice(&finger.hash[0..4]);
|
||||
}
|
||||
|
||||
let mut secret_body =
|
||||
AesCbcSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&iv[..12], secret_body.en_body());
|
||||
if &finger != secret_body.finger() {
|
||||
return Err(anyhow!("aes_cbc finger err"));
|
||||
}
|
||||
}
|
||||
let rs = match &self.cipher {
|
||||
AesCbcEnum::AES128CBC(key) => Aes128CbcDec::new(&(*key).into(), &iv.into())
|
||||
.decrypt_padded_mut::<Pkcs7>(secret_body.en_body_mut()),
|
||||
AesCbcEnum::AES256CBC(key) => Aes256CbcDec::new(&(*key).into(), &iv.into())
|
||||
.decrypt_padded_mut::<Pkcs7>(secret_body.en_body_mut()),
|
||||
};
|
||||
match rs {
|
||||
Ok(buf) => {
|
||||
let len = buf.len();
|
||||
net_packet.set_encrypt_flag(false);
|
||||
//减去末尾的随机数
|
||||
net_packet.set_data_len(HEAD_LEN + len - 4)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("aes_cbc 解密失败:{}", e)),
|
||||
}
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
let data_len = net_packet.data_len();
|
||||
let mut iv = [0; 16];
|
||||
iv[0..12].copy_from_slice(&net_packet.head_tag());
|
||||
if let Some(finger) = &self.finger {
|
||||
iv[12..16].copy_from_slice(&finger.hash[0..4]);
|
||||
net_packet.set_data_len(data_len + 16)?;
|
||||
} else {
|
||||
net_packet.set_data_len(data_len + 4)?;
|
||||
}
|
||||
//先扩充随机数
|
||||
let mut secret_body =
|
||||
AesCbcSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
secret_body.set_random(rand::thread_rng().next_u32());
|
||||
let p_len = secret_body.en_body().len();
|
||||
net_packet.set_data_len_max();
|
||||
let rs = match &self.cipher {
|
||||
AesCbcEnum::AES128CBC(key) => Aes128CbcEnc::new(&(*key).into(), &iv.into())
|
||||
.encrypt_padded_mut::<Pkcs7>(net_packet.payload_mut(), p_len),
|
||||
AesCbcEnum::AES256CBC(key) => Aes256CbcEnc::new(&(*key).into(), &iv.into())
|
||||
.encrypt_padded_mut::<Pkcs7>(net_packet.payload_mut(), p_len),
|
||||
};
|
||||
return match rs {
|
||||
Ok(buf) => {
|
||||
let len = buf.len();
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&iv[..12], buf);
|
||||
//设置实际长度
|
||||
net_packet.set_data_len(HEAD_LEN + len + finger.len())?;
|
||||
let mut secret_body = AesCbcSecretBody::new(net_packet.payload_mut(), true)?;
|
||||
secret_body.set_finger(&finger)?;
|
||||
} else {
|
||||
net_packet.set_data_len(HEAD_LEN + len)?;
|
||||
}
|
||||
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("aes_cbc 加密失败:{}", e)),
|
||||
};
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_aes_cbc() {
|
||||
let d = AesCbcCipher::new_128([0; 16], Some(Finger::new("123")));
|
||||
let mut p = NetPacket::new_encrypt([0; 100]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
let d = AesCbcCipher::new_128([0; 16], None);
|
||||
let mut p = NetPacket::new_encrypt([0; 100]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#[cfg(not(any(feature = "openssl-vendored", feature = "openssl")))]
|
||||
mod rs_aes_ecb;
|
||||
#[cfg(not(any(feature = "openssl-vendored", feature = "openssl")))]
|
||||
pub use rs_aes_ecb::*;
|
||||
|
||||
#[cfg(any(feature = "openssl-vendored", feature = "openssl"))]
|
||||
mod openssl_aes_ecb;
|
||||
#[cfg(any(feature = "openssl-vendored", feature = "openssl"))]
|
||||
pub use openssl_aes_ecb::*;
|
||||
@@ -0,0 +1,219 @@
|
||||
use std::ptr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use libc::c_int;
|
||||
use openssl_sys::EVP_CIPHER_CTX;
|
||||
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
|
||||
pub struct AesEcbCipher {
|
||||
key: Vec<u8>,
|
||||
pub(crate) en_ctx: *mut EVP_CIPHER_CTX,
|
||||
pub(crate) de_ctx: *mut EVP_CIPHER_CTX,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
impl Drop for AesEcbCipher {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
openssl_sys::EVP_CIPHER_CTX_free(self.de_ctx);
|
||||
openssl_sys::EVP_CIPHER_CTX_free(self.en_ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for AesEcbCipher {
|
||||
fn clone(&self) -> Self {
|
||||
if self.key.len() == 16 {
|
||||
AesEcbCipher::new_128(self.key.clone().try_into().unwrap(), self.finger.clone())
|
||||
} else {
|
||||
AesEcbCipher::new_256(self.key.clone().try_into().unwrap(), self.finger.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for AesEcbCipher {}
|
||||
|
||||
unsafe impl Send for AesEcbCipher {}
|
||||
|
||||
impl AesEcbCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl AesEcbCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
unsafe {
|
||||
let cipher = openssl_sys::EVP_aes_128_ecb();
|
||||
let en_ctx = openssl_sys::EVP_CIPHER_CTX_new();
|
||||
openssl_sys::EVP_EncryptInit_ex(
|
||||
en_ctx,
|
||||
cipher,
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
ptr::null(),
|
||||
);
|
||||
|
||||
let de_ctx = openssl_sys::EVP_CIPHER_CTX_new();
|
||||
openssl_sys::EVP_DecryptInit_ex(
|
||||
de_ctx,
|
||||
cipher,
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
ptr::null(),
|
||||
);
|
||||
Self {
|
||||
key: key.to_vec(),
|
||||
en_ctx,
|
||||
de_ctx,
|
||||
finger,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
unsafe {
|
||||
let cipher = openssl_sys::EVP_aes_256_ecb();
|
||||
let en_ctx = openssl_sys::EVP_CIPHER_CTX_new();
|
||||
openssl_sys::EVP_EncryptInit_ex(
|
||||
en_ctx,
|
||||
cipher,
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
ptr::null(),
|
||||
);
|
||||
let de_ctx = openssl_sys::EVP_CIPHER_CTX_new();
|
||||
openssl_sys::EVP_DecryptInit_ex(
|
||||
de_ctx,
|
||||
cipher,
|
||||
ptr::null_mut(),
|
||||
key.as_ptr(),
|
||||
ptr::null(),
|
||||
);
|
||||
Self {
|
||||
key: key.to_vec(),
|
||||
en_ctx,
|
||||
de_ctx,
|
||||
finger,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
|
||||
if let Some(finger) = &self.finger {
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let len = net_packet.payload().len();
|
||||
if len < 12 {
|
||||
return Err(anyhow!("data len err"));
|
||||
}
|
||||
let secret_body = &net_packet.payload()[..len - 12];
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body);
|
||||
if &finger != &net_packet.payload()[len - 12..] {
|
||||
return Err(anyhow!("finger err"));
|
||||
}
|
||||
net_packet.set_data_len(net_packet.data_len() - finger.len())?;
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
let input = net_packet.payload();
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let mut out_len = 0;
|
||||
let ctx = self.de_ctx;
|
||||
unsafe {
|
||||
let out_ptr = out.as_mut_ptr();
|
||||
let in_len = input.len() as c_int;
|
||||
openssl_sys::EVP_DecryptUpdate(ctx, out_ptr, &mut out_len, input.as_ptr(), in_len);
|
||||
let mut last_len = 0;
|
||||
openssl_sys::EVP_DecryptFinal_ex(ctx, out_ptr.offset(out_len as isize), &mut last_len);
|
||||
out_len += last_len;
|
||||
}
|
||||
let out_len = out_len as usize;
|
||||
let text = &out[..out_len];
|
||||
{
|
||||
//校验头部
|
||||
let src_net_packet = NetPacket::new(text)?;
|
||||
if src_net_packet.source() != net_packet.source() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.destination() != net_packet.destination() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.protocol() != net_packet.protocol() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.transport_protocol() != net_packet.transport_protocol() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.is_gateway() != net_packet.is_gateway() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.source_ttl() != net_packet.source_ttl() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
}
|
||||
net_packet.set_encrypt_flag(false);
|
||||
net_packet.set_data_len(out_len)?;
|
||||
net_packet.set_payload(&text[12..])?;
|
||||
Ok(())
|
||||
}
|
||||
/// net_packet 必须预留足够长度 大于 12+16+16
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
let input = net_packet.buffer();
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let mut out_len = 0;
|
||||
let ctx = self.en_ctx;
|
||||
//将头部也参与加密
|
||||
unsafe {
|
||||
let out_ptr = out.as_mut_ptr();
|
||||
let in_len = input.len() as c_int;
|
||||
openssl_sys::EVP_EncryptUpdate(ctx, out_ptr, &mut out_len, input.as_ptr(), in_len);
|
||||
let mut last_len = 0;
|
||||
openssl_sys::EVP_EncryptFinal_ex(ctx, out_ptr.offset(out_len as isize), &mut last_len);
|
||||
out_len += last_len;
|
||||
}
|
||||
let out_len = out_len as usize;
|
||||
if out_len == 0 {
|
||||
return Err(anyhow!("ciphertext len err"));
|
||||
}
|
||||
//密文
|
||||
let ciphertext = &out[..out_len];
|
||||
net_packet.set_data_len(HEAD_LEN + out_len)?;
|
||||
net_packet.payload_mut().copy_from_slice(ciphertext);
|
||||
net_packet.set_encrypt_flag(true);
|
||||
if let Some(finger) = &self.finger {
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let finger = finger.calculate_finger(&nonce_raw, ciphertext);
|
||||
let src_data_len = net_packet.data_len();
|
||||
//设置实际长度
|
||||
net_packet.set_data_len(src_data_len + finger.len())?;
|
||||
|
||||
net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_openssl_aes_ecb() {
|
||||
let d = AesEcbCipher::new_128([0; 16], Some(Finger::new("123")));
|
||||
let mut p = NetPacket::new_encrypt([0; 100]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyInit};
|
||||
use anyhow::anyhow;
|
||||
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
|
||||
type Aes128EcbEnc = ecb::Encryptor<aes::Aes128>;
|
||||
type Aes128EcbDec = ecb::Decryptor<aes::Aes128>;
|
||||
type Aes256EcbEnc = ecb::Encryptor<aes::Aes256>;
|
||||
type Aes256EcbDec = ecb::Decryptor<aes::Aes256>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AesEcbCipher {
|
||||
key: AesEcbEnum,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AesEcbEnum {
|
||||
AES128ECB([u8; 16]),
|
||||
AES256ECB([u8; 32]),
|
||||
}
|
||||
|
||||
impl AesEcbCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
match &self.key {
|
||||
AesEcbEnum::AES128ECB(key) => key,
|
||||
AesEcbEnum::AES256ECB(key) => key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AesEcbCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
Self {
|
||||
key: AesEcbEnum::AES128ECB(key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
Self {
|
||||
key: AesEcbEnum::AES256ECB(key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
|
||||
if let Some(finger) = &self.finger {
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let len = net_packet.payload().len();
|
||||
if len < 12 {
|
||||
return Err(anyhow!("payload len <12"));
|
||||
}
|
||||
let secret_body = &net_packet.payload()[..len - 12];
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body);
|
||||
if &finger != &net_packet.payload()[len - 12..] {
|
||||
return Err(anyhow!("finger err"));
|
||||
}
|
||||
net_packet.set_data_len(net_packet.data_len() - finger.len())?;
|
||||
}
|
||||
if net_packet.payload().len() < 16 {
|
||||
log::error!("数据异常,长度{}小于{}", net_packet.payload().len(), 16);
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let rs = match self.key {
|
||||
AesEcbEnum::AES128ECB(key) => Aes128EcbDec::new(&key.into())
|
||||
.decrypt_padded_b2b_mut::<Pkcs7>(net_packet.payload(), &mut out),
|
||||
AesEcbEnum::AES256ECB(key) => Aes256EcbDec::new(&key.into())
|
||||
.decrypt_padded_b2b_mut::<Pkcs7>(net_packet.payload(), &mut out),
|
||||
};
|
||||
match rs {
|
||||
Ok(buf) => {
|
||||
//校验头部
|
||||
let src_net_packet = NetPacket::new(buf)?;
|
||||
if src_net_packet.source() != net_packet.source() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.destination() != net_packet.destination() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.protocol() != net_packet.protocol() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.transport_protocol() != net_packet.transport_protocol() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.is_gateway() != net_packet.is_gateway() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.source_ttl() != net_packet.source_ttl() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
net_packet.set_data_len(buf.len())?;
|
||||
net_packet.set_payload(src_net_packet.payload())?;
|
||||
net_packet.set_encrypt_flag(false);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("aes_ecb解密失败:{}", e)),
|
||||
}
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut out = [0u8; 1024 * 5];
|
||||
let rs = match self.key {
|
||||
AesEcbEnum::AES128ECB(key) => Aes128EcbEnc::new(&key.into())
|
||||
.encrypt_padded_b2b_mut::<Pkcs7>(net_packet.buffer(), &mut out),
|
||||
AesEcbEnum::AES256ECB(key) => Aes256EcbEnc::new(&key.into())
|
||||
.encrypt_padded_b2b_mut::<Pkcs7>(net_packet.buffer(), &mut out),
|
||||
};
|
||||
|
||||
return match rs {
|
||||
Ok(buf) => {
|
||||
net_packet.set_data_len(HEAD_LEN + buf.len())?;
|
||||
net_packet.set_payload(buf)?;
|
||||
net_packet.set_encrypt_flag(true);
|
||||
|
||||
if let Some(finger) = &self.finger {
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let finger = finger.calculate_finger(&nonce_raw, buf);
|
||||
let src_data_len = net_packet.data_len();
|
||||
//设置实际长度
|
||||
net_packet.set_data_len(src_data_len + finger.len())?;
|
||||
|
||||
net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("aes_ecb加密失败:{}", e)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_aes_ecb() {
|
||||
let d = AesEcbCipher::new_128([0; 16], Some(Finger::new("123")));
|
||||
let mut p = NetPacket::new_encrypt([0; 100]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src)
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
use aes_gcm::aead::consts::{U12, U16};
|
||||
use aes_gcm::aead::generic_array::GenericArray;
|
||||
use aes_gcm::{AeadInPlace, Aes128Gcm, Aes256Gcm, Key, KeyInit, Nonce, Tag};
|
||||
use anyhow::anyhow;
|
||||
use rand::RngCore;
|
||||
|
||||
use crate::cipher::finger::Finger;
|
||||
use crate::protocol::{body::SecretBody, body::AES_GCM_ENCRYPTION_RESERVED, NetPacket};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AesGcmCipher {
|
||||
pub(crate) cipher: AesGcmEnum,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum AesGcmEnum {
|
||||
AES128GCM(Aes128Gcm),
|
||||
AES256GCM(Aes256Gcm),
|
||||
}
|
||||
|
||||
impl AesGcmCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
let key: &Key<Aes128Gcm> = &key.into();
|
||||
Self {
|
||||
cipher: AesGcmEnum::AES128GCM(Aes128Gcm::new(key)),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
let key: &Key<Aes256Gcm> = &key.into();
|
||||
Self {
|
||||
cipher: AesGcmEnum::AES256GCM(Aes256Gcm::new(key)),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < AES_GCM_ENCRYPTION_RESERVED {
|
||||
log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED);
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&nonce_raw);
|
||||
|
||||
let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
let tag = secret_body.tag();
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body());
|
||||
if &finger != secret_body.finger() {
|
||||
return Err(anyhow!("finger err"));
|
||||
}
|
||||
}
|
||||
let tag: GenericArray<u8, U16> = Tag::clone_from_slice(tag);
|
||||
let rs = match &self.cipher {
|
||||
AesGcmEnum::AES128GCM(aes_gcm) => {
|
||||
aes_gcm.decrypt_in_place_detached(nonce, &[], secret_body.body_mut(), &tag)
|
||||
}
|
||||
AesGcmEnum::AES256GCM(aes_gcm) => {
|
||||
aes_gcm.decrypt_in_place_detached(nonce, &[], secret_body.body_mut(), &tag)
|
||||
}
|
||||
};
|
||||
if let Err(e) = rs {
|
||||
return Err(anyhow!("解密失败:{}", e));
|
||||
}
|
||||
net_packet.set_encrypt_flag(false);
|
||||
net_packet.set_data_len(net_packet.data_len() - AES_GCM_ENCRYPTION_RESERVED)?;
|
||||
return Ok(());
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if net_packet.reserve() < AES_GCM_ENCRYPTION_RESERVED {
|
||||
return Err(anyhow!("too short"));
|
||||
}
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let nonce: &GenericArray<u8, U12> = Nonce::from_slice(&nonce_raw);
|
||||
let data_len = net_packet.data_len() + AES_GCM_ENCRYPTION_RESERVED;
|
||||
net_packet.set_data_len(data_len)?;
|
||||
let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
secret_body.set_random(rand::thread_rng().next_u32());
|
||||
let rs = match &self.cipher {
|
||||
AesGcmEnum::AES128GCM(aes_gcm) => {
|
||||
aes_gcm.encrypt_in_place_detached(nonce, &[], secret_body.body_mut())
|
||||
}
|
||||
AesGcmEnum::AES256GCM(aes_gcm) => {
|
||||
aes_gcm.encrypt_in_place_detached(nonce, &[], secret_body.body_mut())
|
||||
}
|
||||
};
|
||||
return match rs {
|
||||
Ok(tag) => {
|
||||
secret_body.set_tag(tag.as_slice())?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body());
|
||||
secret_body.set_finger(&finger)?;
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("加密失败:{}", e)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_aes_gcm() {
|
||||
let d = AesGcmCipher::new_256([0; 32], Some(Finger::new("123")));
|
||||
let mut p =
|
||||
NetPacket::new_encrypt([1; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
|
||||
let d = AesGcmCipher::new_256([0; 32], None);
|
||||
let mut p =
|
||||
NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#[cfg(feature = "ring-cipher")]
|
||||
mod ring_aes_gcm_cipher;
|
||||
#[cfg(feature = "ring-cipher")]
|
||||
pub use ring_aes_gcm_cipher::*;
|
||||
|
||||
#[cfg(not(feature = "ring-cipher"))]
|
||||
mod aes_gcm_cipher;
|
||||
#[cfg(not(feature = "ring-cipher"))]
|
||||
pub use aes_gcm_cipher::*;
|
||||
@@ -0,0 +1,148 @@
|
||||
use anyhow::anyhow;
|
||||
use rand::RngCore;
|
||||
use ring::aead;
|
||||
use ring::aead::{LessSafeKey, UnboundKey};
|
||||
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::body::{SecretBody, AES_GCM_ENCRYPTION_RESERVED};
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AesGcmCipher {
|
||||
pub(crate) cipher: AesGcmEnum,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
pub enum AesGcmEnum {
|
||||
AesGCM128(LessSafeKey, [u8; 16]),
|
||||
AesGCM256(LessSafeKey, [u8; 32]),
|
||||
}
|
||||
|
||||
impl Clone for AesGcmEnum {
|
||||
fn clone(&self) -> Self {
|
||||
match &self {
|
||||
AesGcmEnum::AesGCM128(_, key) => {
|
||||
let c =
|
||||
LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, key.as_slice()).unwrap());
|
||||
AesGcmEnum::AesGCM128(c, *key)
|
||||
}
|
||||
AesGcmEnum::AesGCM256(_, key) => {
|
||||
let c =
|
||||
LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, key.as_slice()).unwrap());
|
||||
AesGcmEnum::AesGCM256(c, *key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AesGcmCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_128_GCM, &key).unwrap());
|
||||
Self {
|
||||
cipher: AesGcmEnum::AesGCM128(cipher, key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
let cipher = LessSafeKey::new(UnboundKey::new(&aead::AES_256_GCM, &key).unwrap());
|
||||
Self {
|
||||
cipher: AesGcmEnum::AesGCM256(cipher, key),
|
||||
finger,
|
||||
}
|
||||
}
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < AES_GCM_ENCRYPTION_RESERVED {
|
||||
log::error!("数据异常,长度小于{}", AES_GCM_ENCRYPTION_RESERVED);
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let nonce = aead::Nonce::assume_unique_for_key(nonce_raw);
|
||||
let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body());
|
||||
if &finger != secret_body.finger() {
|
||||
return Err(anyhow!("ring aes finger err"));
|
||||
}
|
||||
}
|
||||
|
||||
let rs = match &self.cipher {
|
||||
AesGcmEnum::AesGCM128(cipher, _) => {
|
||||
cipher.open_in_place(nonce, aead::Aad::empty(), secret_body.en_body_mut())
|
||||
}
|
||||
AesGcmEnum::AesGCM256(cipher, _) => {
|
||||
cipher.open_in_place(nonce, aead::Aad::empty(), secret_body.en_body_mut())
|
||||
}
|
||||
};
|
||||
if let Err(e) = rs {
|
||||
return Err(anyhow!("解密失败:{}", e));
|
||||
}
|
||||
net_packet.set_encrypt_flag(false);
|
||||
net_packet.set_data_len(net_packet.data_len() - AES_GCM_ENCRYPTION_RESERVED)?;
|
||||
return Ok(());
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
/// 返回加密后载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let nonce = aead::Nonce::assume_unique_for_key(nonce_raw);
|
||||
let data_len = net_packet.data_len() + AES_GCM_ENCRYPTION_RESERVED;
|
||||
net_packet.set_data_len(data_len)?;
|
||||
let mut secret_body = SecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
secret_body.set_random(rand::thread_rng().next_u32());
|
||||
|
||||
let rs = match &self.cipher {
|
||||
AesGcmEnum::AesGCM128(cipher, _) => {
|
||||
cipher.seal_in_place_separate_tag(nonce, aead::Aad::empty(), secret_body.body_mut())
|
||||
}
|
||||
AesGcmEnum::AesGCM256(cipher, _) => {
|
||||
cipher.seal_in_place_separate_tag(nonce, aead::Aad::empty(), secret_body.body_mut())
|
||||
}
|
||||
};
|
||||
return match rs {
|
||||
Ok(tag) => {
|
||||
let tag = tag.as_ref();
|
||||
if tag.len() != 16 {
|
||||
return Err(anyhow!("加密tag长度错误:{}", tag.len()));
|
||||
}
|
||||
secret_body.set_tag(tag)?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body.en_body());
|
||||
secret_body.set_finger(&finger)?;
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("加密失败:{}", e)),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_aes_gcm() {
|
||||
let d = AesGcmCipher::new_256([0; 32], Some(Finger::new("123")));
|
||||
let mut p =
|
||||
NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
|
||||
let d = AesGcmCipher::new_256([0; 32], None);
|
||||
let mut p =
|
||||
NetPacket::new_encrypt([0; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod rs_chacha20;
|
||||
pub use rs_chacha20::*;
|
||||
@@ -0,0 +1,107 @@
|
||||
use aes::cipher::Iv;
|
||||
use anyhow::anyhow;
|
||||
use chacha20::cipher::{Key, KeyIvInit, StreamCipher};
|
||||
use chacha20::ChaCha20;
|
||||
|
||||
use crate::cipher::finger::{gen_nonce, gen_random_nonce};
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::body::{
|
||||
IVSecretBody, SecretTail, SecretTailMut, FINGER_RESERVED, RANDOM_RESERVED,
|
||||
};
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChaCha20Cipher {
|
||||
key: [u8; 32],
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
impl ChaCha20Cipher {
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
Self { key, finger }
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaCha20Cipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaCha20Cipher {
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
let mut head_tag = net_packet.head_tag();
|
||||
|
||||
let mut secret_body = IVSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&head_tag, secret_body.data());
|
||||
if &finger != secret_body.finger() {
|
||||
return Err(anyhow!("ChaCha20 finger err"));
|
||||
}
|
||||
}
|
||||
gen_nonce(&mut head_tag, secret_body.random_buf());
|
||||
ChaCha20::new(
|
||||
Key::<ChaCha20>::from_slice(&self.key),
|
||||
Iv::<ChaCha20>::from_slice(&head_tag),
|
||||
)
|
||||
.apply_keystream(secret_body.data_mut());
|
||||
let len = secret_body.data().len();
|
||||
net_packet.set_encrypt_flag(false);
|
||||
net_packet.set_payload_len(len)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
let data_len = net_packet.data_len();
|
||||
let head_tag = net_packet.head_tag();
|
||||
if let Some(_) = &self.finger {
|
||||
net_packet.set_data_len(data_len + RANDOM_RESERVED + FINGER_RESERVED)?;
|
||||
} else {
|
||||
net_packet.set_data_len(data_len + RANDOM_RESERVED)?;
|
||||
}
|
||||
let mut secret_body = IVSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
let mut nonce = head_tag;
|
||||
secret_body.set_random(&gen_random_nonce(&mut nonce));
|
||||
|
||||
ChaCha20::new(
|
||||
Key::<ChaCha20>::from_slice(&self.key),
|
||||
Iv::<ChaCha20>::from_slice(&nonce),
|
||||
)
|
||||
.apply_keystream(secret_body.data_mut());
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&head_tag, secret_body.data());
|
||||
let mut secret_body = IVSecretBody::new(net_packet.payload_mut(), true)?;
|
||||
secret_body.set_finger(&finger)?;
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chacha20() {
|
||||
let d = ChaCha20Cipher::new_256([0; 32], Some(Finger::new("123")));
|
||||
let mut p =
|
||||
NetPacket::new_encrypt([1; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
|
||||
let d = ChaCha20Cipher::new_256([0; 32], None);
|
||||
let mut p =
|
||||
NetPacket::new_encrypt([2; 13 + crate::protocol::body::ENCRYPTION_RESERVED]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#[cfg(feature = "ring-cipher")]
|
||||
mod ring_chacha20_poly1305;
|
||||
#[cfg(feature = "ring-cipher")]
|
||||
pub use ring_chacha20_poly1305::*;
|
||||
|
||||
#[cfg(not(feature = "ring-cipher"))]
|
||||
mod rs_chacha20_poly1305;
|
||||
#[cfg(not(feature = "ring-cipher"))]
|
||||
pub use rs_chacha20_poly1305::*;
|
||||
@@ -0,0 +1,127 @@
|
||||
use anyhow::anyhow;
|
||||
|
||||
use ring::aead;
|
||||
use ring::aead::{LessSafeKey, UnboundKey};
|
||||
|
||||
use crate::cipher::finger::{gen_nonce, gen_random_nonce};
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::body::{
|
||||
AEADSecretBody, SecretTail, SecretTailMut, FINGER_RESERVED, RANDOM_RESERVED, TAG_RESERVED,
|
||||
};
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChaCha20Poly1305Cipher {
|
||||
key: Vec<u8>,
|
||||
pub(crate) cipher: LessSafeKey,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
impl ChaCha20Poly1305Cipher {
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
let cipher = LessSafeKey::new(UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap());
|
||||
Self {
|
||||
key: key.to_vec(),
|
||||
cipher,
|
||||
finger,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaCha20Poly1305Cipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaCha20Poly1305Cipher {
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < TAG_RESERVED {
|
||||
log::error!("数据异常,长度小于{}", TAG_RESERVED);
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
let mut head_tag = net_packet.head_tag();
|
||||
let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut());
|
||||
if &finger != secret_body.finger() {
|
||||
return Err(anyhow!("ring CHACHA20_POLY1305 finger err"));
|
||||
}
|
||||
}
|
||||
gen_nonce(&mut head_tag, secret_body.random_buf());
|
||||
let nonce = aead::Nonce::assume_unique_for_key(head_tag);
|
||||
let rs = self
|
||||
.cipher
|
||||
.open_in_place(nonce, aead::Aad::empty(), secret_body.data_tag_mut());
|
||||
if let Err(e) = rs {
|
||||
return Err(anyhow!("ring CHACHA20_POLY1305 解密失败:{}", e));
|
||||
}
|
||||
let len = secret_body.data().len();
|
||||
net_packet.set_encrypt_flag(false);
|
||||
net_packet.set_payload_len(len)?;
|
||||
return Ok(());
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
/// 返回加密后载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
let head_tag = net_packet.head_tag();
|
||||
let data_len = net_packet.data_len();
|
||||
if self.finger.is_some() {
|
||||
net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED + FINGER_RESERVED)?;
|
||||
} else {
|
||||
net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED)?;
|
||||
}
|
||||
let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
let mut nonce = head_tag;
|
||||
secret_body.set_random(&gen_random_nonce(&mut nonce));
|
||||
let nonce = aead::Nonce::assume_unique_for_key(nonce);
|
||||
let rs = self.cipher.seal_in_place_separate_tag(
|
||||
nonce,
|
||||
aead::Aad::empty(),
|
||||
secret_body.data_mut(),
|
||||
);
|
||||
match rs {
|
||||
Ok(tag) => {
|
||||
let tag = tag.as_ref();
|
||||
if tag.len() != 16 {
|
||||
return Err(anyhow!("加密tag长度错误:{}", tag.len()));
|
||||
}
|
||||
secret_body.set_tag(tag)?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut());
|
||||
secret_body.set_finger(&finger)?;
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("ring CHACHA20_POLY1305 加密失败:{}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ring_chacha20_poly1305() {
|
||||
let d = ChaCha20Poly1305Cipher::new_256([0; 32], Some(Finger::new("123")));
|
||||
let mut p = NetPacket::new_encrypt([0; 73]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
let d = ChaCha20Poly1305Cipher::new_256([0; 32], None);
|
||||
let mut p = NetPacket::new_encrypt([0; 73]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use crate::cipher::finger::{gen_nonce, gen_random_nonce};
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::body::{
|
||||
AEADSecretBody, SecretTail, SecretTailMut, FINGER_RESERVED, RANDOM_RESERVED, TAG_RESERVED,
|
||||
};
|
||||
use crate::protocol::NetPacket;
|
||||
use anyhow::anyhow;
|
||||
use chacha20poly1305::aead::{Nonce, Tag};
|
||||
use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, Key, KeyInit};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ChaCha20Poly1305Cipher {
|
||||
key: Vec<u8>,
|
||||
pub(crate) cipher: ChaCha20Poly1305,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
impl ChaCha20Poly1305Cipher {
|
||||
pub fn new_256(key: [u8; 32], finger: Option<Finger>) -> Self {
|
||||
let key: &Key = &key.into();
|
||||
let cipher = ChaCha20Poly1305::new(key);
|
||||
Self {
|
||||
key: key.to_vec(),
|
||||
cipher,
|
||||
finger,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaCha20Poly1305Cipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl ChaCha20Poly1305Cipher {
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
if net_packet.payload().len() < TAG_RESERVED {
|
||||
log::error!("数据异常,长度小于{}", TAG_RESERVED);
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
let mut head_tag = net_packet.head_tag();
|
||||
let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut());
|
||||
if &finger != secret_body.finger() {
|
||||
return Err(anyhow!("rs CHACHA20_POLY1305 finger err"));
|
||||
}
|
||||
}
|
||||
gen_nonce(&mut head_tag, secret_body.random_buf());
|
||||
let nonce: Nonce<ChaCha20Poly1305> = head_tag.into();
|
||||
let tag: Tag<ChaCha20Poly1305> =
|
||||
Tag::<ChaCha20Poly1305>::from_slice(secret_body.tag()).clone();
|
||||
if let Err(e) =
|
||||
self.cipher
|
||||
.decrypt_in_place_detached(&nonce, &[], secret_body.data_mut(), &tag)
|
||||
{
|
||||
return Err(anyhow!("rs CHACHA20_POLY1305 decrypt_ipv4 {:?}", e));
|
||||
}
|
||||
let len = secret_body.data().len();
|
||||
net_packet.set_encrypt_flag(false);
|
||||
net_packet.set_payload_len(len)?;
|
||||
Ok(())
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
/// 返回加密后载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
let head_tag = net_packet.head_tag();
|
||||
let data_len = net_packet.data_len();
|
||||
if self.finger.is_some() {
|
||||
net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED + FINGER_RESERVED)?;
|
||||
} else {
|
||||
net_packet.set_data_len(data_len + TAG_RESERVED + RANDOM_RESERVED)?;
|
||||
}
|
||||
let mut secret_body = AEADSecretBody::new(net_packet.payload_mut(), self.finger.is_some())?;
|
||||
let mut nonce = head_tag;
|
||||
secret_body.set_random(&gen_random_nonce(&mut nonce));
|
||||
let nonce = nonce.into();
|
||||
let rs = self
|
||||
.cipher
|
||||
.encrypt_in_place_detached(&nonce, &[], secret_body.data_mut());
|
||||
match rs {
|
||||
Ok(tag) => {
|
||||
let tag: &[u8] = tag.as_ref();
|
||||
if tag.len() != 16 {
|
||||
return Err(anyhow!("加密tag长度错误:{}", tag.len(),));
|
||||
}
|
||||
secret_body.set_tag(tag)?;
|
||||
if let Some(finger) = &self.finger {
|
||||
let finger = finger.calculate_finger(&head_tag, secret_body.data_tag_mut());
|
||||
secret_body.set_finger(&finger)?;
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("rs CHACHA20_POLY1305 加密失败:{}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rs_chacha20_poly1305() {
|
||||
let d = ChaCha20Poly1305Cipher::new_256([0; 32], Some(Finger::new("123")));
|
||||
let mut p = NetPacket::new_encrypt([0; 73]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
let d = ChaCha20Poly1305Cipher::new_256([0; 32], None);
|
||||
let mut p = NetPacket::new_encrypt([0; 73]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
use std::fmt::Display;
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
#[cfg(cipher)]
|
||||
use sha2::Digest;
|
||||
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
use crate::cipher::aes_cbc::AesCbcCipher;
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
use crate::cipher::aes_ecb::AesEcbCipher;
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
use crate::cipher::aes_gcm::AesGcmCipher;
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
use crate::cipher::chacha20::ChaCha20Cipher;
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
use crate::cipher::chacha20_poly1305::ChaCha20Poly1305Cipher;
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
use crate::cipher::sm4_cbc::Sm4CbcCipher;
|
||||
use crate::cipher::xor::XORCipher;
|
||||
#[cfg(cipher)]
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum CipherModel {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
AesGcm,
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Chacha20Poly1305,
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Chacha20,
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
AesCbc,
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
AesEcb,
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Sm4Cbc,
|
||||
Xor,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Display for CipherModel {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
CipherModel::AesGcm => "aes_gcm".to_string(),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
CipherModel::Chacha20Poly1305 => "chacha20_poly1305".to_string(),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
CipherModel::Chacha20 => "chacha20".to_string(),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
CipherModel::AesCbc => "aes_cbc".to_string(),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
CipherModel::AesEcb => "aes_ecb".to_string(),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
CipherModel::Sm4Cbc => "sm4_cbc".to_string(),
|
||||
CipherModel::Xor => "xor".to_string(),
|
||||
CipherModel::None => "none".to_string(),
|
||||
};
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for CipherModel {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().trim() {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
"aes_gcm" => Ok(CipherModel::AesGcm),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
"chacha20_poly1305" => Ok(CipherModel::Chacha20Poly1305),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
"chacha20" => Ok(CipherModel::Chacha20),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
"aes_cbc" => Ok(CipherModel::AesCbc),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
"aes_ecb" => Ok(CipherModel::AesEcb),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
"sm4_cbc" => Ok(CipherModel::Sm4Cbc),
|
||||
"xor" => Ok(CipherModel::Xor),
|
||||
_ => {
|
||||
let mut enums = String::new();
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
enums.push_str("/aes_gcm");
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
enums.push_str("/chacha20_poly1305/chacha20");
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
enums.push_str("/aes_cbc");
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
enums.push_str("/aes_ecb");
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
enums.push_str("/sm4_cbc");
|
||||
enums.push_str("/xor");
|
||||
Err(format!("not match '{}', enum:{}", s, &enums[1..]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Cipher {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
AesGcm((AesGcmCipher, Vec<u8>)),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Chacha20Poly1305(ChaCha20Poly1305Cipher),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Chacha20(ChaCha20Cipher),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
AesCbc(AesCbcCipher),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
AesEcb(AesEcbCipher),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Sm4Cbc(Sm4CbcCipher),
|
||||
Xor(XORCipher),
|
||||
None,
|
||||
}
|
||||
|
||||
impl Cipher {
|
||||
pub fn new_password(
|
||||
model: CipherModel,
|
||||
password: Option<String>,
|
||||
token: Option<String>,
|
||||
) -> anyhow::Result<Self> {
|
||||
if let Some(password) = password {
|
||||
#[cfg(cipher)]
|
||||
let key: [u8; 32] = {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(password.as_bytes());
|
||||
hasher.finalize().into()
|
||||
};
|
||||
match model {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
CipherModel::AesGcm => {
|
||||
let finger = token.map(|token| Finger::new(&token));
|
||||
if password.len() < 8 {
|
||||
let aes = AesGcmCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Ok(Cipher::AesGcm((aes, key[..16].to_vec())))
|
||||
} else {
|
||||
let aes = AesGcmCipher::new_256(key, finger);
|
||||
Ok(Cipher::AesGcm((aes, key.to_vec())))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
CipherModel::Chacha20Poly1305 => {
|
||||
let finger = token.map(|token| Finger::new(&token));
|
||||
let chacha = ChaCha20Poly1305Cipher::new_256(key, finger);
|
||||
Ok(Cipher::Chacha20Poly1305(chacha))
|
||||
}
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
CipherModel::Chacha20 => {
|
||||
let finger = token.map(|token| Finger::new(&token));
|
||||
let chacha = ChaCha20Cipher::new_256(key, finger);
|
||||
Ok(Cipher::Chacha20(chacha))
|
||||
}
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
CipherModel::AesCbc => {
|
||||
let finger = token.map(|token| Finger::new(&token));
|
||||
if password.len() < 8 {
|
||||
let aes = AesCbcCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Ok(Cipher::AesCbc(aes))
|
||||
} else {
|
||||
let aes = AesCbcCipher::new_256(key, finger);
|
||||
Ok(Cipher::AesCbc(aes))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
CipherModel::AesEcb => {
|
||||
let finger = token.map(|token| Finger::new(&token));
|
||||
if password.len() < 8 {
|
||||
let aes = AesEcbCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Ok(Cipher::AesEcb(aes))
|
||||
} else {
|
||||
let aes = AesEcbCipher::new_256(key, finger);
|
||||
Ok(Cipher::AesEcb(aes))
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
CipherModel::Sm4Cbc => {
|
||||
let finger = token.map(|token| Finger::new(&token));
|
||||
let aes = Sm4CbcCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Ok(Cipher::Sm4Cbc(aes))
|
||||
}
|
||||
CipherModel::Xor => {
|
||||
if token.is_some() {
|
||||
Err(anyhow::anyhow!(
|
||||
"'finger' and 'xor' cannot be used simultaneously"
|
||||
))?
|
||||
}
|
||||
Ok(Cipher::Xor(XORCipher::new_256(
|
||||
crate::cipher::xor::simple_hash(&password),
|
||||
)))
|
||||
}
|
||||
CipherModel::None => Ok(Cipher::None),
|
||||
}
|
||||
} else {
|
||||
Ok(Cipher::None)
|
||||
}
|
||||
}
|
||||
#[cfg(not(any(feature = "aes_gcm", feature = "server_encrypt")))]
|
||||
pub fn new_key(_key: [u8; 32], _token: String) -> anyhow::Result<Self> {
|
||||
Err(anyhow!("key error"))
|
||||
}
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
pub fn new_key(key: [u8; 32], token: String) -> anyhow::Result<Self> {
|
||||
let finger = Some(Finger::new(&token));
|
||||
match key.len() {
|
||||
16 => {
|
||||
let aes = AesGcmCipher::new_128(key[..16].try_into().unwrap(), finger);
|
||||
Ok(Cipher::AesGcm((aes, key[..16].to_vec())))
|
||||
}
|
||||
32 => {
|
||||
let aes = AesGcmCipher::new_256(key, finger);
|
||||
Ok(Cipher::AesGcm((aes, key.to_vec())))
|
||||
}
|
||||
_ => Err(anyhow!("key error")),
|
||||
}
|
||||
}
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Cipher::Chacha20Poly1305(chacha20poly1305) => chacha20poly1305.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Cipher::Chacha20(chacha20) => chacha20.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb.decrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc.decrypt_ipv4(net_packet),
|
||||
Cipher::Xor(xor) => xor.decrypt_ipv4(net_packet),
|
||||
Cipher::None => {
|
||||
if net_packet.is_encrypt() {
|
||||
return Err(anyhow!("not key"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Cipher::Chacha20Poly1305(chacha20poly1305) => chacha20poly1305.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Cipher::Chacha20(chacha20) => chacha20.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb.encrypt_ipv4(net_packet),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc.encrypt_ipv4(net_packet),
|
||||
Cipher::Xor(xor) => xor.encrypt_ipv4(net_packet),
|
||||
Cipher::None => Ok(()),
|
||||
}
|
||||
}
|
||||
#[cfg(not(cipher))]
|
||||
pub fn check_finger<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
_net_packet: &NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(cipher)]
|
||||
pub fn check_finger<B: AsRef<[u8]>>(&self, net_packet: &NetPacket<B>) -> anyhow::Result<()> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((aes_gcm, _)) => aes_gcm
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Cipher::Chacha20Poly1305(chacha20poly1305) => chacha20poly1305
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Cipher::Chacha20(chacha20) => chacha20
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => aes_cbc
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => aes_ecb
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => sm4_cbc
|
||||
.finger
|
||||
.as_ref()
|
||||
.map(|f| f.check_finger(net_packet))
|
||||
.unwrap_or(Ok(())),
|
||||
Cipher::Xor(_) => Ok(()),
|
||||
Cipher::None => Ok(()),
|
||||
}
|
||||
}
|
||||
pub fn key(&self) -> Option<&[u8]> {
|
||||
match self {
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
Cipher::AesGcm((_, key)) => Some(key),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Cipher::Chacha20Poly1305(chacha20poly1305) => Some(chacha20poly1305.key()),
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
Cipher::Chacha20(chacha20) => Some(chacha20.key()),
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
Cipher::AesCbc(aes_cbc) => Some(aes_cbc.key()),
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
Cipher::AesEcb(aes_ecb) => Some(aes_ecb.key()),
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
Cipher::Sm4Cbc(sm4_cbc) => Some(sm4_cbc.key()),
|
||||
Cipher::Xor(xor) => Some(xor.key()),
|
||||
Cipher::None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
use anyhow::anyhow;
|
||||
use rand::RngCore;
|
||||
|
||||
use sha2::Digest;
|
||||
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Finger {
|
||||
pub(crate) hash: [u8; 32],
|
||||
}
|
||||
|
||||
impl Finger {
|
||||
pub fn new(str: &str) -> Self {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(str.as_bytes());
|
||||
let hash: [u8; 32] = hasher.finalize().into();
|
||||
Finger { hash }
|
||||
}
|
||||
pub fn check_finger<B: AsRef<[u8]>>(&self, net_packet: &NetPacket<B>) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
let payload_len = net_packet.payload().len();
|
||||
if payload_len < 12 {
|
||||
log::error!("数据异常,长度小于{}", 12);
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
let mut nonce_raw = [0; 12];
|
||||
nonce_raw[0..4].copy_from_slice(&net_packet.source().octets());
|
||||
nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets());
|
||||
nonce_raw[8] = net_packet.protocol().into();
|
||||
nonce_raw[9] = net_packet.transport_protocol();
|
||||
nonce_raw[10] = net_packet.is_gateway() as u8;
|
||||
nonce_raw[11] = net_packet.source_ttl();
|
||||
let payload = net_packet.payload();
|
||||
let finger = self.calculate_finger(&nonce_raw, &payload[..payload_len - 12]);
|
||||
if &finger[..] != &payload[payload_len - 12..] {
|
||||
return Err(anyhow!("finger err"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub fn calculate_finger(&self, nonce: &[u8], secret_body: &[u8]) -> [u8; 12] {
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(nonce);
|
||||
hasher.update(secret_body);
|
||||
hasher.update(&self.hash);
|
||||
let key: [u8; 32] = hasher.finalize().into();
|
||||
return key[20..].try_into().unwrap();
|
||||
}
|
||||
}
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
pub fn head_tag(&self) -> [u8; 12] {
|
||||
let mut tag = [0; 12];
|
||||
tag[0..4].copy_from_slice(&self.buffer()[4..8]);
|
||||
tag[4..8].copy_from_slice(&self.buffer()[8..12]);
|
||||
tag[8] = self.protocol().into();
|
||||
tag[9] = self.transport_protocol();
|
||||
tag[10] = self.is_gateway() as u8;
|
||||
tag[11] = self.source_ttl();
|
||||
tag
|
||||
}
|
||||
}
|
||||
pub fn gen_nonce(tag: &mut [u8], random: &[u8]) {
|
||||
tag[8] = random[0] ^ tag[8];
|
||||
tag[9] = random[1] ^ tag[9];
|
||||
tag[10] = random[2] ^ tag[10];
|
||||
tag[11] = random[3] ^ tag[11];
|
||||
}
|
||||
pub fn gen_random_nonce(tag: &mut [u8; 12]) -> [u8; 4] {
|
||||
let mut random = [0; 4];
|
||||
rand::thread_rng().fill_bytes(&mut random);
|
||||
gen_nonce(tag, &random);
|
||||
random
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
mod cipher;
|
||||
#[cfg(cipher)]
|
||||
mod finger;
|
||||
|
||||
pub use cipher::Cipher;
|
||||
pub use cipher::CipherModel;
|
||||
#[cfg(cipher)]
|
||||
pub use finger::Finger;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
mod rsa_cipher;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub use rsa_cipher::RsaCipher;
|
||||
|
||||
#[cfg(any(feature = "aes_gcm", feature = "server_encrypt"))]
|
||||
mod aes_gcm;
|
||||
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
mod chacha20;
|
||||
#[cfg(feature = "chacha20_poly1305")]
|
||||
mod chacha20_poly1305;
|
||||
|
||||
#[cfg(feature = "aes_ecb")]
|
||||
mod aes_ecb;
|
||||
|
||||
#[cfg(feature = "aes_cbc")]
|
||||
mod aes_cbc;
|
||||
|
||||
#[cfg(feature = "sm4_cbc")]
|
||||
mod sm4_cbc;
|
||||
|
||||
mod xor;
|
||||
pub use xor::simple_hash;
|
||||
@@ -0,0 +1,112 @@
|
||||
use std::io;
|
||||
|
||||
use {
|
||||
crate::protocol::body::{RsaSecretBody, RSA_ENCRYPTION_RESERVED},
|
||||
rand::Rng,
|
||||
rsa::pkcs8::der::Decode,
|
||||
rsa::RsaPublicKey,
|
||||
sha2::Digest,
|
||||
spki::{DecodePublicKey, EncodePublicKey},
|
||||
};
|
||||
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RsaCipher {
|
||||
inner: Inner,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
struct Inner {
|
||||
public_key: RsaPublicKey,
|
||||
finger: String,
|
||||
}
|
||||
|
||||
impl RsaCipher {
|
||||
pub fn new(der: &[u8]) -> io::Result<Self> {
|
||||
match RsaPublicKey::from_public_key_der(der) {
|
||||
Ok(public_key) => {
|
||||
let finger = finger(&public_key)?;
|
||||
let inner = Inner { public_key, finger };
|
||||
Ok(Self { inner })
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("from_public_key_der failed {}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
pub fn finger(&self) -> &String {
|
||||
&self.inner.finger
|
||||
}
|
||||
pub fn public_key(&self) -> io::Result<&RsaPublicKey> {
|
||||
return Ok(&self.inner.public_key);
|
||||
}
|
||||
}
|
||||
pub fn finger(public_key: &RsaPublicKey) -> io::Result<String> {
|
||||
match public_key.to_public_key_der() {
|
||||
Ok(der) => match rsa::pkcs8::SubjectPublicKeyInfoRef::from_der(der.as_bytes()) {
|
||||
Ok(spki) => match spki.fingerprint_base64() {
|
||||
Ok(finger) => Ok(finger),
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("fingerprint_base64 error {}", e),
|
||||
)),
|
||||
},
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("from_der error {}", e),
|
||||
)),
|
||||
},
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("to_public_key_der error {}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
impl RsaCipher {
|
||||
/// net_packet 必须预留足够长度
|
||||
pub fn encrypt<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> io::Result<NetPacket<Vec<u8>>> {
|
||||
if net_packet.reserve() < RSA_ENCRYPTION_RESERVED {
|
||||
return Err(io::Error::new(io::ErrorKind::Other, "too short"));
|
||||
}
|
||||
let data_len = net_packet.data_len() + RSA_ENCRYPTION_RESERVED;
|
||||
net_packet.set_data_len(data_len)?;
|
||||
let mut nonce_raw = [0; 12];
|
||||
nonce_raw[0..4].copy_from_slice(&net_packet.source().octets());
|
||||
nonce_raw[4..8].copy_from_slice(&net_packet.destination().octets());
|
||||
nonce_raw[8] = net_packet.protocol().into();
|
||||
nonce_raw[9] = net_packet.transport_protocol();
|
||||
nonce_raw[10] = net_packet.is_gateway() as u8;
|
||||
nonce_raw[11] = net_packet.source_ttl();
|
||||
|
||||
let mut secret_body = RsaSecretBody::new(net_packet.payload_mut())?;
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.fill(secret_body.random_mut());
|
||||
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(secret_body.body());
|
||||
hasher.update(nonce_raw);
|
||||
let key: [u8; 32] = hasher.finalize().into();
|
||||
secret_body.set_finger(&key[16..])?;
|
||||
match self.inner.public_key.encrypt(
|
||||
&mut rng,
|
||||
rsa::pkcs1v15::Pkcs1v15Encrypt,
|
||||
secret_body.buffer(),
|
||||
) {
|
||||
Ok(enc_data) => {
|
||||
let mut net_packet_e = NetPacket::new(vec![0; 12 + enc_data.len()])?;
|
||||
net_packet_e.buffer_mut()[..12].copy_from_slice(&net_packet.buffer()[..12]);
|
||||
net_packet_e.set_payload(&enc_data)?;
|
||||
Ok(net_packet_e)
|
||||
}
|
||||
Err(e) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("encrypt failed {}", e),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod rs_sm4_cbc;
|
||||
pub use rs_sm4_cbc::*;
|
||||
@@ -0,0 +1,153 @@
|
||||
use crate::cipher::Finger;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
use anyhow::anyhow;
|
||||
use libsm::sm4::cipher_mode::CipherMode;
|
||||
use libsm::sm4::Sm4CipherMode;
|
||||
use rand::RngCore;
|
||||
|
||||
pub struct Sm4CbcCipher {
|
||||
key: [u8; 16],
|
||||
pub(crate) cipher: Sm4CipherMode,
|
||||
pub(crate) finger: Option<Finger>,
|
||||
}
|
||||
|
||||
impl Clone for Sm4CbcCipher {
|
||||
fn clone(&self) -> Self {
|
||||
let cipher = Sm4CipherMode::new(&self.key, CipherMode::Cbc).unwrap();
|
||||
Self {
|
||||
key: self.key,
|
||||
cipher,
|
||||
finger: self.finger.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sm4CbcCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl Sm4CbcCipher {
|
||||
pub fn new_128(key: [u8; 16], finger: Option<Finger>) -> Self {
|
||||
let cipher = Sm4CipherMode::new(&key, CipherMode::Cbc).unwrap();
|
||||
Self {
|
||||
key,
|
||||
cipher,
|
||||
finger,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
|
||||
if let Some(finger) = &self.finger {
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let len = net_packet.payload().len();
|
||||
if len < 12 {
|
||||
return Err(anyhow!("payload len <12"));
|
||||
}
|
||||
let secret_body = &net_packet.payload()[..len - 12];
|
||||
let finger = finger.calculate_finger(&nonce_raw, secret_body);
|
||||
if &finger != &net_packet.payload()[len - 12..] {
|
||||
return Err(anyhow!("finger err"));
|
||||
}
|
||||
net_packet.set_data_len(net_packet.data_len() - finger.len())?;
|
||||
}
|
||||
let payload = net_packet.payload();
|
||||
let len = payload.len();
|
||||
if len < 16 || len > 1024 * 4 {
|
||||
log::error!("数据异常,长度{}小于16或大于4096", len);
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
let mut out = [0u8; 1024 * 4];
|
||||
let data = &payload[..len - 16];
|
||||
let iv = &payload[len - 16..];
|
||||
match self.cipher.decrypt(data, iv, &mut out) {
|
||||
Ok(len) => {
|
||||
let src_net_packet = NetPacket::new(&out[..len])?;
|
||||
if src_net_packet.source() != net_packet.source() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.destination() != net_packet.destination() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.protocol() != net_packet.protocol() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.transport_protocol() != net_packet.transport_protocol() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.is_gateway() != net_packet.is_gateway() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
if src_net_packet.source_ttl() != net_packet.source_ttl() {
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
net_packet.set_data_len(len)?;
|
||||
net_packet.set_payload(src_net_packet.payload())?;
|
||||
net_packet.set_encrypt_flag(false);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("sm4_cbc解密失败:{}", e)),
|
||||
}
|
||||
}
|
||||
/// net_packet 必须预留足够长度
|
||||
/// data_len是有效载荷的长度
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut out = [0u8; 1024 * 4];
|
||||
let mut iv = [0u8; 16];
|
||||
rand::thread_rng().fill_bytes(&mut iv);
|
||||
if net_packet.data_len() > 1024 * 4 - 32 {
|
||||
log::error!(
|
||||
"数据异常,长度{}大于1024 * 4 - 32",
|
||||
net_packet.buffer().len()
|
||||
);
|
||||
return Err(anyhow!("data err"));
|
||||
}
|
||||
match self.cipher.encrypt(net_packet.buffer(), &iv, &mut out) {
|
||||
Ok(len) => {
|
||||
net_packet.set_data_len(HEAD_LEN + len + 16)?;
|
||||
net_packet.payload_mut()[..len].copy_from_slice(&out[..len]);
|
||||
net_packet.payload_mut()[len..].copy_from_slice(&iv);
|
||||
if let Some(finger) = &self.finger {
|
||||
let nonce_raw = net_packet.head_tag();
|
||||
let finger = finger.calculate_finger(&nonce_raw, net_packet.payload());
|
||||
let src_data_len = net_packet.data_len();
|
||||
//设置实际长度
|
||||
net_packet.set_data_len(src_data_len + finger.len())?;
|
||||
|
||||
net_packet.buffer_mut()[src_data_len..].copy_from_slice(&finger);
|
||||
}
|
||||
net_packet.set_encrypt_flag(true);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(anyhow!("sm4_cbc加密失败:{}", e)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sm4_ecb() {
|
||||
let d = Sm4CbcCipher::new_128([0; 16], Some(Finger::new("123")));
|
||||
let mut p = NetPacket::new_encrypt([1; 1024]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src);
|
||||
let d = Sm4CbcCipher::new_128([0; 16], None);
|
||||
let mut p = NetPacket::new_encrypt([1; 102]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod xor;
|
||||
pub use xor::*;
|
||||
@@ -0,0 +1,84 @@
|
||||
use anyhow::anyhow;
|
||||
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
pub fn simple_hash(input: &str) -> [u8; 32] {
|
||||
let mut result = [0u8; 32];
|
||||
let bytes = input.as_bytes();
|
||||
for (index, v) in result.iter_mut().enumerate() {
|
||||
*v = bytes[index % bytes.len()];
|
||||
}
|
||||
|
||||
let mut state = 0u8;
|
||||
|
||||
for (i, &byte) in bytes.iter().enumerate() {
|
||||
let combined = byte.wrapping_add(state).rotate_left((i % 8) as u32);
|
||||
result[i % 32] ^= combined;
|
||||
state = state.wrapping_add(byte).rotate_left(3);
|
||||
}
|
||||
|
||||
for i in 0..32 {
|
||||
result[i] = result[i]
|
||||
.rotate_left((result[(i + 1) % 32] % 8) as u32)
|
||||
.wrapping_add(state);
|
||||
state = state.wrapping_add(result[i]).rotate_left(3);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct XORCipher {
|
||||
key: [u8; 32],
|
||||
}
|
||||
|
||||
impl XORCipher {
|
||||
pub fn new_256(key: [u8; 32]) -> Self {
|
||||
Self { key }
|
||||
}
|
||||
}
|
||||
|
||||
impl XORCipher {
|
||||
pub fn key(&self) -> &[u8] {
|
||||
&self.key
|
||||
}
|
||||
}
|
||||
|
||||
impl XORCipher {
|
||||
pub fn decrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
if !net_packet.is_encrypt() {
|
||||
//未加密的数据直接丢弃
|
||||
return Err(anyhow!("not encrypt"));
|
||||
}
|
||||
let key = &self.key;
|
||||
for (i, byte) in net_packet.payload_mut().iter_mut().enumerate() {
|
||||
*byte ^= key[i & 31];
|
||||
}
|
||||
net_packet.set_encrypt_flag(false);
|
||||
Ok(())
|
||||
}
|
||||
pub fn encrypt_ipv4<B: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
net_packet: &mut NetPacket<B>,
|
||||
) -> anyhow::Result<()> {
|
||||
net_packet.set_encrypt_flag(true);
|
||||
let key = &self.key;
|
||||
for (i, byte) in net_packet.payload_mut().iter_mut().enumerate() {
|
||||
*byte ^= key[i & 31];
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xor() {
|
||||
let d = XORCipher::new_256(simple_hash("password"));
|
||||
let mut p = NetPacket::new_encrypt([0; 1000]).unwrap();
|
||||
let src = p.buffer().to_vec();
|
||||
d.encrypt_ipv4(&mut p).unwrap();
|
||||
d.decrypt_ipv4(&mut p).unwrap();
|
||||
assert_eq!(p.buffer(), &src)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use anyhow::anyhow;
|
||||
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Lz4Compressor;
|
||||
|
||||
impl Lz4Compressor {
|
||||
pub fn compress<I: AsRef<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
in_net_packet: &NetPacket<I>,
|
||||
out: &mut NetPacket<O>,
|
||||
) -> anyhow::Result<()> {
|
||||
out.set_data_len_max();
|
||||
let len = match lz4_flex::compress_into(in_net_packet.payload(), out.payload_mut()) {
|
||||
Ok(len) => len,
|
||||
Err(e) => Err(anyhow!("Lz4 compress {}", e))?,
|
||||
};
|
||||
out.set_payload_len(len)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn decompress<I: AsRef<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
in_net_packet: &NetPacket<I>,
|
||||
out: &mut NetPacket<O>,
|
||||
) -> anyhow::Result<()> {
|
||||
out.set_data_len_max();
|
||||
let len = match lz4_flex::decompress_into(in_net_packet.payload(), out.payload_mut()) {
|
||||
Ok(len) => len,
|
||||
Err(e) => Err(anyhow!("Lz4 decompress {}", e))?,
|
||||
};
|
||||
out.set_payload_len(len)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::anyhow;
|
||||
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
use crate::compression::lz4_compress::Lz4Compressor;
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
use crate::compression::zstd_compress::ZstdCompressor;
|
||||
use crate::protocol::extension::CompressionAlgorithm;
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
use zstd::zstd_safe::CompressionLevel;
|
||||
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
mod lz4_compress;
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
mod zstd_compress;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Compressor {
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
Lz4,
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
Zstd(CompressionLevel),
|
||||
None,
|
||||
}
|
||||
|
||||
impl FromStr for Compressor {
|
||||
type Err = String;
|
||||
#[cfg(not(any(feature = "lz4_compress", feature = "zstd_compress")))]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Err(format!("not match '{}', Compression not supported", s))
|
||||
}
|
||||
#[cfg(any(feature = "lz4_compress", feature = "zstd_compress"))]
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let str = s.trim().to_lowercase();
|
||||
match str.as_str() {
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
"lz4" => Ok(Compressor::Lz4),
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
"zstd" => Ok(Compressor::Zstd(9)),
|
||||
"none" => Ok(Compressor::None),
|
||||
_ => {
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
{
|
||||
let string_array: Vec<String> = str.split(',').map(|s| s.to_string()).collect();
|
||||
if string_array.len() != 2 || string_array[0] != "zstd" {
|
||||
return Err(format!("not match '{}', exp: zstd,10", s));
|
||||
}
|
||||
return match CompressionLevel::from_str(&string_array[1]) {
|
||||
Ok(level) => Ok(Compressor::Zstd(level)),
|
||||
Err(_) => Err(format!("not match '{}', exp: zstd,10", s)),
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "zstd_compress"))]
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
return Err(format!("not match '{}', exp: lz4", s));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "lz4_compress", feature = "zstd_compress")))]
|
||||
impl Compressor {
|
||||
pub fn compress<I: AsRef<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
_in_net_packet: &NetPacket<I>,
|
||||
_out: &mut NetPacket<O>,
|
||||
) -> anyhow::Result<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
pub fn decompress<I: AsRef<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
_algorithm: CompressionAlgorithm,
|
||||
_in_net_packet: &NetPacket<I>,
|
||||
_out: &mut NetPacket<O>,
|
||||
) -> anyhow::Result<()> {
|
||||
Err(anyhow!("Unsupported decompress"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "lz4_compress", feature = "zstd_compress"))]
|
||||
impl Compressor {
|
||||
pub fn compress<I: AsRef<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
&self,
|
||||
in_net_packet: &NetPacket<I>,
|
||||
out: &mut NetPacket<O>,
|
||||
) -> anyhow::Result<bool> {
|
||||
match self {
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
Compressor::Lz4 => {
|
||||
if in_net_packet.data_len() < 128 {
|
||||
return Ok(false);
|
||||
}
|
||||
Lz4Compressor::compress(in_net_packet, out)?;
|
||||
let mut compression_extension_tail = out.append_compression_extension_tail()?;
|
||||
compression_extension_tail.set_algorithm(CompressionAlgorithm::Lz4);
|
||||
//压缩没效果,则放弃压缩
|
||||
if out.data_len() >= in_net_packet.data_len() - 16 {
|
||||
return Ok(false);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
Compressor::Zstd(level) => {
|
||||
if in_net_packet.data_len() < 128 {
|
||||
return Ok(false);
|
||||
}
|
||||
ZstdCompressor::compress(*level, in_net_packet, out)?;
|
||||
let mut compression_extension_tail = out.append_compression_extension_tail()?;
|
||||
compression_extension_tail.set_algorithm(CompressionAlgorithm::Zstd);
|
||||
//压缩没效果,则放弃压缩
|
||||
if out.data_len() >= in_net_packet.data_len() - 16 {
|
||||
return Ok(false);
|
||||
}
|
||||
return Ok(true);
|
||||
}
|
||||
Compressor::None => {}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
pub fn decompress<I: AsRef<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
algorithm: CompressionAlgorithm,
|
||||
in_net_packet: &NetPacket<I>,
|
||||
out: &mut NetPacket<O>,
|
||||
) -> anyhow::Result<()> {
|
||||
match algorithm {
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
CompressionAlgorithm::Lz4 => Lz4Compressor::decompress(in_net_packet, out),
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
CompressionAlgorithm::Zstd => ZstdCompressor::decompress(in_net_packet, out),
|
||||
_ => Err(anyhow!("Unknown decompress {:?}", algorithm)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
fn test_lz4() {
|
||||
use crate::protocol::extension::{CompressionAlgorithm, ExtensionTailPacket};
|
||||
let lz4 = Compressor::Lz4;
|
||||
let in_packet = NetPacket::new([
|
||||
65, 108, 105, 99, 101, 32, 119, 97, 116, 32, 98, 101, 103, 105, 110, 110, 105, 110, 103,
|
||||
32, 116, 111, 32, 103, 101, 116, 32, 118, 101, 114, 121, 32, 116, 105, 114, 101, 100, 32,
|
||||
111, 102, 32, 115, 105, 116, 116, 105, 110, 103, 32, 98, 121, 32, 104, 101, 114, 32, 115,
|
||||
105, 115, 116, 101, 114, 32, 111, 110, 32, 116, 104, 101, 32, 98, 97, 110, 107, 44, 32, 97,
|
||||
110, 100, 32, 111, 102, 32, 104, 97, 118, 105, 110, 103, 32, 110, 111, 116, 104, 105, 110,
|
||||
103, 32, 116, 111, 32, 100, 111, 58, 32, 111, 110, 99, 101, 32, 111, 114, 32, 116, 119,
|
||||
105, 99, 101, 32, 115, 104, 101, 32, 104, 97, 100, 32, 112, 101, 101, 112, 101, 100, 32,
|
||||
105, 110, 116, 111, 32, 116, 104, 101, 32, 98, 111, 111, 107, 32, 104, 101, 114, 32, 115,
|
||||
105, 115, 116, 101, 114, 32, 119, 97, 115, 32, 114, 101, 97, 100, 105, 110, 103, 44, 32,
|
||||
98, 117, 116, 32, 105, 116, 32, 104, 97, 100, 32, 110, 111, 32, 112, 105, 99, 116, 117,
|
||||
114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, 101, 114, 115, 97, 116, 105,
|
||||
])
|
||||
.unwrap();
|
||||
let mut out_packet = NetPacket::new([0; 1000]).unwrap();
|
||||
let mut src_out_packet = NetPacket::new([0; 1000]).unwrap();
|
||||
lz4.compress(&in_packet, &mut out_packet).unwrap();
|
||||
let tail = out_packet.split_tail_packet().unwrap();
|
||||
match tail {
|
||||
ExtensionTailPacket::Compression(c) => match c.algorithm() {
|
||||
CompressionAlgorithm::Lz4 => {
|
||||
Compressor::decompress(CompressionAlgorithm::Lz4, &out_packet, &mut src_out_packet)
|
||||
.unwrap();
|
||||
}
|
||||
_ => {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
assert!(!out_packet.is_extension());
|
||||
assert_eq!(in_packet.payload(), src_out_packet.payload())
|
||||
}
|
||||
#[test]
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
fn test_zstd() {
|
||||
use crate::protocol::extension::{CompressionAlgorithm, ExtensionTailPacket};
|
||||
let zstd = Compressor::Zstd(22);
|
||||
let in_packet = NetPacket::new([
|
||||
65, 108, 105, 99, 101, 32, 119, 97, 115, 32, 98, 101, 103, 105, 110, 110, 105, 110, 103,
|
||||
32, 116, 111, 32, 103, 101, 116, 32, 118, 101, 114, 121, 32, 116, 105, 114, 101, 100, 32,
|
||||
111, 102, 32, 115, 105, 116, 116, 105, 110, 103, 32, 98, 121, 32, 104, 101, 114, 32, 115,
|
||||
105, 115, 116, 101, 114, 32, 111, 110, 32, 116, 104, 101, 32, 98, 97, 110, 107, 44, 32, 97,
|
||||
110, 100, 32, 111, 102, 32, 104, 97, 118, 105, 110, 103, 32, 110, 111, 116, 104, 105, 110,
|
||||
103, 32, 116, 111, 32, 100, 111, 58, 32, 111, 110, 99, 101, 32, 111, 114, 32, 116, 119,
|
||||
105, 99, 101, 32, 115, 104, 101, 32, 104, 97, 100, 32, 112, 101, 101, 112, 101, 100, 32,
|
||||
105, 110, 116, 111, 32, 116, 104, 101, 32, 98, 111, 111, 107, 32, 104, 101, 114, 32, 115,
|
||||
105, 115, 116, 101, 114, 32, 119, 97, 115, 32, 114, 101, 97, 100, 105, 110, 103, 44, 32,
|
||||
98, 117, 116, 32, 105, 116, 32, 104, 97, 100, 32, 110, 111, 32, 112, 105, 99, 116, 117,
|
||||
114, 101, 115, 32, 111, 114, 32, 99, 111, 110, 118, 101, 114, 115, 97, 116, 105,
|
||||
])
|
||||
.unwrap();
|
||||
let mut out_packet = NetPacket::new([0; 1000]).unwrap();
|
||||
let mut src_out_packet = NetPacket::new([0; 1000]).unwrap();
|
||||
zstd.compress(&in_packet, &mut out_packet).unwrap();
|
||||
let tail = out_packet.split_tail_packet().unwrap();
|
||||
match tail {
|
||||
ExtensionTailPacket::Compression(c) => match c.algorithm() {
|
||||
CompressionAlgorithm::Zstd => {
|
||||
Compressor::decompress(
|
||||
CompressionAlgorithm::Zstd,
|
||||
&out_packet,
|
||||
&mut src_out_packet,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
_ => {
|
||||
unimplemented!()
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
assert!(!out_packet.is_extension());
|
||||
assert_eq!(in_packet.payload(), src_out_packet.payload())
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
use crate::protocol::NetPacket;
|
||||
use anyhow::anyhow;
|
||||
use zstd::zstd_safe::CompressionLevel;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ZstdCompressor;
|
||||
|
||||
impl ZstdCompressor {
|
||||
pub fn compress<I: AsRef<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
compression_level: CompressionLevel,
|
||||
in_net_packet: &NetPacket<I>,
|
||||
out: &mut NetPacket<O>,
|
||||
) -> anyhow::Result<()> {
|
||||
out.set_data_len_max();
|
||||
let len = match zstd::zstd_safe::compress(
|
||||
out.payload_mut(),
|
||||
in_net_packet.payload(),
|
||||
compression_level,
|
||||
) {
|
||||
Ok(len) => len,
|
||||
Err(e) => Err(anyhow!("zstd compress {}", e))?,
|
||||
};
|
||||
out.set_payload_len(len)?;
|
||||
Ok(())
|
||||
}
|
||||
pub fn decompress<I: AsRef<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
in_net_packet: &NetPacket<I>,
|
||||
out: &mut NetPacket<O>,
|
||||
) -> anyhow::Result<()> {
|
||||
out.set_data_len_max();
|
||||
let len = match zstd::zstd_safe::decompress(out.payload_mut(), in_net_packet.payload()) {
|
||||
Ok(len) => len,
|
||||
Err(e) => Err(anyhow!("zstd decompress {}", e))?,
|
||||
};
|
||||
out.set_payload_len(len)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,538 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
use rand::Rng;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::idle::Idle;
|
||||
use crate::channel::punch::{NatInfo, Punch};
|
||||
use crate::channel::sender::IpPacketSender;
|
||||
use crate::channel::{init_channel, init_context, Route, RouteKey};
|
||||
use crate::cipher::Cipher;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::cipher::RsaCipher;
|
||||
use crate::compression::Compressor;
|
||||
use crate::core::Config;
|
||||
use crate::external_route::{AllowExternalRoute, ExternalRoute};
|
||||
use crate::handle::handshaker::Handshake;
|
||||
use crate::handle::maintain::PunchReceiver;
|
||||
use crate::handle::recv_data::RecvDataHandler;
|
||||
use crate::handle::{maintain, BaseConfigInfo, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::nat::NatTest;
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
use crate::tun_tap_device::tun_create_helper::{DeviceAdapter, TunDeviceHelper};
|
||||
use crate::tun_tap_device::vnt_device::DeviceWrite;
|
||||
use crate::util::limit::TrafficMeterMultiAddress;
|
||||
use crate::util::{Scheduler, StopManager};
|
||||
use crate::{nat, VntCallback};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Vnt {
|
||||
inner: Arc<VntInner>,
|
||||
}
|
||||
|
||||
impl Vnt {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
pub fn new<Call: VntCallback>(config: Config, callback: Call) -> anyhow::Result<Self> {
|
||||
let inner = Arc::new(VntInner::new(config, callback)?);
|
||||
Ok(Self { inner })
|
||||
}
|
||||
#[cfg(not(feature = "integrated_tun"))]
|
||||
pub fn new_device<Call: VntCallback, Device: DeviceWrite>(
|
||||
config: Config,
|
||||
callback: Call,
|
||||
device: Device,
|
||||
) -> anyhow::Result<Self> {
|
||||
let inner = Arc::new(VntInner::new_device(config, callback, device)?);
|
||||
Ok(Self { inner })
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Vnt {
|
||||
type Target = VntInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VntInner {
|
||||
stop_manager: StopManager,
|
||||
config: Config,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
nat_test: NatTest,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
context: Arc<Mutex<Option<ChannelContext>>>,
|
||||
peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>>,
|
||||
client_secret_hash: Option<[u8; 16]>,
|
||||
compressor: Compressor,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
external_route: ExternalRoute,
|
||||
up_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
down_traffic_meter: Option<TrafficMeterMultiAddress>,
|
||||
}
|
||||
|
||||
impl VntInner {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
pub fn new<Call: VntCallback>(config: Config, callback: Call) -> anyhow::Result<Self> {
|
||||
VntInner::new_device0(config, callback, DeviceAdapter::default())
|
||||
}
|
||||
#[cfg(not(feature = "integrated_tun"))]
|
||||
pub fn new_device<Call: VntCallback, Device: DeviceWrite>(
|
||||
config: Config,
|
||||
callback: Call,
|
||||
device: Device,
|
||||
) -> anyhow::Result<Self> {
|
||||
VntInner::new_device0(config, callback, device)
|
||||
}
|
||||
fn new_device0<Call: VntCallback, Device: DeviceWrite>(
|
||||
config: Config,
|
||||
callback: Call,
|
||||
device: Device,
|
||||
) -> anyhow::Result<Self> {
|
||||
log::info!("config: {:?}", config);
|
||||
let (up_traffic_meter, down_traffic_meter) = if config.enable_traffic {
|
||||
(
|
||||
Some(TrafficMeterMultiAddress::default()),
|
||||
Some(TrafficMeterMultiAddress::default()),
|
||||
)
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
//服务端非对称加密
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
let rsa_cipher: Arc<Mutex<Option<RsaCipher>>> = Arc::new(Mutex::new(None));
|
||||
//服务端对称加密
|
||||
let server_cipher: Cipher = if config.server_encrypt {
|
||||
let mut key = [0u8; 32];
|
||||
rand::thread_rng().fill(&mut key);
|
||||
Cipher::new_key(key, config.token.clone())?
|
||||
} else {
|
||||
Cipher::None
|
||||
};
|
||||
let finger = if config.finger {
|
||||
Some(config.token.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
//客户端对称加密
|
||||
let client_cipher =
|
||||
Cipher::new_password(config.cipher_model, config.password.clone(), finger)?;
|
||||
//当前设备信息
|
||||
let current_device = Arc::new(AtomicCell::new(CurrentDeviceInfo::new0(
|
||||
config.server_address,
|
||||
)));
|
||||
//设备列表
|
||||
let device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>> =
|
||||
Arc::new(Mutex::new((0, HashMap::with_capacity(16))));
|
||||
let local_ipv4 = if let Some(local_ipv4) = config.local_ipv4 {
|
||||
Some(local_ipv4)
|
||||
} else {
|
||||
nat::local_ipv4()
|
||||
};
|
||||
let default_interface = config.local_interface.clone();
|
||||
|
||||
//基础信息
|
||||
let config_info = BaseConfigInfo::new(
|
||||
config.name.clone(),
|
||||
config.token.clone(),
|
||||
config.ip,
|
||||
config.password_hash(),
|
||||
config.server_encrypt,
|
||||
config.device_id.clone(),
|
||||
config.server_address_str.clone(),
|
||||
config.name_servers.clone(),
|
||||
config.mtu.unwrap_or(1420),
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
config.tap,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
config.device_name.clone(),
|
||||
config.allow_wire_guard,
|
||||
default_interface.clone(),
|
||||
);
|
||||
// 服务停止管理器
|
||||
let stop_manager = {
|
||||
let callback = callback.clone();
|
||||
StopManager::new(move || callback.stop())
|
||||
};
|
||||
#[cfg(feature = "port_mapping")]
|
||||
crate::port_mapping::start_port_mapping(
|
||||
stop_manager.clone(),
|
||||
config.port_mapping_list.clone(),
|
||||
)?;
|
||||
let mut ports = config.ports.as_ref().map_or(vec![0, 0], |v| {
|
||||
if v.is_empty() {
|
||||
vec![0, 0]
|
||||
} else {
|
||||
v.clone()
|
||||
}
|
||||
});
|
||||
if config.use_channel_type.is_only_relay() {
|
||||
//中继模式下只监听一个端口就够了
|
||||
ports.truncate(1);
|
||||
}
|
||||
//通道上下文
|
||||
let (context, tcp_listener) = init_context(
|
||||
ports,
|
||||
config.use_channel_type,
|
||||
config.first_latency,
|
||||
config.protocol,
|
||||
config.packet_loss_rate,
|
||||
config.packet_delay,
|
||||
default_interface,
|
||||
up_traffic_meter.clone(),
|
||||
down_traffic_meter.clone(),
|
||||
)?;
|
||||
let local_ipv6 = nat::local_ipv6();
|
||||
let udp_ports = context.main_local_udp_port()?;
|
||||
let tcp_port = tcp_listener.local_addr()?.port();
|
||||
//nat检测工具
|
||||
let nat_test = NatTest::new(
|
||||
context.channel_num(),
|
||||
config.stun_server.clone(),
|
||||
local_ipv4,
|
||||
local_ipv6,
|
||||
udp_ports,
|
||||
tcp_port,
|
||||
config.local_ipv4.is_none(),
|
||||
config.punch_model,
|
||||
);
|
||||
// 定时器
|
||||
let scheduler = Scheduler::new(stop_manager.clone())?;
|
||||
let external_route = ExternalRoute::new(config.in_ips.clone());
|
||||
let out_external_route = AllowExternalRoute::new(config.out_ips.clone());
|
||||
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
let proxy_map = if !config.out_ips.is_empty() && !config.no_proxy {
|
||||
Some(crate::ip_proxy::init_proxy(
|
||||
context.clone(),
|
||||
stop_manager.clone(),
|
||||
current_device.clone(),
|
||||
client_cipher.clone(),
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let (punch_sender, punch_receiver) = maintain::punch_channel();
|
||||
let peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>> =
|
||||
Arc::new(RwLock::new(HashMap::with_capacity(16)));
|
||||
let handshake = Handshake::new(
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher.clone(),
|
||||
);
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
let tun_device_helper = {
|
||||
TunDeviceHelper::new(
|
||||
stop_manager.clone(),
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
external_route.clone(),
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
proxy_map.clone(),
|
||||
client_cipher.clone(),
|
||||
server_cipher.clone(),
|
||||
device_map.clone(),
|
||||
config.compressor,
|
||||
device.clone().into_device_adapter(),
|
||||
)
|
||||
};
|
||||
|
||||
let handler = RecvDataHandler::new(
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher,
|
||||
server_cipher.clone(),
|
||||
client_cipher.clone(),
|
||||
current_device.clone(),
|
||||
device,
|
||||
device_map.clone(),
|
||||
config_info.clone(),
|
||||
nat_test.clone(),
|
||||
callback.clone(),
|
||||
punch_sender,
|
||||
peer_nat_info_map.clone(),
|
||||
external_route.clone(),
|
||||
out_external_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
proxy_map.clone(),
|
||||
handshake.clone(),
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
tun_device_helper,
|
||||
);
|
||||
|
||||
//初始化网络数据通道
|
||||
let (udp_socket_sender, connect_util) =
|
||||
init_channel(tcp_listener, context.clone(), stop_manager.clone(), handler)?;
|
||||
// 打洞逻辑
|
||||
let punch = Punch::new(
|
||||
context.clone(),
|
||||
config.punch_model,
|
||||
connect_util.clone(),
|
||||
nat_test.clone(),
|
||||
current_device.clone(),
|
||||
);
|
||||
|
||||
// #[cfg(not(target_os = "android"))]
|
||||
// tun_helper.start(device)?;
|
||||
|
||||
maintain::idle_gateway(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
config_info.clone(),
|
||||
connect_util.clone(),
|
||||
callback.clone(),
|
||||
0,
|
||||
handshake,
|
||||
);
|
||||
{
|
||||
let context = context.clone();
|
||||
let nat_test = nat_test.clone();
|
||||
let device_map = device_map.clone();
|
||||
let config_info = config_info.clone();
|
||||
let current_device = current_device.clone();
|
||||
if !config.use_channel_type.is_only_relay() {
|
||||
// 定时nat探测
|
||||
maintain::retrieve_nat_type(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
nat_test.clone(),
|
||||
udp_socket_sender,
|
||||
);
|
||||
}
|
||||
let client_cipher = client_cipher.clone();
|
||||
let server_cipher = server_cipher.clone();
|
||||
//延迟启动
|
||||
scheduler.timeout(Duration::from_secs(1), move |scheduler| {
|
||||
start(
|
||||
scheduler,
|
||||
context,
|
||||
nat_test,
|
||||
device_map,
|
||||
current_device,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
punch_receiver,
|
||||
config_info,
|
||||
punch,
|
||||
callback,
|
||||
);
|
||||
});
|
||||
}
|
||||
let compressor = config.compressor;
|
||||
Ok(Self {
|
||||
stop_manager,
|
||||
config,
|
||||
current_device,
|
||||
nat_test,
|
||||
device_map,
|
||||
context: Arc::new(Mutex::new(Some(context))),
|
||||
peer_nat_info_map,
|
||||
client_secret_hash: config_info.client_secret_hash,
|
||||
compressor,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
external_route,
|
||||
up_traffic_meter,
|
||||
down_traffic_meter,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start<Call: VntCallback>(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
nat_test: NatTest,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
punch_receiver: PunchReceiver,
|
||||
config_info: BaseConfigInfo,
|
||||
punch: Punch,
|
||||
callback: Call,
|
||||
) {
|
||||
// 定时心跳
|
||||
maintain::heartbeat(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
device_map.clone(),
|
||||
client_cipher.clone(),
|
||||
server_cipher.clone(),
|
||||
);
|
||||
// 路由空闲检测逻辑
|
||||
let idle = Idle::new(Duration::from_secs(10), context.clone());
|
||||
// 定时空闲检查
|
||||
maintain::idle_route(
|
||||
&scheduler,
|
||||
idle,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
callback,
|
||||
);
|
||||
// 定时客户端中继检测
|
||||
if !context.use_channel_type().is_only_p2p() {
|
||||
maintain::client_relay(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
device_map.clone(),
|
||||
client_cipher.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
if !context.use_channel_type().is_only_relay() {
|
||||
// 定时地址探测
|
||||
maintain::addr_request(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
current_device.clone(),
|
||||
nat_test.clone(),
|
||||
config_info.clone(),
|
||||
);
|
||||
// 定时打洞
|
||||
maintain::punch(
|
||||
&scheduler,
|
||||
context.clone(),
|
||||
nat_test.clone(),
|
||||
device_map.clone(),
|
||||
current_device.clone(),
|
||||
client_cipher.clone(),
|
||||
punch_receiver,
|
||||
punch,
|
||||
);
|
||||
}
|
||||
maintain::up_status(scheduler, context.clone(), current_device.clone())
|
||||
}
|
||||
|
||||
impl VntInner {
|
||||
pub fn name(&self) -> &str {
|
||||
&self.config.name
|
||||
}
|
||||
pub fn server_encrypt(&self) -> bool {
|
||||
self.config.server_encrypt
|
||||
}
|
||||
pub fn client_encrypt(&self) -> bool {
|
||||
self.config.password.is_some()
|
||||
}
|
||||
pub fn client_encrypt_hash(&self) -> Option<&[u8]> {
|
||||
self.client_secret_hash.as_ref().map(|v| v.as_ref())
|
||||
}
|
||||
pub fn current_device(&self) -> CurrentDeviceInfo {
|
||||
self.current_device.load()
|
||||
}
|
||||
pub fn current_device_info(&self) -> Arc<AtomicCell<CurrentDeviceInfo>> {
|
||||
self.current_device.clone()
|
||||
}
|
||||
pub fn peer_nat_info(&self, ip: &Ipv4Addr) -> Option<NatInfo> {
|
||||
self.peer_nat_info_map.read().get(ip).cloned()
|
||||
}
|
||||
pub fn connection_status(&self) -> ConnectStatus {
|
||||
self.current_device.load().status
|
||||
}
|
||||
pub fn nat_info(&self) -> NatInfo {
|
||||
self.nat_test.nat_info()
|
||||
}
|
||||
pub fn device_list(&self) -> Vec<PeerDeviceInfo> {
|
||||
let device_list_lock = self.device_map.lock();
|
||||
let (_epoch, device_list) = device_list_lock.clone();
|
||||
drop(device_list_lock);
|
||||
device_list.into_values().collect()
|
||||
}
|
||||
pub fn route(&self, ip: &Ipv4Addr) -> Option<Route> {
|
||||
self.context.lock().as_ref()?.route_table.route_one(ip)
|
||||
}
|
||||
pub fn is_gateway(&self, ip: &Ipv4Addr) -> bool {
|
||||
self.current_device.load().is_gateway(ip)
|
||||
}
|
||||
pub fn route_key(&self, route_key: &RouteKey) -> Option<Ipv4Addr> {
|
||||
self.context
|
||||
.lock()
|
||||
.as_ref()?
|
||||
.route_table
|
||||
.route_to_id(route_key)
|
||||
}
|
||||
pub fn route_table(&self) -> Vec<(Ipv4Addr, Vec<Route>)> {
|
||||
if let Some(context) = self.context.lock().as_ref() {
|
||||
context.route_table.route_table()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
pub fn up_stream(&self) -> u64 {
|
||||
self.up_traffic_meter.as_ref().map_or(0, |v| v.total())
|
||||
}
|
||||
pub fn up_stream_all(&self) -> Option<(u64, HashMap<Ipv4Addr, u64>)> {
|
||||
self.up_traffic_meter.as_ref().map(|v| v.get_all())
|
||||
}
|
||||
pub fn up_stream_history(&self) -> Option<(u64, HashMap<Ipv4Addr, (u64, Vec<usize>)>)> {
|
||||
self.up_traffic_meter.as_ref().map(|v| v.get_all_history())
|
||||
}
|
||||
pub fn down_stream(&self) -> u64 {
|
||||
self.down_traffic_meter.as_ref().map_or(0, |v| v.total())
|
||||
}
|
||||
pub fn down_stream_all(&self) -> Option<(u64, HashMap<Ipv4Addr, u64>)> {
|
||||
self.down_traffic_meter.as_ref().map(|v| v.get_all())
|
||||
}
|
||||
pub fn down_stream_history(&self) -> Option<(u64, HashMap<Ipv4Addr, (u64, Vec<usize>)>)> {
|
||||
self.down_traffic_meter
|
||||
.as_ref()
|
||||
.map(|v| v.get_all_history())
|
||||
}
|
||||
pub fn stop(&self) {
|
||||
//退出协助回收资源
|
||||
let _ = self.context.lock().take();
|
||||
self.stop_manager.stop()
|
||||
}
|
||||
pub fn is_stopped(&self) -> bool {
|
||||
self.stop_manager.is_stopped()
|
||||
}
|
||||
pub fn add_stop_listener<F>(&self, name: String, f: F) -> anyhow::Result<crate::util::Worker>
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
self.stop_manager.add_listener(name, f)
|
||||
}
|
||||
pub fn wait(&self) {
|
||||
self.stop_manager.wait()
|
||||
}
|
||||
pub fn wait_timeout(&self, dur: Duration) -> bool {
|
||||
self.stop_manager.wait_timeout(dur)
|
||||
}
|
||||
pub fn config(&self) -> &Config {
|
||||
&self.config
|
||||
}
|
||||
pub fn ipv4_packet_sender(&self) -> Option<IpPacketSender> {
|
||||
if let Some(c) = self.context.lock().as_ref() {
|
||||
Some(IpPacketSender::new(
|
||||
c.clone(),
|
||||
self.current_device.clone(),
|
||||
self.compressor.clone(),
|
||||
self.client_cipher.clone(),
|
||||
self.server_cipher.clone(),
|
||||
self.external_route.clone(),
|
||||
self.device_map.clone(),
|
||||
self.config.allow_wire_guard,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VntInner {
|
||||
fn drop(&mut self) {
|
||||
self.stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
use anyhow::anyhow;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::str::FromStr;
|
||||
|
||||
pub use conn::Vnt;
|
||||
|
||||
use crate::channel::punch::PunchModel;
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use crate::channel::{ConnectProtocol, UseChannelType};
|
||||
use crate::cipher::CipherModel;
|
||||
use crate::compression::Compressor;
|
||||
use crate::util::{address_choose, dns_query_all};
|
||||
|
||||
mod conn;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Config {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub tap: bool,
|
||||
pub token: String,
|
||||
pub device_id: String,
|
||||
pub name: String,
|
||||
pub server_address: SocketAddr,
|
||||
pub server_address_str: String,
|
||||
pub name_servers: Vec<String>,
|
||||
pub stun_server: Vec<String>,
|
||||
pub in_ips: Vec<(u32, u32, Ipv4Addr)>,
|
||||
pub out_ips: Vec<(u32, u32)>,
|
||||
pub password: Option<String>,
|
||||
pub mtu: Option<u32>,
|
||||
pub protocol: ConnectProtocol,
|
||||
pub ip: Option<Ipv4Addr>,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
pub no_proxy: bool,
|
||||
pub server_encrypt: bool,
|
||||
pub cipher_model: CipherModel,
|
||||
pub finger: bool,
|
||||
pub punch_model: PunchModel,
|
||||
pub ports: Option<Vec<u16>>,
|
||||
pub first_latency: bool,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
pub device_name: Option<String>,
|
||||
pub use_channel_type: UseChannelType,
|
||||
//控制丢包率
|
||||
pub packet_loss_rate: Option<f64>,
|
||||
pub packet_delay: u32,
|
||||
// 端口映射
|
||||
#[cfg(feature = "port_mapping")]
|
||||
pub port_mapping_list: Vec<(bool, SocketAddr, String)>,
|
||||
pub compressor: Compressor,
|
||||
pub enable_traffic: bool,
|
||||
pub allow_wire_guard: bool,
|
||||
pub local_ipv4: Option<Ipv4Addr>,
|
||||
pub local_interface: LocalInterface,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new(
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
tap: bool,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
server_address_str: String,
|
||||
mut name_servers: Vec<String>,
|
||||
mut stun_server: Vec<String>,
|
||||
mut in_ips: Vec<(u32, u32, Ipv4Addr)>,
|
||||
out_ips: Vec<(u32, u32)>,
|
||||
password: Option<String>,
|
||||
mtu: Option<u32>,
|
||||
ip: Option<Ipv4Addr>,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
no_proxy: bool,
|
||||
server_encrypt: bool,
|
||||
cipher_model: CipherModel,
|
||||
finger: bool,
|
||||
punch_model: PunchModel,
|
||||
ports: Option<Vec<u16>>,
|
||||
first_latency: bool,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
device_name: Option<String>,
|
||||
use_channel_type: UseChannelType,
|
||||
packet_loss_rate: Option<f64>,
|
||||
packet_delay: u32,
|
||||
// 例如 [udp:127.0.0.1:80->10.26.0.10:8080,tcp:127.0.0.1:80->10.26.0.10:8080]
|
||||
#[cfg(feature = "port_mapping")] port_mapping_list: Vec<String>,
|
||||
compressor: Compressor,
|
||||
enable_traffic: bool,
|
||||
// 允许传递wg流量
|
||||
allow_wire_guard: bool,
|
||||
local_dev: Option<String>,
|
||||
) -> anyhow::Result<Self> {
|
||||
for x in stun_server.iter_mut() {
|
||||
if !x.contains(":") {
|
||||
x.push_str(":3478");
|
||||
}
|
||||
}
|
||||
for x in name_servers.iter_mut() {
|
||||
if Ipv6Addr::from_str(x).is_ok() {
|
||||
x.push_str(":53");
|
||||
} else if !x.contains(":") {
|
||||
x.push_str(":53");
|
||||
}
|
||||
}
|
||||
if token.is_empty() || token.len() > 128 {
|
||||
return Err(anyhow!("token too long"));
|
||||
}
|
||||
if device_id.is_empty() || device_id.len() > 128 {
|
||||
return Err(anyhow!("device_id too long"));
|
||||
}
|
||||
if name.is_empty() || name.len() > 128 {
|
||||
return Err(anyhow!("name too long"));
|
||||
}
|
||||
let mut server_address_str = server_address_str.to_lowercase();
|
||||
let mut _query_dns = true;
|
||||
let mut protocol = ConnectProtocol::UDP;
|
||||
if server_address_str.starts_with("ws://") {
|
||||
#[cfg(not(feature = "ws"))]
|
||||
Err(anyhow!("Ws not supported"))?;
|
||||
protocol = ConnectProtocol::WS;
|
||||
_query_dns = false;
|
||||
}
|
||||
if server_address_str.starts_with("wss://") {
|
||||
#[cfg(not(feature = "wss"))]
|
||||
Err(anyhow!("Wss not supported"))?;
|
||||
protocol = ConnectProtocol::WSS;
|
||||
_query_dns = false;
|
||||
}
|
||||
|
||||
let mut server_address = "0.0.0.0:0".parse().unwrap();
|
||||
if _query_dns {
|
||||
if let Some(s) = server_address_str.strip_prefix("udp://") {
|
||||
server_address_str = s.to_string();
|
||||
} else if let Some(s) = server_address_str.strip_prefix("tcp://") {
|
||||
server_address_str = s.to_string();
|
||||
protocol = ConnectProtocol::TCP;
|
||||
}
|
||||
let address_result = dns_query_all(
|
||||
&server_address_str,
|
||||
name_servers.clone(),
|
||||
&LocalInterface::default(),
|
||||
);
|
||||
match address_result {
|
||||
Ok(address) => match address_choose(address) {
|
||||
Ok(resolved_address) => {
|
||||
server_address = resolved_address;
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to choose address: {}", e);
|
||||
println!("Failed to choose address: {}", e);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("DNS query failed: {}", e);
|
||||
println!("DNS query failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "port_mapping")]
|
||||
let port_mapping_list = crate::port_mapping::convert(port_mapping_list)?;
|
||||
|
||||
for (dest, mask, _) in &mut in_ips {
|
||||
*dest = *mask & *dest;
|
||||
}
|
||||
in_ips.sort_by(|(dest1, _, _), (dest2, _, _)| dest2.cmp(dest1));
|
||||
let (local_interface, local_ipv4) = if let Some(local_dev) = local_dev {
|
||||
let (default_interface, ip) = crate::channel::socket::get_interface(local_dev)?;
|
||||
log::info!("default_interface = {:?} local_ip= {ip}", default_interface);
|
||||
(default_interface, Some(ip))
|
||||
} else {
|
||||
(LocalInterface::default(), None)
|
||||
};
|
||||
Ok(Self {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
tap,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
server_address,
|
||||
server_address_str,
|
||||
name_servers,
|
||||
stun_server,
|
||||
in_ips,
|
||||
out_ips,
|
||||
password,
|
||||
mtu,
|
||||
protocol,
|
||||
ip,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
no_proxy,
|
||||
server_encrypt,
|
||||
cipher_model,
|
||||
finger,
|
||||
punch_model,
|
||||
ports,
|
||||
first_latency,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(not(target_os = "android"))]
|
||||
device_name,
|
||||
use_channel_type,
|
||||
packet_loss_rate,
|
||||
packet_delay,
|
||||
#[cfg(feature = "port_mapping")]
|
||||
port_mapping_list,
|
||||
compressor,
|
||||
enable_traffic,
|
||||
allow_wire_guard,
|
||||
local_ipv4,
|
||||
local_interface,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn password_hash(&self) -> Option<[u8; 16]> {
|
||||
if let Some(p) = self.password.as_ref() {
|
||||
match self.cipher_model {
|
||||
CipherModel::Xor => {
|
||||
let key = crate::cipher::simple_hash(&format!("Xor{}{}", p, self.token));
|
||||
Some(key[16..].try_into().unwrap())
|
||||
}
|
||||
CipherModel::None => None,
|
||||
#[cfg(cipher)]
|
||||
_ => {
|
||||
use sha2::Digest;
|
||||
let mut hasher = sha2::Sha256::new();
|
||||
hasher.update(self.cipher_model.to_string().as_bytes());
|
||||
hasher.update(p.as_bytes());
|
||||
hasher.update(self.token.as_bytes());
|
||||
let key: [u8; 32] = hasher.finalize().into();
|
||||
Some(key[16..].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
// 目标网段,子网掩码,网关
|
||||
#[derive(Clone)]
|
||||
pub struct ExternalRoute {
|
||||
route_table: Vec<(u32, u32, Ipv4Addr)>,
|
||||
}
|
||||
|
||||
impl ExternalRoute {
|
||||
pub fn new(mut route_table: Vec<(u32, u32, Ipv4Addr)>) -> Self {
|
||||
for (dest, mask, _) in &mut route_table {
|
||||
*dest = *mask & *dest;
|
||||
}
|
||||
route_table.sort_by(|(dest1, _, _), (dest2, _, _)| dest2.cmp(dest1));
|
||||
Self { route_table }
|
||||
}
|
||||
pub fn route(&self, ip: &Ipv4Addr) -> Option<Ipv4Addr> {
|
||||
if self.route_table.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let ip = u32::from_be_bytes(ip.octets());
|
||||
for (dest, mask, gateway) in self.route_table.iter() {
|
||||
if *mask & ip == *dest {
|
||||
return Some(*gateway);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
pub fn to_route(&self) -> Vec<(Ipv4Addr, Ipv4Addr)> {
|
||||
self.route_table
|
||||
.iter()
|
||||
.map(|(dest, mask, _)| (Ipv4Addr::from(*dest), Ipv4Addr::from(*mask)))
|
||||
.collect::<Vec<(Ipv4Addr, Ipv4Addr)>>()
|
||||
}
|
||||
}
|
||||
|
||||
// 目标网段,子网掩码
|
||||
#[derive(Clone)]
|
||||
pub struct AllowExternalRoute {
|
||||
route_table: Arc<Vec<(u32, u32)>>,
|
||||
}
|
||||
|
||||
impl AllowExternalRoute {
|
||||
pub fn new(mut route_table: Vec<(u32, u32)>) -> Self {
|
||||
for (dest, mask) in &mut route_table {
|
||||
*dest = *mask & *dest;
|
||||
}
|
||||
route_table.sort_by(|(dest1, _), (dest2, _)| dest2.cmp(dest1));
|
||||
Self {
|
||||
route_table: Arc::new(route_table),
|
||||
}
|
||||
}
|
||||
pub fn allow(&self, ip: &Ipv4Addr) -> bool {
|
||||
if self.route_table.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let ip = u32::from_be_bytes(ip.octets());
|
||||
for (dest, mask) in self.route_table.iter() {
|
||||
if *mask & ip == *mask & *dest {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
use crate::handle::PeerDeviceStatus;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use rsa::RsaPublicKey;
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
#[derive(Debug)]
|
||||
pub struct DeviceInfo {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
impl Display for DeviceInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!("name={} ,version={}", self.name, self.version))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
impl DeviceInfo {
|
||||
pub fn new(name: String, version: String) -> Self {
|
||||
return Self { name, version };
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConnectInfo {
|
||||
// 第几次连接,从1开始
|
||||
pub count: usize,
|
||||
// 服务端地址
|
||||
pub address: SocketAddr,
|
||||
}
|
||||
|
||||
impl Display for ConnectInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!("count={} ,address={}", self.count, self.address))
|
||||
}
|
||||
}
|
||||
|
||||
impl ConnectInfo {
|
||||
pub fn new(count: usize, address: SocketAddr) -> Self {
|
||||
Self { count, address }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HandshakeInfo {
|
||||
//服务端公钥
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub public_key: Option<RsaPublicKey>,
|
||||
//服务端指纹
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub finger: Option<String>,
|
||||
//服务端版本
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
impl Display for HandshakeInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
return match &self.finger {
|
||||
None => f.write_str(&format!("no_secret server version={}", self.version)),
|
||||
Some(finger) => f.write_str(&format!(
|
||||
"finger={} ,server version={}",
|
||||
finger, self.version
|
||||
)),
|
||||
};
|
||||
#[cfg(not(feature = "server_encrypt"))]
|
||||
f.write_str(&format!("server version={}", self.version))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
impl HandshakeInfo {
|
||||
pub fn new(public_key: RsaPublicKey, finger: String, version: String) -> Self {
|
||||
Self {
|
||||
public_key: Some(public_key),
|
||||
finger: Some(finger),
|
||||
version,
|
||||
}
|
||||
}
|
||||
pub fn new_no_secret(version: String) -> Self {
|
||||
Self {
|
||||
public_key: None,
|
||||
finger: None,
|
||||
version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "server_encrypt"))]
|
||||
impl HandshakeInfo {
|
||||
pub fn new_no_secret(version: String) -> Self {
|
||||
Self { version }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RegisterInfo {
|
||||
//本机虚拟IP
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
//子网掩码
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
//虚拟网关
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
}
|
||||
|
||||
impl Display for RegisterInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!(
|
||||
"ip={} ,netmask={} ,gateway={}",
|
||||
self.virtual_ip, self.virtual_netmask, self.virtual_gateway,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl RegisterInfo {
|
||||
pub fn new(virtual_ip: Ipv4Addr, virtual_netmask: Ipv4Addr, virtual_gateway: Ipv4Addr) -> Self {
|
||||
Self {
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorInfo {
|
||||
pub code: ErrorType,
|
||||
pub msg: Option<String>,
|
||||
pub source: Option<io::Error>,
|
||||
}
|
||||
|
||||
impl Display for ErrorInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!("ErrorType={:?} ", self.code))?;
|
||||
if let Some(msg) = &self.msg {
|
||||
f.write_str(&format!(",msg={:?} ", msg))?;
|
||||
}
|
||||
if let Some(source) = &self.source {
|
||||
f.write_str(&format!(",source={:?} ", source))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorInfo {
|
||||
pub fn new(code: ErrorType) -> Self {
|
||||
Self {
|
||||
code,
|
||||
msg: None,
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
pub fn new_msg(code: ErrorType, msg: String) -> Self {
|
||||
Self {
|
||||
code,
|
||||
msg: Some(msg),
|
||||
source: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ErrorType {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
LocalIpExists,
|
||||
FailedToCrateDevice,
|
||||
Warn,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl Into<u8> for ErrorType {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
ErrorType::TokenError => 1,
|
||||
ErrorType::Disconnect => 2,
|
||||
ErrorType::AddressExhausted => 3,
|
||||
ErrorType::IpAlreadyExists => 4,
|
||||
ErrorType::InvalidIp => 5,
|
||||
ErrorType::LocalIpExists => 6,
|
||||
ErrorType::FailedToCrateDevice => 101,
|
||||
ErrorType::Warn => 102,
|
||||
ErrorType::Unknown => 255,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DeviceConfig {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub tap: bool,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
pub device_name: Option<String>,
|
||||
//虚拟网卡mtu值
|
||||
pub mtu: u32,
|
||||
//本机虚拟IP
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
//子网掩码
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
//虚拟网关
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
//虚拟网段
|
||||
pub virtual_network: Ipv4Addr,
|
||||
// 额外的路由
|
||||
pub external_route: Vec<(Ipv4Addr, Ipv4Addr)>,
|
||||
}
|
||||
|
||||
impl DeviceConfig {
|
||||
pub fn new(
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
tap: bool,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
device_name: Option<String>,
|
||||
mtu: u32,
|
||||
virtual_ip: Ipv4Addr,
|
||||
virtual_netmask: Ipv4Addr,
|
||||
virtual_gateway: Ipv4Addr,
|
||||
virtual_network: Ipv4Addr,
|
||||
external_route: Vec<(Ipv4Addr, Ipv4Addr)>,
|
||||
) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
tap,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
device_name,
|
||||
mtu,
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
virtual_network,
|
||||
external_route,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DeviceConfig {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!(
|
||||
"ip={} ,netmask={} ,gateway={}, external_route={:?}",
|
||||
self.virtual_ip, self.virtual_netmask, self.virtual_gateway, self.external_route
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PeerClientInfo {
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
pub name: String,
|
||||
pub status: PeerDeviceStatus,
|
||||
pub client_secret: bool,
|
||||
}
|
||||
|
||||
impl PeerClientInfo {
|
||||
pub fn new(
|
||||
virtual_ip: Ipv4Addr,
|
||||
name: String,
|
||||
status: PeerDeviceStatus,
|
||||
client_secret: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
virtual_ip,
|
||||
name,
|
||||
status,
|
||||
client_secret,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for PeerClientInfo {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str(&format!(
|
||||
"ip={} ,name={} ,status={:?}, client_secret={}",
|
||||
self.virtual_ip, self.name, self.status, self.client_secret
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait VntCallback: Clone + Send + Sync + 'static {
|
||||
/// 启动成功
|
||||
fn success(&self) {}
|
||||
|
||||
/// 创建网卡的信息
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
fn create_tun(&self, _info: DeviceInfo) {}
|
||||
/// 连接
|
||||
fn connect(&self, _info: ConnectInfo) {}
|
||||
/// 握手,返回false则拒绝握手,可在此处检查服务端信息
|
||||
fn handshake(&self, _info: HandshakeInfo) -> bool {
|
||||
true
|
||||
}
|
||||
/// 注册,返回false则拒绝注册
|
||||
fn register(&self, _info: RegisterInfo) -> bool {
|
||||
true
|
||||
}
|
||||
#[cfg(not(feature = "integrated_tun"))]
|
||||
fn create_device(&self, _info: DeviceConfig) {}
|
||||
#[cfg(target_os = "android")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
fn generate_tun(&self, _info: DeviceConfig) -> usize {
|
||||
0
|
||||
}
|
||||
fn peer_client_list(&self, _info: Vec<PeerClientInfo>) {}
|
||||
/// 异常信息
|
||||
fn error(&self, _info: ErrorInfo) {}
|
||||
/// 服务停止
|
||||
fn stop(&self) {}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
use crate::compression::Compressor;
|
||||
use crate::protocol::extension::ExtensionTailPacket;
|
||||
use crate::protocol::NetPacket;
|
||||
use anyhow::anyhow;
|
||||
|
||||
pub fn handle_extension_tail<I: AsRef<[u8]> + AsMut<[u8]>, O: AsRef<[u8]> + AsMut<[u8]>>(
|
||||
in_net_packet: &mut NetPacket<I>,
|
||||
out: &mut NetPacket<O>,
|
||||
) -> anyhow::Result<bool> {
|
||||
if in_net_packet.is_extension() {
|
||||
let tail_packet = in_net_packet.split_tail_packet()?;
|
||||
match tail_packet {
|
||||
ExtensionTailPacket::Compression(extension) => {
|
||||
let compression_algorithm = extension.algorithm();
|
||||
Compressor::decompress(compression_algorithm, &in_net_packet, out)?;
|
||||
out.head_mut().copy_from_slice(in_net_packet.head());
|
||||
Ok(true)
|
||||
}
|
||||
ExtensionTailPacket::Unknown => Err(anyhow!("Unknown decompress")),
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use parking_lot::Mutex;
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::cipher::RsaCipher;
|
||||
use crate::handle::{GATEWAY_IP, SELF_IP};
|
||||
use crate::proto::message::HandshakeRequest;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::proto::message::SecretHandshakeRequest;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::protocol::body::RSA_ENCRYPTION_RESERVED;
|
||||
use crate::protocol::{service_packet, NetPacket, Protocol, MAX_TTL};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Handshake {
|
||||
time: Arc<AtomicCell<Instant>>,
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher: Arc<Mutex<Option<RsaCipher>>>,
|
||||
}
|
||||
impl Handshake {
|
||||
pub fn new(
|
||||
#[cfg(feature = "server_encrypt")] rsa_cipher: Arc<Mutex<Option<RsaCipher>>>,
|
||||
) -> Self {
|
||||
Handshake {
|
||||
time: Arc::new(AtomicCell::new(
|
||||
Instant::now()
|
||||
.checked_sub(Duration::from_secs(60))
|
||||
.unwrap_or(Instant::now()),
|
||||
)),
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher,
|
||||
}
|
||||
}
|
||||
pub fn send(&self, context: &ChannelContext, secret: bool, addr: SocketAddr) -> io::Result<()> {
|
||||
let last = self.time.load();
|
||||
//短时间不重复发送
|
||||
if last.elapsed() < Duration::from_secs(3) {
|
||||
return Ok(());
|
||||
}
|
||||
let request_packet = self.handshake_request_packet(secret)?;
|
||||
log::info!("发送握手请求,secret={},{:?}", secret, addr);
|
||||
context.send_default(&request_packet, addr)?;
|
||||
self.time.store(Instant::now());
|
||||
Ok(())
|
||||
}
|
||||
/// 第一次握手数据
|
||||
pub fn handshake_request_packet(&self, secret: bool) -> io::Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = HandshakeRequest::new();
|
||||
request.secret = secret;
|
||||
request.version = crate::VNT_VERSION.to_string();
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
if let Some(finger) = self.rsa_cipher.lock().as_ref().map(|v| v.finger().clone()) {
|
||||
request.key_finger = finger;
|
||||
}
|
||||
let bytes = request.write_to_bytes().map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("handshake_request_packet {:?}", e),
|
||||
)
|
||||
})?;
|
||||
let buf = vec![0u8; 12 + bytes.len()];
|
||||
let mut net_packet = NetPacket::new(buf)?;
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_gateway_flag(true);
|
||||
net_packet.set_destination(GATEWAY_IP);
|
||||
net_packet.set_source(SELF_IP);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::HandshakeRequest.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_payload(&bytes)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
}
|
||||
|
||||
/// 第二次加密握手
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
pub fn secret_handshake_request_packet(
|
||||
rsa_cipher: &RsaCipher,
|
||||
token: String,
|
||||
key: &[u8],
|
||||
) -> io::Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = SecretHandshakeRequest::new();
|
||||
request.token = token;
|
||||
request.key = key.to_vec();
|
||||
let bytes = request.write_to_bytes().map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("secret_handshake_request_packet {:?}", e),
|
||||
)
|
||||
})?;
|
||||
let mut net_packet = NetPacket::new0(
|
||||
12 + bytes.len(),
|
||||
vec![0u8; 12 + bytes.len() + RSA_ENCRYPTION_RESERVED],
|
||||
)?;
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_gateway_flag(true);
|
||||
net_packet.set_destination(GATEWAY_IP);
|
||||
net_packet.set_source(SELF_IP);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::SecretHandshakeRequest.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_payload(&bytes)?;
|
||||
Ok(rsa_cipher.encrypt(&mut net_packet)?)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::punch::NatType;
|
||||
use crate::handle::{BaseConfigInfo, CurrentDeviceInfo};
|
||||
use crate::nat::NatTest;
|
||||
use crate::util::Scheduler;
|
||||
|
||||
pub fn addr_request(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
nat_test: NatTest,
|
||||
_config: BaseConfigInfo,
|
||||
) {
|
||||
pub_address_request(scheduler, context, current_device_info.clone(), nat_test, 0);
|
||||
}
|
||||
|
||||
fn pub_address_request(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
nat_test: NatTest,
|
||||
count: usize,
|
||||
) {
|
||||
let channel_num = context.channel_num();
|
||||
let index = count % channel_num;
|
||||
if let Err(e) = addr_request0(&context, ¤t_device_info, &nat_test, index) {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
let nat_info = nat_test.nat_info();
|
||||
let time = if !nat_info.public_ports.contains(&0) && !nat_info.public_ips.is_empty() {
|
||||
//对称网络探测端口没啥作用,把频率放低,(锥形网络也只在打洞前需要探测端口,后续可以改改)
|
||||
if nat_info.nat_type == NatType::Symmetric {
|
||||
600
|
||||
} else {
|
||||
if index == channel_num - 1 {
|
||||
19
|
||||
} else {
|
||||
9
|
||||
}
|
||||
}
|
||||
} else {
|
||||
3
|
||||
};
|
||||
|
||||
let rs = scheduler.timeout(Duration::from_secs(time), move |s| {
|
||||
pub_address_request(s, context, current_device_info, nat_test, index + 1)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
fn addr_request0(
|
||||
context: &ChannelContext,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
nat_test: &NatTest,
|
||||
index: usize,
|
||||
) -> anyhow::Result<()> {
|
||||
let current_dev = current_device.load();
|
||||
if current_dev.status.offline() {
|
||||
return Ok(());
|
||||
}
|
||||
let (data, addr) = nat_test.send_data()?;
|
||||
context.send_main_udp(index, &data, addr)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::control_packet::PingPacket;
|
||||
use crate::protocol::{control_packet, NetPacket, Protocol};
|
||||
use crate::util::Scheduler;
|
||||
|
||||
/// 定时发送心跳包
|
||||
pub fn heartbeat(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
) {
|
||||
heartbeat0(
|
||||
&context,
|
||||
¤t_device_info.load(),
|
||||
&device_map,
|
||||
&client_cipher,
|
||||
&server_cipher,
|
||||
);
|
||||
// 心跳包 3秒发送一次
|
||||
let rs = scheduler.timeout(Duration::from_secs(3), |s| {
|
||||
heartbeat(
|
||||
s,
|
||||
context,
|
||||
current_device_info,
|
||||
device_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
fn heartbeat0(
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
client_cipher: &Cipher,
|
||||
server_cipher: &Cipher,
|
||||
) {
|
||||
let gateway_ip = current_device.virtual_gateway;
|
||||
let src_ip = current_device.virtual_ip;
|
||||
let channel_num = context.channel_num();
|
||||
// 可能服务器ip发生变化,导致发送失败
|
||||
let mut is_send_gateway = false;
|
||||
match heartbeat_packet_server(device_map, server_cipher, src_ip, gateway_ip) {
|
||||
Ok(net_packet) => {
|
||||
if let Err(e) = context.send_default(&net_packet, current_device.connect_server) {
|
||||
log::warn!("heartbeat err={:?}", e)
|
||||
} else {
|
||||
is_send_gateway = true
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("heartbeat_packet err={:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
for (dest_ip, routes) in context.route_table.route_table() {
|
||||
let net_packet = if current_device.is_gateway(&dest_ip) {
|
||||
if is_send_gateway {
|
||||
continue;
|
||||
}
|
||||
heartbeat_packet_server(device_map, server_cipher, src_ip, gateway_ip)
|
||||
} else {
|
||||
heartbeat_packet_client(client_cipher, src_ip, dest_ip)
|
||||
};
|
||||
let net_packet = match net_packet {
|
||||
Ok(net_packet) => net_packet,
|
||||
Err(e) => {
|
||||
log::error!("heartbeat_packet err={:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
for (index, route) in routes.iter().enumerate() {
|
||||
let limit = if context.first_latency() {
|
||||
channel_num + 1
|
||||
} else {
|
||||
channel_num
|
||||
};
|
||||
if index >= limit {
|
||||
// 多余的通道不再发送心跳包,让它自动过期
|
||||
break;
|
||||
}
|
||||
if let Err(e) = context.send_by_key(&net_packet, route.route_key()) {
|
||||
log::warn!("heartbeat err={:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
let peer_list = { device_map.lock().1.clone() };
|
||||
for peer in peer_list.values() {
|
||||
if !peer.status.is_online() || peer.wireguard {
|
||||
continue;
|
||||
}
|
||||
if current_device.is_gateway(&peer.virtual_ip) {
|
||||
continue;
|
||||
}
|
||||
if current_device.status.offline() {
|
||||
continue;
|
||||
}
|
||||
if context.route_table.route_one(&peer.virtual_ip).is_none() {
|
||||
//路由为空,则向服务端地址发送
|
||||
let net_packet = match heartbeat_packet_client(client_cipher, src_ip, peer.virtual_ip) {
|
||||
Ok(net_packet) => net_packet,
|
||||
Err(e) => {
|
||||
log::error!("heartbeat_packet err={:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if let Err(e) = context.send_default(&net_packet, current_device.connect_server) {
|
||||
log::error!("heartbeat_packet send_default err={:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 客户端中继路径探测,延迟启动
|
||||
pub fn client_relay(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
client_cipher: Cipher,
|
||||
) {
|
||||
let rs = scheduler.timeout(Duration::from_secs(30), move |s| {
|
||||
client_relay_(s, context, current_device, device_map, client_cipher)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
/// 客户端中继路径探测,每30秒探测一次
|
||||
fn client_relay_(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
client_cipher: Cipher,
|
||||
) {
|
||||
if let Err(e) = client_relay0(
|
||||
&context,
|
||||
¤t_device.load(),
|
||||
&device_map,
|
||||
&client_cipher,
|
||||
) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
let rs = scheduler.timeout(Duration::from_secs(30), move |s| {
|
||||
client_relay_(s, context, current_device, device_map, client_cipher)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
fn client_relay0(
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
client_cipher: &Cipher,
|
||||
) -> anyhow::Result<()> {
|
||||
// 离线了不再探测
|
||||
if current_device.status.offline() {
|
||||
return Ok(());
|
||||
}
|
||||
let peer_list = { device_map.lock().1.clone() };
|
||||
let mut routes = context.route_table.route_table_p2p();
|
||||
for peer in peer_list.values() {
|
||||
if peer.wireguard
|
||||
|| !peer.status.is_online()
|
||||
|| peer.virtual_ip == current_device.virtual_ip
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if context
|
||||
.route_table
|
||||
.route_one_p2p(&peer.virtual_ip)
|
||||
.is_some()
|
||||
&& !context.first_latency()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let client_packet =
|
||||
heartbeat_packet_client(client_cipher, current_device.virtual_ip, peer.virtual_ip)?;
|
||||
|
||||
//随机发送到其他地址,看有没有客户端符合转发条件
|
||||
routes.shuffle(&mut rand::thread_rng());
|
||||
|
||||
for (index, (ip, route)) in routes.iter().enumerate() {
|
||||
if current_device.is_gateway(ip) {
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = context.send_by_key(&client_packet, route.route_key()) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
if index >= 2 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 构建心跳包
|
||||
fn heartbeat_packet(
|
||||
src: Ipv4Addr,
|
||||
dest: Ipv4Addr,
|
||||
) -> anyhow::Result<NetPacket<[u8; 12 + 4 + ENCRYPTION_RESERVED]>> {
|
||||
let mut net_packet = NetPacket::new_encrypt([0u8; 12 + 4 + ENCRYPTION_RESERVED])?;
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_protocol(Protocol::Control);
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Ping.into());
|
||||
net_packet.first_set_ttl(5);
|
||||
net_packet.set_source(src);
|
||||
net_packet.set_destination(dest);
|
||||
let mut ping = PingPacket::new(net_packet.payload_mut())?;
|
||||
ping.set_time(crate::handle::now_time() as u16);
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
fn heartbeat_packet_client(
|
||||
client_cipher: &Cipher,
|
||||
src: Ipv4Addr,
|
||||
dest: Ipv4Addr,
|
||||
) -> anyhow::Result<NetPacket<[u8; 12 + 4 + ENCRYPTION_RESERVED]>> {
|
||||
let mut net_packet = heartbeat_packet(src, dest)?;
|
||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
|
||||
fn heartbeat_packet_server(
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
server_cipher: &Cipher,
|
||||
src: Ipv4Addr,
|
||||
dest: Ipv4Addr,
|
||||
) -> anyhow::Result<NetPacket<[u8; 12 + 4 + ENCRYPTION_RESERVED]>> {
|
||||
let mut net_packet = heartbeat_packet(src, dest)?;
|
||||
let mut ping = PingPacket::new(net_packet.payload_mut())?;
|
||||
ping.set_epoch(device_map.lock().0);
|
||||
net_packet.set_gateway_flag(true);
|
||||
server_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::idle::{Idle, IdleType};
|
||||
use crate::channel::sender::ConnectUtil;
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use crate::channel::ConnectProtocol;
|
||||
use crate::handle::callback::{ConnectInfo, ErrorType};
|
||||
use crate::handle::handshaker::Handshake;
|
||||
use crate::handle::{BaseConfigInfo, ConnectStatus, CurrentDeviceInfo};
|
||||
use crate::util::{address_choose, dns_query_all, Scheduler};
|
||||
use crate::{ErrorInfo, VntCallback};
|
||||
|
||||
pub fn idle_route<Call: VntCallback>(
|
||||
scheduler: &Scheduler,
|
||||
idle: Idle,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
call: Call,
|
||||
) {
|
||||
let delay = idle_route0(&idle, &context, ¤t_device_info, &call);
|
||||
let rs = scheduler.timeout(delay, move |s| {
|
||||
idle_route(s, idle, context, current_device_info, call)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn idle_gateway<Call: VntCallback>(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
config: BaseConfigInfo,
|
||||
connect_util: ConnectUtil,
|
||||
call: Call,
|
||||
mut connect_count: usize,
|
||||
handshake: Handshake,
|
||||
) {
|
||||
idle_gateway0(
|
||||
&context,
|
||||
¤t_device_info,
|
||||
&config,
|
||||
&connect_util,
|
||||
&call,
|
||||
&mut connect_count,
|
||||
&handshake,
|
||||
);
|
||||
let rs = scheduler.timeout(Duration::from_secs(8), move |s| {
|
||||
idle_gateway(
|
||||
s,
|
||||
context,
|
||||
current_device_info,
|
||||
config,
|
||||
connect_util,
|
||||
call,
|
||||
connect_count,
|
||||
handshake,
|
||||
)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
fn idle_gateway0<Call: VntCallback>(
|
||||
context: &ChannelContext,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
config: &BaseConfigInfo,
|
||||
connect_util: &ConnectUtil,
|
||||
call: &Call,
|
||||
connect_count: &mut usize,
|
||||
handshake: &Handshake,
|
||||
) {
|
||||
if let Err(e) = check_gateway_channel(
|
||||
context,
|
||||
current_device,
|
||||
config,
|
||||
connect_util,
|
||||
call,
|
||||
connect_count,
|
||||
handshake,
|
||||
) {
|
||||
let cur = current_device.load();
|
||||
call.error(ErrorInfo::new_msg(
|
||||
ErrorType::Disconnect,
|
||||
format!("connect:{},error:{:?}", cur.connect_server, e),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn idle_route0<Call: VntCallback>(
|
||||
idle: &Idle,
|
||||
context: &ChannelContext,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
call: &Call,
|
||||
) -> Duration {
|
||||
let cur = current_device.load();
|
||||
match idle.next_idle() {
|
||||
IdleType::Timeout(ip, route) => {
|
||||
log::info!("route Timeout {:?},{:?}", ip, route);
|
||||
context.remove_route(&ip, route.route_key());
|
||||
if cur.is_gateway(&ip) {
|
||||
//网关路由过期,则需要改变状态
|
||||
crate::handle::change_status(current_device, ConnectStatus::Connecting);
|
||||
call.error(ErrorInfo::new(ErrorType::Disconnect));
|
||||
}
|
||||
Duration::from_millis(100)
|
||||
}
|
||||
IdleType::Sleep(duration) => duration,
|
||||
IdleType::None => Duration::from_millis(3000),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_gateway_channel<Call: VntCallback>(
|
||||
context: &ChannelContext,
|
||||
current_device_info: &AtomicCell<CurrentDeviceInfo>,
|
||||
config: &BaseConfigInfo,
|
||||
connect_util: &ConnectUtil,
|
||||
call: &Call,
|
||||
count: &mut usize,
|
||||
handshake: &Handshake,
|
||||
) -> io::Result<()> {
|
||||
let mut current_device = current_device_info.load();
|
||||
if current_device.status.offline() {
|
||||
*count += 1;
|
||||
let connect_protocol = context.main_protocol();
|
||||
if connect_protocol.is_transport() {
|
||||
// 传输层的协议需要探测服务器地址
|
||||
current_device =
|
||||
domain_request0(current_device_info, config, context.default_interface());
|
||||
}
|
||||
//需要重连
|
||||
call.connect(ConnectInfo::new(*count, current_device.connect_server));
|
||||
log::info!("发送握手请求,{:?}", config);
|
||||
if let Err(e) = handshake.send(context, config.server_secret, current_device.connect_server)
|
||||
{
|
||||
log::warn!("{:?}", e);
|
||||
let request_packet = handshake.handshake_request_packet(config.server_secret)?;
|
||||
match connect_protocol {
|
||||
ConnectProtocol::UDP => {}
|
||||
ConnectProtocol::TCP => {
|
||||
connect_util.try_connect_tcp(
|
||||
request_packet.into_buffer(),
|
||||
current_device.connect_server,
|
||||
);
|
||||
}
|
||||
ConnectProtocol::WS | ConnectProtocol::WSS => {
|
||||
connect_util
|
||||
.try_connect_ws(request_packet.into_buffer(), config.server_addr.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn domain_request0(
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
config: &BaseConfigInfo,
|
||||
default_interface: &LocalInterface,
|
||||
) -> CurrentDeviceInfo {
|
||||
let mut current_dev = current_device.load();
|
||||
|
||||
// 探测服务端地址变化
|
||||
match dns_query_all(
|
||||
&config.server_addr,
|
||||
config.name_servers.clone(),
|
||||
default_interface,
|
||||
) {
|
||||
Ok(addrs) => {
|
||||
log::info!(
|
||||
"domain {} dns {:?} addr {:?}",
|
||||
config.server_addr,
|
||||
config.name_servers,
|
||||
addrs
|
||||
);
|
||||
|
||||
match address_choose(addrs) {
|
||||
Ok(addr) => {
|
||||
if addr != current_dev.connect_server {
|
||||
let mut tmp = current_dev.clone();
|
||||
tmp.connect_server = addr;
|
||||
let rs = current_device.compare_exchange(current_dev, tmp);
|
||||
log::info!(
|
||||
"服务端地址变化,旧地址:{},新地址:{},替换结果:{}",
|
||||
current_dev.connect_server,
|
||||
addr,
|
||||
rs.is_ok()
|
||||
);
|
||||
if rs.is_ok() {
|
||||
current_dev.connect_server = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("域名地址选择失败:{:?},domain={}", e, config.server_addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("域名解析失败:{:?},domain={}", e, config.server_addr);
|
||||
}
|
||||
}
|
||||
current_dev
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
mod heartbeat;
|
||||
pub use heartbeat::client_relay;
|
||||
pub use heartbeat::heartbeat;
|
||||
|
||||
mod re_nat_type;
|
||||
pub use re_nat_type::retrieve_nat_type;
|
||||
|
||||
mod addr_request;
|
||||
pub use addr_request::*;
|
||||
|
||||
mod punch;
|
||||
pub use punch::*;
|
||||
|
||||
mod idle;
|
||||
pub use idle::idle_gateway;
|
||||
pub use idle::idle_route;
|
||||
|
||||
mod up_status;
|
||||
pub use up_status::*;
|
||||
@@ -0,0 +1,354 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::ops::{Div, Mul};
|
||||
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use protobuf::Message;
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::punch::{NatInfo, NatType, Punch};
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::nat::NatTest;
|
||||
use crate::proto::message::{PunchInfo, PunchNatType};
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::{control_packet, other_turn_packet, NetPacket, Protocol, MAX_TTL};
|
||||
use crate::util::Scheduler;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PunchSender {
|
||||
sender_self: SyncSender<(Ipv4Addr, NatInfo)>,
|
||||
sender_peer: SyncSender<(Ipv4Addr, NatInfo)>,
|
||||
sender_cone_self: SyncSender<(Ipv4Addr, NatInfo)>,
|
||||
sender_cone_peer: SyncSender<(Ipv4Addr, NatInfo)>,
|
||||
}
|
||||
|
||||
impl PunchSender {
|
||||
pub fn send(&self, src_peer: bool, ip: Ipv4Addr, info: NatInfo) -> bool {
|
||||
log::info!(
|
||||
"发送打洞协商消息,是否对端发起:{},ip:{},info:{:?}",
|
||||
src_peer,
|
||||
ip,
|
||||
info
|
||||
);
|
||||
let sender = match info.nat_type {
|
||||
NatType::Symmetric => {
|
||||
if src_peer {
|
||||
&self.sender_peer
|
||||
} else {
|
||||
&self.sender_self
|
||||
}
|
||||
}
|
||||
NatType::Cone => {
|
||||
if src_peer {
|
||||
&self.sender_cone_peer
|
||||
} else {
|
||||
&self.sender_cone_self
|
||||
}
|
||||
}
|
||||
};
|
||||
sender.try_send((ip, info)).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PunchReceiver {
|
||||
receiver_peer: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
receiver_self: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
receiver_cone_peer: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
receiver_cone_self: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
}
|
||||
|
||||
pub fn punch_channel() -> (PunchSender, PunchReceiver) {
|
||||
let (sender_self, receiver_self) = sync_channel(0);
|
||||
let (sender_peer, receiver_peer) = sync_channel(0);
|
||||
let (sender_cone_peer, receiver_cone_peer) = sync_channel(0);
|
||||
let (sender_cone_self, receiver_cone_self) = sync_channel(0);
|
||||
(
|
||||
PunchSender {
|
||||
sender_self,
|
||||
sender_peer,
|
||||
sender_cone_peer,
|
||||
sender_cone_self,
|
||||
},
|
||||
PunchReceiver {
|
||||
receiver_peer,
|
||||
receiver_self,
|
||||
receiver_cone_peer,
|
||||
receiver_cone_self,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn punch(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
nat_test: NatTest,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
receiver: PunchReceiver,
|
||||
punch: Punch,
|
||||
) {
|
||||
let punch_record = Arc::new(Mutex::new(HashMap::new()));
|
||||
let last_punch_record = HashMap::new();
|
||||
punch_request(
|
||||
scheduler,
|
||||
context,
|
||||
nat_test,
|
||||
device_map,
|
||||
current_device.clone(),
|
||||
client_cipher.clone(),
|
||||
0,
|
||||
punch_record.clone(),
|
||||
last_punch_record,
|
||||
);
|
||||
let f = |receiver: Receiver<(Ipv4Addr, NatInfo)>| {
|
||||
let punch = punch.clone();
|
||||
let current_device = current_device.clone();
|
||||
let client_cipher = client_cipher.clone();
|
||||
let punch_record = punch_record.clone();
|
||||
thread::Builder::new()
|
||||
.name("punch".into())
|
||||
.spawn(move || {
|
||||
punch_start(receiver, punch, current_device, client_cipher, punch_record);
|
||||
})
|
||||
.expect("punch");
|
||||
};
|
||||
f(receiver.receiver_peer);
|
||||
f(receiver.receiver_self);
|
||||
f(receiver.receiver_cone_peer);
|
||||
f(receiver.receiver_cone_self);
|
||||
}
|
||||
|
||||
/// 接收打洞消息,配合对端打洞
|
||||
fn punch_start(
|
||||
receiver: Receiver<(Ipv4Addr, NatInfo)>,
|
||||
mut punch: Punch,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
punch_record: Arc<Mutex<HashMap<Ipv4Addr, usize>>>,
|
||||
) {
|
||||
while let Ok((peer_ip, nat_info)) = receiver.recv() {
|
||||
let mut packet = NetPacket::new_encrypt([0u8; 12 + ENCRYPTION_RESERVED]).unwrap();
|
||||
packet.set_default_version();
|
||||
packet.first_set_ttl(1);
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::PunchRequest.into());
|
||||
packet.set_source(current_device.load().virtual_ip());
|
||||
packet.set_destination(peer_ip);
|
||||
let count = {
|
||||
let mut guard = punch_record.lock();
|
||||
if let Some(v) = guard.get_mut(&peer_ip) {
|
||||
*v += 1;
|
||||
*v
|
||||
} else {
|
||||
guard.insert(peer_ip, 0);
|
||||
0
|
||||
}
|
||||
};
|
||||
log::info!("第{}次发起打洞,目标:{:?},{:?} ", count, peer_ip, nat_info);
|
||||
|
||||
if let Err(e) = client_cipher.encrypt_ipv4(&mut packet) {
|
||||
log::error!("{:?}", e);
|
||||
continue;
|
||||
}
|
||||
if let Err(e) = punch.punch(packet.buffer(), peer_ip, nat_info, count < 2, count) {
|
||||
log::warn!("{:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 定时发起打洞请求
|
||||
fn punch_request(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
nat_test: NatTest,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
count: usize,
|
||||
punch_record: Arc<Mutex<HashMap<Ipv4Addr, usize>>>,
|
||||
mut last_punch_record: HashMap<Ipv4Addr, PunchRecordItem>,
|
||||
) {
|
||||
let curr = current_device.load();
|
||||
let secs = if curr.status.online() {
|
||||
if let Err(e) = punch0(
|
||||
&context,
|
||||
&nat_test,
|
||||
&device_map,
|
||||
curr,
|
||||
&client_cipher,
|
||||
&punch_record,
|
||||
&mut last_punch_record,
|
||||
count,
|
||||
) {
|
||||
log::warn!("{:?}", e)
|
||||
}
|
||||
let sleep_time = [6, 7];
|
||||
Duration::from_secs(sleep_time[count % sleep_time.len()])
|
||||
} else {
|
||||
Duration::from_secs(5)
|
||||
};
|
||||
let rs = scheduler.timeout(secs, move |s| {
|
||||
punch_request(
|
||||
s,
|
||||
context,
|
||||
nat_test,
|
||||
device_map,
|
||||
current_device,
|
||||
client_cipher,
|
||||
count + 1,
|
||||
punch_record,
|
||||
last_punch_record,
|
||||
);
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
#[derive(Copy, Clone, Default)]
|
||||
struct PunchRecordItem {
|
||||
pub punch_record: usize,
|
||||
pub last_p2p_num: usize,
|
||||
}
|
||||
|
||||
/// 随机对需要打洞的客户端发起打洞请求
|
||||
fn punch0(
|
||||
context: &ChannelContext,
|
||||
nat_test: &NatTest,
|
||||
device_map: &Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
current_device: CurrentDeviceInfo,
|
||||
client_cipher: &Cipher,
|
||||
punch_record: &Mutex<HashMap<Ipv4Addr, usize>>,
|
||||
last_punch_record: &mut HashMap<Ipv4Addr, PunchRecordItem>,
|
||||
total_count: usize,
|
||||
) -> anyhow::Result<()> {
|
||||
let nat_info = nat_test.nat_info();
|
||||
if total_count < 10
|
||||
&& (nat_info.public_ips.is_empty()
|
||||
|| nat_info.public_ports.is_empty()
|
||||
|| nat_info.public_ports[0] == 0
|
||||
|| nat_info.public_ports.iter().filter(|&&v| v == 0).count()
|
||||
> nat_info.public_ports.len() / 2)
|
||||
{
|
||||
log::info!("未获取到公网地址,暂时放弃打洞,第{}轮", total_count);
|
||||
return Ok(());
|
||||
}
|
||||
let current_ip = current_device.virtual_ip;
|
||||
let mut list: Vec<PeerDeviceInfo> = device_map
|
||||
.lock()
|
||||
.1
|
||||
.values()
|
||||
.filter(|info| !info.wireguard && info.virtual_ip > current_ip)
|
||||
.cloned()
|
||||
.collect();
|
||||
list.shuffle(&mut rand::thread_rng());
|
||||
for info in list {
|
||||
if info.status.is_offline() {
|
||||
// 客户端掉线了要重置打洞记录
|
||||
punch_record.lock().remove(&info.virtual_ip);
|
||||
continue;
|
||||
}
|
||||
let mut punch_count = punch_record
|
||||
.lock()
|
||||
.get(&info.virtual_ip)
|
||||
.cloned()
|
||||
.unwrap_or(0)
|
||||
.mul(2)
|
||||
.div(3);
|
||||
let p2p_num = context.route_table.p2p_num(&info.virtual_ip);
|
||||
let mut max_punch_interval = 50;
|
||||
if p2p_num > 0 {
|
||||
if p2p_num >= context.channel_num() {
|
||||
//通道数满足要求,不再打洞
|
||||
if punch_count != 0 {
|
||||
punch_record.lock().remove(&info.virtual_ip);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
//有p2p通道,但是通道数量不够,则继续打洞
|
||||
// 提高等待上限
|
||||
max_punch_interval = 300;
|
||||
}
|
||||
// 能发起打洞的前提是自己空闲,这里会间隔5秒以上发起一次打洞,所以假定上一轮打洞已结束
|
||||
let last_punch = last_punch_record.entry(info.virtual_ip).or_default();
|
||||
if last_punch.last_p2p_num > p2p_num {
|
||||
// 打的洞掉线了,需要重置重新打
|
||||
punch_record.lock().remove(&info.virtual_ip);
|
||||
punch_count = 0;
|
||||
}
|
||||
|
||||
// 梯度增加打洞时间间隔
|
||||
if total_count > last_punch.punch_record + punch_count.min(max_punch_interval) {
|
||||
// 记录打洞周期,抑制下一次打洞,从而递减打洞频率
|
||||
last_punch.punch_record = total_count;
|
||||
last_punch.last_p2p_num = p2p_num;
|
||||
let packet = punch_packet(
|
||||
client_cipher,
|
||||
current_device.virtual_ip(),
|
||||
&nat_info,
|
||||
info.virtual_ip,
|
||||
)?;
|
||||
log::info!(
|
||||
"目标:{:?},当前nat:{:?} 第{}次发起打洞协商请求, 第:{}轮",
|
||||
info.virtual_ip,
|
||||
nat_info,
|
||||
punch_count,
|
||||
total_count,
|
||||
);
|
||||
context.send_default(&packet, current_device.connect_server)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn punch_packet(
|
||||
client_cipher: &Cipher,
|
||||
virtual_ip: Ipv4Addr,
|
||||
nat_info: &NatInfo,
|
||||
dest: Ipv4Addr,
|
||||
) -> anyhow::Result<NetPacket<Vec<u8>>> {
|
||||
let mut punch_reply = PunchInfo::new();
|
||||
punch_reply.reply = false;
|
||||
punch_reply.public_ip_list = nat_info
|
||||
.public_ips
|
||||
.iter()
|
||||
.map(|ip| u32::from_be_bytes(ip.octets()))
|
||||
.collect();
|
||||
punch_reply.public_port = nat_info.public_ports.get(0).map_or(0, |v| *v as u32);
|
||||
punch_reply.public_ports = nat_info.public_ports.iter().map(|e| *e as u32).collect();
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.public_tcp_port = nat_info.public_tcp_port as u32;
|
||||
punch_reply.local_ip = u32::from(nat_info.local_ipv4().unwrap_or(Ipv4Addr::UNSPECIFIED));
|
||||
punch_reply.local_port = nat_info.udp_ports[0] as u32;
|
||||
punch_reply.tcp_port = nat_info.tcp_port as u32;
|
||||
punch_reply.udp_ports = nat_info.udp_ports.iter().map(|e| *e as u32).collect();
|
||||
if let Some(ipv6) = nat_info.ipv6 {
|
||||
punch_reply.ipv6_port = nat_info.udp_ports[0] as u32;
|
||||
punch_reply.ipv6 = ipv6.octets().to_vec();
|
||||
}
|
||||
punch_reply.nat_type = protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
|
||||
punch_reply.punch_model = protobuf::EnumOrUnknown::new(nat_info.punch_model.into());
|
||||
log::info!("请求打洞={:?}", punch_reply);
|
||||
let bytes = punch_reply
|
||||
.write_to_bytes()
|
||||
.map_err(|e| anyhow!("punch_packet {:?}", e))?;
|
||||
let mut net_packet = NetPacket::new_encrypt(vec![0u8; 12 + bytes.len() + ENCRYPTION_RESERVED])?;
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_protocol(Protocol::OtherTurn);
|
||||
net_packet.set_transport_protocol(other_turn_packet::Protocol::Punch.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_source(virtual_ip);
|
||||
net_packet.set_destination(dest);
|
||||
net_packet.set_payload(&bytes)?;
|
||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::sender::AcceptSocketSender;
|
||||
use crate::nat;
|
||||
use crate::nat::NatTest;
|
||||
use crate::util::Scheduler;
|
||||
|
||||
/// 10分钟探测一次nat
|
||||
pub fn retrieve_nat_type(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
nat_test: NatTest,
|
||||
udp_socket_sender: AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
|
||||
) {
|
||||
retrieve_nat_type0(context.clone(), nat_test.clone(), udp_socket_sender.clone());
|
||||
scheduler.timeout(Duration::from_secs(60 * 10), move |s| {
|
||||
retrieve_nat_type(s, context, nat_test, udp_socket_sender)
|
||||
});
|
||||
}
|
||||
|
||||
fn retrieve_nat_type0(
|
||||
context: ChannelContext,
|
||||
nat_test: NatTest,
|
||||
udp_socket_sender: AcceptSocketSender<Option<Vec<mio::net::UdpSocket>>>,
|
||||
) {
|
||||
thread::Builder::new()
|
||||
.name("natTest".into())
|
||||
.spawn(move || {
|
||||
if nat_test.can_update() {
|
||||
let local_ipv4 = if nat_test.update_local_ipv4 {
|
||||
nat::local_ipv4()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let local_ipv6 = nat::local_ipv6();
|
||||
match nat_test.re_test(local_ipv4, local_ipv6, context.default_interface()) {
|
||||
Ok(nat_info) => {
|
||||
log::info!("当前nat信息:{:?}", nat_info);
|
||||
if let Err(e) = context.switch(nat_info.nat_type, &udp_socket_sender) {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("nat re_test {:?}", e);
|
||||
}
|
||||
};
|
||||
#[cfg(feature = "upnp")]
|
||||
nat_test.reset_upnp();
|
||||
log::info!("刷新nat结束")
|
||||
}
|
||||
})
|
||||
.expect("natTest");
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::proto::message::{ClientStatusInfo, PunchNatType, RouteItem};
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::{service_packet, NetPacket, Protocol, HEAD_LEN, MAX_TTL};
|
||||
use crate::util::Scheduler;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use protobuf::Message;
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
/// 上报状态给服务器
|
||||
pub fn up_status(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
) {
|
||||
let _ = scheduler.timeout(Duration::from_secs(60), move |x| {
|
||||
up_status0(x, context, current_device_info)
|
||||
});
|
||||
}
|
||||
|
||||
fn up_status0(
|
||||
scheduler: &Scheduler,
|
||||
context: ChannelContext,
|
||||
current_device_info: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
) {
|
||||
if let Err(e) = send_up_status_packet(&context, ¤t_device_info) {
|
||||
log::warn!("{:?}", e)
|
||||
}
|
||||
let rs = scheduler.timeout(Duration::from_secs(10 * 60), move |x| {
|
||||
up_status0(x, context, current_device_info)
|
||||
});
|
||||
if !rs {
|
||||
log::info!("定时任务停止");
|
||||
}
|
||||
}
|
||||
|
||||
fn send_up_status_packet(
|
||||
context: &ChannelContext,
|
||||
current_device_info: &AtomicCell<CurrentDeviceInfo>,
|
||||
) -> io::Result<()> {
|
||||
let device_info = current_device_info.load();
|
||||
if device_info.status.offline() {
|
||||
return Ok(());
|
||||
}
|
||||
let routes = context.route_table.route_table_p2p();
|
||||
if routes.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut message = ClientStatusInfo::new();
|
||||
message.source = device_info.virtual_ip.into();
|
||||
for (ip, _) in routes {
|
||||
let mut item = RouteItem::new();
|
||||
item.next_ip = ip.into();
|
||||
message.p2p_list.push(item);
|
||||
}
|
||||
message.up_stream = context.up_traffic_meter.as_ref().map_or(0, |v| v.total());
|
||||
message.down_stream = context.down_traffic_meter.as_ref().map_or(0, |v| v.total());
|
||||
message.nat_type = protobuf::EnumOrUnknown::new(if context.is_cone() {
|
||||
PunchNatType::Cone
|
||||
} else {
|
||||
PunchNatType::Symmetric
|
||||
});
|
||||
let buf = message
|
||||
.write_to_bytes()
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("up_status_packet {:?}", e)))?;
|
||||
let mut net_packet =
|
||||
NetPacket::new_encrypt(vec![0; HEAD_LEN + buf.len() + ENCRYPTION_RESERVED])?;
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_gateway_flag(true);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol_into(service_packet::Protocol::ClientStatusInfo);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_source(device_info.virtual_ip);
|
||||
net_packet.set_destination(device_info.virtual_gateway);
|
||||
net_packet.set_payload(&buf)?;
|
||||
context.send_default(&net_packet, device_info.connect_server)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
|
||||
pub mod callback;
|
||||
mod extension;
|
||||
pub mod handshaker;
|
||||
pub mod maintain;
|
||||
pub mod recv_data;
|
||||
pub mod registrar;
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
pub mod tun_tap;
|
||||
|
||||
const SELF_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 2);
|
||||
const GATEWAY_IP: Ipv4Addr = Ipv4Addr::new(0, 0, 0, 1);
|
||||
|
||||
pub fn now_time() -> u64 {
|
||||
let now = std::time::SystemTime::now();
|
||||
if let Ok(timestamp) = now.duration_since(std::time::UNIX_EPOCH) {
|
||||
timestamp.as_secs() * 1000 + u64::from(timestamp.subsec_millis())
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct PeerDeviceInfo {
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
pub name: String,
|
||||
pub status: PeerDeviceStatus,
|
||||
pub client_secret: bool,
|
||||
pub client_secret_hash: Vec<u8>,
|
||||
pub wireguard: bool,
|
||||
}
|
||||
|
||||
impl PeerDeviceInfo {
|
||||
pub fn new(
|
||||
virtual_ip: Ipv4Addr,
|
||||
name: String,
|
||||
status: u8,
|
||||
client_secret: bool,
|
||||
client_secret_hash: Vec<u8>,
|
||||
wireguard: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
virtual_ip,
|
||||
name,
|
||||
status: PeerDeviceStatus::from(status),
|
||||
client_secret,
|
||||
client_secret_hash,
|
||||
wireguard,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BaseConfigInfo {
|
||||
pub name: String,
|
||||
pub token: String,
|
||||
pub ip: Option<Ipv4Addr>,
|
||||
pub client_secret_hash: Option<[u8; 16]>,
|
||||
pub server_secret: bool,
|
||||
pub device_id: String,
|
||||
pub server_addr: String,
|
||||
pub name_servers: Vec<String>,
|
||||
pub mtu: u32,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
pub tap: bool,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
pub device_name: Option<String>,
|
||||
pub allow_wire_guard: bool,
|
||||
pub default_interface: LocalInterface,
|
||||
}
|
||||
|
||||
impl BaseConfigInfo {
|
||||
pub fn new(
|
||||
name: String,
|
||||
token: String,
|
||||
ip: Option<Ipv4Addr>,
|
||||
client_secret_hash: Option<[u8; 16]>,
|
||||
server_secret: bool,
|
||||
device_id: String,
|
||||
server_addr: String,
|
||||
name_servers: Vec<String>,
|
||||
mtu: u32,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
tap: bool,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
device_name: Option<String>,
|
||||
allow_wire_guard: bool,
|
||||
default_interface: LocalInterface,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
token,
|
||||
ip,
|
||||
client_secret_hash,
|
||||
server_secret,
|
||||
device_id,
|
||||
server_addr,
|
||||
name_servers,
|
||||
mtu,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
tap,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
device_name,
|
||||
allow_wire_guard,
|
||||
default_interface,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub enum PeerDeviceStatus {
|
||||
Online,
|
||||
Offline,
|
||||
}
|
||||
|
||||
impl PeerDeviceStatus {
|
||||
pub fn is_online(&self) -> bool {
|
||||
self == &PeerDeviceStatus::Online
|
||||
}
|
||||
pub fn is_offline(&self) -> bool {
|
||||
self == &PeerDeviceStatus::Offline
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for PeerDeviceStatus {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
PeerDeviceStatus::Online => 0,
|
||||
PeerDeviceStatus::Offline => 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for PeerDeviceStatus {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
0 => PeerDeviceStatus::Online,
|
||||
_ => PeerDeviceStatus::Offline,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum ConnectStatus {
|
||||
Connecting,
|
||||
Connected,
|
||||
}
|
||||
|
||||
impl ConnectStatus {
|
||||
pub fn online(&self) -> bool {
|
||||
self == &ConnectStatus::Connected
|
||||
}
|
||||
pub fn offline(&self) -> bool {
|
||||
self == &ConnectStatus::Connecting
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub struct CurrentDeviceInfo {
|
||||
//本机虚拟IP
|
||||
pub virtual_ip: Ipv4Addr,
|
||||
//子网掩码
|
||||
pub virtual_netmask: Ipv4Addr,
|
||||
//虚拟网关
|
||||
pub virtual_gateway: Ipv4Addr,
|
||||
//网络地址
|
||||
pub virtual_network: Ipv4Addr,
|
||||
//直接广播地址
|
||||
pub broadcast_ip: Ipv4Addr,
|
||||
//链接的服务器地址
|
||||
pub connect_server: SocketAddr,
|
||||
//连接状态
|
||||
pub status: ConnectStatus,
|
||||
}
|
||||
|
||||
impl CurrentDeviceInfo {
|
||||
pub fn new(
|
||||
virtual_ip: Ipv4Addr,
|
||||
virtual_netmask: Ipv4Addr,
|
||||
virtual_gateway: Ipv4Addr,
|
||||
connect_server: SocketAddr,
|
||||
) -> Self {
|
||||
let broadcast_ip = (!u32::from_be_bytes(virtual_netmask.octets()))
|
||||
| u32::from_be_bytes(virtual_gateway.octets());
|
||||
let broadcast_ip = Ipv4Addr::from(broadcast_ip);
|
||||
let virtual_network = u32::from_be_bytes(virtual_netmask.octets())
|
||||
& u32::from_be_bytes(virtual_gateway.octets());
|
||||
let virtual_network = Ipv4Addr::from(virtual_network);
|
||||
Self {
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
virtual_network,
|
||||
broadcast_ip,
|
||||
connect_server,
|
||||
status: ConnectStatus::Connecting,
|
||||
}
|
||||
}
|
||||
pub fn new0(connect_server: SocketAddr) -> Self {
|
||||
Self {
|
||||
virtual_ip: Ipv4Addr::UNSPECIFIED,
|
||||
virtual_gateway: Ipv4Addr::UNSPECIFIED,
|
||||
virtual_netmask: Ipv4Addr::UNSPECIFIED,
|
||||
virtual_network: Ipv4Addr::UNSPECIFIED,
|
||||
broadcast_ip: Ipv4Addr::UNSPECIFIED,
|
||||
connect_server,
|
||||
status: ConnectStatus::Connecting,
|
||||
}
|
||||
}
|
||||
pub fn update(
|
||||
&mut self,
|
||||
virtual_ip: Ipv4Addr,
|
||||
virtual_netmask: Ipv4Addr,
|
||||
virtual_gateway: Ipv4Addr,
|
||||
) {
|
||||
let broadcast_ip = (!u32::from_be_bytes(virtual_netmask.octets()))
|
||||
| u32::from_be_bytes(virtual_ip.octets());
|
||||
let broadcast_ip = Ipv4Addr::from(broadcast_ip);
|
||||
let virtual_network =
|
||||
u32::from_be_bytes(virtual_netmask.octets()) & u32::from_be_bytes(virtual_ip.octets());
|
||||
let virtual_network = Ipv4Addr::from(virtual_network);
|
||||
self.virtual_ip = virtual_ip;
|
||||
self.virtual_netmask = virtual_netmask;
|
||||
self.virtual_gateway = virtual_gateway;
|
||||
self.broadcast_ip = broadcast_ip;
|
||||
self.virtual_network = virtual_network;
|
||||
}
|
||||
#[inline]
|
||||
pub fn virtual_ip(&self) -> Ipv4Addr {
|
||||
self.virtual_ip
|
||||
}
|
||||
#[inline]
|
||||
pub fn virtual_gateway(&self) -> Ipv4Addr {
|
||||
self.virtual_gateway
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_gateway(&self, ip: &Ipv4Addr) -> bool {
|
||||
&self.virtual_gateway == ip || ip == &GATEWAY_IP
|
||||
}
|
||||
#[inline]
|
||||
pub fn not_in_network(&self, ip: Ipv4Addr) -> bool {
|
||||
u32::from(ip) & u32::from(self.virtual_netmask) != u32::from(self.virtual_network)
|
||||
}
|
||||
pub fn is_server_addr(&self, addr: SocketAddr) -> bool {
|
||||
if self.connect_server == addr {
|
||||
return true;
|
||||
}
|
||||
let f = |ip: IpAddr| match ip {
|
||||
IpAddr::V4(v4) => Some(v4),
|
||||
IpAddr::V6(v6) => v6.to_ipv4(),
|
||||
};
|
||||
addr.port() == self.connect_server.port() && f(addr.ip()) == f(self.connect_server.ip())
|
||||
}
|
||||
}
|
||||
pub fn change_status(
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
connect_status: ConnectStatus,
|
||||
) -> CurrentDeviceInfo {
|
||||
loop {
|
||||
let cur = current_device.load();
|
||||
let mut new_info = cur;
|
||||
new_info.status = connect_status;
|
||||
if current_device.compare_exchange(cur, new_info).is_ok() {
|
||||
return new_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
use anyhow::anyhow;
|
||||
use std::collections::HashMap;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::RwLock;
|
||||
use protobuf::Message;
|
||||
|
||||
use packet::icmp::{icmp, Kind};
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::punch::NatInfo;
|
||||
use crate::channel::{Route, RouteKey};
|
||||
use crate::cipher::Cipher;
|
||||
use crate::external_route::AllowExternalRoute;
|
||||
use crate::handle::extension::handle_extension_tail;
|
||||
use crate::handle::maintain::PunchSender;
|
||||
use crate::handle::recv_data::PacketHandler;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
use crate::ip_proxy::{IpProxyMap, ProxyHandler};
|
||||
use crate::nat::NatTest;
|
||||
use crate::proto::message::{PunchInfo, PunchNatType};
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::control_packet::ControlPacket;
|
||||
use crate::protocol::{
|
||||
control_packet, ip_turn_packet, other_turn_packet, NetPacket, Protocol, MAX_TTL,
|
||||
};
|
||||
use crate::tun_tap_device::vnt_device::DeviceWrite;
|
||||
|
||||
/// 处理来源于客户端的包
|
||||
#[derive(Clone)]
|
||||
pub struct ClientPacketHandler<Device> {
|
||||
device: Device,
|
||||
client_cipher: Cipher,
|
||||
punch_sender: PunchSender,
|
||||
peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>>,
|
||||
nat_test: NatTest,
|
||||
route: AllowExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
}
|
||||
|
||||
impl<Device: DeviceWrite> ClientPacketHandler<Device> {
|
||||
pub fn new(
|
||||
device: Device,
|
||||
client_cipher: Cipher,
|
||||
punch_sender: PunchSender,
|
||||
peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>>,
|
||||
nat_test: NatTest,
|
||||
route: AllowExternalRoute,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
) -> Self {
|
||||
Self {
|
||||
device,
|
||||
client_cipher,
|
||||
punch_sender,
|
||||
peer_nat_info_map,
|
||||
nat_test,
|
||||
route,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Device: DeviceWrite> PacketHandler for ClientPacketHandler<Device> {
|
||||
fn handle(
|
||||
&self,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
mut extend: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> anyhow::Result<()> {
|
||||
self.client_cipher.decrypt_ipv4(&mut net_packet)?;
|
||||
context
|
||||
.route_table
|
||||
.update_read_time(&net_packet.source(), &route_key);
|
||||
//处理扩展
|
||||
let net_packet = if net_packet.is_extension() {
|
||||
//这样重用数组,减少一次数据拷贝
|
||||
if handle_extension_tail(&mut net_packet, &mut extend)? {
|
||||
extend
|
||||
} else {
|
||||
net_packet
|
||||
}
|
||||
} else {
|
||||
net_packet
|
||||
};
|
||||
match net_packet.protocol() {
|
||||
Protocol::Service => {}
|
||||
Protocol::Error => {}
|
||||
Protocol::Control => {
|
||||
self.control(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::IpTurn => {
|
||||
self.ip_turn(net_packet, context, current_device, route_key)?;
|
||||
}
|
||||
Protocol::OtherTurn => {
|
||||
self.other_turn(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Unknown(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Device: DeviceWrite> ClientPacketHandler<Device> {
|
||||
fn ip_turn(
|
||||
&self,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()> {
|
||||
let destination = net_packet.destination();
|
||||
let source = net_packet.source();
|
||||
match ip_turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
ip_turn_packet::Protocol::Ipv4 => {
|
||||
let mut ipv4 = IpV4Packet::new(net_packet.payload_mut())?;
|
||||
match ipv4.protocol() {
|
||||
ipv4::protocol::Protocol::Icmp => {
|
||||
if ipv4.destination_ip() == destination {
|
||||
let mut icmp_packet = icmp::IcmpPacket::new(ipv4.payload_mut())?;
|
||||
if icmp_packet.kind() == Kind::EchoRequest {
|
||||
//开启ping
|
||||
icmp_packet.set_kind(Kind::EchoReply);
|
||||
icmp_packet.update_checksum();
|
||||
ipv4.set_source_ip(destination);
|
||||
ipv4.set_destination_ip(source);
|
||||
ipv4.update_checksum();
|
||||
net_packet.set_source(destination);
|
||||
net_packet.set_destination(source);
|
||||
//不管加不加密,和接收到的数据长度都一致
|
||||
self.client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
context.send_by_key(&net_packet, route_key)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
// ip代理只关心实际目标
|
||||
let real_dest = ipv4.destination_ip();
|
||||
if real_dest != destination
|
||||
&& !(real_dest.is_broadcast()
|
||||
|| real_dest.is_multicast()
|
||||
|| real_dest == current_device.broadcast_ip
|
||||
|| real_dest.is_unspecified())
|
||||
{
|
||||
if !self.route.allow(&real_dest) {
|
||||
//拦截不符合的目标
|
||||
return Ok(());
|
||||
}
|
||||
match ipv4.protocol() {
|
||||
ipv4::protocol::Protocol::Tcp => {
|
||||
let payload = ipv4.payload();
|
||||
if payload.len() < 20 {
|
||||
return Ok(());
|
||||
}
|
||||
let destination_port =
|
||||
u16::from_be_bytes(payload[2..4].try_into().unwrap());
|
||||
if self.nat_test.is_local_tcp(real_dest, destination_port) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
ipv4::protocol::Protocol::Udp => {
|
||||
let payload = ipv4.payload();
|
||||
if payload.len() < 8 {
|
||||
return Ok(());
|
||||
}
|
||||
let destination_port =
|
||||
u16::from_be_bytes(payload[2..4].try_into().unwrap());
|
||||
if self.nat_test.is_local_udp(real_dest, destination_port) {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
if let Some(ip_proxy_map) = &self.ip_proxy_map {
|
||||
if ip_proxy_map.recv_handle(&mut ipv4, source, destination)? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.device.write(net_packet.payload())?;
|
||||
}
|
||||
ip_turn_packet::Protocol::WGIpv4 => {
|
||||
// WG客户端的数据不会直接发过来,不用处理
|
||||
}
|
||||
ip_turn_packet::Protocol::Ipv4Broadcast => {
|
||||
//客户端不帮忙转发广播包,所以不会出现这种类型的数据
|
||||
}
|
||||
ip_turn_packet::Protocol::Unknown(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn control(
|
||||
&self,
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()> {
|
||||
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
|
||||
let source = net_packet.source();
|
||||
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
ControlPacket::PingPacket(_) => {
|
||||
let route = Route::from_default_rt(route_key, metric);
|
||||
context.route_table.add_route_if_absent(source, route);
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::Pong.into());
|
||||
net_packet.set_source(current_device.virtual_ip);
|
||||
net_packet.set_destination(source);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
self.client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
context.send_by_key(&net_packet, route_key)?;
|
||||
}
|
||||
ControlPacket::PongPacket(pong_packet) => {
|
||||
let current_time = crate::handle::now_time() as u16;
|
||||
if current_time < pong_packet.time() {
|
||||
return Ok(());
|
||||
}
|
||||
let rt = (current_time - pong_packet.time()) as i64;
|
||||
let route = Route::from(route_key, metric, rt);
|
||||
context.route_table.add_route(source, route);
|
||||
}
|
||||
ControlPacket::PunchRequest => {
|
||||
log::info!("PunchRequest={:?},source={}", route_key, source);
|
||||
if context.use_channel_type().is_only_relay() {
|
||||
return Ok(());
|
||||
}
|
||||
//忽略掉来源于自己的包
|
||||
if self
|
||||
.nat_test
|
||||
.is_local_address(route_key.protocol().is_base_tcp(), route_key.addr)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
//回应
|
||||
net_packet.set_transport_protocol(control_packet::Protocol::PunchResponse.into());
|
||||
net_packet.set_source(current_device.virtual_ip);
|
||||
net_packet.set_destination(source);
|
||||
net_packet.first_set_ttl(1);
|
||||
self.client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
context.send_by_key(&net_packet, route_key)?;
|
||||
// 收到PunchRequest就添加路由,会导致单向通信的问题,删掉试试
|
||||
// let route = Route::from_default_rt(route_key, 1);
|
||||
// context.route_table.add_route_if_absent(source, route);
|
||||
}
|
||||
ControlPacket::PunchResponse => {
|
||||
log::info!("PunchResponse={:?},source={}", route_key, source);
|
||||
if context.use_channel_type().is_only_relay() {
|
||||
return Ok(());
|
||||
}
|
||||
if self
|
||||
.nat_test
|
||||
.is_local_address(route_key.protocol().is_base_tcp(), route_key.addr)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
let route = Route::from_default_rt(route_key, metric);
|
||||
context.route_table.add_route_if_absent(source, route);
|
||||
}
|
||||
ControlPacket::AddrRequest => match route_key.addr.ip() {
|
||||
std::net::IpAddr::V4(ipv4) => {
|
||||
let mut packet = NetPacket::new_encrypt([0; 12 + 6 + ENCRYPTION_RESERVED])?;
|
||||
packet.set_default_version();
|
||||
packet.set_protocol(Protocol::Control);
|
||||
packet.set_transport_protocol(control_packet::Protocol::AddrResponse.into());
|
||||
packet.first_set_ttl(MAX_TTL);
|
||||
packet.set_source(current_device.virtual_ip);
|
||||
packet.set_destination(source);
|
||||
let mut addr_packet = control_packet::AddrPacket::new(packet.payload_mut())?;
|
||||
addr_packet.set_ipv4(ipv4);
|
||||
addr_packet.set_port(route_key.addr.port());
|
||||
self.client_cipher.encrypt_ipv4(&mut packet)?;
|
||||
context.send_by_key(&packet, route_key)?;
|
||||
}
|
||||
std::net::IpAddr::V6(_) => {}
|
||||
},
|
||||
ControlPacket::AddrResponse(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn other_turn(
|
||||
&self,
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()> {
|
||||
if context.use_channel_type().is_only_relay() {
|
||||
return Ok(());
|
||||
}
|
||||
let source = net_packet.source();
|
||||
match other_turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
other_turn_packet::Protocol::Punch => {
|
||||
let mut punch_info = PunchInfo::parse_from_bytes(net_packet.payload())
|
||||
.map_err(|e| anyhow!("PunchInfo {:?}", e))?;
|
||||
let public_ips = punch_info
|
||||
.public_ip_list
|
||||
.iter()
|
||||
.map(|v| Ipv4Addr::from(v.to_be_bytes()))
|
||||
.collect();
|
||||
let local_ipv4 = Some(Ipv4Addr::from(punch_info.local_ip.to_be_bytes()));
|
||||
let tcp_port = punch_info.tcp_port as u16;
|
||||
let public_tcp_port = punch_info.public_tcp_port as u16;
|
||||
let ipv6 = if punch_info.ipv6.len() == 16 {
|
||||
let ipv6: [u8; 16] = punch_info.ipv6.try_into().unwrap();
|
||||
Some(Ipv6Addr::from(ipv6))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
//兼容旧版本
|
||||
if punch_info.public_ports.is_empty() {
|
||||
punch_info.public_ports.push(punch_info.public_port);
|
||||
}
|
||||
//兼容旧版本
|
||||
if punch_info.udp_ports.is_empty() {
|
||||
punch_info.udp_ports.push(punch_info.local_port);
|
||||
}
|
||||
let peer_nat_info = NatInfo::new(
|
||||
public_ips,
|
||||
punch_info.public_ports.iter().map(|e| *e as u16).collect(),
|
||||
punch_info.public_port_range as u16,
|
||||
local_ipv4,
|
||||
ipv6,
|
||||
punch_info.udp_ports.iter().map(|e| *e as u16).collect(),
|
||||
tcp_port,
|
||||
public_tcp_port,
|
||||
punch_info.nat_type.enum_value_or_default().into(),
|
||||
punch_info.punch_model.enum_value_or_default().into(),
|
||||
);
|
||||
{
|
||||
let peer_nat_info = peer_nat_info.clone();
|
||||
self.peer_nat_info_map.write().insert(source, peer_nat_info);
|
||||
}
|
||||
if !punch_info.reply {
|
||||
let mut punch_reply = PunchInfo::new();
|
||||
punch_reply.reply = true;
|
||||
let nat_info = self.nat_test.nat_info();
|
||||
punch_reply.public_ip_list = nat_info
|
||||
.public_ips
|
||||
.iter()
|
||||
.map(|ip| u32::from_be_bytes(ip.octets()))
|
||||
.collect();
|
||||
punch_reply.public_port = nat_info.public_ports.get(0).map_or(0, |v| *v as u32);
|
||||
punch_reply.public_ports =
|
||||
nat_info.public_ports.iter().map(|e| *e as u32).collect();
|
||||
punch_reply.public_port_range = nat_info.public_port_range as u32;
|
||||
punch_reply.tcp_port = nat_info.tcp_port as u32;
|
||||
punch_reply.public_tcp_port = nat_info.public_tcp_port as u32;
|
||||
punch_reply.nat_type =
|
||||
protobuf::EnumOrUnknown::new(PunchNatType::from(nat_info.nat_type));
|
||||
punch_reply.punch_model =
|
||||
protobuf::EnumOrUnknown::new(nat_info.punch_model.into());
|
||||
punch_reply.local_ip =
|
||||
u32::from(nat_info.local_ipv4().unwrap_or(Ipv4Addr::UNSPECIFIED));
|
||||
punch_reply.local_port = nat_info.udp_ports[0] as u32;
|
||||
punch_reply.udp_ports = nat_info.udp_ports.iter().map(|e| *e as u32).collect();
|
||||
if let Some(ipv6) = nat_info.ipv6() {
|
||||
punch_reply.ipv6 = ipv6.octets().to_vec();
|
||||
punch_reply.ipv6_port = nat_info.udp_ports[0] as u32;
|
||||
}
|
||||
let bytes = punch_reply
|
||||
.write_to_bytes()
|
||||
.map_err(|e| anyhow!("punch_reply {:?}", e))?;
|
||||
let mut punch_packet =
|
||||
NetPacket::new_encrypt(vec![0u8; 12 + bytes.len() + ENCRYPTION_RESERVED])?;
|
||||
punch_packet.set_default_version();
|
||||
punch_packet.set_protocol(Protocol::OtherTurn);
|
||||
punch_packet.set_transport_protocol(other_turn_packet::Protocol::Punch.into());
|
||||
punch_packet.first_set_ttl(MAX_TTL);
|
||||
punch_packet.set_source(current_device.virtual_ip());
|
||||
punch_packet.set_destination(source);
|
||||
punch_packet.set_payload(&bytes)?;
|
||||
self.client_cipher.encrypt_ipv4(&mut punch_packet)?;
|
||||
if self.punch_sender.send(true, source, peer_nat_info) {
|
||||
context.send_by_key(&punch_packet, route_key)?;
|
||||
}
|
||||
} else {
|
||||
self.punch_sender.send(false, source, peer_nat_info);
|
||||
}
|
||||
}
|
||||
other_turn_packet::Protocol::Unknown(e) => {
|
||||
log::warn!("不支持的转发协议 {:?},source:{:?}", e, source);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::handler::RecvChannelHandler;
|
||||
use crate::channel::punch::NatInfo;
|
||||
use crate::channel::RouteKey;
|
||||
use crate::cipher::Cipher;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::cipher::RsaCipher;
|
||||
use crate::external_route::{AllowExternalRoute, ExternalRoute};
|
||||
use crate::handle::callback::VntCallback;
|
||||
use crate::handle::handshaker::Handshake;
|
||||
use crate::handle::maintain::PunchSender;
|
||||
use crate::handle::recv_data::client::ClientPacketHandler;
|
||||
use crate::handle::recv_data::server::ServerPacketHandler;
|
||||
use crate::handle::recv_data::turn::TurnPacketHandler;
|
||||
use crate::handle::{BaseConfigInfo, CurrentDeviceInfo, PeerDeviceInfo, SELF_IP};
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
use crate::ip_proxy::IpProxyMap;
|
||||
use crate::nat::NatTest;
|
||||
use crate::protocol::{NetPacket, HEAD_LEN};
|
||||
use crate::tun_tap_device::vnt_device::DeviceWrite;
|
||||
|
||||
mod client;
|
||||
mod server;
|
||||
mod turn;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RecvDataHandler<Call, Device> {
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
turn: TurnPacketHandler,
|
||||
client: ClientPacketHandler<Device>,
|
||||
server: ServerPacketHandler<Call, Device>,
|
||||
nat_test: NatTest,
|
||||
}
|
||||
|
||||
impl<Call: VntCallback, Device: DeviceWrite> RecvChannelHandler for RecvDataHandler<Call, Device> {
|
||||
fn handle(
|
||||
&self,
|
||||
buf: &mut [u8],
|
||||
extend: &mut [u8],
|
||||
route_key: RouteKey,
|
||||
context: &ChannelContext,
|
||||
) {
|
||||
if buf.len() < HEAD_LEN {
|
||||
return;
|
||||
}
|
||||
//判断stun响应包
|
||||
if route_key.protocol().is_udp() {
|
||||
if let Ok(rs) = self
|
||||
.nat_test
|
||||
.recv_data(route_key.index(), route_key.addr, buf)
|
||||
{
|
||||
if rs {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Err(e) = self.handle0(buf, extend, route_key, context) {
|
||||
log::error!(
|
||||
"[{}]-{:?}-{:?}",
|
||||
thread::current().name().unwrap_or(""),
|
||||
route_key.addr,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: VntCallback, Device: DeviceWrite> RecvDataHandler<Call, Device> {
|
||||
pub fn new(
|
||||
#[cfg(feature = "server_encrypt")] rsa_cipher: Arc<Mutex<Option<RsaCipher>>>,
|
||||
server_cipher: Cipher,
|
||||
client_cipher: Cipher,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device: Device,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
config_info: BaseConfigInfo,
|
||||
nat_test: NatTest,
|
||||
callback: Call,
|
||||
punch_sender: PunchSender,
|
||||
peer_nat_info_map: Arc<RwLock<HashMap<Ipv4Addr, NatInfo>>>,
|
||||
external_route: ExternalRoute,
|
||||
route: AllowExternalRoute,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
handshake: Handshake,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
tun_device_helper: crate::tun_tap_device::tun_create_helper::TunDeviceHelper,
|
||||
) -> Self {
|
||||
let server = ServerPacketHandler::new(
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher,
|
||||
server_cipher,
|
||||
current_device.clone(),
|
||||
device.clone(),
|
||||
device_map,
|
||||
config_info,
|
||||
nat_test.clone(),
|
||||
callback,
|
||||
external_route.clone(),
|
||||
handshake,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
tun_device_helper,
|
||||
);
|
||||
let client = ClientPacketHandler::new(
|
||||
device.clone(),
|
||||
client_cipher,
|
||||
punch_sender,
|
||||
peer_nat_info_map,
|
||||
nat_test.clone(),
|
||||
route,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map,
|
||||
);
|
||||
let turn = TurnPacketHandler::new();
|
||||
Self {
|
||||
current_device,
|
||||
turn,
|
||||
client,
|
||||
server,
|
||||
nat_test,
|
||||
}
|
||||
}
|
||||
fn handle0(
|
||||
&self,
|
||||
buf: &mut [u8],
|
||||
extend: &mut [u8],
|
||||
route_key: RouteKey,
|
||||
context: &ChannelContext,
|
||||
) -> anyhow::Result<()> {
|
||||
let net_packet = NetPacket::new(buf)?;
|
||||
|
||||
let extend = NetPacket::unchecked(extend);
|
||||
if net_packet.ttl() == 0 || net_packet.source_ttl() < net_packet.ttl() {
|
||||
log::warn!("丢弃过时包:{:?} {}", net_packet.head(), route_key.addr);
|
||||
return Ok(());
|
||||
}
|
||||
let current_device = self.current_device.load();
|
||||
let dest = net_packet.destination();
|
||||
if dest == current_device.virtual_ip
|
||||
|| dest.is_broadcast()
|
||||
|| dest.is_multicast()
|
||||
|| dest == SELF_IP
|
||||
|| dest.is_unspecified()
|
||||
|| dest == current_device.broadcast_ip
|
||||
{
|
||||
// 统计流量
|
||||
if let Some(down_traffic_meter) = &context.down_traffic_meter {
|
||||
down_traffic_meter.add_traffic(net_packet.source(), net_packet.data_len())
|
||||
}
|
||||
//发给自己的包
|
||||
if net_packet.is_gateway() {
|
||||
//服务端-客户端包
|
||||
self.server
|
||||
.handle(net_packet, extend, route_key, context, ¤t_device)
|
||||
} else {
|
||||
//客户端-客户端包
|
||||
self.client
|
||||
.handle(net_packet, extend, route_key, context, ¤t_device)
|
||||
}
|
||||
} else {
|
||||
//转发包
|
||||
self.turn
|
||||
.handle(net_packet, extend, route_key, context, ¤t_device)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PacketHandler {
|
||||
fn handle(
|
||||
&self,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
extend: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> anyhow::Result<()>;
|
||||
}
|
||||
@@ -0,0 +1,599 @@
|
||||
use anyhow::anyhow;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use packet::icmp::{icmp, Kind};
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use parking_lot::Mutex;
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::{Route, RouteKey};
|
||||
use crate::cipher::Cipher;
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::cipher::RsaCipher;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::callback::{ErrorInfo, ErrorType, HandshakeInfo, RegisterInfo, VntCallback};
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
use crate::handle::handshaker;
|
||||
use crate::handle::handshaker::Handshake;
|
||||
use crate::handle::recv_data::PacketHandler;
|
||||
use crate::handle::{registrar, BaseConfigInfo, ConnectStatus, CurrentDeviceInfo, PeerDeviceInfo};
|
||||
use crate::nat::NatTest;
|
||||
use crate::proto::message::{DeviceList, HandshakeResponse, RegistrationResponse};
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::control_packet::ControlPacket;
|
||||
use crate::protocol::error_packet::InErrorPacket;
|
||||
use crate::protocol::{ip_turn_packet, service_packet, NetPacket, Protocol, MAX_TTL};
|
||||
use crate::tun_tap_device::vnt_device::DeviceWrite;
|
||||
use crate::{proto, PeerClientInfo};
|
||||
|
||||
/// 处理来源于服务端的包
|
||||
#[derive(Clone)]
|
||||
pub struct ServerPacketHandler<Call, Device> {
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher: Arc<Mutex<Option<RsaCipher>>>,
|
||||
server_cipher: Cipher,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device: Device,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
config_info: BaseConfigInfo,
|
||||
nat_test: NatTest,
|
||||
callback: Call,
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
up_key_time: Arc<AtomicCell<Instant>>,
|
||||
external_route: ExternalRoute,
|
||||
handshake: Handshake,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
tun_device_helper: crate::tun_tap_device::tun_create_helper::TunDeviceHelper,
|
||||
}
|
||||
|
||||
impl<Call, Device> ServerPacketHandler<Call, Device> {
|
||||
pub fn new(
|
||||
#[cfg(feature = "server_encrypt")] rsa_cipher: Arc<Mutex<Option<RsaCipher>>>,
|
||||
server_cipher: Cipher,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
device: Device,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
config_info: BaseConfigInfo,
|
||||
nat_test: NatTest,
|
||||
callback: Call,
|
||||
external_route: ExternalRoute,
|
||||
handshake: Handshake,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
tun_device_helper: crate::tun_tap_device::tun_create_helper::TunDeviceHelper,
|
||||
) -> Self {
|
||||
Self {
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
rsa_cipher,
|
||||
server_cipher,
|
||||
current_device,
|
||||
device,
|
||||
device_map,
|
||||
config_info,
|
||||
nat_test,
|
||||
callback,
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
up_key_time: Arc::new(AtomicCell::new(
|
||||
Instant::now()
|
||||
.checked_sub(Duration::from_secs(60))
|
||||
.unwrap_or(Instant::now()),
|
||||
)),
|
||||
external_route,
|
||||
handshake,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
tun_device_helper,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: VntCallback, Device: DeviceWrite> PacketHandler for ServerPacketHandler<Call, Device> {
|
||||
fn handle(
|
||||
&self,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
_extend: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
) -> anyhow::Result<()> {
|
||||
if !current_device.is_server_addr(route_key.addr) {
|
||||
//拦截不是服务端的流量
|
||||
log::warn!(
|
||||
"route_key={:?},不是来源于服务端地址{}",
|
||||
route_key,
|
||||
current_device.connect_server
|
||||
);
|
||||
}
|
||||
context
|
||||
.route_table
|
||||
.update_read_time(&net_packet.source(), &route_key);
|
||||
if net_packet.protocol() == Protocol::Error
|
||||
&& net_packet.transport_protocol()
|
||||
== crate::protocol::error_packet::Protocol::NoKey.into()
|
||||
{
|
||||
//服务端通知客户端上传密钥
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
{
|
||||
let mutex_guard = self.rsa_cipher.lock();
|
||||
if let Some(rsa_cipher) = mutex_guard.as_ref() {
|
||||
let last = self.up_key_time.load();
|
||||
if last.elapsed() < Duration::from_secs(1)
|
||||
|| self
|
||||
.up_key_time
|
||||
.compare_exchange(last, Instant::now())
|
||||
.is_err()
|
||||
{
|
||||
//短时间不重复上传服务端密钥
|
||||
return Ok(());
|
||||
}
|
||||
if let Some(key) = self.server_cipher.key() {
|
||||
log::info!("上传密钥到服务端:{:?}", route_key);
|
||||
let packet = handshaker::secret_handshake_request_packet(
|
||||
rsa_cipher,
|
||||
self.config_info.token.clone(),
|
||||
key,
|
||||
)?;
|
||||
context.send_by_key(&packet, route_key)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Ok(());
|
||||
} else if net_packet.protocol() == Protocol::Service
|
||||
&& net_packet.transport_protocol() == service_packet::Protocol::HandshakeResponse.into()
|
||||
{
|
||||
let response = HandshakeResponse::parse_from_bytes(net_packet.payload())
|
||||
.map_err(|e| anyhow!("HandshakeResponse {:?}", e))?;
|
||||
log::info!("握手响应:{:?},{}", route_key, response);
|
||||
//设置为默认通道
|
||||
context.set_default_route_key(route_key);
|
||||
//如果开启了加密,则发送加密握手请求
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
if let Some(key) = self.server_cipher.key() {
|
||||
{
|
||||
let guard = self.rsa_cipher.lock();
|
||||
if let Some(rsa_cipher) = guard.as_ref() {
|
||||
if rsa_cipher.finger() == &response.key_finger {
|
||||
let packet = handshaker::secret_handshake_request_packet(
|
||||
rsa_cipher,
|
||||
self.config_info.token.clone(),
|
||||
key,
|
||||
)?;
|
||||
drop(guard);
|
||||
context.send_by_key(&packet, route_key)?;
|
||||
return Ok(());
|
||||
}
|
||||
log::warn!(
|
||||
"拒绝服务端密钥对变化,原指纹:{:?},新指纹:{:?},addr:{:?}",
|
||||
rsa_cipher.finger(),
|
||||
response.key_finger,
|
||||
route_key
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
drop(guard);
|
||||
}
|
||||
let rsa_cipher = RsaCipher::new(&response.public_key)?;
|
||||
if rsa_cipher.finger() != &response.key_finger {
|
||||
log::info!(
|
||||
"服务端密钥和指纹不匹 配拒绝握手,指纹1:{:?},指纹2:{:?}",
|
||||
rsa_cipher.finger(),
|
||||
response.key_finger
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
let handshake_info = HandshakeInfo::new(
|
||||
rsa_cipher.public_key()?.clone(),
|
||||
response.key_finger,
|
||||
response.version,
|
||||
);
|
||||
log::info!("加密握手请求:{:?}", handshake_info);
|
||||
|
||||
if self.callback.handshake(handshake_info) {
|
||||
let packet = handshaker::secret_handshake_request_packet(
|
||||
&rsa_cipher,
|
||||
self.config_info.token.clone(),
|
||||
key,
|
||||
)?;
|
||||
context.send_by_key(&packet, route_key)?;
|
||||
self.rsa_cipher.lock().replace(rsa_cipher);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
#[cfg(feature = "server_encrypt")]
|
||||
if let Ok(rsa_cipher) = RsaCipher::new(&response.public_key) {
|
||||
self.rsa_cipher.lock().replace(rsa_cipher);
|
||||
}
|
||||
let handshake_info = HandshakeInfo::new_no_secret(response.version);
|
||||
if self.callback.handshake(handshake_info) {
|
||||
//没有加密,则发送注册请求
|
||||
self.register(current_device, context, route_key)?;
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
//服务端数据解密
|
||||
self.server_cipher.decrypt_ipv4(&mut net_packet)?;
|
||||
match net_packet.protocol() {
|
||||
Protocol::Service => {
|
||||
self.service(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Error => {
|
||||
self.error(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::Control => {
|
||||
self.control(context, current_device, net_packet, route_key)?;
|
||||
}
|
||||
Protocol::IpTurn => {
|
||||
match ip_turn_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
ip_turn_packet::Protocol::Ipv4 => {
|
||||
let ipv4 = IpV4Packet::new(net_packet.payload())?;
|
||||
match ipv4.protocol() {
|
||||
ipv4::protocol::Protocol::Icmp => {
|
||||
if ipv4.destination_ip() == current_device.virtual_ip {
|
||||
let icmp_packet = icmp::IcmpPacket::new(ipv4.payload())?;
|
||||
if icmp_packet.kind() == Kind::EchoReply {
|
||||
//网关ip ping的回应
|
||||
self.device.write(net_packet.payload())?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
ip_turn_packet::Protocol::WGIpv4 => {
|
||||
if self.config_info.allow_wire_guard {
|
||||
self.device.write(net_packet.payload())?;
|
||||
}
|
||||
}
|
||||
ip_turn_packet::Protocol::Ipv4Broadcast => {}
|
||||
ip_turn_packet::Protocol::Unknown(_) => {}
|
||||
}
|
||||
}
|
||||
Protocol::OtherTurn => {}
|
||||
Protocol::Unknown(_) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<Call: VntCallback, Device: DeviceWrite> ServerPacketHandler<Call, Device> {
|
||||
fn service(
|
||||
&self,
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()> {
|
||||
match service_packet::Protocol::from(net_packet.transport_protocol()) {
|
||||
service_packet::Protocol::RegistrationResponse => {
|
||||
let response = RegistrationResponse::parse_from_bytes(net_packet.payload())
|
||||
.map_err(|e| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("RegistrationResponse {:?}", e),
|
||||
)
|
||||
})?;
|
||||
let virtual_ip = Ipv4Addr::from(response.virtual_ip);
|
||||
let virtual_netmask = Ipv4Addr::from(response.virtual_netmask);
|
||||
let virtual_gateway = Ipv4Addr::from(response.virtual_gateway);
|
||||
let virtual_network =
|
||||
Ipv4Addr::from(response.virtual_ip & response.virtual_netmask);
|
||||
let register_info = RegisterInfo::new(virtual_ip, virtual_netmask, virtual_gateway);
|
||||
log::info!("注册成功:{:?}", register_info);
|
||||
if self.callback.register(register_info) {
|
||||
let route = Route::from_default_rt(route_key, 1);
|
||||
context
|
||||
.route_table
|
||||
.add_route_if_absent(virtual_gateway, route);
|
||||
let public_ip = response.public_ip.into();
|
||||
let public_port = response.public_port as u16;
|
||||
self.nat_test
|
||||
.update_addr(route_key.index(), public_ip, public_port);
|
||||
if route_key.protocol().is_tcp() {
|
||||
log::info!("更新公网tcp端口 {public_port}");
|
||||
self.nat_test.update_tcp_port(public_port);
|
||||
}
|
||||
let old = current_device;
|
||||
let mut cur = *current_device;
|
||||
loop {
|
||||
let mut new_current_device = cur;
|
||||
new_current_device.update(virtual_ip, virtual_netmask, virtual_gateway);
|
||||
new_current_device.virtual_ip = virtual_ip;
|
||||
new_current_device.virtual_netmask = virtual_netmask;
|
||||
new_current_device.virtual_gateway = virtual_gateway;
|
||||
new_current_device.status = ConnectStatus::Connected;
|
||||
if let Err(c) = self
|
||||
.current_device
|
||||
.compare_exchange(cur, new_current_device)
|
||||
{
|
||||
cur = c;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if old.virtual_ip != virtual_ip
|
||||
|| old.virtual_gateway != virtual_gateway
|
||||
|| old.virtual_netmask != virtual_netmask
|
||||
{
|
||||
if old.virtual_ip != Ipv4Addr::UNSPECIFIED {
|
||||
log::info!("ip发生变化,old:{:?},response={:?}", old, response);
|
||||
}
|
||||
let device_config = crate::handle::callback::DeviceConfig::new(
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(target_os = "windows")]
|
||||
self.config_info.tap,
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
#[cfg(any(
|
||||
target_os = "windows",
|
||||
target_os = "linux",
|
||||
target_os = "macos"
|
||||
))]
|
||||
self.config_info.device_name.clone(),
|
||||
self.config_info.mtu,
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
virtual_network,
|
||||
self.external_route.to_route(),
|
||||
);
|
||||
#[cfg(not(feature = "integrated_tun"))]
|
||||
self.callback.create_device(device_config);
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
{
|
||||
self.tun_device_helper.stop();
|
||||
#[cfg(any(
|
||||
target_os = "windows",
|
||||
target_os = "linux",
|
||||
target_os = "macos"
|
||||
))]
|
||||
match crate::tun_tap_device::create_device(
|
||||
device_config,
|
||||
&self.callback,
|
||||
) {
|
||||
Ok(device) => {
|
||||
let tun_info = crate::handle::callback::DeviceInfo::new(
|
||||
device.name().unwrap_or("unknown".into()),
|
||||
"".into(),
|
||||
);
|
||||
log::info!("tun信息{:?}", tun_info);
|
||||
self.callback.create_tun(tun_info);
|
||||
self.tun_device_helper
|
||||
.start(device, self.config_info.allow_wire_guard)?;
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("{:?}", e);
|
||||
self.callback.error(e);
|
||||
}
|
||||
}
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
let device_config = crate::handle::callback::DeviceConfig::new(
|
||||
self.config_info.mtu,
|
||||
virtual_ip,
|
||||
virtual_netmask,
|
||||
virtual_gateway,
|
||||
virtual_network,
|
||||
self.external_route.to_route(),
|
||||
);
|
||||
let device_fd = self.callback.generate_tun(device_config);
|
||||
if device_fd == 0 {
|
||||
self.callback.error(ErrorInfo::new_msg(
|
||||
ErrorType::FailedToCrateDevice,
|
||||
"device_fd == 0".into(),
|
||||
));
|
||||
} else {
|
||||
let device =
|
||||
unsafe { tun_rs::SyncDevice::from_fd(device_fd as _) };
|
||||
if let Err(e) = self
|
||||
.tun_device_helper
|
||||
.start(Arc::new(device), self.config_info.allow_wire_guard)
|
||||
{
|
||||
self.callback.error(ErrorInfo::new_msg(
|
||||
ErrorType::FailedToCrateDevice,
|
||||
format!("{:?}", e),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.set_device_info_list(response.device_info_list, response.epoch as _);
|
||||
if old.status.offline() {
|
||||
self.callback.success();
|
||||
}
|
||||
}
|
||||
}
|
||||
service_packet::Protocol::PushDeviceList => {
|
||||
let response = DeviceList::parse_from_bytes(net_packet.payload()).map_err(|e| {
|
||||
io::Error::new(io::ErrorKind::Other, format!("PushDeviceList {:?}", e))
|
||||
})?;
|
||||
self.set_device_info_list(response.device_info_list, response.epoch as _);
|
||||
}
|
||||
service_packet::Protocol::SecretHandshakeResponse => {
|
||||
log::info!("SecretHandshakeResponse");
|
||||
//加密握手结束,发送注册数据
|
||||
self.register(current_device, context, route_key)?;
|
||||
}
|
||||
_ => {
|
||||
log::warn!(
|
||||
"service_packet::Protocol::Unknown = {:?}",
|
||||
net_packet.head()
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn set_device_info_list(&self, device_info_list: Vec<proto::message::DeviceInfo>, epoch: u16) {
|
||||
let ip_list: Vec<PeerDeviceInfo> = device_info_list
|
||||
.into_iter()
|
||||
.map(|info| {
|
||||
PeerDeviceInfo::new(
|
||||
Ipv4Addr::from(info.virtual_ip),
|
||||
info.name,
|
||||
info.device_status as u8,
|
||||
info.client_secret,
|
||||
info.client_secret_hash,
|
||||
info.wireguard,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
{
|
||||
let mut dev = self.device_map.lock();
|
||||
//这里可能会收到旧的消息,但是随着时间推移总会收到新的
|
||||
dev.0 = epoch;
|
||||
dev.1.clear();
|
||||
for info in ip_list.clone() {
|
||||
dev.1.insert(info.virtual_ip, info);
|
||||
}
|
||||
}
|
||||
self.callback.peer_client_list(
|
||||
ip_list
|
||||
.into_iter()
|
||||
.map(|v| PeerClientInfo::new(v.virtual_ip, v.name, v.status, v.client_secret))
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
fn register(
|
||||
&self,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
context: &ChannelContext,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()> {
|
||||
if current_device.status.online() {
|
||||
log::info!("已连接的不需要注册,{:?}", self.config_info);
|
||||
return Ok(());
|
||||
}
|
||||
//设置为默认通道
|
||||
context.set_default_route_key(route_key);
|
||||
let token = self.config_info.token.clone();
|
||||
let device_id = self.config_info.device_id.clone();
|
||||
let name = self.config_info.name.clone();
|
||||
let client_secret = self
|
||||
.config_info
|
||||
.client_secret_hash
|
||||
.as_ref()
|
||||
.map(|v| v.as_ref());
|
||||
let mut ip = self.config_info.ip;
|
||||
if ip.is_none() {
|
||||
ip = Some(current_device.virtual_ip)
|
||||
}
|
||||
let response = registrar::registration_request_packet(
|
||||
&self.server_cipher,
|
||||
token,
|
||||
device_id,
|
||||
name,
|
||||
ip,
|
||||
false,
|
||||
false,
|
||||
client_secret,
|
||||
)?;
|
||||
log::info!("发送注册请求,{:?}", self.config_info);
|
||||
//注册请求只发送到默认通道
|
||||
context.send_default(&response, current_device.connect_server)?;
|
||||
Ok(())
|
||||
}
|
||||
fn error(
|
||||
&self,
|
||||
context: &ChannelContext,
|
||||
_current_device: &CurrentDeviceInfo,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> io::Result<()> {
|
||||
match InErrorPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
InErrorPacket::TokenError => {
|
||||
// token错误,可能是服务端设置了白名单
|
||||
let err = ErrorInfo::new(ErrorType::TokenError);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::Disconnect => {
|
||||
crate::handle::change_status(&self.current_device, ConnectStatus::Connecting);
|
||||
let err = ErrorInfo::new(ErrorType::Disconnect);
|
||||
self.callback.error(err);
|
||||
//掉线epoch要归零
|
||||
{
|
||||
let mut dev = self.device_map.lock();
|
||||
dev.0 = 0;
|
||||
drop(dev);
|
||||
}
|
||||
self.handshake
|
||||
.send(context, self.config_info.server_secret, route_key.addr)?;
|
||||
// self.register(current_device, context, route_key)?;
|
||||
}
|
||||
InErrorPacket::AddressExhausted => {
|
||||
// 地址用尽
|
||||
let err = ErrorInfo::new(ErrorType::AddressExhausted);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::OtherError(e) => {
|
||||
let err = ErrorInfo::new_msg(ErrorType::Unknown, e.message()?);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::IpAlreadyExists => {
|
||||
let err = ErrorInfo::new(ErrorType::IpAlreadyExists);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::InvalidIp => {
|
||||
let err = ErrorInfo::new(ErrorType::InvalidIp);
|
||||
self.callback.error(err);
|
||||
}
|
||||
InErrorPacket::NoKey => {
|
||||
//这个类型最开头已经处理过,这里忽略
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn control(
|
||||
&self,
|
||||
context: &ChannelContext,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
net_packet: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
) -> anyhow::Result<()> {
|
||||
match ControlPacket::new(net_packet.transport_protocol(), net_packet.payload())? {
|
||||
ControlPacket::PongPacket(pong_packet) => {
|
||||
let current_time = crate::handle::now_time() as u16;
|
||||
if current_time < pong_packet.time() {
|
||||
return Ok(());
|
||||
}
|
||||
let metric = net_packet.source_ttl() - net_packet.ttl() + 1;
|
||||
let rt = (current_time - pong_packet.time()) as i64;
|
||||
let route = Route::from(route_key, metric, rt);
|
||||
context.route_table.add_route(net_packet.source(), route);
|
||||
let epoch = self.device_map.lock().0;
|
||||
if pong_packet.epoch() != epoch {
|
||||
//纪元不一致,可能有新客户端连接,向服务端拉取客户端列表
|
||||
let mut poll_device = NetPacket::new_encrypt([0; 12 + ENCRYPTION_RESERVED])?;
|
||||
poll_device.set_source(current_device.virtual_ip);
|
||||
poll_device.set_destination(current_device.virtual_gateway);
|
||||
poll_device.set_default_version();
|
||||
poll_device.set_gateway_flag(true);
|
||||
poll_device.first_set_ttl(MAX_TTL);
|
||||
poll_device.set_protocol(Protocol::Service);
|
||||
poll_device
|
||||
.set_transport_protocol(service_packet::Protocol::PullDeviceList.into());
|
||||
self.server_cipher.encrypt_ipv4(&mut poll_device)?;
|
||||
//发送到默认服务端即可
|
||||
context.send_default(&poll_device, current_device.connect_server)?;
|
||||
}
|
||||
}
|
||||
ControlPacket::AddrResponse(addr_packet) => {
|
||||
//更新本地公网ipv4
|
||||
self.nat_test.update_addr(
|
||||
route_key.index(),
|
||||
addr_packet.ipv4(),
|
||||
addr_packet.port(),
|
||||
);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::RouteKey;
|
||||
use crate::handle::recv_data::PacketHandler;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::protocol::NetPacket;
|
||||
use anyhow::Context;
|
||||
|
||||
/// 处理客户端中转包
|
||||
#[derive(Clone)]
|
||||
pub struct TurnPacketHandler {}
|
||||
|
||||
impl TurnPacketHandler {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketHandler for TurnPacketHandler {
|
||||
fn handle(
|
||||
&self,
|
||||
mut net_packet: NetPacket<&mut [u8]>,
|
||||
_extend: NetPacket<&mut [u8]>,
|
||||
route_key: RouteKey,
|
||||
context: &ChannelContext,
|
||||
_current_device: &CurrentDeviceInfo,
|
||||
) -> anyhow::Result<()> {
|
||||
// ttl减一
|
||||
let ttl = net_packet.incr_ttl();
|
||||
if ttl > 0 {
|
||||
if net_packet.is_gateway() {
|
||||
// 暂时不转发服务端包
|
||||
return Ok(());
|
||||
}
|
||||
let destination = net_packet.destination();
|
||||
if let Some(route) = context.route_table.route_one(&destination) {
|
||||
if route.addr == route_key.addr {
|
||||
//防止环路
|
||||
log::warn!("来源和目标相同 {:?},{:?}", route_key, net_packet.head());
|
||||
return Ok(());
|
||||
}
|
||||
if route.metric <= ttl {
|
||||
return context
|
||||
.send_by_key(&net_packet, route.route_key())
|
||||
.context("转发失败");
|
||||
}
|
||||
}
|
||||
//其他没有路由的不转发
|
||||
}
|
||||
log::info!("没有路由 {:?},{:?}", route_key, net_packet.head());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use anyhow::anyhow;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use protobuf::Message;
|
||||
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::{GATEWAY_IP, SELF_IP};
|
||||
use crate::proto::message::RegistrationRequest;
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::{service_packet, NetPacket, Protocol, MAX_TTL};
|
||||
|
||||
/// 注册数据
|
||||
pub fn registration_request_packet(
|
||||
server_cipher: &Cipher,
|
||||
token: String,
|
||||
device_id: String,
|
||||
name: String,
|
||||
ip: Option<Ipv4Addr>,
|
||||
is_fast: bool,
|
||||
allow_ip_change: bool,
|
||||
client_secret_hash: Option<&[u8]>,
|
||||
) -> anyhow::Result<NetPacket<Vec<u8>>> {
|
||||
let mut request = RegistrationRequest::new();
|
||||
request.token = token;
|
||||
request.device_id = device_id;
|
||||
request.name = name;
|
||||
if let Some(ip) = ip {
|
||||
request.virtual_ip = ip.into();
|
||||
}
|
||||
request.allow_ip_change = allow_ip_change;
|
||||
request.is_fast = is_fast;
|
||||
request.version = crate::VNT_VERSION.to_string();
|
||||
if let Some(client_secret_hash) = client_secret_hash {
|
||||
request.client_secret = true;
|
||||
request
|
||||
.client_secret_hash
|
||||
.extend_from_slice(client_secret_hash);
|
||||
}
|
||||
let bytes = request
|
||||
.write_to_bytes()
|
||||
.map_err(|e| anyhow!("RegistrationRequest {:?}", e))?;
|
||||
let buf = vec![0u8; 12 + bytes.len() + ENCRYPTION_RESERVED];
|
||||
let mut net_packet = NetPacket::new_encrypt(buf)?;
|
||||
net_packet.set_destination(GATEWAY_IP);
|
||||
net_packet.set_source(SELF_IP);
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_gateway_flag(true);
|
||||
net_packet.set_protocol(Protocol::Service);
|
||||
net_packet.set_transport_protocol(service_packet::Protocol::RegistrationRequest.into());
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_payload(&bytes)?;
|
||||
server_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
Ok(net_packet)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
pub mod tun_handler;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
mod platform;
|
||||
|
||||
pub(crate) use platform::*;
|
||||
|
||||
/// 仅仅是停止tun,不停止vnt
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DeviceStop {
|
||||
f: Arc<Mutex<Option<Box<dyn FnOnce() + Send>>>>,
|
||||
stopped: Arc<AtomicCell<bool>>,
|
||||
}
|
||||
|
||||
impl DeviceStop {
|
||||
pub fn set_stop_fn<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
self.f.lock().replace(Box::new(f));
|
||||
}
|
||||
pub fn stop(&self) {
|
||||
if let Some(f) = self.f.lock().take() {
|
||||
f()
|
||||
}
|
||||
}
|
||||
pub fn stopped(&self) {
|
||||
self.stopped.store(true);
|
||||
}
|
||||
pub fn is_stopped(&self) -> bool {
|
||||
self.stopped.load()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::BUFFER_SIZE;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::compression::Compressor;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::tun_tap::DeviceStop;
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
use crate::ip_proxy::IpProxyMap;
|
||||
use crate::util::StopManager;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use tun_rs::{InterruptEvent, SyncDevice};
|
||||
|
||||
pub(crate) fn start_simple(
|
||||
stop_manager: StopManager,
|
||||
context: &ChannelContext,
|
||||
device: Arc<SyncDevice>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
device_stop: DeviceStop,
|
||||
allow_wire_guard: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let event = Arc::new(InterruptEvent::new()?);
|
||||
let worker = {
|
||||
let event = event.clone();
|
||||
stop_manager.add_listener("tun_device".into(), move || {
|
||||
if let Err(e) = event.trigger() {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
})?
|
||||
};
|
||||
let worker_cell = Arc::new(AtomicCell::new(Some(worker)));
|
||||
|
||||
{
|
||||
let worker_cell = worker_cell.clone();
|
||||
device_stop.set_stop_fn(move || {
|
||||
if let Some(worker) = worker_cell.take() {
|
||||
worker.stop_self()
|
||||
}
|
||||
});
|
||||
}
|
||||
if let Err(e) = start_simple0(
|
||||
context,
|
||||
device,
|
||||
event,
|
||||
current_device,
|
||||
ip_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
device_map,
|
||||
compressor,
|
||||
allow_wire_guard,
|
||||
) {
|
||||
log::error!("{:?}", e);
|
||||
}
|
||||
device_stop.stopped();
|
||||
if let Some(worker) = worker_cell.take() {
|
||||
worker.stop_all();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_simple0(
|
||||
context: &ChannelContext,
|
||||
device: Arc<SyncDevice>,
|
||||
event: Arc<InterruptEvent>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
allow_wire_guard: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut buf = [0; BUFFER_SIZE];
|
||||
let mut extend = [0; BUFFER_SIZE];
|
||||
loop {
|
||||
let len = device.recv_intr(&mut buf[12..],&event)? + 12;
|
||||
// buf是重复利用的,需要重置头部
|
||||
buf[..12].fill(0);
|
||||
match crate::handle::tun_tap::tun_handler::handle(
|
||||
context,
|
||||
&mut buf,
|
||||
len,
|
||||
&mut extend,
|
||||
&device,
|
||||
current_device.load(),
|
||||
&ip_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
&ip_proxy_map,
|
||||
&client_cipher,
|
||||
&server_cipher,
|
||||
&device_map,
|
||||
&compressor,
|
||||
allow_wire_guard,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!("tun/tap {:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use packet::icmp::icmp::IcmpPacket;
|
||||
use packet::icmp::Kind;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use packet::ip::ipv4::protocol::Protocol;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
use tun_rs::SyncDevice;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::sender::{send_to_wg, send_to_wg_broadcast};
|
||||
use crate::cipher::Cipher;
|
||||
use crate::compression::Compressor;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::tun_tap::DeviceStop;
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
use crate::ip_proxy::IpProxyMap;
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
use crate::protocol;
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use crate::protocol::ip_turn_packet::BroadcastPacket;
|
||||
use crate::protocol::{ip_turn_packet, NetPacket, MAX_TTL};
|
||||
use crate::util::StopManager;
|
||||
fn icmp(device_writer: &SyncDevice, mut ipv4_packet: IpV4Packet<&mut [u8]>) -> anyhow::Result<()> {
|
||||
if ipv4_packet.protocol() == Protocol::Icmp {
|
||||
let mut icmp = IcmpPacket::new(ipv4_packet.payload_mut())?;
|
||||
if icmp.kind() == Kind::EchoRequest {
|
||||
icmp.set_kind(Kind::EchoReply);
|
||||
icmp.update_checksum();
|
||||
let src = ipv4_packet.source_ip();
|
||||
ipv4_packet.set_source_ip(ipv4_packet.destination_ip());
|
||||
ipv4_packet.set_destination_ip(src);
|
||||
ipv4_packet.update_checksum();
|
||||
device_writer.send(ipv4_packet.buffer)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn start(
|
||||
stop_manager: StopManager,
|
||||
context: ChannelContext,
|
||||
device: Arc<SyncDevice>,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
device_stop: DeviceStop,
|
||||
allow_wire_guard: bool,
|
||||
) -> io::Result<()> {
|
||||
thread::Builder::new()
|
||||
.name("tunHandlerS".into())
|
||||
.spawn(move || {
|
||||
if let Err(e) = crate::handle::tun_tap::start_simple(
|
||||
stop_manager,
|
||||
&context,
|
||||
device,
|
||||
current_device,
|
||||
ip_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
device_map,
|
||||
compressor,
|
||||
device_stop,
|
||||
allow_wire_guard,
|
||||
) {
|
||||
log::warn!("stop:{}", e);
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn broadcast(
|
||||
server_cipher: &Cipher,
|
||||
sender: &ChannelContext,
|
||||
net_packet: &mut NetPacket<&mut [u8]>,
|
||||
current_device: &CurrentDeviceInfo,
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
) -> anyhow::Result<()> {
|
||||
let list: Vec<Ipv4Addr> = device_map
|
||||
.lock()
|
||||
.1
|
||||
.values()
|
||||
.filter(|info| !info.wireguard && info.status.is_online())
|
||||
.map(|info| info.virtual_ip)
|
||||
.collect();
|
||||
if list.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
const MAX_COUNT: usize = 8;
|
||||
let mut p2p_ips = Vec::with_capacity(8);
|
||||
let mut relay = false;
|
||||
let mut overflow = false;
|
||||
for (index, peer_ip) in list.into_iter().enumerate() {
|
||||
if index > MAX_COUNT {
|
||||
overflow = true;
|
||||
break;
|
||||
}
|
||||
if let Some(route) = sender.route_table.route_one_p2p(&peer_ip) {
|
||||
if sender.send_by_key(&net_packet, route.route_key()).is_ok() {
|
||||
p2p_ips.push(peer_ip);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
relay = true;
|
||||
}
|
||||
if !overflow && !relay {
|
||||
//全部p2p,不需要服务器中转
|
||||
return Ok(());
|
||||
}
|
||||
if current_device.status.offline() {
|
||||
//离线的不再转发
|
||||
return Ok(());
|
||||
}
|
||||
if p2p_ips.is_empty() {
|
||||
//都没有p2p则直接由服务器转发
|
||||
sender.send_default(&net_packet, current_device.connect_server)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let buf = vec![0u8; 12 + 1 + p2p_ips.len() * 4 + net_packet.data_len() + ENCRYPTION_RESERVED];
|
||||
//剩余的发送到服务端,需要告知哪些已发送过
|
||||
let mut server_packet = NetPacket::new_encrypt(buf)?;
|
||||
server_packet.set_default_version();
|
||||
server_packet.set_gateway_flag(true);
|
||||
server_packet.first_set_ttl(MAX_TTL);
|
||||
server_packet.set_source(net_packet.source());
|
||||
//使用对应的目的地址
|
||||
server_packet.set_destination(net_packet.destination());
|
||||
server_packet.set_protocol(protocol::Protocol::IpTurn);
|
||||
server_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4Broadcast.into());
|
||||
|
||||
let mut broadcast = BroadcastPacket::unchecked(server_packet.payload_mut());
|
||||
broadcast.set_address(&p2p_ips)?;
|
||||
broadcast.set_data(net_packet.buffer())?;
|
||||
server_cipher.encrypt_ipv4(&mut server_packet)?;
|
||||
sender.send_default(&server_packet, current_device.connect_server)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 接收tun数据,并且转发到udp上
|
||||
/// 实现一个原地发送,必须保证是如下结构
|
||||
/// |12字节开头|ip报文|至少1024字节结尾|
|
||||
///
|
||||
pub(crate) fn handle(
|
||||
context: &ChannelContext,
|
||||
buf: &mut [u8],
|
||||
data_len: usize, //数据总长度=12+ip包长度
|
||||
extend: &mut [u8],
|
||||
device_writer: &SyncDevice,
|
||||
current_device: CurrentDeviceInfo,
|
||||
ip_route: &ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] proxy_map: &Option<IpProxyMap>,
|
||||
client_cipher: &Cipher,
|
||||
server_cipher: &Cipher,
|
||||
device_map: &Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>,
|
||||
compressor: &Compressor,
|
||||
allow_wire_guard: bool,
|
||||
) -> anyhow::Result<()> {
|
||||
//忽略掉结构不对的情况(ipv6数据、win tap会读到空数据),不然日志打印太多了
|
||||
let ipv4_packet = match IpV4Packet::new(&mut buf[12..data_len]) {
|
||||
Ok(packet) => packet,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
let src_ip = ipv4_packet.source_ip();
|
||||
let dest_ip = ipv4_packet.destination_ip();
|
||||
if src_ip == dest_ip {
|
||||
return icmp(&device_writer, ipv4_packet);
|
||||
}
|
||||
let protocol = ipv4_packet.protocol();
|
||||
let src_ip = ipv4_packet.source_ip();
|
||||
let mut dest_ip = ipv4_packet.destination_ip();
|
||||
let mut net_packet = NetPacket::new0(data_len, buf)?;
|
||||
let mut out = NetPacket::unchecked(extend);
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_protocol(protocol::Protocol::IpTurn);
|
||||
net_packet.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into());
|
||||
net_packet.first_set_ttl(6);
|
||||
net_packet.set_source(src_ip);
|
||||
net_packet.set_destination(dest_ip);
|
||||
if dest_ip == current_device.virtual_gateway {
|
||||
// 发到网关的加密方式不一样,要单独处理
|
||||
if protocol == Protocol::Icmp {
|
||||
net_packet.set_gateway_flag(true);
|
||||
server_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
context.send_default(&net_packet, current_device.connect_server)?;
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
if !dest_ip.is_multicast() && !dest_ip.is_broadcast() && current_device.broadcast_ip != dest_ip
|
||||
{
|
||||
if current_device.not_in_network(dest_ip) {
|
||||
if let Some(r_dest_ip) = ip_route.route(&dest_ip) {
|
||||
//路由的目标不能是自己
|
||||
if r_dest_ip == src_ip {
|
||||
return Ok(());
|
||||
}
|
||||
//需要修改目的地址
|
||||
dest_ip = r_dest_ip;
|
||||
net_packet.set_destination(r_dest_ip);
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
if let Some(proxy_map) = proxy_map {
|
||||
let mut ipv4_packet = IpV4Packet::new(net_packet.payload_mut())?;
|
||||
proxy_map.send_handle(&mut ipv4_packet)?;
|
||||
}
|
||||
}
|
||||
|
||||
if dest_ip.is_multicast() {
|
||||
//当作广播处理
|
||||
dest_ip = Ipv4Addr::BROADCAST;
|
||||
net_packet.set_destination(Ipv4Addr::BROADCAST);
|
||||
}
|
||||
let is_broadcast = dest_ip.is_broadcast() || current_device.broadcast_ip == dest_ip;
|
||||
if allow_wire_guard {
|
||||
if is_broadcast {
|
||||
// wg客户端和vnt客户端分开广播
|
||||
let exists_wg = device_map
|
||||
.lock()
|
||||
.1
|
||||
.values()
|
||||
.any(|v| v.status.is_online() && v.wireguard);
|
||||
if exists_wg {
|
||||
send_to_wg_broadcast(context, &net_packet, server_cipher, ¤t_device)?;
|
||||
}
|
||||
} else {
|
||||
// 如果是wg客户端则发到vnts转发
|
||||
let guard = device_map.lock();
|
||||
if let Some(peer_info) = guard.1.get(&dest_ip) {
|
||||
if peer_info.status.is_offline() {
|
||||
return Ok(());
|
||||
}
|
||||
if peer_info.wireguard {
|
||||
drop(guard);
|
||||
send_to_wg(context, &mut net_packet, server_cipher, ¤t_device)?;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut net_packet = if compressor.compress(&net_packet, &mut out)? {
|
||||
out.set_default_version();
|
||||
out.set_protocol(protocol::Protocol::IpTurn);
|
||||
out.set_transport_protocol(ip_turn_packet::Protocol::Ipv4.into());
|
||||
out.first_set_ttl(6);
|
||||
out.set_source(src_ip);
|
||||
out.set_destination(dest_ip);
|
||||
out
|
||||
} else {
|
||||
net_packet
|
||||
};
|
||||
if is_broadcast {
|
||||
// 广播 发送到直连目标
|
||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
broadcast(
|
||||
server_cipher,
|
||||
context,
|
||||
&mut net_packet,
|
||||
¤t_device,
|
||||
device_map,
|
||||
)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
client_cipher.encrypt_ipv4(&mut net_packet)?;
|
||||
context.send_ipv4_by_id(
|
||||
&net_packet,
|
||||
&dest_ip,
|
||||
current_device.connect_server,
|
||||
current_device.status.online(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
use anyhow::Context;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use packet::icmp::icmp;
|
||||
use packet::icmp::icmp::HeaderOther;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::channel::socket::{LocalInterface, VntSocketTrait};
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
use crate::protocol;
|
||||
use crate::protocol::{NetPacket, MAX_TTL};
|
||||
#[derive(Clone)]
|
||||
pub struct IcmpProxy {
|
||||
icmp_socket: Arc<std::net::UdpSocket>,
|
||||
// 对端-> 真实来源
|
||||
nat_map: Arc<Mutex<HashMap<(Ipv4Addr, u16, u16), Ipv4Addr>>>,
|
||||
}
|
||||
|
||||
impl IcmpProxy {
|
||||
pub async fn new(
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Self> {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
let icmp_socket = socket2::Socket::new(
|
||||
socket2::Domain::IPV4,
|
||||
socket2::Type::RAW,
|
||||
Some(socket2::Protocol::ICMPV4),
|
||||
)
|
||||
.context("new Socket RAW ICMPV4 failed")?;
|
||||
#[cfg(target_os = "android")]
|
||||
let icmp_socket = socket2::Socket::new(
|
||||
socket2::Domain::IPV4,
|
||||
socket2::Type::DGRAM,
|
||||
Some(socket2::Protocol::ICMPV4),
|
||||
)
|
||||
.context("new Socket DGRAM ICMPV4 failed")?;
|
||||
let addr: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
|
||||
icmp_socket
|
||||
.bind(&socket2::SockAddr::from(addr))
|
||||
.context("bind Socket ICMPV4 failed")?;
|
||||
icmp_socket.set_nonblocking(true)?;
|
||||
if let Err(e) = icmp_socket.set_ip_unicast_if(default_interface) {
|
||||
log::warn!("set_ip_unicast_if {:?}", e)
|
||||
}
|
||||
let std_socket: std::net::UdpSocket = icmp_socket.into();
|
||||
|
||||
let tokio_icmp_socket = UdpSocket::from_std(std_socket.try_clone()?)?;
|
||||
let nat_map: Arc<Mutex<HashMap<(Ipv4Addr, u16, u16), Ipv4Addr>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(16)));
|
||||
{
|
||||
let nat_map = nat_map.clone();
|
||||
tokio::spawn(async {
|
||||
if let Err(e) = icmp_proxy(
|
||||
tokio_icmp_socket,
|
||||
nat_map,
|
||||
context,
|
||||
current_device,
|
||||
client_cipher,
|
||||
)
|
||||
.await
|
||||
{
|
||||
log::warn!("icmp_proxy:{:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
Ok(Self {
|
||||
icmp_socket: Arc::new(std_socket),
|
||||
nat_map,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn icmp_proxy(
|
||||
icmp_socket: UdpSocket,
|
||||
// 对端-> 真实来源
|
||||
nat_map: Arc<Mutex<HashMap<(Ipv4Addr, u16, u16), Ipv4Addr>>>,
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
) -> io::Result<()> {
|
||||
let mut buf = [0u8; 65535 - 20 - 8];
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
let start = 12;
|
||||
#[cfg(target_os = "android")]
|
||||
let start = 12 + 20;
|
||||
loop {
|
||||
let (len, addr) = icmp_socket.recv_from(&mut buf[start..]).await?;
|
||||
if let IpAddr::V4(peer_ip) = addr.ip() {
|
||||
#[cfg(target_os = "android")]
|
||||
{
|
||||
let buf = &mut buf[12..];
|
||||
// ipv4 头部20字节
|
||||
buf[0] = 0b0100_0110;
|
||||
//写入总长度
|
||||
buf[2..4].copy_from_slice(&((20 + len) as u16).to_be_bytes());
|
||||
|
||||
let mut ipv4 = IpV4Packet::unchecked(buf);
|
||||
ipv4.set_flags(2);
|
||||
ipv4.set_ttl(1);
|
||||
ipv4.set_protocol(packet::ip::ipv4::protocol::Protocol::Icmp);
|
||||
ipv4.set_source_ip(peer_ip);
|
||||
}
|
||||
recv_handle(
|
||||
&mut buf,
|
||||
start + len,
|
||||
peer_ip,
|
||||
&nat_map,
|
||||
&context,
|
||||
¤t_device,
|
||||
&client_cipher,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_handle(
|
||||
buf: &mut [u8],
|
||||
data_len: usize,
|
||||
peer_ip: Ipv4Addr,
|
||||
nat_map: &Mutex<HashMap<(Ipv4Addr, u16, u16), Ipv4Addr>>,
|
||||
context: &ChannelContext,
|
||||
current_device: &AtomicCell<CurrentDeviceInfo>,
|
||||
client_cipher: &Cipher,
|
||||
) {
|
||||
match IpV4Packet::new(&mut buf[12..data_len]) {
|
||||
Ok(mut ipv4_packet) => match icmp::IcmpPacket::new(ipv4_packet.payload()) {
|
||||
Ok(icmp_packet) => match icmp_packet.header_other() {
|
||||
HeaderOther::Identifier(id, seq) => {
|
||||
if let Some(dest_ip) = nat_map.lock().get(&(peer_ip, id, seq)).cloned() {
|
||||
ipv4_packet.set_destination_ip(dest_ip);
|
||||
ipv4_packet.update_checksum();
|
||||
|
||||
let current_device = current_device.load();
|
||||
let virtual_ip = current_device.virtual_ip();
|
||||
|
||||
let mut net_packet = NetPacket::new0(data_len, buf).unwrap();
|
||||
net_packet.set_default_version();
|
||||
net_packet.set_protocol(protocol::Protocol::IpTurn);
|
||||
net_packet.set_transport_protocol(
|
||||
protocol::ip_turn_packet::Protocol::Ipv4.into(),
|
||||
);
|
||||
net_packet.first_set_ttl(MAX_TTL);
|
||||
net_packet.set_source(virtual_ip);
|
||||
net_packet.set_destination(dest_ip);
|
||||
if let Err(e) = client_cipher.encrypt_ipv4(&mut net_packet) {
|
||||
log::warn!("加密失败:{}", e);
|
||||
return;
|
||||
}
|
||||
if let Err(e) = context.send_ipv4_by_id(
|
||||
&net_packet,
|
||||
&dest_ip,
|
||||
current_device.connect_server,
|
||||
current_device.status.online(),
|
||||
) {
|
||||
log::warn!("发送到目标失败:{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
h => {
|
||||
log::debug!("不支持的icmp代理 {:?},{:?}", peer_ip, h)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("icmp {:?}", e)
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("icmp {:?}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// icmp用Identifier来区分,没有Identifier的一律不转发
|
||||
impl ProxyHandler for IcmpProxy {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool> {
|
||||
if ipv4.offset() != 0 || ipv4.flags() & 1 == 1 {
|
||||
// ip分片的直接丢弃
|
||||
return Ok(true);
|
||||
}
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
//转发到代理目标地址
|
||||
let icmp_packet = icmp::IcmpPacket::new(ipv4.payload())?;
|
||||
match icmp_packet.header_other() {
|
||||
HeaderOther::Identifier(id, seq) => {
|
||||
self.nat_map.lock().insert((dest_ip, id, seq), source);
|
||||
self.icmp_socket.send_to(
|
||||
ipv4.payload(),
|
||||
SocketAddr::from(SocketAddrV4::new(dest_ip, 0)),
|
||||
)?;
|
||||
}
|
||||
header_other => {
|
||||
log::warn!(
|
||||
"不支持的ip代理Icmp协议:{}->{}->{},{:?}",
|
||||
source,
|
||||
destination,
|
||||
dest_ip,
|
||||
header_other
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
fn send_handle(&self, _ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::{io, thread};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
use packet::ip::ipv4;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::handle::CurrentDeviceInfo;
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
use crate::ip_proxy::icmp_proxy::IcmpProxy;
|
||||
use crate::ip_proxy::tcp_proxy::TcpProxy;
|
||||
use crate::ip_proxy::udp_proxy::UdpProxy;
|
||||
use crate::util::StopManager;
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
pub mod icmp_proxy;
|
||||
pub mod tcp_proxy;
|
||||
pub mod udp_proxy;
|
||||
|
||||
pub trait ProxyHandler {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool>;
|
||||
fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IpProxyMap {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
icmp_proxy: IcmpProxy,
|
||||
tcp_proxy: TcpProxy,
|
||||
udp_proxy: UdpProxy,
|
||||
}
|
||||
|
||||
pub fn init_proxy(
|
||||
context: ChannelContext,
|
||||
stop_manager: StopManager,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
client_cipher: Cipher,
|
||||
) -> anyhow::Result<IpProxyMap> {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.thread_name("ipProxy")
|
||||
.build()?;
|
||||
let proxy_map = runtime.block_on(init_proxy0(context, current_device, client_cipher))?;
|
||||
let (sender, receiver) = tokio::sync::oneshot::channel::<()>();
|
||||
let worker = stop_manager.add_listener("ipProxy".into(), move || {
|
||||
let _ = sender.send(());
|
||||
})?;
|
||||
thread::Builder::new()
|
||||
.name("ipProxy".into())
|
||||
.spawn(move || {
|
||||
runtime.block_on(async {
|
||||
let _ = receiver.await;
|
||||
});
|
||||
runtime.shutdown_background();
|
||||
drop(worker);
|
||||
})?;
|
||||
|
||||
return Ok(proxy_map);
|
||||
}
|
||||
|
||||
async fn init_proxy0(
|
||||
context: ChannelContext,
|
||||
_current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
_client_cipher: Cipher,
|
||||
) -> anyhow::Result<IpProxyMap> {
|
||||
let default_interface = context.default_interface().clone();
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
let icmp_proxy =
|
||||
IcmpProxy::new(context, _current_device, _client_cipher, &default_interface).await?;
|
||||
let tcp_proxy = TcpProxy::new(default_interface.clone()).await?;
|
||||
let udp_proxy = UdpProxy::new(default_interface.clone()).await?;
|
||||
|
||||
Ok(IpProxyMap {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
icmp_proxy,
|
||||
tcp_proxy,
|
||||
udp_proxy,
|
||||
})
|
||||
}
|
||||
|
||||
impl ProxyHandler for IpProxyMap {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool> {
|
||||
match ipv4.protocol() {
|
||||
ipv4::protocol::Protocol::Tcp => self.tcp_proxy.recv_handle(ipv4, source, destination),
|
||||
ipv4::protocol::Protocol::Udp => self.udp_proxy.recv_handle(ipv4, source, destination),
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
ipv4::protocol::Protocol::Icmp => {
|
||||
self.icmp_proxy.recv_handle(ipv4, source, destination)
|
||||
}
|
||||
_ => {
|
||||
log::warn!(
|
||||
"不支持的ip代理ipv4协议{:?}:{}->{}->{}",
|
||||
ipv4.protocol(),
|
||||
source,
|
||||
destination,
|
||||
ipv4.destination_ip()
|
||||
);
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> {
|
||||
match ipv4.protocol() {
|
||||
ipv4::protocol::Protocol::Tcp => self.tcp_proxy.send_handle(ipv4),
|
||||
ipv4::protocol::Protocol::Udp => self.udp_proxy.send_handle(ipv4),
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
ipv4::protocol::Protocol::Icmp => self.icmp_proxy.send_handle(ipv4),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
use anyhow::Context;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::{collections::HashMap, io, net::SocketAddr};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
use crate::channel::socket::{create_tcp, LocalInterface};
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use packet::tcp::tcp::TcpPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TcpProxy {
|
||||
port: u16,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
}
|
||||
|
||||
impl TcpProxy {
|
||||
pub async fn new(default_interface: LocalInterface) -> anyhow::Result<Self> {
|
||||
let nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(16)));
|
||||
let tcp_listener = TcpListener::bind(format!("0.0.0.0:{}", 0))
|
||||
.await
|
||||
.context("TcpProxy bind failed")?;
|
||||
let port = tcp_listener.local_addr()?.port();
|
||||
{
|
||||
let nat_map = nat_map.clone();
|
||||
tokio::spawn(tcp_proxy(tcp_listener, nat_map, default_interface));
|
||||
}
|
||||
Ok(Self { port, nat_map })
|
||||
}
|
||||
}
|
||||
|
||||
impl ProxyHandler for TcpProxy {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool> {
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
//转发到代理目标地址
|
||||
let mut tcp_packet = TcpPacket::new(source, destination, ipv4.payload_mut())?;
|
||||
let source_port = tcp_packet.source_port();
|
||||
let dest_port = tcp_packet.destination_port();
|
||||
tcp_packet.set_destination_port(self.port);
|
||||
tcp_packet.update_checksum();
|
||||
ipv4.set_destination_ip(destination);
|
||||
ipv4.update_checksum();
|
||||
let key = SocketAddrV4::new(source, source_port);
|
||||
self.nat_map
|
||||
.lock()
|
||||
.insert(key, SocketAddrV4::new(dest_ip, dest_port));
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> {
|
||||
let src_ip = ipv4.source_ip();
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
let dest_addr = {
|
||||
let tcp_packet = TcpPacket::new(src_ip, dest_ip, ipv4.payload_mut())?;
|
||||
SocketAddrV4::new(dest_ip, tcp_packet.destination_port())
|
||||
};
|
||||
if let Some(source_addr) = self.nat_map.lock().get(&dest_addr) {
|
||||
let source_ip = *source_addr.ip();
|
||||
let mut tcp_packet = TcpPacket::new(source_ip, dest_ip, ipv4.payload_mut())?;
|
||||
tcp_packet.set_source_port(source_addr.port());
|
||||
tcp_packet.update_checksum();
|
||||
ipv4.set_source_ip(source_ip);
|
||||
ipv4.update_checksum();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn tcp_proxy(
|
||||
tcp_listener: TcpListener,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
default_interface: LocalInterface,
|
||||
) {
|
||||
loop {
|
||||
match tcp_listener.accept().await {
|
||||
Ok((tcp_stream, sender_addr)) => match sender_addr {
|
||||
SocketAddr::V4(sender_addr) => {
|
||||
if let Some(dest_addr) = nat_map.lock().get(&sender_addr).cloned() {
|
||||
let default_interface = default_interface.clone();
|
||||
tokio::spawn(async move {
|
||||
let peer_tcp_stream = match tcp_connect(
|
||||
sender_addr.port(),
|
||||
dest_addr.into(),
|
||||
&default_interface,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(peer_tcp_stream) => peer_tcp_stream,
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"tcp代理异常:{:?},来源:{},目标:{}",
|
||||
e,
|
||||
sender_addr,
|
||||
dest_addr
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
proxy(sender_addr, dest_addr, tcp_stream, peer_tcp_stream).await
|
||||
});
|
||||
} else {
|
||||
log::warn!("tcp代理异常: 来源:{},未找到目标", sender_addr);
|
||||
}
|
||||
}
|
||||
SocketAddr::V6(_) => {}
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("tcp代理监听:{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 优先使用来源端口建立tcp连接
|
||||
async fn tcp_connect(
|
||||
src_port: u16,
|
||||
addr: SocketAddr,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<TcpStream> {
|
||||
let socket = create_tcp(true, default_interface)?;
|
||||
if socket
|
||||
.bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, src_port).into())
|
||||
.is_err()
|
||||
{
|
||||
socket.bind(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0).into())?;
|
||||
}
|
||||
let _ = socket.set_nodelay(true);
|
||||
let tcp_stream = tokio::time::timeout(Duration::from_secs(5), socket.connect(addr))
|
||||
.await
|
||||
.with_context(|| format!("TCP connection timeout {}", addr))?
|
||||
.with_context(|| format!("TCP connection target failed {}", addr))?;
|
||||
Ok(tcp_stream)
|
||||
}
|
||||
|
||||
async fn proxy(
|
||||
sender_addr: SocketAddrV4,
|
||||
dest_addr: SocketAddrV4,
|
||||
client: TcpStream,
|
||||
server: TcpStream,
|
||||
) {
|
||||
let (mut client_read, mut client_write) = client.into_split();
|
||||
let (mut server_read, mut server_write) = server.into_split();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = tokio::io::copy(&mut client_read, &mut server_write).await {
|
||||
log::warn!("client tcp proxy {}->{},{:?}", sender_addr, dest_addr, e);
|
||||
}
|
||||
});
|
||||
if let Err(e) = tokio::io::copy(&mut server_read, &mut client_write).await {
|
||||
log::warn!("server tcp proxy {}->{},{:?}", sender_addr, dest_addr, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
use anyhow::Context;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{collections::HashMap, io, net::SocketAddr};
|
||||
|
||||
use parking_lot::Mutex;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::channel::socket::{bind_udp, LocalInterface};
|
||||
use crate::ip_proxy::ProxyHandler;
|
||||
use packet::ip::ipv4::packet::IpV4Packet;
|
||||
use packet::udp::udp::UdpPacket;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UdpProxy {
|
||||
port: u16,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
}
|
||||
|
||||
impl UdpProxy {
|
||||
pub async fn new(default_interface: LocalInterface) -> anyhow::Result<Self> {
|
||||
let nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(16)));
|
||||
let udp = UdpSocket::bind(format!("0.0.0.0:{}", 0))
|
||||
.await
|
||||
.context("UdpProxy bind failed")?;
|
||||
let port = udp.local_addr()?.port();
|
||||
{
|
||||
let nat_map = nat_map.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = udp_proxy(udp, nat_map, default_interface).await {
|
||||
log::warn!("udp_proxy:{:?}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
Ok(Self { port, nat_map })
|
||||
}
|
||||
}
|
||||
|
||||
impl ProxyHandler for UdpProxy {
|
||||
fn recv_handle(
|
||||
&self,
|
||||
ipv4: &mut IpV4Packet<&mut [u8]>,
|
||||
source: Ipv4Addr,
|
||||
destination: Ipv4Addr,
|
||||
) -> io::Result<bool> {
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
//转发到代理目标地址
|
||||
let mut udp_packet = UdpPacket::new(source, destination, ipv4.payload_mut())?;
|
||||
let source_port = udp_packet.source_port();
|
||||
let dest_port = udp_packet.destination_port();
|
||||
udp_packet.set_destination_port(self.port);
|
||||
udp_packet.update_checksum();
|
||||
ipv4.set_destination_ip(destination);
|
||||
ipv4.update_checksum();
|
||||
let key = SocketAddrV4::new(source, source_port);
|
||||
self.nat_map
|
||||
.lock()
|
||||
.insert(key.into(), SocketAddrV4::new(dest_ip, dest_port).into());
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn send_handle(&self, ipv4: &mut IpV4Packet<&mut [u8]>) -> io::Result<()> {
|
||||
let src_ip = ipv4.source_ip();
|
||||
let dest_ip = ipv4.destination_ip();
|
||||
let dest_addr = {
|
||||
let udp_packet = UdpPacket::new(src_ip, dest_ip, ipv4.payload_mut())?;
|
||||
SocketAddrV4::new(dest_ip, udp_packet.destination_port())
|
||||
};
|
||||
if let Some(source_addr) = self.nat_map.lock().get(&dest_addr) {
|
||||
let source_ip = *source_addr.ip();
|
||||
let mut udp_packet = UdpPacket::new(source_ip, dest_ip, ipv4.payload_mut())?;
|
||||
udp_packet.set_source_port(source_addr.port());
|
||||
udp_packet.update_checksum();
|
||||
ipv4.set_source_ip(source_ip);
|
||||
ipv4.update_checksum();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
async fn udp_proxy(
|
||||
udp: UdpSocket,
|
||||
nat_map: Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
default_interface: LocalInterface,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut buf = [0u8; 65536];
|
||||
|
||||
let inner_map: Arc<Mutex<HashMap<SocketAddrV4, (Arc<UdpSocket>, Arc<AtomicCell<Instant>>)>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(64)));
|
||||
let udp_socket = Arc::new(udp);
|
||||
loop {
|
||||
match udp_socket.recv_from(&mut buf).await {
|
||||
Ok((len, sender_addr)) => match sender_addr {
|
||||
SocketAddr::V4(sender_addr) => {
|
||||
if let Err(e) = udp_proxy0(
|
||||
&buf[..len],
|
||||
sender_addr,
|
||||
&inner_map,
|
||||
&nat_map,
|
||||
&udp_socket,
|
||||
&default_interface,
|
||||
)
|
||||
.await
|
||||
{
|
||||
log::warn!("udp proxy {} {:?}", sender_addr, e);
|
||||
}
|
||||
}
|
||||
SocketAddr::V6(_) => {}
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("udp代理异常:{:?}", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async fn udp_proxy0(
|
||||
buf: &[u8],
|
||||
sender_addr: SocketAddrV4,
|
||||
inner_map: &Arc<Mutex<HashMap<SocketAddrV4, (Arc<UdpSocket>, Arc<AtomicCell<Instant>>)>>>,
|
||||
map: &Arc<Mutex<HashMap<SocketAddrV4, SocketAddrV4>>>,
|
||||
udp_socket: &Arc<UdpSocket>,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<()> {
|
||||
let option = inner_map.lock().get(&sender_addr).cloned();
|
||||
if let Some((udp, time)) = option {
|
||||
time.store(Instant::now());
|
||||
udp.send(buf).await?;
|
||||
} else {
|
||||
let option = map.lock().get(&sender_addr).cloned();
|
||||
if let Some(dest_addr) = option {
|
||||
//先使用相同的端口,冲突了再随机端口
|
||||
let peer_udp_socket = match bind_udp(
|
||||
format!("0.0.0.0:{}", sender_addr.port()).parse().unwrap(),
|
||||
default_interface,
|
||||
) {
|
||||
Ok(udp) => udp,
|
||||
Err(_) => bind_udp("0.0.0.0:0".parse().unwrap(), default_interface)?,
|
||||
};
|
||||
let peer_udp_socket = UdpSocket::from_std(peer_udp_socket.into())?;
|
||||
peer_udp_socket.connect(dest_addr).await?;
|
||||
peer_udp_socket.send(buf).await?;
|
||||
let peer_udp_socket = Arc::new(peer_udp_socket);
|
||||
let inner_map = inner_map.clone();
|
||||
let time = Arc::new(AtomicCell::new(Instant::now()));
|
||||
inner_map
|
||||
.lock()
|
||||
.insert(sender_addr, (peer_udp_socket.clone(), time.clone()));
|
||||
let udp_socket = udp_socket.clone();
|
||||
let map = map.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut buf = [0u8; 65536];
|
||||
loop {
|
||||
match tokio::time::timeout(
|
||||
Duration::from_secs(600),
|
||||
peer_udp_socket.recv(&mut buf),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(rs) => match rs {
|
||||
Ok(len) => match udp_socket.send_to(&buf[..len], sender_addr).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!("udp proxy {}->{} {:?}", sender_addr, dest_addr, e);
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!("udp proxy {}->{} {:?}", sender_addr, dest_addr, e);
|
||||
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
if time.load().elapsed() > Duration::from_secs(580) {
|
||||
//超时关闭
|
||||
log::warn!("udp proxy timeout {}->{}", sender_addr, dest_addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inner_map.lock().remove(&sender_addr);
|
||||
map.lock().remove(&sender_addr);
|
||||
});
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
pub const VNT_VERSION: &'static str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
pub mod channel;
|
||||
pub mod cipher;
|
||||
pub mod core;
|
||||
mod external_route;
|
||||
pub mod handle;
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
mod ip_proxy;
|
||||
pub mod nat;
|
||||
#[cfg(feature = "port_mapping")]
|
||||
mod port_mapping;
|
||||
mod proto;
|
||||
pub mod protocol;
|
||||
mod tun_tap_device;
|
||||
pub use tun_tap_device::*;
|
||||
pub mod util;
|
||||
|
||||
pub use handle::callback::*;
|
||||
|
||||
pub mod compression;
|
||||
pub use packet;
|
||||
|
||||
pub(crate) fn ignore_io_interrupted(e: std::io::Error) -> std::io::Result<()> {
|
||||
if e.kind() == std::io::ErrorKind::Interrupted {
|
||||
log::warn!("ignore_io_interrupted");
|
||||
Ok(())
|
||||
} else {
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
use anyhow::{anyhow, Context};
|
||||
use std::io;
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, ToSocketAddrs};
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use rand::prelude::SliceRandom;
|
||||
use rand::Rng;
|
||||
|
||||
use crate::channel::punch::{NatInfo, NatType, PunchModel};
|
||||
use crate::channel::socket::LocalInterface;
|
||||
#[cfg(feature = "upnp")]
|
||||
use crate::util::UPnP;
|
||||
|
||||
mod stun;
|
||||
|
||||
pub fn local_ipv4_() -> io::Result<Ipv4Addr> {
|
||||
let socket = UdpSocket::bind("0.0.0.0:0")?;
|
||||
socket.connect("8.8.8.8:80")?;
|
||||
let addr = socket.local_addr()?;
|
||||
match addr.ip() {
|
||||
IpAddr::V4(ip) => Ok(ip),
|
||||
IpAddr::V6(_) => Ok(Ipv4Addr::UNSPECIFIED),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_ipv4() -> Option<Ipv4Addr> {
|
||||
match local_ipv4_() {
|
||||
Ok(ipv4) => Some(ipv4),
|
||||
Err(e) => {
|
||||
log::warn!("获取ipv4失败:{:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_ipv6_() -> io::Result<Ipv6Addr> {
|
||||
let socket = UdpSocket::bind("[::]:0")?;
|
||||
socket.connect("[2001:4860:4860:0000:0000:0000:0000:8888]:80")?;
|
||||
let addr = socket.local_addr()?;
|
||||
match addr.ip() {
|
||||
IpAddr::V4(_) => Ok(Ipv6Addr::UNSPECIFIED),
|
||||
IpAddr::V6(ip) => Ok(ip),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_ipv6() -> Option<Ipv6Addr> {
|
||||
match local_ipv6_() {
|
||||
Ok(ipv6) => {
|
||||
if is_ipv6_global(&ipv6) {
|
||||
return Some(ipv6);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("获取ipv6失败:{:?}", e);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub const fn is_ipv4_global(ipv4: &Ipv4Addr) -> bool {
|
||||
!(ipv4.octets()[0] == 0 // "This network"
|
||||
|| ipv4.is_private()
|
||||
|| ipv4.octets()[0] == 100 && (ipv4.octets()[1] & 0b1100_0000 == 0b0100_0000)//ipv4.is_shared()
|
||||
|| ipv4.is_loopback()
|
||||
|| ipv4.is_link_local()
|
||||
// addresses reserved for future protocols (`192.0.0.0/24`)
|
||||
// .9 and .10 are documented as globally reachable so they're excluded
|
||||
|| (
|
||||
ipv4.octets()[0] == 192 && ipv4.octets()[1] == 0 && ipv4.octets()[2] == 0
|
||||
&& ipv4.octets()[3] != 9 && ipv4.octets()[3] != 10
|
||||
)
|
||||
|| ipv4.is_documentation()
|
||||
|| ipv4.octets()[0] == 198 && (ipv4.octets()[1] & 0xfe) == 18//ipv4.is_benchmarking()
|
||||
|| ipv4.octets()[0] & 240 == 240 && !ipv4.is_broadcast()//ipv4.is_reserved()
|
||||
|| ipv4.is_broadcast())
|
||||
}
|
||||
|
||||
pub const fn is_ipv6_global(ipv6addr: &Ipv6Addr) -> bool {
|
||||
!(ipv6addr.is_unspecified()
|
||||
|| ipv6addr.is_loopback()
|
||||
// IPv4-mapped Address (`::ffff:0:0/96`)
|
||||
|| matches!(ipv6addr.segments(), [0, 0, 0, 0, 0, 0xffff, _, _])
|
||||
// IPv4-IPv6 Translat. (`64:ff9b:1::/48`)
|
||||
|| matches!(ipv6addr.segments(), [0x64, 0xff9b, 1, _, _, _, _, _])
|
||||
// Discard-Only Address Block (`100::/64`)
|
||||
|| matches!(ipv6addr.segments(), [0x100, 0, 0, 0, _, _, _, _])
|
||||
// IETF Protocol Assignments (`2001::/23`)
|
||||
|| (matches!(ipv6addr.segments(), [0x2001, b, _, _, _, _, _, _] if b < 0x200)
|
||||
&& !(
|
||||
// Port Control Protocol Anycast (`2001:1::1`)
|
||||
u128::from_be_bytes(ipv6addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0001
|
||||
// Traversal Using Relays around NAT Anycast (`2001:1::2`)
|
||||
|| u128::from_be_bytes(ipv6addr.octets()) == 0x2001_0001_0000_0000_0000_0000_0000_0002
|
||||
// AMT (`2001:3::/32`)
|
||||
|| matches!(ipv6addr.segments(), [0x2001, 3, _, _, _, _, _, _])
|
||||
// AS112-v6 (`2001:4:112::/48`)
|
||||
|| matches!(ipv6addr.segments(), [0x2001, 4, 0x112, _, _, _, _, _])
|
||||
// ORCHIDv2 (`2001:20::/28`)
|
||||
|| matches!(ipv6addr.segments(), [0x2001, b, _, _, _, _, _, _] if b >= 0x20 && b <= 0x2F)
|
||||
))
|
||||
|| (ipv6addr.segments()[0] == 0x2001) && (ipv6addr.segments()[1] == 0xdb8)//ipv6addr.is_documentation()
|
||||
|| (ipv6addr.segments()[0] & 0xfe00) == 0xfc00//ipv6addr.is_unique_local()
|
||||
|| (ipv6addr.segments()[0] & 0xffc0) == 0xfe80) //ipv6addr.is_unicast_link_local())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct NatTest {
|
||||
stun_server: Vec<String>,
|
||||
info: Arc<Mutex<NatInfo>>,
|
||||
time: Arc<AtomicCell<Instant>>,
|
||||
udp_ports: Vec<u16>,
|
||||
tcp_port: u16,
|
||||
#[cfg(feature = "upnp")]
|
||||
upnp: UPnP,
|
||||
pub(crate) update_local_ipv4: bool,
|
||||
}
|
||||
|
||||
impl NatTest {
|
||||
pub fn new(
|
||||
_channel_num: usize,
|
||||
stun_server: Vec<String>,
|
||||
local_ipv4: Option<Ipv4Addr>,
|
||||
ipv6: Option<Ipv6Addr>,
|
||||
udp_ports: Vec<u16>,
|
||||
tcp_port: u16,
|
||||
update_local_ipv4: bool,
|
||||
punch_model: PunchModel,
|
||||
) -> NatTest {
|
||||
let ports = vec![0; udp_ports.len()];
|
||||
let nat_info = NatInfo::new(
|
||||
Vec::new(),
|
||||
ports,
|
||||
0,
|
||||
local_ipv4,
|
||||
ipv6,
|
||||
udp_ports.clone(),
|
||||
tcp_port,
|
||||
0,
|
||||
NatType::Cone,
|
||||
punch_model,
|
||||
);
|
||||
let info = Arc::new(Mutex::new(nat_info));
|
||||
#[cfg(feature = "upnp")]
|
||||
let upnp = UPnP::default();
|
||||
#[cfg(feature = "upnp")]
|
||||
for port in &udp_ports {
|
||||
upnp.add_udp_port(*port);
|
||||
}
|
||||
#[cfg(feature = "upnp")]
|
||||
upnp.add_tcp_port(tcp_port);
|
||||
let instant = Instant::now();
|
||||
NatTest {
|
||||
stun_server,
|
||||
info,
|
||||
time: Arc::new(AtomicCell::new(
|
||||
instant
|
||||
.checked_sub(Duration::from_secs(100))
|
||||
.unwrap_or(instant),
|
||||
)),
|
||||
udp_ports,
|
||||
tcp_port,
|
||||
#[cfg(feature = "upnp")]
|
||||
upnp,
|
||||
update_local_ipv4,
|
||||
}
|
||||
}
|
||||
pub fn can_update(&self) -> bool {
|
||||
let last = self.time.load();
|
||||
last.elapsed() > Duration::from_secs(10)
|
||||
&& self.time.compare_exchange(last, Instant::now()).is_ok()
|
||||
}
|
||||
|
||||
pub fn nat_info(&self) -> NatInfo {
|
||||
self.info.lock().clone()
|
||||
}
|
||||
pub fn is_local_udp(&self, ipv4: Ipv4Addr, port: u16) -> bool {
|
||||
for x in &self.udp_ports {
|
||||
if x == &port {
|
||||
let guard = self.info.lock();
|
||||
if let Some(ip) = guard.local_ipv4 {
|
||||
if ipv4 == ip {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
pub fn is_local_tcp(&self, ipv4: Ipv4Addr, port: u16) -> bool {
|
||||
if self.tcp_port == port {
|
||||
let guard = self.info.lock();
|
||||
if let Some(ip) = guard.local_ipv4 {
|
||||
if ipv4 == ip {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
pub fn is_local_address(&self, is_tcp: bool, addr: SocketAddr) -> bool {
|
||||
let port = addr.port();
|
||||
let check_ip = || {
|
||||
let guard = self.info.lock();
|
||||
match addr.ip() {
|
||||
IpAddr::V4(ipv4) => {
|
||||
if let Some(ip) = guard.local_ipv4 {
|
||||
if ipv4 == ip {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
IpAddr::V6(ipv6) => {
|
||||
if let Some(ip) = guard.ipv6 {
|
||||
if ipv6 == ip {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
};
|
||||
if is_tcp {
|
||||
if self.tcp_port == port {
|
||||
return check_ip();
|
||||
}
|
||||
} else {
|
||||
for x in &self.udp_ports {
|
||||
if x == &port {
|
||||
return check_ip();
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
pub fn update_addr(&self, index: usize, ip: Ipv4Addr, port: u16) -> bool {
|
||||
let mut guard = self.info.lock();
|
||||
guard.update_addr(index, ip, port)
|
||||
}
|
||||
pub fn update_tcp_port(&self, port: u16) {
|
||||
let mut guard = self.info.lock();
|
||||
guard.update_tcp_port(port)
|
||||
}
|
||||
pub fn re_test(
|
||||
&self,
|
||||
local_ipv4: Option<Ipv4Addr>,
|
||||
ipv6: Option<Ipv6Addr>,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<NatInfo> {
|
||||
let mut stun_server = self.stun_server.clone();
|
||||
if stun_server.len() > 5 {
|
||||
stun_server.shuffle(&mut rand::thread_rng());
|
||||
stun_server.truncate(5);
|
||||
log::info!("stun_server truncate {:?}", stun_server);
|
||||
}
|
||||
let (nat_type, public_ips, port_range) =
|
||||
stun::stun_test_nat(stun_server, default_interface)?;
|
||||
if public_ips.is_empty() {
|
||||
Err(anyhow!("public_ips.is_empty"))?
|
||||
}
|
||||
let mut guard = self.info.lock();
|
||||
guard.nat_type = nat_type;
|
||||
guard.public_ips = public_ips;
|
||||
guard.public_port_range = port_range;
|
||||
if local_ipv4.is_some() {
|
||||
guard.local_ipv4 = local_ipv4;
|
||||
}
|
||||
guard.ipv6 = ipv6;
|
||||
|
||||
Ok(guard.clone())
|
||||
}
|
||||
#[cfg(feature = "upnp")]
|
||||
pub fn reset_upnp(&self) {
|
||||
let local_ipv4 = self.info.lock().local_ipv4.clone();
|
||||
if let Some(local_ipv4) = local_ipv4 {
|
||||
self.upnp.reset(local_ipv4)
|
||||
}
|
||||
}
|
||||
pub fn send_data(&self) -> anyhow::Result<(Vec<u8>, SocketAddr)> {
|
||||
let len = self.stun_server.len();
|
||||
let stun_server = if len == 1 {
|
||||
&self.stun_server[0]
|
||||
} else {
|
||||
let index = rand::thread_rng().gen_range(0..self.stun_server.len());
|
||||
&self.stun_server[index]
|
||||
};
|
||||
let addr = stun_server
|
||||
.to_socket_addrs()?
|
||||
.next()
|
||||
.with_context(|| format!("stun error {:?}", stun_server))?;
|
||||
Ok((stun::send_stun_request(), addr))
|
||||
}
|
||||
pub fn recv_data(
|
||||
&self,
|
||||
index: usize,
|
||||
source_addr: SocketAddr,
|
||||
buf: &[u8],
|
||||
) -> anyhow::Result<bool> {
|
||||
if buf[0] == 0x01 && buf[1] == 0x01 {
|
||||
if let Some(addr) = stun::recv_stun_response(buf) {
|
||||
if let Err(e) = self.recv_data_(index, source_addr, addr) {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
}
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
fn recv_data_(
|
||||
&self,
|
||||
index: usize,
|
||||
source_addr: SocketAddr,
|
||||
addr: SocketAddr,
|
||||
) -> anyhow::Result<()> {
|
||||
if let SocketAddr::V4(addr) = addr {
|
||||
let mut check_fail = true;
|
||||
let source_ip = match source_addr.ip() {
|
||||
IpAddr::V4(ip) => ip,
|
||||
IpAddr::V6(ip) => {
|
||||
if let Some(ip) = ip.to_ipv4() {
|
||||
ip
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
};
|
||||
'a: for stun_server in &self.stun_server {
|
||||
for x in stun_server.to_socket_addrs()? {
|
||||
if source_addr.port() == x.port() {
|
||||
if let IpAddr::V4(ip) = x.ip() {
|
||||
if ip == source_ip {
|
||||
check_fail = false;
|
||||
break 'a;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if !check_fail {
|
||||
if is_ipv4_global(addr.ip()) {
|
||||
if self.update_addr(index, *addr.ip(), addr.port()) {
|
||||
log::info!("回应地址{:?},来源stun {:?}", addr, source_addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
use std::collections::HashSet;
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::channel::punch::NatType;
|
||||
use crate::channel::socket::{bind_udp, LocalInterface};
|
||||
use rand::RngCore;
|
||||
use std::net::UdpSocket;
|
||||
use stun_format::Attr;
|
||||
|
||||
pub fn stun_test_nat(
|
||||
stun_servers: Vec<String>,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let mut nat_type = NatType::Cone;
|
||||
let mut port_range = 0;
|
||||
let mut hash_set = HashSet::new();
|
||||
for _ in 0..2 {
|
||||
let stun_servers = stun_servers.clone();
|
||||
match stun_test_nat0(stun_servers, default_interface) {
|
||||
Ok((nat_type_t, ip_list_t, port_range_t)) => {
|
||||
if nat_type_t == NatType::Symmetric {
|
||||
nat_type = NatType::Symmetric;
|
||||
}
|
||||
for x in ip_list_t {
|
||||
hash_set.insert(x);
|
||||
}
|
||||
if port_range < port_range_t {
|
||||
port_range = port_range_t;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("{:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok((nat_type, hash_set.into_iter().collect(), port_range))
|
||||
}
|
||||
|
||||
pub fn stun_test_nat0(
|
||||
stun_servers: Vec<String>,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<(NatType, Vec<Ipv4Addr>, u16)> {
|
||||
let udp = bind_udp("0.0.0.0:0".parse().unwrap(), default_interface)?;
|
||||
udp.set_nonblocking(false)?;
|
||||
let udp: UdpSocket = udp.into();
|
||||
udp.set_read_timeout(Some(Duration::from_millis(500)))?;
|
||||
let mut nat_type = NatType::Cone;
|
||||
let mut min_port = u16::MAX;
|
||||
let mut max_port = 0;
|
||||
let mut hash_set = HashSet::new();
|
||||
let mut pub_addrs = HashSet::new();
|
||||
for x in &stun_servers {
|
||||
match test_nat(&udp, x) {
|
||||
Ok(addr) => {
|
||||
pub_addrs.extend(addr);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("stun {} error {:?} ", x, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if pub_addrs.len() > 1 {
|
||||
nat_type = NatType::Symmetric;
|
||||
}
|
||||
for addr in &pub_addrs {
|
||||
if let SocketAddr::V4(addr) = addr {
|
||||
hash_set.insert(*addr.ip());
|
||||
if min_port > addr.port() {
|
||||
min_port = addr.port()
|
||||
}
|
||||
if max_port < addr.port() {
|
||||
max_port = addr.port()
|
||||
}
|
||||
}
|
||||
}
|
||||
if hash_set.is_empty() {
|
||||
Ok((nat_type, vec![], 0))
|
||||
} else {
|
||||
Ok((
|
||||
nat_type,
|
||||
hash_set.into_iter().collect(),
|
||||
max_port - min_port,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn test_nat(udp: &UdpSocket, stun_server: &String) -> io::Result<HashSet<SocketAddr>> {
|
||||
udp.connect(stun_server)?;
|
||||
let tid = rand::thread_rng().next_u64() as u128;
|
||||
let mut addr = HashSet::new();
|
||||
let (mapped_addr1, changed_addr1) = test_nat_(&udp, stun_server, true, true, tid)?;
|
||||
if mapped_addr1.is_ipv4() {
|
||||
addr.insert(mapped_addr1);
|
||||
}
|
||||
if let Some(changed_addr1) = changed_addr1 {
|
||||
if udp.connect(changed_addr1).is_ok() {
|
||||
match test_nat_(&udp, stun_server, false, false, tid + 1) {
|
||||
Ok((mapped_addr2, _)) => {
|
||||
if mapped_addr2.is_ipv4() {
|
||||
addr.insert(mapped_addr1);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("stun {} error {:?} ", stun_server, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log::info!(
|
||||
"stun {} mapped_addr {:?} changed_addr {:?}",
|
||||
stun_server,
|
||||
addr,
|
||||
changed_addr1,
|
||||
);
|
||||
|
||||
Ok(addr)
|
||||
}
|
||||
|
||||
fn test_nat_(
|
||||
udp: &UdpSocket,
|
||||
stun_server: &String,
|
||||
change_ip: bool,
|
||||
change_port: bool,
|
||||
tid: u128,
|
||||
) -> io::Result<(SocketAddr, Option<SocketAddr>)> {
|
||||
for _ in 0..2 {
|
||||
let mut buf = [0u8; 28];
|
||||
let mut msg = stun_format::MsgBuilder::from(buf.as_mut_slice());
|
||||
msg.typ(stun_format::MsgType::BindingRequest);
|
||||
msg.tid(tid);
|
||||
msg.add_attr(Attr::ChangeRequest {
|
||||
change_ip,
|
||||
change_port,
|
||||
});
|
||||
udp.send(msg.as_bytes())?;
|
||||
let mut buf = [0; 10240];
|
||||
let (len, _addr) = match udp.recv_from(&mut buf) {
|
||||
Ok(rs) => rs,
|
||||
Err(e) => {
|
||||
log::warn!("stun {} error {:?}", stun_server, e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let msg = stun_format::Msg::from(&buf[..len]);
|
||||
let mut mapped_addr = None;
|
||||
let mut changed_addr = None;
|
||||
for x in msg.attrs_iter() {
|
||||
match x {
|
||||
Attr::MappedAddress(addr) => {
|
||||
if mapped_addr.is_none() {
|
||||
let _ = mapped_addr.insert(stun_addr(addr));
|
||||
}
|
||||
}
|
||||
Attr::ChangedAddress(addr) => {
|
||||
if changed_addr.is_none() {
|
||||
let _ = changed_addr.insert(stun_addr(addr));
|
||||
}
|
||||
}
|
||||
Attr::XorMappedAddress(addr) => {
|
||||
if mapped_addr.is_none() {
|
||||
let _ = mapped_addr.insert(stun_addr(addr));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if changed_addr.is_some() && mapped_addr.is_some() {
|
||||
return Ok((mapped_addr.unwrap(), changed_addr));
|
||||
}
|
||||
}
|
||||
if let Some(addr) = mapped_addr {
|
||||
return Ok((addr, changed_addr));
|
||||
}
|
||||
}
|
||||
Err(io::Error::new(io::ErrorKind::Other, "stun response err"))
|
||||
}
|
||||
|
||||
fn stun_addr(addr: stun_format::SocketAddr) -> SocketAddr {
|
||||
match addr {
|
||||
stun_format::SocketAddr::V4(ip, port) => {
|
||||
SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::from(ip), port))
|
||||
}
|
||||
stun_format::SocketAddr::V6(ip, port) => {
|
||||
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::from(ip), port, 0, 0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const TAG: u128 = 1827549368 << 64;
|
||||
|
||||
pub fn send_stun_request() -> Vec<u8> {
|
||||
let mut buf = [0u8; 28];
|
||||
let mut msg = stun_format::MsgBuilder::from(buf.as_mut_slice());
|
||||
msg.typ(stun_format::MsgType::BindingRequest);
|
||||
let id = rand::thread_rng().next_u64() as u128;
|
||||
msg.tid(id | TAG);
|
||||
msg.add_attr(Attr::ChangeRequest {
|
||||
change_ip: false,
|
||||
change_port: false,
|
||||
});
|
||||
msg.as_bytes().to_vec()
|
||||
}
|
||||
|
||||
pub fn recv_stun_response(buf: &[u8]) -> Option<SocketAddr> {
|
||||
let msg = stun_format::Msg::from(buf);
|
||||
if let Some(tid) = msg.tid() {
|
||||
if tid & TAG != TAG {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
for x in msg.attrs_iter() {
|
||||
match x {
|
||||
Attr::MappedAddress(addr) => {
|
||||
return Some(stun_addr(addr));
|
||||
}
|
||||
Attr::XorMappedAddress(addr) => {
|
||||
return Some(stun_addr(addr));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
use std::net::SocketAddr;
|
||||
use std::str::FromStr;
|
||||
use std::thread;
|
||||
|
||||
use anyhow::Context;
|
||||
|
||||
use crate::util::StopManager;
|
||||
|
||||
mod tcp_mapping;
|
||||
|
||||
mod udp_mapping;
|
||||
|
||||
pub fn convert(vec: Vec<String>) -> anyhow::Result<Vec<(bool, SocketAddr, String)>> {
|
||||
let mut rs = Vec::with_capacity(vec.len());
|
||||
for x in vec {
|
||||
let string = x.trim().to_lowercase();
|
||||
if let Some(udp_mapping) = string.strip_prefix("udp:") {
|
||||
let mut split = udp_mapping.split("-");
|
||||
let bind_addr = split.next().with_context(|| {
|
||||
format!(
|
||||
"udp_mapping error {:?},eg: udp:127.0.0.1:80-10.26.0.10:8080",
|
||||
x
|
||||
)
|
||||
})?;
|
||||
let bind_addr = SocketAddr::from_str(bind_addr)
|
||||
.with_context(|| format!("udp_mapping error {}", bind_addr))?;
|
||||
let dest = split.next().with_context(|| {
|
||||
format!(
|
||||
"udp_mapping error {:?},eg: udp:127.0.0.1:80-10.26.0.10:8080",
|
||||
x
|
||||
)
|
||||
})?;
|
||||
rs.push((false, bind_addr, dest.to_string()));
|
||||
continue;
|
||||
}
|
||||
if let Some(tcp_mapping) = string.strip_prefix("tcp:") {
|
||||
let mut split = tcp_mapping.split("-");
|
||||
let bind_addr = split.next().with_context(|| {
|
||||
format!(
|
||||
"tcp_mapping error {:?},eg: tcp:127.0.0.1:80-10.26.0.10:8080",
|
||||
x
|
||||
)
|
||||
})?;
|
||||
let bind_addr = SocketAddr::from_str(bind_addr)
|
||||
.with_context(|| format!("udp_mapping error {}", bind_addr))?;
|
||||
let dest = split.next().with_context(|| {
|
||||
format!(
|
||||
"tcp_mapping error {:?},eg: tcp:127.0.0.1:80-10.26.0.10:8080",
|
||||
x
|
||||
)
|
||||
})?;
|
||||
rs.push((true, bind_addr, dest.to_string()));
|
||||
continue;
|
||||
}
|
||||
Err(anyhow::anyhow!(
|
||||
"port_mapping error {:?},eg: tcp:127.0.0.1:80-10.26.0.10:8080",
|
||||
x
|
||||
))?;
|
||||
}
|
||||
Ok(rs)
|
||||
}
|
||||
pub fn start_port_mapping(
|
||||
stop_manager: StopManager,
|
||||
vec: Vec<(bool, SocketAddr, String)>,
|
||||
) -> anyhow::Result<()> {
|
||||
if vec.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let (sender, receiver) = tokio::sync::oneshot::channel::<()>();
|
||||
let worker = stop_manager.add_listener("portMapping".into(), move || {
|
||||
let _ = sender.send(());
|
||||
})?;
|
||||
thread::Builder::new()
|
||||
.name("portMapping".into())
|
||||
.spawn(move || {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.thread_name("portMapping")
|
||||
.build().unwrap();
|
||||
runtime.block_on(start_port_mapping0(vec)).unwrap();
|
||||
runtime.block_on(async {
|
||||
let _ = receiver.await;
|
||||
});
|
||||
runtime.shutdown_background();
|
||||
drop(worker);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn start_port_mapping0(vec: Vec<(bool, SocketAddr, String)>) -> anyhow::Result<()> {
|
||||
for (is_tcp, bind_addr, destination) in vec {
|
||||
if is_tcp {
|
||||
tcp_mapping::tcp_mapping(bind_addr, destination).await?;
|
||||
} else {
|
||||
udp_mapping::udp_mapping(bind_addr, destination).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
use anyhow::Context;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
pub async fn tcp_mapping(bind_addr: SocketAddr, destination: String) -> anyhow::Result<()> {
|
||||
let tcp_listener = TcpListener::bind(bind_addr)
|
||||
.await
|
||||
.with_context(|| format!("TCP binding {:?} failed", bind_addr))?;
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = tcp_mapping_(bind_addr, tcp_listener, destination).await {
|
||||
log::warn!("tcp_mapping {:?}", e);
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn tcp_mapping_(
|
||||
bind_addr: SocketAddr,
|
||||
tcp_listener: TcpListener,
|
||||
destination: String,
|
||||
) -> anyhow::Result<()> {
|
||||
loop {
|
||||
let (tcp_stream, _) = tcp_listener.accept().await?;
|
||||
|
||||
let destination = destination.clone();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = copy(tcp_stream, &destination).await {
|
||||
log::warn!("tcp port mapping {}->{} {:?}", bind_addr, destination, e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn copy(source_tcp: TcpStream, destination: &String) -> anyhow::Result<()> {
|
||||
// 或许这里也应该绑定最匹配的网卡,不然全局代理会影响映射
|
||||
let dest_tcp = TcpStream::connect(destination)
|
||||
.await
|
||||
.with_context(|| format!("TCP connection target failed {:?}", destination))?;
|
||||
let _ = source_tcp.set_nodelay(true);
|
||||
let _ = dest_tcp.set_nodelay(true);
|
||||
|
||||
let destination = dest_tcp.peer_addr()?;
|
||||
let (mut client_read, mut client_write) = source_tcp.into_split();
|
||||
let (mut server_read, mut server_write) = dest_tcp.into_split();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = tokio::io::copy(&mut client_read, &mut server_write).await {
|
||||
log::warn!("client tcp proxy ->{:},{:?}", destination, e);
|
||||
}
|
||||
});
|
||||
if let Err(e) = tokio::io::copy(&mut server_read, &mut client_write).await {
|
||||
log::warn!("server tcp proxy ->{:?},{:?}", destination, e);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
use anyhow::Context;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::HashMap;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
pub async fn udp_mapping(bind_addr: SocketAddr, destination: String) -> anyhow::Result<()> {
|
||||
let udp = UdpSocket::bind(bind_addr)
|
||||
.await
|
||||
.with_context(|| format!("port proxy UDP binding {:?} failed", bind_addr))?;
|
||||
let udp = Arc::new(udp);
|
||||
|
||||
let inner_map: Arc<Mutex<HashMap<SocketAddr, (Arc<UdpSocket>, Arc<AtomicCell<Instant>>)>>> =
|
||||
Arc::new(Mutex::new(HashMap::with_capacity(64)));
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut buf = [0; 65536];
|
||||
loop {
|
||||
match udp.recv_from(&mut buf).await {
|
||||
Ok((len, src_addr)) => {
|
||||
if let Err(e) =
|
||||
udp_mapping0(&buf[..len], src_addr, &inner_map, &udp, &destination).await
|
||||
{
|
||||
log::warn!("udp port mapping {}->{} {:?}", src_addr, destination, e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("port proxy UDP {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn udp_mapping0(
|
||||
buf: &[u8],
|
||||
src_addr: SocketAddr,
|
||||
inner_map: &Arc<Mutex<HashMap<SocketAddr, (Arc<UdpSocket>, Arc<AtomicCell<Instant>>)>>>,
|
||||
udp_socket: &Arc<UdpSocket>,
|
||||
destination: &String,
|
||||
) -> anyhow::Result<()> {
|
||||
let option = inner_map.lock().get(&src_addr).cloned();
|
||||
if let Some((udp, time)) = option {
|
||||
time.store(Instant::now());
|
||||
udp.send(buf).await?;
|
||||
} else {
|
||||
let dest_udp = UdpSocket::bind("0.0.0.0:0").await?;
|
||||
dest_udp.connect(destination).await?;
|
||||
dest_udp.send(buf).await?;
|
||||
let destination_addr = dest_udp.peer_addr()?;
|
||||
let udp_socket = udp_socket.clone();
|
||||
let inner_map = inner_map.clone();
|
||||
let dest_udp = Arc::new(dest_udp);
|
||||
let time = Arc::new(AtomicCell::new(Instant::now()));
|
||||
inner_map
|
||||
.lock()
|
||||
.insert(src_addr, (dest_udp.clone(), time.clone()));
|
||||
tokio::spawn(async move {
|
||||
let mut buf = [0u8; 65536];
|
||||
loop {
|
||||
match tokio::time::timeout(Duration::from_secs(600), dest_udp.recv(&mut buf)).await
|
||||
{
|
||||
Ok(rs) => match rs {
|
||||
Ok(len) => match udp_socket.send_to(&buf[..len], src_addr).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"udp port mapping {}->{} {:?}",
|
||||
src_addr,
|
||||
destination_addr,
|
||||
e
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::warn!(
|
||||
"udp port mapping {}->{} {:?}",
|
||||
src_addr,
|
||||
destination_addr,
|
||||
e
|
||||
);
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(_) => {
|
||||
if time.load().elapsed() > Duration::from_secs(580) {
|
||||
//超时关闭
|
||||
log::warn!(
|
||||
"udp port mapping timeout {}->{} ",
|
||||
src_addr,
|
||||
destination_addr
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
inner_map.lock().remove(&src_addr);
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,593 @@
|
||||
use std::{fmt, io};
|
||||
|
||||
pub const ENCRYPTION_RESERVED: usize = 16 + 32 + 12;
|
||||
pub const AES_GCM_ENCRYPTION_RESERVED: usize = 32;
|
||||
pub const RSA_ENCRYPTION_RESERVED: usize = 32;
|
||||
|
||||
pub const RANDOM_RESERVED: usize = 4;
|
||||
pub const FINGER_RESERVED: usize = 12;
|
||||
pub const TAG_RESERVED: usize = 16;
|
||||
|
||||
/*
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| random(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
pub trait SecretTail {
|
||||
fn buffer(&self) -> &[u8];
|
||||
fn exist_finger(&self) -> bool;
|
||||
fn random_buf(&self) -> &[u8] {
|
||||
let buf = self.buffer();
|
||||
let mut end = buf.len();
|
||||
if self.exist_finger() {
|
||||
end -= FINGER_RESERVED;
|
||||
}
|
||||
&buf[end - RANDOM_RESERVED..end]
|
||||
}
|
||||
fn finger(&self) -> &[u8] {
|
||||
if self.exist_finger() {
|
||||
let buf = self.buffer();
|
||||
let end = buf.len();
|
||||
&buf[end - FINGER_RESERVED..end]
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait SecretTailMut: SecretTail {
|
||||
fn buffer_mut(&mut self) -> &mut [u8];
|
||||
fn set_random(&mut self, random: &[u8]) {
|
||||
let f = self.exist_finger();
|
||||
let buf = self.buffer_mut();
|
||||
let mut end = buf.len();
|
||||
if f {
|
||||
end -= FINGER_RESERVED;
|
||||
}
|
||||
buf[end - RANDOM_RESERVED..end].copy_from_slice(random);
|
||||
}
|
||||
fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> {
|
||||
if self.exist_finger() {
|
||||
if finger.len() != FINGER_RESERVED {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"finger.len != 12",
|
||||
));
|
||||
}
|
||||
let buf = self.buffer_mut();
|
||||
let end = buf.len();
|
||||
buf[end - FINGER_RESERVED..end].copy_from_slice(finger);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"not exist finger",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* aead加密数据体
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| tag(32) |
|
||||
| tag(32) |
|
||||
| tag(32) |
|
||||
| tag(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| random(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger,
|
||||
确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹)
|
||||
*/
|
||||
pub struct AEADSecretBody<B> {
|
||||
buffer: B,
|
||||
exist_finger: bool,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> AEADSecretBody<B> {
|
||||
pub fn new(buffer: B, exist_finger: bool) -> io::Result<AEADSecretBody<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
let min_len = if exist_finger {
|
||||
TAG_RESERVED + RANDOM_RESERVED + FINGER_RESERVED
|
||||
} else {
|
||||
TAG_RESERVED + RANDOM_RESERVED
|
||||
};
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < min_len || len > 65535 - 20 - 8 - 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("AEADSecretBody length overflow {}", len),
|
||||
));
|
||||
}
|
||||
Ok(AEADSecretBody {
|
||||
buffer,
|
||||
exist_finger,
|
||||
})
|
||||
}
|
||||
pub fn data(&self) -> &[u8] {
|
||||
let mut end = self.buffer.as_ref().len() - TAG_RESERVED - RANDOM_RESERVED;
|
||||
if self.exist_finger {
|
||||
end -= FINGER_RESERVED;
|
||||
}
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
pub fn tag(&self) -> &[u8] {
|
||||
let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED;
|
||||
if self.exist_finger {
|
||||
end -= FINGER_RESERVED;
|
||||
}
|
||||
&self.buffer.as_ref()[end - TAG_RESERVED..end]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> SecretTail for AEADSecretBody<B> {
|
||||
#[inline]
|
||||
fn buffer(&self) -> &[u8] {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
#[inline]
|
||||
fn exist_finger(&self) -> bool {
|
||||
self.exist_finger
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> SecretTailMut for AEADSecretBody<B> {
|
||||
#[inline]
|
||||
fn buffer_mut(&mut self) -> &mut [u8] {
|
||||
self.buffer.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> AEADSecretBody<B> {
|
||||
/// 数据部分
|
||||
pub fn data_mut(&mut self) -> &mut [u8] {
|
||||
let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED - TAG_RESERVED;
|
||||
if self.exist_finger {
|
||||
end -= FINGER_RESERVED;
|
||||
}
|
||||
&mut self.buffer.as_mut()[..end]
|
||||
}
|
||||
/// 数据和tag部分
|
||||
pub fn data_tag_mut(&mut self) -> &mut [u8] {
|
||||
let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED;
|
||||
if self.exist_finger {
|
||||
end -= FINGER_RESERVED;
|
||||
}
|
||||
&mut self.buffer.as_mut()[..end]
|
||||
}
|
||||
pub fn set_tag(&mut self, tag: &[u8]) -> io::Result<()> {
|
||||
if tag.len() != 16 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "tag.len != 16"));
|
||||
}
|
||||
let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED;
|
||||
if self.exist_finger {
|
||||
end -= FINGER_RESERVED;
|
||||
}
|
||||
self.buffer.as_mut()[end - TAG_RESERVED..end].copy_from_slice(tag);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/* 带随机数的加密数据体
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| random(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger,
|
||||
确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹)
|
||||
*/
|
||||
pub struct IVSecretBody<B> {
|
||||
buffer: B,
|
||||
exist_finger: bool,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> IVSecretBody<B> {
|
||||
pub fn new(buffer: B, exist_finger: bool) -> io::Result<IVSecretBody<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
let min_len = if exist_finger {
|
||||
FINGER_RESERVED + RANDOM_RESERVED
|
||||
} else {
|
||||
RANDOM_RESERVED
|
||||
};
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < min_len || len > 65535 - 20 - 8 - 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
format!("IVSecretBody length overflow {}", len),
|
||||
));
|
||||
}
|
||||
Ok(IVSecretBody {
|
||||
buffer,
|
||||
exist_finger,
|
||||
})
|
||||
}
|
||||
pub fn data(&self) -> &[u8] {
|
||||
let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED;
|
||||
if self.exist_finger {
|
||||
end -= FINGER_RESERVED;
|
||||
}
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> IVSecretBody<B> {
|
||||
pub fn data_mut(&mut self) -> &mut [u8] {
|
||||
let mut end = self.buffer.as_ref().len() - RANDOM_RESERVED;
|
||||
if self.exist_finger {
|
||||
end -= FINGER_RESERVED;
|
||||
}
|
||||
&mut self.buffer.as_mut()[..end]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> SecretTail for IVSecretBody<B> {
|
||||
#[inline]
|
||||
fn buffer(&self) -> &[u8] {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
#[inline]
|
||||
fn exist_finger(&self) -> bool {
|
||||
self.exist_finger
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> SecretTailMut for IVSecretBody<B> {
|
||||
#[inline]
|
||||
fn buffer_mut(&mut self) -> &mut [u8] {
|
||||
self.buffer.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
/* aes_gcm加密数据体
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| random(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| tag(32) |
|
||||
| tag(32) |
|
||||
| tag(32) |
|
||||
| tag(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger,
|
||||
确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹)
|
||||
*/
|
||||
pub struct SecretBody<B> {
|
||||
buffer: B,
|
||||
exist_finger: bool,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> SecretBody<B> {
|
||||
pub fn new(buffer: B, exist_finger: bool) -> io::Result<SecretBody<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
let min_len = if exist_finger { 32 } else { 32 - 12 };
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < min_len || len > 65535 - 20 - 8 - 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"SecretBody length overflow",
|
||||
));
|
||||
}
|
||||
Ok(SecretBody {
|
||||
buffer,
|
||||
exist_finger,
|
||||
})
|
||||
}
|
||||
pub fn random(&self) -> u32 {
|
||||
let mut end = self.buffer.as_ref().len() - 16;
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
u32::from_be_bytes(self.buffer.as_ref()[end - 4..end].try_into().unwrap())
|
||||
}
|
||||
pub fn body(&self) -> &[u8] {
|
||||
let mut end = self.buffer.as_ref().len() - 16;
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
pub fn tag(&self) -> &[u8] {
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&self.buffer.as_ref()[end - 16..end]
|
||||
}
|
||||
/// 数据部分+tag部分
|
||||
pub fn en_body(&self) -> &[u8] {
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
pub fn finger(&self) -> &[u8] {
|
||||
if self.exist_finger {
|
||||
let end = self.buffer.as_ref().len();
|
||||
&self.buffer.as_ref()[end - 12..end]
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> SecretBody<B> {
|
||||
pub fn set_random(&mut self, random: u32) {
|
||||
let mut end = self.buffer.as_ref().len() - 16;
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
self.buffer.as_mut()[end - 4..end].copy_from_slice(&random.to_be_bytes());
|
||||
}
|
||||
|
||||
pub fn set_tag(&mut self, tag: &[u8]) -> io::Result<()> {
|
||||
if tag.len() != 16 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "tag.len != 16"));
|
||||
}
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
self.buffer.as_mut()[end - 16..end].copy_from_slice(tag);
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> {
|
||||
if self.exist_finger {
|
||||
if finger.len() != 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"finger.len != 12",
|
||||
));
|
||||
}
|
||||
let end = self.buffer.as_ref().len();
|
||||
self.buffer.as_mut()[end - 12..end].copy_from_slice(finger);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"not exist finger",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// 数据部分
|
||||
pub fn body_mut(&mut self) -> &mut [u8] {
|
||||
let mut end = self.buffer.as_ref().len() - 16;
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&mut self.buffer.as_mut()[..end]
|
||||
}
|
||||
pub fn tag_mut(&mut self) -> &mut [u8] {
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&mut self.buffer.as_mut()[end - 16..end]
|
||||
}
|
||||
/// 数据部分+tag部分
|
||||
pub fn en_body_mut(&mut self) -> &mut [u8] {
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&mut self.buffer.as_mut()[..end]
|
||||
}
|
||||
pub fn buffer_mut(&mut self) -> &mut [u8] {
|
||||
self.buffer.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for SecretBody<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SecretBody")
|
||||
.field("random", &self.random())
|
||||
.field("body", &self.body())
|
||||
.field("tag", &self.tag())
|
||||
.field("finger", &self.finger())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
/* aes_cbc加密数据体
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| random(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|
||||
注:finger用于快速校验数据是否被修改,上层可使用token、协议头参与计算finger,
|
||||
确保服务端和客户端都能感知修改(服务端不能解密也能校验指纹)
|
||||
*/
|
||||
pub struct AesCbcSecretBody<B> {
|
||||
buffer: B,
|
||||
exist_finger: bool,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> AesCbcSecretBody<B> {
|
||||
pub fn new(buffer: B, exist_finger: bool) -> io::Result<AesCbcSecretBody<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
let min_len = if exist_finger { 16 } else { 16 - 12 };
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < min_len || len > 65535 - 20 - 8 - 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"AesCbcSecretBody length overflow",
|
||||
));
|
||||
}
|
||||
Ok(AesCbcSecretBody {
|
||||
buffer,
|
||||
exist_finger,
|
||||
})
|
||||
}
|
||||
pub fn en_body(&self) -> &[u8] {
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
pub fn finger(&self) -> &[u8] {
|
||||
if self.exist_finger {
|
||||
let end = self.buffer.as_ref().len();
|
||||
&self.buffer.as_ref()[end - 12..end]
|
||||
} else {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> AesCbcSecretBody<B> {
|
||||
pub fn set_random(&mut self, random: u32) {
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
self.buffer.as_mut()[end - 4..end].copy_from_slice(&random.to_be_bytes());
|
||||
}
|
||||
pub fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> {
|
||||
if self.exist_finger {
|
||||
if finger.len() != 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"finger.len != 12",
|
||||
));
|
||||
}
|
||||
let end = self.buffer.as_ref().len();
|
||||
self.buffer.as_mut()[end - 12..end].copy_from_slice(finger);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"cbc not exist finger",
|
||||
))
|
||||
}
|
||||
}
|
||||
pub fn en_body_mut(&mut self) -> &mut [u8] {
|
||||
let mut end = self.buffer.as_ref().len();
|
||||
if self.exist_finger {
|
||||
end -= 12;
|
||||
}
|
||||
&mut self.buffer.as_mut()[..end]
|
||||
}
|
||||
}
|
||||
|
||||
/* rsa加密数据体
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体(n) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| random(32) |
|
||||
| random(32) |
|
||||
| random(32) |
|
||||
| random(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
| finger(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
pub struct RsaSecretBody<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> RsaSecretBody<B> {
|
||||
pub fn new(buffer: B) -> io::Result<RsaSecretBody<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
// 不能大于udp最大载荷长度
|
||||
if len < 32 || len > 65535 - 20 - 8 - 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"length overflow",
|
||||
));
|
||||
}
|
||||
Ok(RsaSecretBody { buffer })
|
||||
}
|
||||
pub fn data(&self) -> &[u8] {
|
||||
let end = self.buffer.as_ref().len() - 32;
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
pub fn random(&self) -> &[u8] {
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
&self.buffer.as_ref()[end - 16..end]
|
||||
}
|
||||
pub fn body(&self) -> &[u8] {
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
&self.buffer.as_ref()[..end]
|
||||
}
|
||||
pub fn finger(&self) -> &[u8] {
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
&self.buffer.as_ref()[end..]
|
||||
}
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> RsaSecretBody<B> {
|
||||
pub fn set_random(&mut self, random: &[u8]) -> io::Result<()> {
|
||||
if random.len() != 16 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"random.len != 16",
|
||||
));
|
||||
}
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
self.buffer.as_mut()[end - 16..end].copy_from_slice(random);
|
||||
Ok(())
|
||||
}
|
||||
pub fn random_mut(&mut self) -> &mut [u8] {
|
||||
let end = self.buffer.as_ref().len() - 16;
|
||||
&mut self.buffer.as_mut()[end - 16..end]
|
||||
}
|
||||
pub fn set_finger(&mut self, finger: &[u8]) -> io::Result<()> {
|
||||
if finger.len() != 16 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"finger.len != 16",
|
||||
));
|
||||
}
|
||||
let end = self.buffer.as_ref().len();
|
||||
self.buffer.as_mut()[end - 16..end].copy_from_slice(finger);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
/// ping请求
|
||||
/*
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| time | echo |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
Ping,
|
||||
/*
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| time | echo |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
Pong,
|
||||
/// 打洞请求
|
||||
PunchRequest,
|
||||
/// 打洞响应
|
||||
PunchResponse,
|
||||
///获取对端看到的地址
|
||||
AddrRequest,
|
||||
AddrResponse,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Ping,
|
||||
2 => Protocol::Pong,
|
||||
3 => Protocol::PunchRequest,
|
||||
4 => Protocol::PunchResponse,
|
||||
5 => Protocol::AddrRequest,
|
||||
6 => Protocol::AddrResponse,
|
||||
val => Protocol::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Ping => 1,
|
||||
Protocol::Pong => 2,
|
||||
Protocol::PunchRequest => 3,
|
||||
Protocol::PunchResponse => 4,
|
||||
Protocol::AddrRequest => 5,
|
||||
Protocol::AddrResponse => 6,
|
||||
Protocol::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ControlPacket<B> {
|
||||
PingPacket(PingPacket<B>),
|
||||
PongPacket(PongPacket<B>),
|
||||
PunchRequest,
|
||||
PunchResponse,
|
||||
AddrRequest,
|
||||
AddrResponse(AddrPacket<B>),
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ControlPacket<B> {
|
||||
pub fn new(protocol: u8, buffer: B) -> io::Result<ControlPacket<B>> {
|
||||
match Protocol::from(protocol) {
|
||||
Protocol::Ping => Ok(ControlPacket::PingPacket(PingPacket::new(buffer)?)),
|
||||
Protocol::Pong => Ok(ControlPacket::PongPacket(PongPacket::new(buffer)?)),
|
||||
Protocol::PunchRequest => Ok(ControlPacket::PunchRequest),
|
||||
Protocol::PunchResponse => Ok(ControlPacket::PunchResponse),
|
||||
Protocol::AddrRequest => Ok(ControlPacket::AddrRequest),
|
||||
Protocol::AddrResponse => Ok(ControlPacket::AddrResponse(AddrPacket::new(buffer)?)),
|
||||
Protocol::Unknown(_) => Err(io::Error::new(io::ErrorKind::InvalidData, "Unsupported")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 网络探针
|
||||
pub struct PingPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
pub type PongPacket<B> = PingPacket<B>;
|
||||
|
||||
impl<B: AsRef<[u8]>> PingPacket<B> {
|
||||
pub fn new(buffer: B) -> io::Result<PingPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len < 4 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "len < 4"));
|
||||
}
|
||||
Ok(PingPacket { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> PingPacket<B> {
|
||||
pub fn time(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[..2].try_into().unwrap())
|
||||
}
|
||||
pub fn epoch(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[2..4].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> PingPacket<B> {
|
||||
pub fn set_time(&mut self, time: u16) {
|
||||
self.buffer.as_mut()[..2].copy_from_slice(&time.to_be_bytes())
|
||||
}
|
||||
pub fn set_epoch(&mut self, epoch: u16) {
|
||||
self.buffer.as_mut()[2..4].copy_from_slice(&epoch.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for PingPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PingPacket")
|
||||
.field("time", &self.time())
|
||||
.field("epoch", &self.epoch())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AddrPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> AddrPacket<B> {
|
||||
pub fn new(buffer: B) -> io::Result<AddrPacket<B>> {
|
||||
let len = buffer.as_ref().len();
|
||||
if len < 6 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "len < 6"));
|
||||
}
|
||||
Ok(AddrPacket { buffer })
|
||||
}
|
||||
pub fn ipv4(&self) -> Ipv4Addr {
|
||||
let buf = self.buffer.as_ref();
|
||||
Ipv4Addr::new(buf[0], buf[1], buf[2], buf[3])
|
||||
}
|
||||
pub fn port(&self) -> u16 {
|
||||
u16::from_be_bytes(self.buffer.as_ref()[4..6].try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> AddrPacket<B> {
|
||||
pub fn set_ipv4(&mut self, ip: Ipv4Addr) {
|
||||
self.buffer.as_mut()[..4].copy_from_slice(&ip.octets())
|
||||
}
|
||||
pub fn set_port(&mut self, port: u16) {
|
||||
self.buffer.as_mut()[4..6].copy_from_slice(&port.to_be_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for AddrPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("AddrPacket")
|
||||
.field("ipv4", &self.ipv4())
|
||||
.field("port", &self.port())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
use crate::error::*;
|
||||
use std::io;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
NoKey,
|
||||
Other(u8),
|
||||
}
|
||||
|
||||
@@ -12,6 +16,10 @@ impl From<u8> for Protocol {
|
||||
match value {
|
||||
1 => Self::TokenError,
|
||||
2 => Self::Disconnect,
|
||||
3 => Self::AddressExhausted,
|
||||
4 => Self::IpAlreadyExists,
|
||||
5 => Self::InvalidIp,
|
||||
6 => Self::NoKey,
|
||||
val => Self::Other(val),
|
||||
}
|
||||
}
|
||||
@@ -22,6 +30,10 @@ impl Into<u8> for Protocol {
|
||||
match self {
|
||||
Protocol::TokenError => 1,
|
||||
Protocol::Disconnect => 2,
|
||||
Protocol::AddressExhausted => 3,
|
||||
Protocol::IpAlreadyExists => 4,
|
||||
Protocol::InvalidIp => 5,
|
||||
Protocol::NoKey => 6,
|
||||
Protocol::Other(val) => val,
|
||||
}
|
||||
}
|
||||
@@ -30,14 +42,22 @@ impl Into<u8> for Protocol {
|
||||
pub enum InErrorPacket<B> {
|
||||
TokenError,
|
||||
Disconnect,
|
||||
AddressExhausted,
|
||||
IpAlreadyExists,
|
||||
InvalidIp,
|
||||
NoKey,
|
||||
OtherError(ErrorPacket<B>),
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> InErrorPacket<B> {
|
||||
pub fn new(protocol: u8, buffer: B) -> Result<InErrorPacket<B>> {
|
||||
pub fn new(protocol: u8, buffer: B) -> io::Result<InErrorPacket<B>> {
|
||||
match Protocol::from(protocol) {
|
||||
Protocol::TokenError => Ok(InErrorPacket::TokenError),
|
||||
Protocol::Disconnect => Ok(InErrorPacket::Disconnect),
|
||||
Protocol::AddressExhausted => Ok(InErrorPacket::AddressExhausted),
|
||||
Protocol::IpAlreadyExists => Ok(InErrorPacket::IpAlreadyExists),
|
||||
Protocol::InvalidIp => Ok(InErrorPacket::InvalidIp),
|
||||
Protocol::NoKey => Ok(InErrorPacket::NoKey),
|
||||
Protocol::Other(_) => Ok(InErrorPacket::OtherError(ErrorPacket::new(buffer)?)),
|
||||
}
|
||||
}
|
||||
@@ -48,16 +68,16 @@ pub struct ErrorPacket<B> {
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ErrorPacket<B> {
|
||||
pub fn new(buffer: B) -> Result<ErrorPacket<B>> {
|
||||
pub fn new(buffer: B) -> io::Result<ErrorPacket<B>> {
|
||||
Ok(Self { buffer })
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> ErrorPacket<B> {
|
||||
pub fn message(&self) -> Result<String> {
|
||||
pub fn message(&self) -> io::Result<String> {
|
||||
match String::from_utf8(self.buffer.as_ref().to_vec()) {
|
||||
Ok(str) => Ok(str),
|
||||
Err(_) => Err(Error::InvalidPacket),
|
||||
Err(_) => Err(io::Error::new(io::ErrorKind::Other, "Utf8Error")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/* 扩展协议
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 扩展数据(n) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 扩展数据(n) | type(8) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
注:扩展数据的长度由type决定
|
||||
*/
|
||||
|
||||
use anyhow::anyhow;
|
||||
use std::io;
|
||||
|
||||
use crate::protocol::NetPacket;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum ExtensionTailType {
|
||||
Compression,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for ExtensionTailType {
|
||||
fn from(value: u8) -> Self {
|
||||
if value == 0 {
|
||||
ExtensionTailType::Compression
|
||||
} else {
|
||||
ExtensionTailType::Unknown(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ExtensionTailPacket<B> {
|
||||
Compression(CompressionExtensionTail<B>),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> NetPacket<B> {
|
||||
/// 分离尾部数据
|
||||
pub fn split_tail_packet(&mut self) -> anyhow::Result<ExtensionTailPacket<&[u8]>> {
|
||||
if self.is_extension() {
|
||||
let payload = self.payload();
|
||||
if let Some(v) = payload.last() {
|
||||
return match ExtensionTailType::from(*v) {
|
||||
ExtensionTailType::Compression => {
|
||||
let data_len = self.data_len - 4;
|
||||
self.set_data_len(data_len)?;
|
||||
self.set_extension_flag(false);
|
||||
Ok(ExtensionTailPacket::Compression(
|
||||
CompressionExtensionTail::new(
|
||||
&self.raw_buffer()[data_len..data_len + 4],
|
||||
),
|
||||
))
|
||||
}
|
||||
ExtensionTailType::Unknown(e) => Err(anyhow!("unknown extension {}", e)),
|
||||
};
|
||||
}
|
||||
}
|
||||
Err(anyhow!("not extension"))
|
||||
}
|
||||
/// 追加压缩扩展
|
||||
pub fn append_compression_extension_tail(
|
||||
&mut self,
|
||||
) -> io::Result<CompressionExtensionTail<&mut [u8]>> {
|
||||
let len = self.data_len;
|
||||
//增加数据长度
|
||||
self.set_data_len(self.data_len + 4)?;
|
||||
self.set_extension_flag(true);
|
||||
let mut tail = CompressionExtensionTail::new(&mut self.buffer_mut()[len..]);
|
||||
tail.init();
|
||||
return Ok(tail);
|
||||
}
|
||||
}
|
||||
|
||||
/* 扩展协议
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| algorithm(8) | | type(8) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
注:扩展数据的长度由type决定
|
||||
*/
|
||||
/// 压缩扩展
|
||||
pub struct CompressionExtensionTail<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> CompressionExtensionTail<B> {
|
||||
pub fn new(buffer: B) -> CompressionExtensionTail<B> {
|
||||
assert_eq!(buffer.as_ref().len(), 4);
|
||||
CompressionExtensionTail { buffer }
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> CompressionExtensionTail<B> {
|
||||
pub fn algorithm(&self) -> CompressionAlgorithm {
|
||||
self.buffer.as_ref()[0].into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> CompressionExtensionTail<B> {
|
||||
pub fn init(&mut self) {
|
||||
self.buffer.as_mut().fill(0);
|
||||
}
|
||||
pub fn set_algorithm(&mut self, algorithm: CompressionAlgorithm) {
|
||||
self.buffer.as_mut()[0] = algorithm.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum CompressionAlgorithm {
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
Lz4,
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
Zstd,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for CompressionAlgorithm {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
1 => CompressionAlgorithm::Lz4,
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
2 => CompressionAlgorithm::Zstd,
|
||||
v => CompressionAlgorithm::Unknown(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CompressionAlgorithm> for u8 {
|
||||
fn from(value: CompressionAlgorithm) -> Self {
|
||||
match value {
|
||||
#[cfg(feature = "lz4_compress")]
|
||||
CompressionAlgorithm::Lz4 => 1,
|
||||
#[cfg(feature = "zstd_compress")]
|
||||
CompressionAlgorithm::Zstd => 2,
|
||||
CompressionAlgorithm::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Protocol {
|
||||
Ipv4,
|
||||
WGIpv4,
|
||||
Ipv4Broadcast,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
4 => Protocol::Ipv4,
|
||||
5 => Protocol::WGIpv4,
|
||||
201 => Protocol::Ipv4Broadcast,
|
||||
val => Protocol::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Protocol> for u8 {
|
||||
fn from(val: Protocol) -> Self {
|
||||
match val {
|
||||
Protocol::Ipv4 => 4,
|
||||
Protocol::WGIpv4 => 5,
|
||||
Protocol::Ipv4Broadcast => 201,
|
||||
Protocol::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BroadcastPacket<B> {
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> BroadcastPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
Self { buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<Self> {
|
||||
let len = buffer.as_ref().len();
|
||||
let packet = Self::unchecked(buffer);
|
||||
if len < 2 + 4 || packet.addr_num() == 0 {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"BroadcastPacket InvalidData",
|
||||
))
|
||||
} else {
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> BroadcastPacket<B> {
|
||||
pub fn addr_num(&self) -> u8 {
|
||||
self.buffer.as_ref()[0]
|
||||
}
|
||||
/// 已经发送给了这些地址
|
||||
pub fn addresses(&self) -> Vec<Ipv4Addr> {
|
||||
let num = self.addr_num() as usize;
|
||||
let mut list = Vec::with_capacity(num);
|
||||
let buf = self.buffer.as_ref();
|
||||
let mut offset = 1;
|
||||
for _ in 0..num {
|
||||
list.push(Ipv4Addr::new(
|
||||
buf[offset],
|
||||
buf[offset + 1],
|
||||
buf[offset + 2],
|
||||
buf[offset + 3],
|
||||
));
|
||||
offset += 4;
|
||||
}
|
||||
list
|
||||
}
|
||||
pub fn data(&self) -> io::Result<&[u8]> {
|
||||
let start = 1 + self.addr_num() as usize * 4;
|
||||
if start > self.buffer.as_ref().len() {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, "InvalidData"))
|
||||
} else {
|
||||
Ok(&self.buffer.as_ref()[start..])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> BroadcastPacket<B> {
|
||||
pub fn set_address(&mut self, addr: &[Ipv4Addr]) -> io::Result<()> {
|
||||
let buf = self.buffer.as_mut();
|
||||
if buf.len() < 1 + addr.len() * 4 || addr.len() > u8::MAX as usize {
|
||||
Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"addr invalid data",
|
||||
))
|
||||
} else {
|
||||
buf[0] = addr.len() as u8;
|
||||
let mut offset = 1;
|
||||
for ip in addr {
|
||||
buf[offset..offset + 4].copy_from_slice(&ip.octets());
|
||||
offset += 4;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub fn set_data(&mut self, data: &[u8]) -> io::Result<()> {
|
||||
let num = self.addr_num() as usize;
|
||||
let start = 1 + 4 * num;
|
||||
let buf = self.buffer.as_mut();
|
||||
if start >= buf.len() || start + data.len() != buf.len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"data invalid data",
|
||||
));
|
||||
}
|
||||
buf[start..].copy_from_slice(data);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
use crate::protocol::body::ENCRYPTION_RESERVED;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::{fmt, io};
|
||||
|
||||
/*
|
||||
0 15 31
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|e |s |x |u| 版本(4) | 协议(8) | 上层协议(8) | 初始ttl(4) | 生存时间(4) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 源ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 目的ip地址(32) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| 数据体 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
注:e为是否加密标志,s为服务端通信包标志,x扩展标志,u未使用
|
||||
*/
|
||||
pub const HEAD_LEN: usize = 12;
|
||||
|
||||
pub mod body;
|
||||
pub mod control_packet;
|
||||
pub mod error_packet;
|
||||
pub mod extension;
|
||||
pub mod ip_turn_packet;
|
||||
pub mod other_turn_packet;
|
||||
pub mod service_packet;
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Version {
|
||||
V2,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Version {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
// 版本从2开始,用于和stun协议的binging响应区分开
|
||||
2 => Version::V2,
|
||||
val => Version::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Version {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Version::V2 => 2,
|
||||
Version::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
/// 服务包 用于和服务端交互
|
||||
Service,
|
||||
/// 响应异常
|
||||
Error,
|
||||
/// 控制协议
|
||||
Control,
|
||||
/// 转发ip数据
|
||||
IpTurn,
|
||||
/// 转发其他数据
|
||||
OtherTurn,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Service,
|
||||
2 => Protocol::Error,
|
||||
3 => Protocol::Control,
|
||||
4 => Protocol::IpTurn,
|
||||
5 => Protocol::OtherTurn,
|
||||
val => Protocol::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Service => 1,
|
||||
Protocol::Error => 2,
|
||||
Protocol::Control => 3,
|
||||
Protocol::IpTurn => 4,
|
||||
Protocol::OtherTurn => 5,
|
||||
Protocol::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const MAX_TTL: u8 = 0b1111;
|
||||
pub const MAX_SOURCE: u8 = 0b11110000;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct NetPacket<B> {
|
||||
data_len: usize,
|
||||
buffer: B,
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
pub fn unchecked(buffer: B) -> Self {
|
||||
let data_len = buffer.as_ref().len();
|
||||
Self { data_len, buffer }
|
||||
}
|
||||
pub fn new(buffer: B) -> io::Result<NetPacket<B>> {
|
||||
let data_len = buffer.as_ref().len();
|
||||
Self::new0(data_len, buffer)
|
||||
}
|
||||
pub fn new_encrypt(buffer: B) -> io::Result<NetPacket<B>> {
|
||||
if 12 + ENCRYPTION_RESERVED > buffer.as_ref().len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"length overflow",
|
||||
));
|
||||
}
|
||||
//加密需要预留ENCRYPTION_RESERVED字节
|
||||
let data_len = buffer.as_ref().len() - ENCRYPTION_RESERVED;
|
||||
Self::new0(data_len, buffer)
|
||||
}
|
||||
pub fn new0(data_len: usize, buffer: B) -> io::Result<NetPacket<B>> {
|
||||
if data_len > buffer.as_ref().len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"length overflow",
|
||||
));
|
||||
}
|
||||
if data_len < 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"data_len too short",
|
||||
));
|
||||
}
|
||||
Ok(NetPacket { data_len, buffer })
|
||||
}
|
||||
#[inline]
|
||||
pub fn buffer(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[..self.data_len]
|
||||
}
|
||||
pub fn raw_buffer(&self) -> &[u8] {
|
||||
self.buffer.as_ref()
|
||||
}
|
||||
pub fn data_len(&self) -> usize {
|
||||
self.data_len
|
||||
}
|
||||
pub fn reserve(&self) -> usize {
|
||||
self.buffer.as_ref().len() - self.data_len
|
||||
}
|
||||
pub fn into_buffer(self) -> B {
|
||||
self.buffer
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> NetPacket<B> {
|
||||
/// 数据加密
|
||||
pub fn is_encrypt(&self) -> bool {
|
||||
self.buffer.as_ref()[0] & 0x80 == 0x80
|
||||
}
|
||||
/// 网关通信的标识
|
||||
pub fn is_gateway(&self) -> bool {
|
||||
self.buffer.as_ref()[0] & 0x40 == 0x40
|
||||
}
|
||||
/// 扩展协议
|
||||
pub fn is_extension(&self) -> bool {
|
||||
self.buffer.as_ref()[0] & 0x20 == 0x20
|
||||
}
|
||||
pub fn version(&self) -> Version {
|
||||
Version::from(self.buffer.as_ref()[0] & 0x0F)
|
||||
}
|
||||
pub fn protocol(&self) -> Protocol {
|
||||
Protocol::from(self.buffer.as_ref()[1])
|
||||
}
|
||||
pub fn transport_protocol(&self) -> u8 {
|
||||
self.buffer.as_ref()[2]
|
||||
}
|
||||
pub fn ttl(&self) -> u8 {
|
||||
self.buffer.as_ref()[3] & MAX_TTL
|
||||
}
|
||||
pub fn source_ttl(&self) -> u8 {
|
||||
self.buffer.as_ref()[3] >> 4
|
||||
}
|
||||
pub fn source(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[4..8].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn destination(&self) -> Ipv4Addr {
|
||||
let tmp: [u8; 4] = self.buffer.as_ref()[8..12].try_into().unwrap();
|
||||
Ipv4Addr::from(tmp)
|
||||
}
|
||||
pub fn payload(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[12..self.data_len]
|
||||
}
|
||||
pub fn head(&self) -> &[u8] {
|
||||
&self.buffer.as_ref()[..12]
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]> + AsMut<[u8]>> NetPacket<B> {
|
||||
pub fn head_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[..12]
|
||||
}
|
||||
pub fn buffer_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[..self.data_len]
|
||||
}
|
||||
pub fn set_encrypt_flag(&mut self, is_encrypt: bool) {
|
||||
if is_encrypt {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x80
|
||||
} else {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0x7F
|
||||
};
|
||||
}
|
||||
pub fn set_gateway_flag(&mut self, is_gateway: bool) {
|
||||
if is_gateway {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x40
|
||||
} else {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0xBF
|
||||
};
|
||||
}
|
||||
pub fn set_extension_flag(&mut self, is_extension: bool) {
|
||||
if is_extension {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] | 0x20
|
||||
} else {
|
||||
self.buffer.as_mut()[0] = self.buffer.as_ref()[0] & 0xDF
|
||||
};
|
||||
}
|
||||
pub fn set_default_version(&mut self) {
|
||||
let v: u8 = Version::V2.into();
|
||||
self.buffer.as_mut()[0] = (self.buffer.as_ref()[0] & 0xF0) | (0x0F & v);
|
||||
}
|
||||
pub fn set_protocol(&mut self, protocol: Protocol) {
|
||||
self.buffer.as_mut()[1] = protocol.into();
|
||||
}
|
||||
pub fn set_transport_protocol(&mut self, transport_protocol: u8) {
|
||||
self.buffer.as_mut()[2] = transport_protocol;
|
||||
}
|
||||
pub fn set_transport_protocol_into<P: Into<u8>>(&mut self, transport_protocol: P) {
|
||||
self.buffer.as_mut()[2] = transport_protocol.into();
|
||||
}
|
||||
pub fn first_set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[3] = ttl << 4 | ttl;
|
||||
}
|
||||
pub fn set_ttl(&mut self, ttl: u8) {
|
||||
self.buffer.as_mut()[3] = (self.buffer.as_mut()[3] & MAX_SOURCE) | (MAX_TTL & ttl);
|
||||
}
|
||||
pub fn incr_ttl(&mut self) -> u8 {
|
||||
let ttl = self.ttl() - 1;
|
||||
self.set_ttl(ttl);
|
||||
ttl
|
||||
}
|
||||
pub fn set_source_ttl(&mut self, source_ttl: u8) {
|
||||
self.buffer.as_mut()[3] = (source_ttl << 4) | (MAX_TTL & self.buffer.as_ref()[3]);
|
||||
}
|
||||
pub fn set_source(&mut self, source: Ipv4Addr) {
|
||||
self.buffer.as_mut()[4..8].copy_from_slice(&source.octets());
|
||||
}
|
||||
pub fn set_destination(&mut self, destination: Ipv4Addr) {
|
||||
self.buffer.as_mut()[8..12].copy_from_slice(&destination.octets());
|
||||
}
|
||||
pub fn set_payload(&mut self, payload: &[u8]) -> io::Result<()> {
|
||||
if self.data_len - 12 != payload.len() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"data_len - 12 != payload.len",
|
||||
));
|
||||
}
|
||||
self.buffer.as_mut()[12..self.data_len].copy_from_slice(payload);
|
||||
Ok(())
|
||||
}
|
||||
pub fn payload_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.buffer.as_mut()[12..self.data_len]
|
||||
}
|
||||
pub fn set_data_len(&mut self, data_len: usize) -> io::Result<()> {
|
||||
if data_len > self.buffer.as_ref().len() || data_len < 12 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidData,
|
||||
"data_len invalid",
|
||||
));
|
||||
}
|
||||
self.data_len = data_len;
|
||||
Ok(())
|
||||
}
|
||||
pub fn set_payload_len(&mut self, payload_len: usize) -> io::Result<()> {
|
||||
let data_len = HEAD_LEN + payload_len;
|
||||
self.set_data_len(data_len)
|
||||
}
|
||||
pub fn set_data_len_max(&mut self) {
|
||||
self.data_len = self.buffer.as_ref().len();
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: AsRef<[u8]>> fmt::Debug for NetPacket<B> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("NetPacket")
|
||||
.field("version", &self.version())
|
||||
.field("gateway", &self.is_gateway())
|
||||
.field("encrypt", &self.is_encrypt())
|
||||
.field("protocol", &self.protocol())
|
||||
.field("transport_protocol", &self.transport_protocol())
|
||||
.field("ttl", &self.ttl())
|
||||
.field("source_ttl", &self.source_ttl())
|
||||
.field("source", &self.source())
|
||||
.field("destination", &self.destination())
|
||||
.field("payload", &self.payload())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
pub enum Protocol {
|
||||
Punch,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Protocol::Punch,
|
||||
val => Protocol::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Protocol::Punch => 1,
|
||||
Protocol::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
#[derive(Eq, PartialEq, Copy, Clone, Debug)]
|
||||
pub enum Protocol {
|
||||
/// 注册请求
|
||||
RegistrationRequest,
|
||||
/// 注册响应
|
||||
RegistrationResponse,
|
||||
/// 拉取设备列表
|
||||
PullDeviceList,
|
||||
/// 推送设备列表
|
||||
PushDeviceList,
|
||||
/// 和服务端握手
|
||||
HandshakeRequest,
|
||||
HandshakeResponse,
|
||||
SecretHandshakeRequest,
|
||||
SecretHandshakeResponse,
|
||||
/// 客户端上报状态
|
||||
ClientStatusInfo,
|
||||
Unknown(u8),
|
||||
}
|
||||
|
||||
impl From<u8> for Protocol {
|
||||
fn from(value: u8) -> Self {
|
||||
match value {
|
||||
1 => Self::RegistrationRequest,
|
||||
2 => Self::RegistrationResponse,
|
||||
3 => Self::PullDeviceList,
|
||||
4 => Self::PushDeviceList,
|
||||
5 => Self::HandshakeRequest,
|
||||
6 => Self::HandshakeResponse,
|
||||
7 => Self::SecretHandshakeRequest,
|
||||
8 => Self::SecretHandshakeResponse,
|
||||
9 => Self::ClientStatusInfo,
|
||||
val => Self::Unknown(val),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u8> for Protocol {
|
||||
fn into(self) -> u8 {
|
||||
match self {
|
||||
Self::RegistrationRequest => 1,
|
||||
Self::RegistrationResponse => 2,
|
||||
Self::PullDeviceList => 3,
|
||||
Self::PushDeviceList => 4,
|
||||
Self::HandshakeRequest => 5,
|
||||
Self::HandshakeResponse => 6,
|
||||
Self::SecretHandshakeRequest => 7,
|
||||
Self::SecretHandshakeResponse => 8,
|
||||
Self::ClientStatusInfo => 9,
|
||||
Self::Unknown(val) => val,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use tun_rs::SyncDevice;
|
||||
|
||||
use crate::{DeviceConfig, ErrorInfo, ErrorType, VntCallback};
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
const DEFAULT_TUN_NAME: &str = "vnt-tun";
|
||||
|
||||
pub fn create_device<Call: VntCallback>(
|
||||
config: DeviceConfig,
|
||||
call: &Call,
|
||||
) -> Result<Arc<SyncDevice>, ErrorInfo> {
|
||||
let device = match create_device0(&config) {
|
||||
Ok(device) => device,
|
||||
Err(e) => {
|
||||
return Err(ErrorInfo::new_msg(
|
||||
ErrorType::FailedToCrateDevice,
|
||||
format!("create device {:?}", e),
|
||||
));
|
||||
}
|
||||
};
|
||||
#[cfg(windows)]
|
||||
let index = device.if_index().unwrap();
|
||||
#[cfg(unix)]
|
||||
let index = &device.name().unwrap();
|
||||
if let Err(e) = add_route(index, Ipv4Addr::BROADCAST, Ipv4Addr::BROADCAST) {
|
||||
log::warn!("添加广播路由失败 ={:?}", e);
|
||||
}
|
||||
|
||||
if let Err(e) = add_route(
|
||||
index,
|
||||
Ipv4Addr::from([224, 0, 0, 0]),
|
||||
Ipv4Addr::from([240, 0, 0, 0]),
|
||||
) {
|
||||
log::warn!("添加组播路由失败 ={:?}", e);
|
||||
}
|
||||
|
||||
for (dest, mask) in config.external_route {
|
||||
if let Err(e) = add_route(index, dest, mask) {
|
||||
log::warn!("添加路由失败,请检查-i参数是否和现有路由冲突 ={:?}", e);
|
||||
call.error(ErrorInfo::new_msg(
|
||||
ErrorType::Warn,
|
||||
format!(
|
||||
"警告! 添加路由失败,请检查-i参数是否和现有路由冲突 ={:?}",
|
||||
e
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
fn create_device0(config: &DeviceConfig) -> io::Result<Arc<SyncDevice>> {
|
||||
let mut tun_builder = tun_rs::DeviceBuilder::default();
|
||||
tun_builder = tun_builder.ipv4(config.virtual_ip, config.virtual_netmask, None);
|
||||
|
||||
match &config.device_name {
|
||||
None => {
|
||||
#[cfg(any(target_os = "windows", target_os = "linux"))]
|
||||
{
|
||||
tun_builder = tun_builder.name(DEFAULT_TUN_NAME);
|
||||
}
|
||||
}
|
||||
Some(name) => {
|
||||
tun_builder = tun_builder.name(name);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
let name = config
|
||||
.device_name
|
||||
.clone()
|
||||
.unwrap_or_else(|| DEFAULT_TUN_NAME.to_string());
|
||||
_ = delete_adapter_info_from_reg(&name);
|
||||
tun_builder = tun_builder.metric(0).ring_capacity(4 * 1024 * 1024);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let device_name = config
|
||||
.device_name
|
||||
.clone()
|
||||
.unwrap_or(DEFAULT_TUN_NAME.to_string());
|
||||
if &device_name == DEFAULT_TUN_NAME {
|
||||
delete_device(DEFAULT_TUN_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
let device = tun_builder.mtu(config.mtu as u16).build_sync()?;
|
||||
Ok(Arc::new(device))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn delete_device(name: &str) {
|
||||
// 删除默认网卡,此操作有风险,后续可能去除
|
||||
use std::process::Command;
|
||||
let cmd = format!("ip link delete {}", name);
|
||||
let delete_tun = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(&cmd)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !delete_tun.status.success() {
|
||||
log::warn!("删除网卡失败:{:?}", delete_tun);
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
fn delete_adapter_info_from_reg(dev_name: &str) -> std::io::Result<()> {
|
||||
use std::collections::HashSet;
|
||||
use winreg::{enums::HKEY_LOCAL_MACHINE, enums::KEY_ALL_ACCESS, RegKey};
|
||||
let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
|
||||
let profiles_key = hklm.open_subkey_with_flags(
|
||||
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Profiles",
|
||||
KEY_ALL_ACCESS,
|
||||
)?;
|
||||
let mut profile_guid_set = HashSet::new();
|
||||
for sub_key_name in profiles_key.enum_keys().filter_map(Result::ok) {
|
||||
let sub_key = profiles_key.open_subkey(&sub_key_name)?;
|
||||
match sub_key.get_value::<String, _>("Description") {
|
||||
Ok(profile_name) => {
|
||||
if dev_name == profile_name {
|
||||
match profiles_key.delete_subkey_all(&sub_key_name) {
|
||||
Ok(_) => {
|
||||
log::info!("deleted Profiles sub_key: {}", sub_key_name);
|
||||
profile_guid_set.insert(sub_key_name);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Failed to delete Profiles sub_key {}: {}", sub_key_name, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => log::warn!(
|
||||
"Failed to read Description for sub_key {}: {}",
|
||||
sub_key_name,
|
||||
e
|
||||
),
|
||||
}
|
||||
}
|
||||
let unmanaged_key = hklm.open_subkey_with_flags(
|
||||
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Signatures\\Unmanaged",
|
||||
KEY_ALL_ACCESS,
|
||||
)?;
|
||||
for sub_key_name in unmanaged_key.enum_keys().filter_map(Result::ok) {
|
||||
let sub_key = unmanaged_key.open_subkey(&sub_key_name)?;
|
||||
match sub_key.get_value::<String, _>("ProfileGuid") {
|
||||
Ok(profile_guid) => {
|
||||
if profile_guid_set.contains(&profile_guid) {
|
||||
match unmanaged_key.delete_subkey_all(&sub_key_name) {
|
||||
Ok(_) => log::info!("deleted Unmanaged sub_key: {}", sub_key_name),
|
||||
Err(e) => {
|
||||
log::warn!("Failed to delete Unmanaged sub_key {}: {}", sub_key_name, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => log::warn!(
|
||||
"Failed to read Description for sub_key {}: {}",
|
||||
sub_key_name,
|
||||
e
|
||||
),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn add_route(index: u32, dest: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
"route add {:?} mask {:?} {:?} metric {} if {}",
|
||||
dest,
|
||||
netmask,
|
||||
Ipv4Addr::UNSPECIFIED,
|
||||
1,
|
||||
index
|
||||
);
|
||||
exe_cmd(&cmd)
|
||||
}
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn exe_cmd(cmd: &str) -> io::Result<()> {
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
println!("exe cmd: {}", cmd);
|
||||
let out = std::process::Command::new("cmd")
|
||||
.creation_flags(windows_sys::Win32::System::Threading::CREATE_NO_WINDOW)
|
||||
.arg("/C")
|
||||
.arg(&cmd)
|
||||
.output()?;
|
||||
if !out.status.success() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("cmd={},out={:?}", cmd, String::from_utf8(out.stderr)),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
let cmd = format!(
|
||||
"route -n add {} -netmask {} -interface {}",
|
||||
address, netmask, name
|
||||
);
|
||||
exe_cmd(&cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
pub fn add_route(name: &str, address: Ipv4Addr, netmask: Ipv4Addr) -> io::Result<()> {
|
||||
let cmd = if netmask.is_broadcast() {
|
||||
format!("route add -host {:?} {}", address, name)
|
||||
} else {
|
||||
format!(
|
||||
"route add -net {}/{} {}",
|
||||
address,
|
||||
u32::from(netmask).count_ones(),
|
||||
name
|
||||
)
|
||||
};
|
||||
exe_cmd(&cmd)?;
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(any(target_os = "macos", target_os = "linux"))]
|
||||
pub fn exe_cmd(cmd: &str) -> io::Result<std::process::Output> {
|
||||
use std::process::Command;
|
||||
println!("exe cmd: {}", cmd);
|
||||
let out = Command::new("sh")
|
||||
.arg("-c")
|
||||
.arg(cmd)
|
||||
.output()
|
||||
.expect("sh exec error!");
|
||||
if !out.status.success() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
format!("cmd={},out={:?}", cmd, out),
|
||||
));
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
pub use create_device::create_device;
|
||||
|
||||
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
mod create_device;
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
pub mod tun_create_helper;
|
||||
|
||||
pub mod vnt_device;
|
||||
@@ -0,0 +1,141 @@
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::channel::context::ChannelContext;
|
||||
use crate::cipher::Cipher;
|
||||
use crate::compression::Compressor;
|
||||
use crate::external_route::ExternalRoute;
|
||||
use crate::handle::tun_tap::DeviceStop;
|
||||
use crate::handle::{CurrentDeviceInfo, PeerDeviceInfo};
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
use crate::ip_proxy::IpProxyMap;
|
||||
use crate::tun_tap_device::vnt_device::DeviceWrite;
|
||||
use crate::util::StopManager;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use parking_lot::Mutex;
|
||||
use tun_rs::SyncDevice;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DeviceAdapter {
|
||||
tun: Arc<Mutex<Option<Arc<SyncDevice>>>>,
|
||||
}
|
||||
|
||||
impl DeviceAdapter {
|
||||
pub fn insert(&self, device: Arc<SyncDevice>) {
|
||||
let r = self.tun.lock().replace(device);
|
||||
assert!(r.is_none());
|
||||
}
|
||||
/// 要保证先remove 再insert
|
||||
pub fn remove(&self) {
|
||||
drop(self.tun.lock().take());
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceWrite for DeviceAdapter {
|
||||
#[inline]
|
||||
fn write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
if let Some(tun) = self.tun.lock().as_ref() {
|
||||
tun.send(buf)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::NotFound, "not tun device"))
|
||||
}
|
||||
}
|
||||
|
||||
fn into_device_adapter(self) -> DeviceAdapter {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TunDeviceHelper {
|
||||
inner: Arc<Mutex<TunDeviceHelperInner>>,
|
||||
device_adapter: DeviceAdapter,
|
||||
device_stop: Arc<Mutex<Option<DeviceStop>>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TunDeviceHelperInner {
|
||||
stop_manager: StopManager,
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
}
|
||||
|
||||
impl TunDeviceHelper {
|
||||
pub fn new(
|
||||
stop_manager: StopManager,
|
||||
context: ChannelContext,
|
||||
current_device: Arc<AtomicCell<CurrentDeviceInfo>>,
|
||||
ip_route: ExternalRoute,
|
||||
#[cfg(feature = "ip_proxy")] ip_proxy_map: Option<IpProxyMap>,
|
||||
client_cipher: Cipher,
|
||||
server_cipher: Cipher,
|
||||
device_map: Arc<Mutex<(u16, HashMap<Ipv4Addr, PeerDeviceInfo>)>>,
|
||||
compressor: Compressor,
|
||||
device_adapter: DeviceAdapter,
|
||||
) -> Self {
|
||||
let inner = TunDeviceHelperInner {
|
||||
stop_manager,
|
||||
context,
|
||||
current_device,
|
||||
ip_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
ip_proxy_map,
|
||||
client_cipher,
|
||||
server_cipher,
|
||||
device_map,
|
||||
compressor,
|
||||
};
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(inner)),
|
||||
device_adapter,
|
||||
device_stop: Default::default(),
|
||||
}
|
||||
}
|
||||
pub fn stop(&self) {
|
||||
//先停止旧的,再启动新的,改变旧网卡的IP太麻烦
|
||||
if let Some(device_stop) = self.device_stop.lock().take() {
|
||||
self.device_adapter.remove();
|
||||
loop {
|
||||
device_stop.stop();
|
||||
std::thread::sleep(std::time::Duration::from_millis(300));
|
||||
//确保停止了
|
||||
if device_stop.is_stopped() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/// 要保证先stop 再start
|
||||
pub fn start(&self, device: Arc<SyncDevice>, allow_wire_guard: bool) -> io::Result<()> {
|
||||
self.device_adapter.insert(device.clone());
|
||||
let device_stop = DeviceStop::default();
|
||||
let s = self.device_stop.lock().replace(device_stop.clone());
|
||||
assert!(s.is_none());
|
||||
let inner = self.inner.lock().clone();
|
||||
crate::handle::tun_tap::tun_handler::start(
|
||||
inner.stop_manager,
|
||||
inner.context,
|
||||
device,
|
||||
inner.current_device,
|
||||
inner.ip_route,
|
||||
#[cfg(feature = "ip_proxy")]
|
||||
inner.ip_proxy_map,
|
||||
inner.client_cipher,
|
||||
inner.server_cipher,
|
||||
inner.device_map,
|
||||
inner.compressor,
|
||||
device_stop,
|
||||
allow_wire_guard,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
use std::io;
|
||||
|
||||
pub trait DeviceWrite: Clone + Send + Sync + 'static {
|
||||
fn write(&self, buf: &[u8]) -> io::Result<usize>;
|
||||
#[cfg(feature = "integrated_tun")]
|
||||
fn into_device_adapter(self) -> crate::tun_tap_device::tun_create_helper::DeviceAdapter;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct U64Adder {
|
||||
count: Arc<AtomicCell<u64>>,
|
||||
}
|
||||
|
||||
impl U64Adder {
|
||||
pub fn add(&self, num: u64) {
|
||||
self.count.fetch_add(num);
|
||||
}
|
||||
pub fn get(&self) -> u64 {
|
||||
self.count.load()
|
||||
}
|
||||
pub fn watch(&self) -> WatchU64Adder {
|
||||
WatchU64Adder {
|
||||
count: self.count.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WatchU64Adder {
|
||||
count: Arc<AtomicCell<u64>>,
|
||||
}
|
||||
|
||||
impl WatchU64Adder {
|
||||
pub fn get(&self) -> u64 {
|
||||
self.count.load()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
mod adder;
|
||||
pub use adder::*;
|
||||
@@ -0,0 +1,436 @@
|
||||
use crate::channel::socket::LocalInterface;
|
||||
use anyhow::Context;
|
||||
use dns_parser::{Builder, Packet, QueryClass, QueryType, RData, ResponseCode};
|
||||
use http_req::request::{RedirectPolicy, Request};
|
||||
use http_req::uri::Uri;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs, UdpSocket};
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
use std::{io, thread};
|
||||
|
||||
thread_local! {
|
||||
static HISTORY: RefCell<HashMap<SocketAddr,usize>> = RefCell::new(HashMap::new());
|
||||
}
|
||||
|
||||
/// 保留一个地址使用记录,使用过的地址后续不再选中,直到地址全使用过
|
||||
pub fn address_choose(addrs: Vec<SocketAddr>) -> anyhow::Result<SocketAddr> {
|
||||
HISTORY.with(|history| {
|
||||
let mut available = Vec::new();
|
||||
for x in &addrs {
|
||||
let num = history.borrow().get(x).map_or(0, |v| *v);
|
||||
if num < 3 {
|
||||
available.push(*x);
|
||||
}
|
||||
}
|
||||
if available.is_empty() {
|
||||
available = addrs;
|
||||
history.borrow_mut().clear();
|
||||
}
|
||||
let addr = address_choose0(available)?;
|
||||
history
|
||||
.borrow_mut()
|
||||
.entry(addr)
|
||||
.and_modify(|v| {
|
||||
*v += 1;
|
||||
})
|
||||
.or_insert(1);
|
||||
Ok(addr)
|
||||
})
|
||||
}
|
||||
|
||||
/// 后续实现选择延迟最低的可用地址,需要服务端配合
|
||||
/// 现在是选择第一个地址,优先ipv6
|
||||
fn address_choose0(addrs: Vec<SocketAddr>) -> anyhow::Result<SocketAddr> {
|
||||
let v4: Vec<SocketAddr> = addrs.iter().filter(|v| v.is_ipv4()).copied().collect();
|
||||
let v6: Vec<SocketAddr> = addrs.iter().filter(|v| v.is_ipv6()).copied().collect();
|
||||
let check_addr = |addrs: &Vec<SocketAddr>| -> anyhow::Result<SocketAddr> {
|
||||
let mut err = Vec::new();
|
||||
if !addrs.is_empty() {
|
||||
let udp = if addrs[0].is_ipv6() {
|
||||
UdpSocket::bind("[::]:0")?
|
||||
} else {
|
||||
UdpSocket::bind("0.0.0.0:0")?
|
||||
};
|
||||
for addr in addrs {
|
||||
if let Err(e) = udp.connect(addr) {
|
||||
err.push((*addr, e));
|
||||
} else {
|
||||
return Ok(*addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(anyhow::anyhow!("Unable to connect to address {:?}", err))
|
||||
};
|
||||
if v6.is_empty() {
|
||||
return check_addr(&v4);
|
||||
}
|
||||
if v4.is_empty() {
|
||||
return check_addr(&v6);
|
||||
}
|
||||
match check_addr(&v6) {
|
||||
Ok(addr) => Ok(addr),
|
||||
Err(e1) => match check_addr(&v4) {
|
||||
Ok(addr) => Ok(addr),
|
||||
Err(e2) => Err(anyhow::anyhow!("{} , {}", e1, e2)),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dns_query_all(
|
||||
domain: &str,
|
||||
mut name_servers: Vec<String>,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Vec<SocketAddr>> {
|
||||
let mut current_domain = domain.to_string(); // 引入可变变量存储当前域名
|
||||
match SocketAddr::from_str(¤t_domain) {
|
||||
Ok(addr) => Ok(vec![addr]),
|
||||
Err(_) => {
|
||||
// 重定向判断 http:
|
||||
let current_domain_lower = current_domain.to_lowercase();
|
||||
let redirect_domain = current_domain_lower
|
||||
.strip_prefix("http:")
|
||||
.or_else(|| current_domain_lower.strip_prefix("https:"))
|
||||
.map(|v| v.to_string());
|
||||
|
||||
// 执行重定向检查
|
||||
if let Some(stripped) = redirect_domain {
|
||||
if let Some(redirected_url) = check_for_redirect(&stripped)? {
|
||||
// 去掉 URL 开头的协议部分
|
||||
let final_domain = remove_http_prefix(&redirected_url);
|
||||
println!("Server Address: {}", final_domain);
|
||||
|
||||
// 检查是否为 IP 和端口组合
|
||||
if let Ok(socket_addr) = SocketAddr::from_str(&final_domain) {
|
||||
// 如果是 IP 和端口格式,直接返回结果
|
||||
return Ok(vec![socket_addr]);
|
||||
} else {
|
||||
// 如果不是 IP 和端口格式,则更新为重定向地址
|
||||
current_domain = final_domain;
|
||||
}
|
||||
}
|
||||
}
|
||||
let txt_domain = current_domain
|
||||
.to_lowercase()
|
||||
.strip_prefix("txt:")
|
||||
.map(|v| v.to_string());
|
||||
if name_servers.is_empty() {
|
||||
if txt_domain.is_some() {
|
||||
name_servers.push("223.5.5.5:53".into());
|
||||
name_servers.push("119.29.29.29:53".into());
|
||||
name_servers.push("114.114.114.114:53".into());
|
||||
} else {
|
||||
return Ok(current_domain
|
||||
.to_socket_addrs()
|
||||
.with_context(|| format!("DNS query failed {:?}", current_domain))?
|
||||
.collect());
|
||||
}
|
||||
}
|
||||
|
||||
let mut err: Option<anyhow::Error> = None;
|
||||
for name_server in name_servers {
|
||||
if let Some(domain) = txt_domain.as_ref() {
|
||||
match txt_dns(domain, name_server, default_interface) {
|
||||
Ok(addr) => {
|
||||
if !addr.is_empty() {
|
||||
println!("TXT: {:?}", addr);
|
||||
return Ok(addr);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if let Some(err) = &mut err {
|
||||
*err = anyhow::anyhow!("{} {}", err, e);
|
||||
} else {
|
||||
err.replace(anyhow::anyhow!("{}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let end_index = current_domain
|
||||
.rfind(':')
|
||||
.with_context(|| format!("{:?} not port", current_domain))?;
|
||||
let host = &domain[..end_index];
|
||||
let port = u16::from_str(&domain[end_index + 1..])
|
||||
.with_context(|| format!("{:?} not port", current_domain))?;
|
||||
let th1 = {
|
||||
let host = host.to_string();
|
||||
let name_server = name_server.clone();
|
||||
let default_interface = default_interface.clone();
|
||||
thread::spawn(move || a_dns(host, name_server, &default_interface))
|
||||
};
|
||||
let th2 = {
|
||||
let host = host.to_string();
|
||||
let name_server = name_server.clone();
|
||||
let default_interface = default_interface.clone();
|
||||
thread::spawn(move || aaaa_dns(host, name_server, &default_interface))
|
||||
};
|
||||
let mut addr = Vec::new();
|
||||
match th1.join().unwrap() {
|
||||
Ok(rs) => {
|
||||
for ip in rs {
|
||||
addr.push(SocketAddr::new(ip.into(), port));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
err.replace(anyhow::anyhow!("{}", e));
|
||||
}
|
||||
}
|
||||
match th2.join().unwrap() {
|
||||
Ok(rs) => {
|
||||
for ip in rs {
|
||||
addr.push(SocketAddr::new(ip.into(), port));
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
if addr.is_empty() {
|
||||
if let Some(err) = &mut err {
|
||||
*err = anyhow::anyhow!("{},{}", err, e);
|
||||
} else {
|
||||
err.replace(anyhow::anyhow!("{}", e));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if addr.is_empty() {
|
||||
continue;
|
||||
}
|
||||
return Ok(addr);
|
||||
}
|
||||
if let Some(e) = err {
|
||||
Err(e)
|
||||
} else {
|
||||
Err(anyhow::anyhow!("DNS query failed {:?}", current_domain))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_host_port(addr: &str) -> bool {
|
||||
// 处理 IPv6 地址(格式为 [::1]:8080)
|
||||
if addr.starts_with('[') {
|
||||
if let Some(idx) = addr.rfind(']') {
|
||||
if let Some(port_idx) = addr[idx + 1..].find(':') {
|
||||
let port = &addr[idx + 1 + port_idx + 1..]; // 提取端口部分
|
||||
return !port.is_empty() && port.chars().all(|c| c.is_numeric());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 处理 IPv4 和普通域名(格式为 example.com:443 或 192.168.1.1:8080)
|
||||
if let Some((_host, port)) = addr.rsplit_once(':') {
|
||||
return !port.is_empty() && port.chars().all(|c| c.is_numeric());
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn check_for_redirect(domain: &String) -> anyhow::Result<Option<String>> {
|
||||
// 确保域名有 http:// 或 https:// 前缀
|
||||
let mut url = if domain.starts_with("http://") || domain.starts_with("https://") {
|
||||
domain.clone()
|
||||
} else {
|
||||
format!("http://{}", domain)
|
||||
};
|
||||
|
||||
let mut count = 0; // 重定向次数计数器
|
||||
let mut last_redirect_url: Option<String> = None; // 记录最后一个重定向的 URL
|
||||
|
||||
loop {
|
||||
count += 1;
|
||||
if count > 3 {
|
||||
println!("重定向次数超过 3 次,跳过");
|
||||
return Ok(last_redirect_url);
|
||||
}
|
||||
|
||||
// 解析 URL
|
||||
let uri = match Uri::try_from(url.as_str()) {
|
||||
Ok(u) => u,
|
||||
Err(e) => {
|
||||
println!("解析地址失败: {}", e);
|
||||
return Ok(last_redirect_url);
|
||||
}
|
||||
};
|
||||
|
||||
let mut response_body = Vec::new();
|
||||
|
||||
// 发送 HTTP 请求
|
||||
let response = match Request::new(&uri)
|
||||
.timeout(Duration::from_secs(10))
|
||||
.redirect_policy(RedirectPolicy::Limit(0))
|
||||
.send(&mut response_body)
|
||||
{
|
||||
Ok(resp) => {
|
||||
println!("HTTP Status Code: {}", resp.status_code());
|
||||
resp
|
||||
}
|
||||
Err(e) => {
|
||||
return Ok(last_redirect_url);
|
||||
}
|
||||
};
|
||||
|
||||
let body_str = String::from_utf8_lossy(&response_body);
|
||||
let cleaned_body = body_str.replace('\n', "").replace('\r', "");
|
||||
println!("Response Body: {}", cleaned_body);
|
||||
// 处理 3XX 重定向
|
||||
if response.status_code().is_redirect() {
|
||||
if let Some(location) = response.headers().get("Location") {
|
||||
url = location.to_string().trim_end_matches('/').to_string();
|
||||
last_redirect_url = Some(url.clone()); // 更新最后的重定向地址
|
||||
println!("Location: {}", url);
|
||||
continue;
|
||||
} else {
|
||||
return Ok(last_redirect_url);
|
||||
}
|
||||
}
|
||||
// 处理 200 响应
|
||||
else if response.status_code().is_success() {
|
||||
for line in body_str.lines() {
|
||||
let trimmed = line.trim();
|
||||
if parse_host_port(trimmed) {
|
||||
println!("text: {}", trimmed);
|
||||
return Ok(Some(trimmed.to_string()));
|
||||
}
|
||||
}
|
||||
return Ok(last_redirect_url);
|
||||
}
|
||||
return Ok(last_redirect_url);
|
||||
}
|
||||
}
|
||||
|
||||
/// 去掉 http:// 或 https:// 前缀
|
||||
fn remove_http_prefix(url: &str) -> String {
|
||||
url.trim_start_matches("http://")
|
||||
.trim_start_matches("https://")
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn query<'a>(
|
||||
udp: &UdpSocket,
|
||||
domain: &str,
|
||||
name_server: SocketAddr,
|
||||
record_type: QueryType,
|
||||
buf: &'a mut [u8],
|
||||
) -> anyhow::Result<Packet<'a>> {
|
||||
let mut builder = Builder::new_query(1, true);
|
||||
builder.add_question(domain, false, record_type, QueryClass::IN);
|
||||
let packet = builder.build().unwrap();
|
||||
|
||||
udp.connect(name_server)
|
||||
.with_context(|| format!("DNS {:?} error ", name_server))?;
|
||||
let mut count = 0;
|
||||
let len = loop {
|
||||
udp.send(&packet)?;
|
||||
|
||||
match udp.recv(buf) {
|
||||
Ok(len) => {
|
||||
break len;
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == io::ErrorKind::TimedOut || e.kind() == io::ErrorKind::WouldBlock {
|
||||
count += 1;
|
||||
if count < 3 {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Err(e).with_context(|| format!("DNS {:?} recv error ", name_server))?
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let pkt = Packet::parse(&buf[..len])
|
||||
.with_context(|| format!("domain {:?} DNS {:?} data error ", domain, name_server))?;
|
||||
if pkt.header.response_code != ResponseCode::NoError {
|
||||
return Err(anyhow::anyhow!(
|
||||
"response_code {} DNS {:?} domain {:?}",
|
||||
pkt.header.response_code,
|
||||
name_server,
|
||||
domain
|
||||
));
|
||||
}
|
||||
if pkt.answers.is_empty() {
|
||||
return Err(anyhow::anyhow!(
|
||||
"No records received DNS {:?} domain {:?}",
|
||||
name_server,
|
||||
domain
|
||||
));
|
||||
}
|
||||
|
||||
Ok(pkt)
|
||||
}
|
||||
|
||||
pub fn txt_dns(
|
||||
domain: &str,
|
||||
name_server: String,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Vec<SocketAddr>> {
|
||||
let name_server: SocketAddr = name_server.parse()?;
|
||||
let udp = bind_udp(name_server, default_interface)?;
|
||||
let mut buf = [0; 65536];
|
||||
let message = query(&udp, domain, name_server, QueryType::TXT, &mut buf)?;
|
||||
let mut rs = Vec::new();
|
||||
for record in message.answers {
|
||||
if let RData::TXT(txt) = record.data {
|
||||
for x in txt.iter() {
|
||||
let txt = std::str::from_utf8(x).context("record type txt is not string")?;
|
||||
let addr =
|
||||
SocketAddr::from_str(txt).context("record type txt is not SocketAddr")?;
|
||||
rs.push(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(rs)
|
||||
}
|
||||
|
||||
fn bind_udp(
|
||||
name_server: SocketAddr,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<UdpSocket> {
|
||||
let addr: SocketAddr = if name_server.is_ipv4() {
|
||||
"0.0.0.0:0".parse().unwrap()
|
||||
} else {
|
||||
"[::]:0".parse().unwrap()
|
||||
};
|
||||
let socket = crate::channel::socket::bind_udp(addr, default_interface)?;
|
||||
socket.set_nonblocking(false)?;
|
||||
socket.set_read_timeout(Some(Duration::from_millis(800)))?;
|
||||
Ok(socket.into())
|
||||
}
|
||||
|
||||
pub fn a_dns(
|
||||
domain: String,
|
||||
name_server: String,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Vec<Ipv4Addr>> {
|
||||
let name_server: SocketAddr = name_server.parse()?;
|
||||
let udp = bind_udp(name_server, default_interface)?;
|
||||
let mut buf = [0; 65536];
|
||||
let message = query(&udp, &domain, name_server, QueryType::A, &mut buf)?;
|
||||
let mut rs = Vec::new();
|
||||
for record in message.answers {
|
||||
if let RData::A(a) = record.data {
|
||||
rs.push(a.0);
|
||||
}
|
||||
}
|
||||
Ok(rs)
|
||||
}
|
||||
|
||||
pub fn aaaa_dns(
|
||||
domain: String,
|
||||
name_server: String,
|
||||
default_interface: &LocalInterface,
|
||||
) -> anyhow::Result<Vec<Ipv6Addr>> {
|
||||
let name_server: SocketAddr = name_server.parse()?;
|
||||
let udp = bind_udp(name_server, default_interface)?;
|
||||
let mut buf = [0; 65536];
|
||||
let message = query(&udp, &domain, name_server, QueryType::AAAA, &mut buf)?;
|
||||
let mut rs = Vec::new();
|
||||
for record in message.answers {
|
||||
if let RData::AAAA(a) = record.data {
|
||||
rs.push(a.0);
|
||||
}
|
||||
}
|
||||
Ok(rs)
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
mod rate_limiter;
|
||||
pub use rate_limiter::*;
|
||||
|
||||
mod traffic_meter;
|
||||
pub use traffic_meter::*;
|
||||
@@ -0,0 +1,62 @@
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConcurrentRateLimiter {
|
||||
inner: Arc<Mutex<RateLimiter>>,
|
||||
}
|
||||
|
||||
impl ConcurrentRateLimiter {
|
||||
pub fn new(capacity: usize, refill_rate: usize) -> Self {
|
||||
let inner = RateLimiter::new(capacity, refill_rate);
|
||||
Self {
|
||||
inner: Arc::new(Mutex::new(inner)),
|
||||
}
|
||||
}
|
||||
pub fn try_acquire(&self) -> bool {
|
||||
self.inner.lock().try_acquire()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RateLimiter {
|
||||
capacity: usize,
|
||||
tokens: usize,
|
||||
refill_rate: usize,
|
||||
last_refill: Instant,
|
||||
}
|
||||
|
||||
impl RateLimiter {
|
||||
// 初始化限流器
|
||||
pub fn new(capacity: usize, refill_rate: usize) -> Self {
|
||||
Self {
|
||||
capacity,
|
||||
tokens: capacity,
|
||||
refill_rate,
|
||||
last_refill: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试获取一个令牌
|
||||
pub fn try_acquire(&mut self) -> bool {
|
||||
self.refill();
|
||||
if self.tokens > 0 {
|
||||
self.tokens -= 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 补充令牌
|
||||
fn refill(&mut self) {
|
||||
let now = Instant::now();
|
||||
let elapsed = now.duration_since(self.last_refill).as_secs() as usize;
|
||||
let new_tokens = elapsed * self.refill_rate;
|
||||
|
||||
if new_tokens > 0 {
|
||||
self.tokens = std::cmp::min(self.capacity, self.tokens + new_tokens);
|
||||
self.last_refill = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
use parking_lot::Mutex;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TrafficMeterMultiAddress {
|
||||
history_capacity: usize,
|
||||
inner: Arc<Mutex<(u64, HashMap<Ipv4Addr, TrafficMeter>)>>,
|
||||
}
|
||||
|
||||
impl Default for TrafficMeterMultiAddress {
|
||||
fn default() -> Self {
|
||||
TrafficMeterMultiAddress::new(100)
|
||||
}
|
||||
}
|
||||
|
||||
impl TrafficMeterMultiAddress {
|
||||
pub fn new(history_capacity: usize) -> Self {
|
||||
let inner = Arc::new(Mutex::new((0, HashMap::new())));
|
||||
Self {
|
||||
inner,
|
||||
history_capacity,
|
||||
}
|
||||
}
|
||||
pub fn add_traffic(&self, ip: Ipv4Addr, amount: usize) {
|
||||
let mut guard = self.inner.lock();
|
||||
guard.0 += amount as u64;
|
||||
guard
|
||||
.1
|
||||
.entry(ip)
|
||||
.or_insert(TrafficMeter::new(self.history_capacity))
|
||||
.add_traffic(amount)
|
||||
}
|
||||
pub fn total(&self) -> u64 {
|
||||
self.inner.lock().0
|
||||
}
|
||||
pub fn get_all(&self) -> (u64, HashMap<Ipv4Addr, u64>) {
|
||||
let guard = self.inner.lock();
|
||||
(
|
||||
guard.0,
|
||||
guard.1.iter().map(|(ip, t)| (*ip, t.total())).collect(),
|
||||
)
|
||||
}
|
||||
pub fn get_all_history(&self) -> (u64, HashMap<Ipv4Addr, (u64, Vec<usize>)>) {
|
||||
let guard = self.inner.lock();
|
||||
(
|
||||
guard.0,
|
||||
guard
|
||||
.1
|
||||
.iter()
|
||||
.map(|(ip, t)| (*ip, (t.total(), t.get_history())))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
pub fn get_history(&self, ip: &Ipv4Addr) -> Option<(u64, Vec<usize>)> {
|
||||
self.inner
|
||||
.lock()
|
||||
.1
|
||||
.get(ip)
|
||||
.map(|t| (t.total(), t.get_history()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ConcurrentTrafficMeter {
|
||||
inner: Arc<Mutex<TrafficMeter>>,
|
||||
}
|
||||
|
||||
impl ConcurrentTrafficMeter {
|
||||
pub fn new(history_capacity: usize) -> Self {
|
||||
let inner = Arc::new(Mutex::new(TrafficMeter::new(history_capacity)));
|
||||
Self { inner }
|
||||
}
|
||||
pub fn add_traffic(&self, amount: usize) {
|
||||
self.inner.lock().add_traffic(amount)
|
||||
}
|
||||
pub fn get_history(&self) -> Vec<usize> {
|
||||
self.inner.lock().get_history()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TrafficMeter {
|
||||
start_time: Instant,
|
||||
total: u64,
|
||||
count: usize,
|
||||
history_capacity: usize,
|
||||
history: VecDeque<usize>,
|
||||
}
|
||||
|
||||
impl TrafficMeter {
|
||||
// 初始化一个新的 TrafficMeter
|
||||
pub fn new(history_capacity: usize) -> Self {
|
||||
Self {
|
||||
start_time: Instant::now(),
|
||||
total: 0,
|
||||
count: 0,
|
||||
history: VecDeque::with_capacity(history_capacity),
|
||||
history_capacity,
|
||||
}
|
||||
}
|
||||
|
||||
// 增加流量计数
|
||||
pub fn add_traffic(&mut self, amount: usize) {
|
||||
self.total += amount as u64;
|
||||
self.count += amount;
|
||||
self.check_time();
|
||||
}
|
||||
|
||||
// 检查时间是否超过一秒,如果是,记录流量并重置计数器和时间
|
||||
fn check_time(&mut self) {
|
||||
if self.start_time.elapsed() >= Duration::new(1, 0) {
|
||||
// 将当前计数添加到历史记录
|
||||
if self.history.len() >= self.history_capacity {
|
||||
self.history.pop_front(); // 保持历史记录不超过capacity
|
||||
}
|
||||
self.history.push_back(self.count);
|
||||
|
||||
// 重置计数器和时间
|
||||
self.count = 0;
|
||||
self.start_time = Instant::now();
|
||||
}
|
||||
}
|
||||
pub fn total(&self) -> u64 {
|
||||
self.total
|
||||
}
|
||||
// 获取流量记录
|
||||
pub fn get_history(&self) -> Vec<usize> {
|
||||
self.history.iter().cloned().collect()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
mod notify;
|
||||
mod scheduler;
|
||||
pub use notify::{StopManager, Worker};
|
||||
pub use scheduler::Scheduler;
|
||||
|
||||
// mod counter;
|
||||
// pub use counter::*;
|
||||
|
||||
mod dns_query;
|
||||
pub use dns_query::*;
|
||||
|
||||
#[cfg(feature = "upnp")]
|
||||
mod upnp;
|
||||
#[cfg(feature = "upnp")]
|
||||
pub use upnp::*;
|
||||
|
||||
pub mod limit;
|
||||
@@ -0,0 +1,174 @@
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
use std::thread::Thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use parking_lot::Mutex;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StopManager {
|
||||
inner: Arc<StopManagerInner>,
|
||||
}
|
||||
|
||||
impl StopManager {
|
||||
pub fn new<F>(f: F) -> Self
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
Self {
|
||||
inner: Arc::new(StopManagerInner::new(f)),
|
||||
}
|
||||
}
|
||||
pub fn add_listener<F>(&self, name: String, f: F) -> anyhow::Result<Worker>
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
self.inner.add_listener(name, f)
|
||||
}
|
||||
pub fn stop(&self) {
|
||||
self.inner.stop();
|
||||
}
|
||||
pub fn wait(&self) {
|
||||
self.inner.wait();
|
||||
}
|
||||
pub fn wait_timeout(&self, dur: Duration) -> bool {
|
||||
self.inner.wait_timeout(dur)
|
||||
}
|
||||
pub fn is_stopped(&self) -> bool {
|
||||
self.inner.is_stopped()
|
||||
}
|
||||
}
|
||||
|
||||
struct StopManagerInner {
|
||||
listeners: Mutex<(bool, Vec<(String, Box<dyn FnOnce() + Send>)>)>,
|
||||
park_threads: Mutex<Vec<Thread>>,
|
||||
worker_num: AtomicUsize,
|
||||
state: AtomicBool,
|
||||
stop_call: Mutex<Option<Box<dyn FnOnce() + Send>>>,
|
||||
}
|
||||
|
||||
impl StopManagerInner {
|
||||
fn new<F>(f: F) -> Self
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
Self {
|
||||
listeners: Mutex::new((false, Vec::with_capacity(32))),
|
||||
park_threads: Mutex::new(Vec::with_capacity(4)),
|
||||
worker_num: AtomicUsize::new(0),
|
||||
state: AtomicBool::new(false),
|
||||
stop_call: Mutex::new(Some(Box::new(f))),
|
||||
}
|
||||
}
|
||||
fn add_listener<F>(self: &Arc<Self>, name: String, f: F) -> anyhow::Result<Worker>
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
if name.is_empty() {
|
||||
return Err(anyhow!("name cannot be empty"));
|
||||
}
|
||||
let mut guard = self.listeners.lock();
|
||||
if guard.0 {
|
||||
return Err(anyhow!("stopped"));
|
||||
}
|
||||
for (n, _) in &guard.1 {
|
||||
if &name == n {
|
||||
return Err(anyhow!("stop add_listener {:?} name already exists", name));
|
||||
}
|
||||
}
|
||||
guard.1.push((name.clone(), Box::new(f)));
|
||||
Ok(Worker::new(name, self.clone()))
|
||||
}
|
||||
fn stop(&self) {
|
||||
self.state.store(true, Ordering::Release);
|
||||
let mut guard = self.listeners.lock();
|
||||
guard.0 = true;
|
||||
for (_name, listener) in guard.1.drain(..) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
pub fn is_stopped(&self) -> bool {
|
||||
self.worker_num.load(Ordering::Acquire) == 0
|
||||
}
|
||||
fn wait(&self) {
|
||||
{
|
||||
let mut guard = self.park_threads.lock();
|
||||
guard.push(thread::current());
|
||||
drop(guard);
|
||||
}
|
||||
loop {
|
||||
if self.worker_num.load(Ordering::Acquire) == 0 {
|
||||
return;
|
||||
}
|
||||
thread::park()
|
||||
}
|
||||
}
|
||||
fn wait_timeout(&self, dur: Duration) -> bool {
|
||||
{
|
||||
let mut guard = self.park_threads.lock();
|
||||
guard.push(thread::current());
|
||||
drop(guard);
|
||||
}
|
||||
if self.worker_num.load(Ordering::Acquire) == 0 {
|
||||
return true;
|
||||
}
|
||||
thread::park_timeout(dur);
|
||||
self.worker_num.load(Ordering::Acquire) == 0
|
||||
}
|
||||
fn stop_call(&self) {
|
||||
self.stop();
|
||||
if let Some(call) = self.stop_call.lock().take() {
|
||||
call();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Worker {
|
||||
name: String,
|
||||
inner: Arc<StopManagerInner>,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
fn new(name: String, inner: Arc<StopManagerInner>) -> Self {
|
||||
let _ = inner.worker_num.fetch_add(1, Ordering::AcqRel);
|
||||
Self { name, inner }
|
||||
}
|
||||
fn release0(&self) {
|
||||
let inner = &self.inner;
|
||||
let worker_name = &self.name;
|
||||
{
|
||||
let mut mutex_guard = inner.listeners.lock();
|
||||
if let Some(pos) = mutex_guard
|
||||
.1
|
||||
.iter()
|
||||
.position(|(name, _)| name == worker_name)
|
||||
{
|
||||
let (_, listener) = mutex_guard.1.remove(pos);
|
||||
listener();
|
||||
}
|
||||
}
|
||||
|
||||
let count = inner.worker_num.fetch_sub(1, Ordering::AcqRel);
|
||||
if count == 1 {
|
||||
for x in inner.park_threads.lock().drain(..) {
|
||||
x.unpark();
|
||||
}
|
||||
self.inner.stop_call();
|
||||
}
|
||||
}
|
||||
pub fn stop_all(self) {
|
||||
self.inner.stop()
|
||||
}
|
||||
pub fn stop_self(self) {
|
||||
drop(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Worker {
|
||||
fn drop(&mut self) {
|
||||
self.release0();
|
||||
log::info!("stop {}", self.name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
use crate::util::StopManager;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use std::collections::BinaryHeap;
|
||||
use std::sync::mpsc::TrySendError;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
sync::mpsc::{sync_channel, Receiver, SyncSender},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
struct DelayedTask {
|
||||
f: Box<dyn FnOnce(&Scheduler) + Send>,
|
||||
next: Instant,
|
||||
}
|
||||
|
||||
impl Eq for DelayedTask {}
|
||||
|
||||
impl PartialEq for DelayedTask {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.next.eq(&other.next)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for DelayedTask {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.next.partial_cmp(&other.next).map(|ord| ord.reverse())
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for DelayedTask {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.next.cmp(&other.next).reverse()
|
||||
}
|
||||
}
|
||||
|
||||
enum Op {
|
||||
Task(DelayedTask),
|
||||
Stop,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Scheduler {
|
||||
sender: SyncSender<Op>,
|
||||
state: Arc<AtomicCell<SchedulerState>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
enum SchedulerState {
|
||||
Running,
|
||||
ShutdownNow, // 立即停止任务执行,队列中剩余的任务不再执行
|
||||
_Shutdown, //执行完队列中剩余的任务再停止
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
pub fn new(stop_manager: StopManager) -> anyhow::Result<Self> {
|
||||
let (sender, receiver) = sync_channel::<Op>(32);
|
||||
let state = Arc::new(AtomicCell::new(SchedulerState::Running));
|
||||
let s = Self { sender, state };
|
||||
let s_inner = s.clone();
|
||||
let worker = {
|
||||
let scheduler = s.clone();
|
||||
stop_manager.add_listener("Scheduler".into(), move || {
|
||||
scheduler.shutdown_now();
|
||||
})?
|
||||
};
|
||||
std::thread::Builder::new()
|
||||
.name("Scheduler".into())
|
||||
.spawn(move || {
|
||||
run(receiver, &s_inner);
|
||||
s_inner.shutdown_now();
|
||||
worker.stop_all();
|
||||
})
|
||||
.expect("Scheduler");
|
||||
Ok(s)
|
||||
}
|
||||
pub fn timeout<F>(&self, time: Duration, f: F) -> bool
|
||||
where
|
||||
F: FnOnce(&Scheduler) + Send + 'static,
|
||||
{
|
||||
if self.state.load() != SchedulerState::Running {
|
||||
log::error!("定时任务执行停止");
|
||||
return false;
|
||||
}
|
||||
let task = DelayedTask {
|
||||
f: Box::new(f),
|
||||
next: Instant::now().checked_add(time).unwrap(),
|
||||
};
|
||||
// 如果是任务中调用此方法,那这里用send可能会导致整个定时任务阻塞
|
||||
// 任务总数不能大于或等于通道长度,所以改成try_send快速失败
|
||||
match self.sender.try_send(Op::Task(task)) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
match e {
|
||||
TrySendError::Full(_) => {
|
||||
log::error!("定时任务队列达到上限");
|
||||
}
|
||||
TrySendError::Disconnected(_) => {
|
||||
log::error!("定时任务执行停止 通道关闭");
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn shutdown_now(&self) {
|
||||
self.state.store(SchedulerState::ShutdownNow);
|
||||
let _ = self.sender.send(Op::Stop);
|
||||
}
|
||||
}
|
||||
|
||||
fn run(receiver: Receiver<Op>, s_inner: &Scheduler) {
|
||||
let mut binary_heap = BinaryHeap::<DelayedTask>::with_capacity(32);
|
||||
loop {
|
||||
while let Some(task) = binary_heap.peek() {
|
||||
if s_inner.state.load() == SchedulerState::ShutdownNow {
|
||||
return;
|
||||
}
|
||||
let now = Instant::now();
|
||||
if now < task.next {
|
||||
//需要等待对应时间
|
||||
match receiver.recv_timeout(task.next - now) {
|
||||
Ok(op) => {
|
||||
if add_task(op, &mut binary_heap) {
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Err(e) => match e {
|
||||
std::sync::mpsc::RecvTimeoutError::Timeout => continue,
|
||||
std::sync::mpsc::RecvTimeoutError::Disconnected => return,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
if let Some(task) = binary_heap.pop() {
|
||||
(task.f)(s_inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
//取出所有任务
|
||||
loop {
|
||||
match receiver.try_recv() {
|
||||
Ok(op) => {
|
||||
if add_task(op, &mut binary_heap) {
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Err(e) => match e {
|
||||
std::sync::mpsc::TryRecvError::Empty => break,
|
||||
std::sync::mpsc::TryRecvError::Disconnected => return,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if binary_heap.is_empty() {
|
||||
//任务队列为空时陷入等待
|
||||
if let Ok(op) = receiver.recv() {
|
||||
if add_task(op, &mut binary_heap) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_task(op: Op, binary_heap: &mut BinaryHeap<DelayedTask>) -> bool {
|
||||
return match op {
|
||||
Op::Task(task) => {
|
||||
binary_heap.push(task);
|
||||
true
|
||||
}
|
||||
Op::Stop => false,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
use igd::{search_gateway, PortMappingProtocol};
|
||||
use std::net::{Ipv4Addr, SocketAddrV4};
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::Mutex;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct UPnP {
|
||||
inner: Arc<UpnpInner>,
|
||||
}
|
||||
|
||||
impl Deref for UPnP {
|
||||
type Target = UpnpInner;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct UpnpInner {
|
||||
list: Mutex<Vec<(PortMappingProtocol, u16)>>,
|
||||
}
|
||||
|
||||
impl UpnpInner {
|
||||
pub fn add_tcp_port(&self, port: u16) {
|
||||
self.list.lock().push((PortMappingProtocol::TCP, port));
|
||||
}
|
||||
pub fn add_udp_port(&self, port: u16) {
|
||||
self.list.lock().push((PortMappingProtocol::UDP, port));
|
||||
}
|
||||
pub fn reset(&self, local_ip: Ipv4Addr) {
|
||||
let gateway = match search_gateway(Default::default()) {
|
||||
Ok(gateway) => gateway,
|
||||
Err(e) => {
|
||||
log::warn!("search_gateway {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let guard = self.list.lock();
|
||||
|
||||
// 不支持upnp的情况会阻塞30秒,之后再改这个库
|
||||
for (protocol, port) in guard.iter() {
|
||||
let local_addr = SocketAddrV4::new(local_ip, *port);
|
||||
log::info!("add upnp protocol={} {}", protocol, local_addr);
|
||||
if let Err(e) = gateway.add_port(*protocol, *port, local_addr, 700, "upnp") {
|
||||
log::warn!(
|
||||
"add upnp failed protocol={},port={} err:{:?}",
|
||||
protocol,
|
||||
port,
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UpnpInner {
|
||||
fn drop(&mut self) {
|
||||
// let gateway = match search_gateway(Default::default()) {
|
||||
// Ok(gateway) => gateway,
|
||||
// Err(e) => {
|
||||
// log::warn!("search_gateway {:?}", e);
|
||||
// return;
|
||||
// }
|
||||
// };
|
||||
//
|
||||
// let guard = self.list.lock();
|
||||
// for (protocol, port) in guard.iter() {
|
||||
// if let Err(e) = gateway.remove_port(*protocol, *port) {
|
||||
// log::warn!(
|
||||
// "remove upnp failed protocol={},port={} err:{:?}",
|
||||
// protocol,
|
||||
// port,
|
||||
// e
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user