mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
3430 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6e75e9c5a | ||
|
|
4901434209 | ||
|
|
13d174c09c | ||
|
|
5363c063d1 | ||
|
|
32d300248e | ||
|
|
3426906a4f | ||
|
|
3aaa942c94 | ||
|
|
95d8887ab0 | ||
|
|
6272e15b47 | ||
|
|
20b4f6b24d | ||
|
|
6ee279d83e | ||
|
|
b00ff43be7 | ||
|
|
dfbefee477 | ||
|
|
8c2de4973c | ||
|
|
e1527dc137 | ||
|
|
0957f6b143 | ||
|
|
7db2e9dc4a | ||
|
|
b1c701085b | ||
|
|
e8d6c803cd | ||
|
|
f4a2dda94e | ||
|
|
c4216379ed | ||
|
|
52195bf296 | ||
|
|
10fe75ed87 | ||
|
|
1c659d6ef6 | ||
|
|
3ac86db038 | ||
|
|
4a77a03033 | ||
|
|
3820b51960 | ||
|
|
e070ce4e34 | ||
|
|
0bb0adbf3e | ||
|
|
ddd25f0945 | ||
|
|
162bd592f8 | ||
|
|
85fa728d41 | ||
|
|
c7db9010ad | ||
|
|
be16545063 | ||
|
|
c730839989 | ||
|
|
4ee364640d | ||
|
|
56dd0db001 | ||
|
|
626ed720a6 | ||
|
|
b8fd9ba83f | ||
|
|
316a4457af | ||
|
|
347a2c2150 | ||
|
|
42d3770b14 | ||
|
|
39ca07bcc6 | ||
|
|
914566ece0 | ||
|
|
f537e7b2c6 | ||
|
|
06020b8f54 | ||
|
|
b486d1cd27 | ||
|
|
b3b38015c2 | ||
|
|
4c6988e3bc | ||
|
|
0bd4db4cc7 | ||
|
|
bc72800fef | ||
|
|
951f8972c7 | ||
|
|
38b694a055 | ||
|
|
44a9c3ca0c | ||
|
|
6bf823fb15 | ||
|
|
43a751ee0b | ||
|
|
207212557e | ||
|
|
fd1aeeac92 | ||
|
|
50ba52756f | ||
|
|
e630b8f8a8 | ||
|
|
cf5081d300 | ||
|
|
8864cbf80a | ||
|
|
81d7a832c0 | ||
|
|
d41fabbc9f | ||
|
|
46f62e1af9 | ||
|
|
b91efaa973 | ||
|
|
e3238ff75c | ||
|
|
9cc4e8d03a | ||
|
|
34c98e03c1 | ||
|
|
41e40bbc0d | ||
|
|
ca49944c85 | ||
|
|
d5e9fc7677 | ||
|
|
6db7c5733d | ||
|
|
418f86ecbd | ||
|
|
c68c5af856 | ||
|
|
950dffbe06 | ||
|
|
5d557003b6 | ||
|
|
3b8c3c1346 | ||
|
|
1853263f6c | ||
|
|
b0f6d81f57 | ||
|
|
9ba0329432 | ||
|
|
614101c4b8 | ||
|
|
50e4fb138a | ||
|
|
6dba0c6e0e | ||
|
|
0f2d2156e6 | ||
|
|
13b17c5a93 | ||
|
|
511499d950 | ||
|
|
6632b71273 | ||
|
|
60ef70cee4 | ||
|
|
b3ba0a7241 | ||
|
|
fc73dabc0b | ||
|
|
1121d45eb6 | ||
|
|
18b6353803 | ||
|
|
c0c0642bd1 | ||
|
|
3cf26a84dc | ||
|
|
44d6d4405e | ||
|
|
cafa027f0b | ||
|
|
1c970b0714 | ||
|
|
6636e432d7 | ||
|
|
158889b85c | ||
|
|
92bebb7ecc | ||
|
|
fff34e77f5 | ||
|
|
df18692af9 | ||
|
|
276a78cb2e | ||
|
|
a1e820182c | ||
|
|
272090fc8f | ||
|
|
ab6bc52a0f | ||
|
|
c69c369502 | ||
|
|
a5b1b24fee | ||
|
|
40cfbc5d61 | ||
|
|
ffad1ecd6d | ||
|
|
e1b5803902 | ||
|
|
492d71a924 | ||
|
|
6d01a3a7d1 | ||
|
|
b71e20dfa3 | ||
|
|
474158dd18 | ||
|
|
914db816c2 | ||
|
|
4485d6fdf4 | ||
|
|
2c394661a6 | ||
|
|
611c1a7502 | ||
|
|
4e8858a764 | ||
|
|
fb46de5ca6 | ||
|
|
65db96e663 | ||
|
|
5109d40d8e | ||
|
|
1ba1fa37f9 | ||
|
|
9c97ee6407 | ||
|
|
7477d2c219 | ||
|
|
a6fb3b602e | ||
|
|
d9b9457b56 | ||
|
|
cfb6ddbfc6 | ||
|
|
7de21c1f93 | ||
|
|
100f3380c4 | ||
|
|
20e484bb8b | ||
|
|
94fc1a1cee | ||
|
|
ae28df5276 | ||
|
|
3a4f1382f3 | ||
|
|
01a7e08585 | ||
|
|
847fd15af2 | ||
|
|
a21fb17d73 | ||
|
|
0c34bd440b | ||
|
|
1008510750 | ||
|
|
34d6eb52d0 | ||
|
|
5820425b6c | ||
|
|
a4b39a3648 | ||
|
|
3dc5542a28 | ||
|
|
dde4643e77 | ||
|
|
7a857e08c1 | ||
|
|
80a3bd6a3b | ||
|
|
7fb8ee60b4 | ||
|
|
dca4cf2edb | ||
|
|
2bc33f22df | ||
|
|
d14c6e2829 | ||
|
|
a4ce224cd1 | ||
|
|
ab1cd3f5cf | ||
|
|
1e75de9bb8 | ||
|
|
19a03c42a5 | ||
|
|
9e5d1bf0fc | ||
|
|
c5f784719d | ||
|
|
60aa459dfc | ||
|
|
53d71d29ff | ||
|
|
81658d2ff9 | ||
|
|
9fa67b0e0a | ||
|
|
88ba494701 | ||
|
|
efacfced45 | ||
|
|
b3c836f298 | ||
|
|
3330bf4f2f | ||
|
|
e634c89995 | ||
|
|
5aa53eee43 | ||
|
|
42483b6f32 | ||
|
|
00bbb81375 | ||
|
|
5271cdacf2 | ||
|
|
6d01726961 | ||
|
|
12feac1f50 | ||
|
|
39c1c3567b | ||
|
|
63ae6850d3 | ||
|
|
bec24e052c | ||
|
|
91eb2b2c4a | ||
|
|
0bae2a3397 | ||
|
|
42ec6db746 | ||
|
|
7a9dc0eec0 | ||
|
|
6441c9d5d8 | ||
|
|
2930d39ce7 | ||
|
|
1500e805dd | ||
|
|
b14d1801f0 | ||
|
|
bc11181d5e | ||
|
|
9739e677aa | ||
|
|
056f076ae8 | ||
|
|
7dfb6f4a13 | ||
|
|
b347b719f3 | ||
|
|
7b537a4e94 | ||
|
|
291f28fcce | ||
|
|
fa9c39732d | ||
|
|
bfdf006bd2 | ||
|
|
057d6ca05b | ||
|
|
7d7f5ff4e2 | ||
|
|
6e32f4bc85 | ||
|
|
8460a8f4ef | ||
|
|
8c09a7429c | ||
|
|
346bf14b7b | ||
|
|
8e3c9410dc | ||
|
|
cb0552e20d | ||
|
|
8c8127dda6 | ||
|
|
1d8a481d59 | ||
|
|
dd4f066e95 | ||
|
|
5e0d4163a2 | ||
|
|
7fb2d13a8b | ||
|
|
acde10b46e | ||
|
|
c0bcab8bc5 | ||
|
|
fd6d0922ab | ||
|
|
8179e7dbf8 | ||
|
|
eabeeaccfe | ||
|
|
94bba69dee | ||
|
|
4d23de96d5 | ||
|
|
681810ea38 | ||
|
|
d500fe66fd | ||
|
|
05c2adeefd | ||
|
|
d46e0fb474 | ||
|
|
330fab2efa | ||
|
|
d59d36f93c | ||
|
|
fd6827fdca | ||
|
|
dca94f17d7 | ||
|
|
d4e16881ff | ||
|
|
cd3f274763 | ||
|
|
1947be4957 | ||
|
|
21de4709ea | ||
|
|
ec76381a0b | ||
|
|
66661417d7 | ||
|
|
81b79e6e53 | ||
|
|
5ae93d852e | ||
|
|
96cb663fa8 | ||
|
|
1efc2a9b5d | ||
|
|
9441c1cffe | ||
|
|
ef30d2d3b6 | ||
|
|
1673966e36 | ||
|
|
1d8f913364 | ||
|
|
a549ebc25f | ||
|
|
ce853786b5 | ||
|
|
7e0ab6d0b1 | ||
|
|
d6f907a05b | ||
|
|
b2d1962b81 | ||
|
|
b0a6c9fa53 | ||
|
|
7a0337f3db | ||
|
|
c1dbd3ffd0 | ||
|
|
1ea6d2016d | ||
|
|
416589cc93 | ||
|
|
41ce9d47e5 | ||
|
|
d7e4deab4e | ||
|
|
27782ceddd | ||
|
|
a6f62a99b9 | ||
|
|
4e4def4fb9 | ||
|
|
7af3b751d4 | ||
|
|
897cfad399 | ||
|
|
f87a51034e | ||
|
|
3f409d0e28 | ||
|
|
543566840c | ||
|
|
1c3174a277 | ||
|
|
fc2ae6f887 | ||
|
|
63e175d389 | ||
|
|
9bfbba6fea | ||
|
|
69d245c4bd | ||
|
|
7738eae4b0 | ||
|
|
3d5fb07ca8 | ||
|
|
0f0fb266c7 | ||
|
|
5c3d6298b0 | ||
|
|
028f0bdb8d | ||
|
|
44bcdc6866 | ||
|
|
b9f6f92bad | ||
|
|
1607535416 | ||
|
|
f6ced9279b | ||
|
|
95af716a96 | ||
|
|
07fe51fa25 | ||
|
|
822995cbaf | ||
|
|
db0e02c05d | ||
|
|
856dda68db | ||
|
|
163cbcb89d | ||
|
|
a79f614e12 | ||
|
|
7d3a818565 | ||
|
|
978bb47b92 | ||
|
|
b791a6a348 | ||
|
|
c21c1f5225 | ||
|
|
d6253b1dee | ||
|
|
390bb07cca | ||
|
|
309822d933 | ||
|
|
8cb612c10c | ||
|
|
7e244455c4 | ||
|
|
907fe3d8d9 | ||
|
|
6420e33fb8 | ||
|
|
0b560fdd27 | ||
|
|
04297eda80 | ||
|
|
c13fd2261e | ||
|
|
034bff5b2f | ||
|
|
987ad214ff | ||
|
|
d11ac64b95 | ||
|
|
4c8c3b6947 | ||
|
|
d47d8d22a3 | ||
|
|
0ec9defc6e | ||
|
|
4776f11b6a | ||
|
|
3118d7bede | ||
|
|
a83be187f3 | ||
|
|
deb5e435e5 | ||
|
|
9f824f3aa9 | ||
|
|
032e68da05 | ||
|
|
486661d6c6 | ||
|
|
aa86ab97f0 | ||
|
|
a17f07495a | ||
|
|
16d3440a4c | ||
|
|
d7b412c1eb | ||
|
|
e5751334d6 | ||
|
|
1fb1d7e4e9 | ||
|
|
adc20e78da | ||
|
|
d499e250e0 | ||
|
|
68166c22b3 | ||
|
|
06b2b26e39 | ||
|
|
641ca3d49d | ||
|
|
6d259e00a3 | ||
|
|
2bc5b97662 | ||
|
|
676c61aa99 | ||
|
|
eade8003ef | ||
|
|
817bbefac6 | ||
|
|
cded6206dc | ||
|
|
c287fb58bd | ||
|
|
1b97f9b6c9 | ||
|
|
14ca3fc2f3 | ||
|
|
4bc1143418 | ||
|
|
4267063dba | ||
|
|
8f8b4536b6 | ||
|
|
8121ab5163 | ||
|
|
76fab1fea8 | ||
|
|
143b235a22 | ||
|
|
3a89f2c32f | ||
|
|
7bab92042a | ||
|
|
7379b4ddd2 | ||
|
|
298181999d | ||
|
|
61e1e7fe8f | ||
|
|
b3050af1a7 | ||
|
|
275da075e0 | ||
|
|
9925e2732a | ||
|
|
59b3daabc5 | ||
|
|
f2b0f64138 | ||
|
|
5df77eb474 | ||
|
|
f202fb9af6 | ||
|
|
5b769869d0 | ||
|
|
8e266058ae | ||
|
|
7c21712e80 | ||
|
|
193fc343fe | ||
|
|
a1e9c3d270 | ||
|
|
629261c4be | ||
|
|
f6d3a6239c | ||
|
|
83c5131b67 | ||
|
|
36afef3498 | ||
|
|
52c0485b0c | ||
|
|
8c23a091da | ||
|
|
ca121f80ee | ||
|
|
b48846506f | ||
|
|
a1c72be2a9 | ||
|
|
2098368417 | ||
|
|
4014d86a57 | ||
|
|
e84e8748bd | ||
|
|
bd8166e630 | ||
|
|
03d1519b39 | ||
|
|
36c4719570 | ||
|
|
7c970771c5 | ||
|
|
3f64c042bd | ||
|
|
e336cbfb2d | ||
|
|
24eec76428 | ||
|
|
71c9b15ff1 | ||
|
|
2940f0d67c | ||
|
|
cbb1d2d3b5 | ||
|
|
36dd11a899 | ||
|
|
be88969b79 | ||
|
|
d91ad54ed9 | ||
|
|
1330228080 | ||
|
|
3ea1eca350 | ||
|
|
a4e6d8120b | ||
|
|
3219de235c | ||
|
|
4e5c2ff620 | ||
|
|
63e25f0ff9 | ||
|
|
840225b580 | ||
|
|
bd221d60d6 | ||
|
|
8a3bb50143 | ||
|
|
e4cd1a465c | ||
|
|
2173a9f246 | ||
|
|
973a838e2a | ||
|
|
d95ee55497 | ||
|
|
124e2e759c | ||
|
|
ac918e3618 | ||
|
|
009a720c32 | ||
|
|
0dbfa43dad | ||
|
|
e0b4d36a74 | ||
|
|
a441474d75 | ||
|
|
cfd3c3628e | ||
|
|
474d52f805 | ||
|
|
7ee8bdf2f3 | ||
|
|
8a9757111f | ||
|
|
65dda4a70b | ||
|
|
1ed39dbbed | ||
|
|
8162c2e4e4 | ||
|
|
a7d74f3f98 | ||
|
|
ad83ae1e7a | ||
|
|
066374906e | ||
|
|
ec79a4a6f6 | ||
|
|
9fae215db4 | ||
|
|
92b40c9485 | ||
|
|
19fc59739f | ||
|
|
7e0ae4c601 | ||
|
|
81c2f4b30b | ||
|
|
e238f7ed37 | ||
|
|
2756f3332c | ||
|
|
14b3eefbaf | ||
|
|
dc946582a4 | ||
|
|
dfa14a73a8 | ||
|
|
112aa845f4 | ||
|
|
150a309175 | ||
|
|
55c14819a3 | ||
|
|
598897caa6 | ||
|
|
cf3f8a796a | ||
|
|
bffc294b13 | ||
|
|
4cc3b7f9fb | ||
|
|
b3161dde93 | ||
|
|
5550eabac1 | ||
|
|
b2b320174b | ||
|
|
dd79348b35 | ||
|
|
bd6ce7d4da | ||
|
|
7a67670e1a | ||
|
|
539bf482b9 | ||
|
|
ed67ce7f33 | ||
|
|
d91c7e5e79 | ||
|
|
4f1dfe2ef7 | ||
|
|
36ea6c13df | ||
|
|
3acb0aac98 | ||
|
|
fdf4b3878f | ||
|
|
2fe71782a7 | ||
|
|
89dfe2b763 | ||
|
|
9b62f238ed | ||
|
|
987688f196 | ||
|
|
46cb95f16c | ||
|
|
4e1fcbb706 | ||
|
|
e4c038762b | ||
|
|
86dfa200a6 | ||
|
|
165cf980d2 | ||
|
|
13ccb16a4a | ||
|
|
f4b5426865 | ||
|
|
c2f62ba52a | ||
|
|
b2d2c56a09 | ||
|
|
abf0f5ac87 | ||
|
|
fa1965deb4 | ||
|
|
1f76dc78d8 | ||
|
|
4448884a3e | ||
|
|
e3fc23bae8 | ||
|
|
29ceed74a2 | ||
|
|
382308c3fd | ||
|
|
3d1b6e29c6 | ||
|
|
3a9a5ec669 | ||
|
|
8c37c491a9 | ||
|
|
fdf11e6038 | ||
|
|
8e558f0826 | ||
|
|
69804c23f1 | ||
|
|
9aa9a62ed4 | ||
|
|
d9b79f47c8 | ||
|
|
249bc42667 | ||
|
|
644c184f7c | ||
|
|
66cfae7b3b | ||
|
|
bd2c2acd5f | ||
|
|
13aab750dd | ||
|
|
7a51abc2f9 | ||
|
|
44a3e08095 | ||
|
|
2aa8cf7104 | ||
|
|
1b1cfe1b92 | ||
|
|
199c2cdb66 | ||
|
|
726828a487 | ||
|
|
fcbf81a3d4 | ||
|
|
7637b51ba5 | ||
|
|
3afed3b316 | ||
|
|
3d6e334007 | ||
|
|
6c848a57b6 | ||
|
|
eb12d43800 | ||
|
|
465366e644 | ||
|
|
289e9c809f | ||
|
|
8b40354786 | ||
|
|
8de8de1b1e | ||
|
|
4b76c76712 | ||
|
|
6b9a270506 | ||
|
|
da2c49ab66 | ||
|
|
af2a3f3a65 | ||
|
|
6369a900da | ||
|
|
e877247032 | ||
|
|
5bcc5ff873 | ||
|
|
a52064463e | ||
|
|
6ed7f19673 | ||
|
|
9aba0ba5a8 | ||
|
|
5803a84bd7 | ||
|
|
ce0bf0f4b4 | ||
|
|
65ed57aff4 | ||
|
|
1317b80fca | ||
|
|
f0d6145fa6 | ||
|
|
c0c157ecef | ||
|
|
4bb607f180 | ||
|
|
2eec205e31 | ||
|
|
bd8cdd345a | ||
|
|
7caf3ea7d0 | ||
|
|
ba89c60b6d | ||
|
|
084e48d6dd | ||
|
|
1bed3f3936 | ||
|
|
cd860bfbf8 | ||
|
|
439c2d445c | ||
|
|
7f71d5dbd8 | ||
|
|
831c835106 | ||
|
|
5dfb7cb938 | ||
|
|
044d6a2207 | ||
|
|
955b46534d | ||
|
|
0e8d80e055 | ||
|
|
92fc736cfa | ||
|
|
60ed43c11b | ||
|
|
319f72ae2a | ||
|
|
04dc34260f | ||
|
|
a8196d1f33 | ||
|
|
1ce6ad5ccc | ||
|
|
145e36925f | ||
|
|
c07928144c | ||
|
|
d8c30f6cbb | ||
|
|
e968c6a2a4 | ||
|
|
ffc3a31d09 | ||
|
|
d6e037dd28 | ||
|
|
83b9b3bf4a | ||
|
|
1cb89ce20d | ||
|
|
d75b916153 | ||
|
|
192b484a8c | ||
|
|
85e2137d0e | ||
|
|
c1042c8f20 | ||
|
|
c91b05bd4b | ||
|
|
f8a09df5c0 | ||
|
|
9363db816c | ||
|
|
22af4da4d4 | ||
|
|
16fa10b056 | ||
|
|
f044851abb | ||
|
|
217e99a0e2 | ||
|
|
1bc4aea217 | ||
|
|
4997934bfe | ||
|
|
4905dded87 | ||
|
|
ff6447ae2b | ||
|
|
7f51857fa5 | ||
|
|
78c3babc37 | ||
|
|
83300044dd | ||
|
|
55f891e2aa | ||
|
|
7ae40d89c1 | ||
|
|
29cc1cf390 | ||
|
|
960d9a8534 | ||
|
|
bcc8529bfc | ||
|
|
d773647a20 | ||
|
|
3a5a0837c7 | ||
|
|
44cfe6af1c | ||
|
|
cf6d445080 | ||
|
|
422f8b3660 | ||
|
|
b097938f47 | ||
|
|
c231eff4b1 | ||
|
|
1ddc96f965 | ||
|
|
13111c4b42 | ||
|
|
7c70dbce65 | ||
|
|
25559f1772 | ||
|
|
c010c83654 | ||
|
|
2057531e8c | ||
|
|
277d4d9333 | ||
|
|
051e642c0c | ||
|
|
a8778e358d | ||
|
|
d2edbfd6fa | ||
|
|
d96dbe9365 | ||
|
|
35b5dcdb22 | ||
|
|
66f3bd186f | ||
|
|
7ae38a71cc | ||
|
|
2ed356be65 | ||
|
|
99436c1334 | ||
|
|
9e57a4ea28 | ||
|
|
19e5b8cc50 | ||
|
|
33310732a6 | ||
|
|
a03bf89190 | ||
|
|
1b089ca5e6 | ||
|
|
21e23d5511 | ||
|
|
8a2c4ab3de | ||
|
|
040585bf3d | ||
|
|
9030b3e04c | ||
|
|
0b46495afd | ||
|
|
ace16d473f | ||
|
|
925c51420d | ||
|
|
764b8ab7a5 | ||
|
|
cb6a1bfb1d | ||
|
|
775b9f30f0 | ||
|
|
76fd1c5c58 | ||
|
|
3e2605490f | ||
|
|
7094588c53 | ||
|
|
3523047243 | ||
|
|
bdcbaa031d | ||
|
|
f722b3e9cb | ||
|
|
2d46cb072e | ||
|
|
28cf450bfa | ||
|
|
4aa48fb4b6 | ||
|
|
aa86593702 | ||
|
|
faa368cc07 | ||
|
|
a840ed06b7 | ||
|
|
7196bfd157 | ||
|
|
a6785e9143 | ||
|
|
4d2f26b1cd | ||
|
|
188987a8ff | ||
|
|
14d74d3230 | ||
|
|
bcd6bd6b04 | ||
|
|
8e4bd7fe4a | ||
|
|
8ab552793a | ||
|
|
29944f6bf2 | ||
|
|
162b60a05b | ||
|
|
da50d92d1e | ||
|
|
a746f5657f | ||
|
|
65ccc5bfce | ||
|
|
34939f9381 | ||
|
|
26e7821aaa | ||
|
|
298c5f0de2 | ||
|
|
a6c2b25f6f | ||
|
|
3a8c90c0d4 | ||
|
|
a25ce2296a | ||
|
|
280407a553 | ||
|
|
32c98e2161 | ||
|
|
2cbdb0bc17 | ||
|
|
4317694c64 | ||
|
|
e0879fbccb | ||
|
|
9cb8e194b0 | ||
|
|
dc914b1806 | ||
|
|
c70817b21a | ||
|
|
77918fd412 | ||
|
|
90d02234c7 | ||
|
|
b0b1c5af71 | ||
|
|
a8bd87938d | ||
|
|
10d2f0a565 | ||
|
|
c68aca4ada | ||
|
|
f46d96c4c6 | ||
|
|
e7b1ded486 | ||
|
|
719de94821 | ||
|
|
7ea0249e6e | ||
|
|
feab95ce4b | ||
|
|
ca6f755634 | ||
|
|
70b30f7849 | ||
|
|
01ab027615 | ||
|
|
11f5db871f | ||
|
|
d83fc3181b | ||
|
|
b4657a0d05 | ||
|
|
a5d6820453 | ||
|
|
7b16aa6050 | ||
|
|
c5d3c0c6f8 | ||
|
|
43c1a87c48 | ||
|
|
3755002381 | ||
|
|
dba38408c9 | ||
|
|
5b2bc23d03 | ||
|
|
a4cfdcb5c4 | ||
|
|
b6097160f1 | ||
|
|
fde1c08945 | ||
|
|
417eb56a9b | ||
|
|
0b28812f7e | ||
|
|
5ad25376bb | ||
|
|
b3ab85f3b5 | ||
|
|
11231abe8a | ||
|
|
c577706415 | ||
|
|
f1eea6a0bf | ||
|
|
8ce55f90d3 | ||
|
|
723f35ec5a | ||
|
|
025d9d3276 | ||
|
|
4f0c1d11eb | ||
|
|
1aae921ce7 | ||
|
|
2e1c508bc4 | ||
|
|
cea6ea4344 | ||
|
|
57310fdbd6 | ||
|
|
62ca6212ce | ||
|
|
d4f5871e74 | ||
|
|
a739580d3f | ||
|
|
5203565175 | ||
|
|
c91f6db68a | ||
|
|
b776b85fc3 | ||
|
|
b35e5f1582 | ||
|
|
7d5a929b5e | ||
|
|
c2e7bc13a6 | ||
|
|
97818c6f32 | ||
|
|
a8973f5463 | ||
|
|
75d790137d | ||
|
|
7ef6c72fc0 | ||
|
|
c5f8e2249e | ||
|
|
585a6c29d4 | ||
|
|
6b6df15dd9 | ||
|
|
f4de68cb22 | ||
|
|
86d5cbc355 | ||
|
|
88f9b69e2a | ||
|
|
d77c782f69 | ||
|
|
c115131ed2 | ||
|
|
178dedf78c | ||
|
|
b0c64afc6e | ||
|
|
be0c1c0912 | ||
|
|
2e8fa88fcb | ||
|
|
b1b5904852 | ||
|
|
08f029850f | ||
|
|
f3d4077142 | ||
|
|
59dd479a6d | ||
|
|
76d9f1ea37 | ||
|
|
858b497199 | ||
|
|
cee9f1df95 | ||
|
|
5bc2001ce3 | ||
|
|
652226dbf0 | ||
|
|
4688e6d534 | ||
|
|
1b0fc180c4 | ||
|
|
2524972807 | ||
|
|
8f51dc2c22 | ||
|
|
b363b50320 | ||
|
|
88a48a5c79 | ||
|
|
7be951b962 | ||
|
|
3dcc4e6bc1 | ||
|
|
573ee0b584 | ||
|
|
213629ef52 | ||
|
|
27e1579e4c | ||
|
|
f2c401b6c0 | ||
|
|
442c63d7a4 | ||
|
|
5babfb0f1e | ||
|
|
0ad3078524 | ||
|
|
f765c25020 | ||
|
|
4145251afd | ||
|
|
88c3532162 | ||
|
|
84b3ad3221 | ||
|
|
e699d3d02d | ||
|
|
9da984b866 | ||
|
|
fc08d15a79 | ||
|
|
ffaabe8674 | ||
|
|
0233ab4deb | ||
|
|
c9dc010c0b | ||
|
|
557696b1d8 | ||
|
|
9fefbb0c4a | ||
|
|
eb9ea97e21 | ||
|
|
673b7a95b7 | ||
|
|
d5f27ecb0e | ||
|
|
8f8b928cc4 | ||
|
|
965896b932 | ||
|
|
042adb5e34 | ||
|
|
67927bd8f4 | ||
|
|
259a63e612 | ||
|
|
adcf2158bf | ||
|
|
05c914156a | ||
|
|
f69884d573 | ||
|
|
d097554f7d | ||
|
|
1e2fd57c4c | ||
|
|
8b8007695c | ||
|
|
68f3c877ee | ||
|
|
ae442ee015 | ||
|
|
99b5f1b7b8 | ||
|
|
8071df0e68 | ||
|
|
88d1aab7a3 | ||
|
|
08001ba373 | ||
|
|
ebc24cee55 | ||
|
|
ae3bb30d8a | ||
|
|
63d6b23344 | ||
|
|
c009e6bd04 | ||
|
|
38d85a49e7 | ||
|
|
0edc149ecc | ||
|
|
10d6cd9896 | ||
|
|
6913da7efa | ||
|
|
34df1b1646 | ||
|
|
992603496e | ||
|
|
b9552c42f1 | ||
|
|
37e4dfc5d5 | ||
|
|
15b7284a8f | ||
|
|
b57a62fece | ||
|
|
9c7de5ad03 | ||
|
|
c065fae422 | ||
|
|
cfde1f8c27 | ||
|
|
c45f72a63e | ||
|
|
e1d9eca7bd | ||
|
|
573e5eb5bd | ||
|
|
d9090486e3 | ||
|
|
b4e7a91645 | ||
|
|
92dd68fca1 | ||
|
|
82e955ec02 | ||
|
|
2e66c4c9f5 | ||
|
|
0c6ee5e139 | ||
|
|
9a19b5994b | ||
|
|
920586f56c | ||
|
|
919aa2895a | ||
|
|
75690598e3 | ||
|
|
ac2caf2787 | ||
|
|
5640c96fd5 | ||
|
|
0396c4a4de | ||
|
|
f061fe581a | ||
|
|
5405876d84 | ||
|
|
4b9de0777b | ||
|
|
a59e073536 | ||
|
|
67492bf024 | ||
|
|
77c83c4f42 | ||
|
|
259baa0e84 | ||
|
|
dca48c7eec | ||
|
|
0d83a34cfd | ||
|
|
7386b0a523 | ||
|
|
eda13f9023 | ||
|
|
d0e9fe1e3e | ||
|
|
2b7bab04dd | ||
|
|
ad5f890a1e | ||
|
|
fa191e2928 | ||
|
|
6d8a23ec16 | ||
|
|
12371650f9 | ||
|
|
79e1d54e4c | ||
|
|
447f5f69c9 | ||
|
|
e08a26d015 | ||
|
|
975265b0af | ||
|
|
4d5e9c52b2 | ||
|
|
d1b154c285 | ||
|
|
381f6b184e | ||
|
|
59681398cb | ||
|
|
adf887a06b | ||
|
|
42f70cd55d | ||
|
|
3704a4ff47 | ||
|
|
5b8d637f6a | ||
|
|
436621f79f | ||
|
|
0ea5fbfe0a | ||
|
|
f1acd122bc | ||
|
|
739b6645f8 | ||
|
|
7a7ae4cc83 | ||
|
|
db83cbe58f | ||
|
|
87228429d6 | ||
|
|
2651723b50 | ||
|
|
b8a01d2ff1 | ||
|
|
5c20751937 | ||
|
|
06b0a50462 | ||
|
|
0d589895f6 | ||
|
|
230c2aaf26 | ||
|
|
1d8807a6ba | ||
|
|
81978b214c | ||
|
|
8704234669 | ||
|
|
5699b7bae5 | ||
|
|
2756cb8b8f | ||
|
|
e726d216bb | ||
|
|
3480824290 | ||
|
|
c8b935151a | ||
|
|
5e5aefa290 | ||
|
|
0e14b54b6d | ||
|
|
c6ddae2d8e | ||
|
|
bc0aed186e | ||
|
|
d092b21da7 | ||
|
|
a8061003dd | ||
|
|
50f0099645 | ||
|
|
c270687223 | ||
|
|
a92652f4ad | ||
|
|
9ba961fa72 | ||
|
|
006e4526e8 | ||
|
|
55dbbb3546 | ||
|
|
c166bc9b18 | ||
|
|
e4fe18e435 | ||
|
|
cea38549da | ||
|
|
0487e730ba | ||
|
|
8fdd7205d7 | ||
|
|
1d8d71cfb6 | ||
|
|
10bd017e57 | ||
|
|
f36a9c4409 | ||
|
|
70f39eb959 | ||
|
|
3a3b0cc847 | ||
|
|
01da9e3ca2 | ||
|
|
f168e4586c | ||
|
|
03ff390685 | ||
|
|
2a77486567 | ||
|
|
32a5950aad | ||
|
|
f1370189b6 | ||
|
|
65d721285b | ||
|
|
565f844b7f | ||
|
|
248992b27b | ||
|
|
3125e05b49 | ||
|
|
bdd6037726 | ||
|
|
9d292bb6a4 | ||
|
|
12b9b49902 | ||
|
|
93b8bd7f02 | ||
|
|
cd8169c0a5 | ||
|
|
b4a9d4df8c | ||
|
|
d62525abb6 | ||
|
|
a4988fd7cb | ||
|
|
d91691c344 | ||
|
|
164d3566e3 | ||
|
|
058120d001 | ||
|
|
59f292333f | ||
|
|
b7a2c11e81 | ||
|
|
3d07ddfba5 | ||
|
|
9286e4794b | ||
|
|
81276cb7f5 | ||
|
|
e270f90f8d | ||
|
|
b1fdfec18c | ||
|
|
1dfa09cda9 | ||
|
|
913438e3ff | ||
|
|
1aa939ae73 | ||
|
|
a914608264 | ||
|
|
fb59d80897 | ||
|
|
5d0852c1e2 | ||
|
|
e0e50faa47 | ||
|
|
f6721a2ced | ||
|
|
e384ec32b8 | ||
|
|
d93361939c | ||
|
|
644c0e3d33 | ||
|
|
b1333b7d99 | ||
|
|
673a2acade | ||
|
|
752e74d33c | ||
|
|
6bacf94a62 | ||
|
|
336cd60920 | ||
|
|
76c9b66db4 | ||
|
|
0c5ca28a14 | ||
|
|
db63bb4495 | ||
|
|
34afb54c21 | ||
|
|
69888e148e | ||
|
|
98a55c0613 | ||
|
|
5425e9aee3 | ||
|
|
7fef5f5654 | ||
|
|
fc94e846a6 | ||
|
|
7d7bbf15bf | ||
|
|
8a545b98ec | ||
|
|
ecdb60b44e | ||
|
|
2eea85b786 | ||
|
|
87fd0e6f29 | ||
|
|
ea191afd9d | ||
|
|
89b624308e | ||
|
|
facdf0ca9c | ||
|
|
98484d54c0 | ||
|
|
ea31ca5ee8 | ||
|
|
6b5b9b3d62 | ||
|
|
975dab6d1d | ||
|
|
eaa7adc88c | ||
|
|
f76b014a52 | ||
|
|
8676a1b4ef | ||
|
|
e1eaa2097e | ||
|
|
6f2357c695 | ||
|
|
91427264c3 | ||
|
|
74aa961561 | ||
|
|
aa47e11471 | ||
|
|
89d69a5d5a | ||
|
|
3bbe1e9c0c | ||
|
|
6377631ae7 | ||
|
|
3562ac1438 | ||
|
|
e152785de9 | ||
|
|
dd259f1852 | ||
|
|
5001cea3a3 | ||
|
|
a4d586b24e | ||
|
|
46f927fc1b | ||
|
|
7419f992e7 | ||
|
|
b83e7e6c5c | ||
|
|
5f463d5f6b | ||
|
|
2e301c2919 | ||
|
|
9526d42ec5 | ||
|
|
a566479ddb | ||
|
|
1bba0f6bb2 | ||
|
|
232d42881b | ||
|
|
abeaf76fe9 | ||
|
|
03d4584562 | ||
|
|
f2f5226ebb | ||
|
|
660860b92d | ||
|
|
c0a1a8b47c | ||
|
|
bd82e81e26 | ||
|
|
0a94df592c | ||
|
|
66506ea1ce | ||
|
|
4a4292a0dc | ||
|
|
7bff4db483 | ||
|
|
9208da8a50 | ||
|
|
70fcd93ca7 | ||
|
|
9ba9bd4415 | ||
|
|
480ce6f522 | ||
|
|
f1254fd5d4 | ||
|
|
10ebcff48e | ||
|
|
6ee227675a | ||
|
|
89059abe15 | ||
|
|
4503223a4e | ||
|
|
07c31a90f3 | ||
|
|
bbcb9af01f | ||
|
|
1cd415a3ae | ||
|
|
c344e75701 | ||
|
|
0305e4cf8a | ||
|
|
bc86b0345f | ||
|
|
8b0ce30dfc | ||
|
|
4b983300fe | ||
|
|
8829ebba6c | ||
|
|
11b90d2113 | ||
|
|
9d8d4c09c6 | ||
|
|
c90d5bb67c | ||
|
|
7263d9f03e | ||
|
|
d5e77e9bb2 | ||
|
|
1ecd5250fc | ||
|
|
44af5e04e4 | ||
|
|
4582a4fd95 | ||
|
|
2d513277f2 | ||
|
|
7934974d92 | ||
|
|
4dce35b1e6 | ||
|
|
e5f5f96771 | ||
|
|
d4a0076aba | ||
|
|
cd9cd84c5b | ||
|
|
93eca799dd | ||
|
|
34f090662a | ||
|
|
1a1d54387c | ||
|
|
9575f70f38 | ||
|
|
b4e9ed7d18 | ||
|
|
3d4e2a275c | ||
|
|
b526718846 | ||
|
|
a4883cfa15 | ||
|
|
a41f179785 | ||
|
|
bef628212e | ||
|
|
ef3030abe5 | ||
|
|
754ad20eff | ||
|
|
647175cf12 | ||
|
|
5f0a440f0a | ||
|
|
d68544038c | ||
|
|
df36b0eb7e | ||
|
|
4f4748b8df | ||
|
|
028a896303 | ||
|
|
578083df3e | ||
|
|
c5e1823f15 | ||
|
|
5f396d6311 | ||
|
|
5c64c2ff42 | ||
|
|
2dcb91b284 | ||
|
|
d708e7f682 | ||
|
|
a8a4ef82cd | ||
|
|
1286f1c968 | ||
|
|
9368a93279 | ||
|
|
143aaa2d28 | ||
|
|
b8dcdece38 | ||
|
|
be7f4c5da7 | ||
|
|
890807b8d7 | ||
|
|
8e1687e7b3 | ||
|
|
d8510ead43 | ||
|
|
c74db4b81c | ||
|
|
4ee9b4524d | ||
|
|
28cf351878 | ||
|
|
c5e2ec5e00 | ||
|
|
fe3ebc4c84 | ||
|
|
6688f9a5ef | ||
|
|
3167ae21b0 | ||
|
|
bc92586323 | ||
|
|
c40a463549 | ||
|
|
87a85fff08 | ||
|
|
b68381db58 | ||
|
|
25c1884961 | ||
|
|
9980df2c67 | ||
|
|
ed09c1171b | ||
|
|
c473b10667 | ||
|
|
c15e53e9c0 | ||
|
|
e9d3278fc5 | ||
|
|
b683c07d55 | ||
|
|
681f055b16 | ||
|
|
f4cb4c1756 | ||
|
|
1cc67bbbe8 | ||
|
|
0df0450107 | ||
|
|
cb324ca723 | ||
|
|
442a0c48e7 | ||
|
|
d97acacae6 | ||
|
|
3643d6b5d5 | ||
|
|
2edd64470b | ||
|
|
c42e2fe02d | ||
|
|
12c67b5db4 | ||
|
|
a943cc09fe | ||
|
|
1ceda52f59 | ||
|
|
04ee419951 | ||
|
|
f687728c3a | ||
|
|
bde5d27a20 | ||
|
|
07a1651fa2 | ||
|
|
32b47bee2c | ||
|
|
fbf75ea3b9 | ||
|
|
a157aba74f | ||
|
|
e45e5df377 | ||
|
|
85c7bfa160 | ||
|
|
8182f97c15 | ||
|
|
eba824f5d0 | ||
|
|
40456ebaae | ||
|
|
99983798a4 | ||
|
|
93ed032015 | ||
|
|
9359f5b296 | ||
|
|
3b467c19cb | ||
|
|
470a6f0ab2 | ||
|
|
fe8a0c1a6b | ||
|
|
f0d098d0ef | ||
|
|
f17df1f16d | ||
|
|
b1f8f9830b | ||
|
|
e78ccc6bec | ||
|
|
b54892a783 | ||
|
|
16c37a0f3d | ||
|
|
141fb78237 | ||
|
|
eb31b9a4d6 | ||
|
|
f10d9e1332 | ||
|
|
6d63521622 | ||
|
|
e1aa066489 | ||
|
|
c78ec12e99 | ||
|
|
3fa4e2f58d | ||
|
|
c64aaade70 | ||
|
|
e8d8b290a6 | ||
|
|
456d9e79e6 | ||
|
|
3095e14247 | ||
|
|
a332d68704 | ||
|
|
84ca992e91 | ||
|
|
f91f3796a8 | ||
|
|
22250ae552 | ||
|
|
8a95b5b5b0 | ||
|
|
7506619f4c | ||
|
|
577d9ddf65 | ||
|
|
43c3bdf7c5 | ||
|
|
8ba142eb45 | ||
|
|
0fc4e01b1e | ||
|
|
f83ebbcd3a | ||
|
|
77ec4b5cad | ||
|
|
9a687976bc | ||
|
|
5a796a86d7 | ||
|
|
71d4221af2 | ||
|
|
526ba37435 | ||
|
|
f3c080f8a4 | ||
|
|
09a0cf07e4 | ||
|
|
4a3bf46c30 | ||
|
|
30dfe12910 | ||
|
|
ae2b5dfd3e | ||
|
|
cb0f968467 | ||
|
|
e8e3db6888 | ||
|
|
012ade5000 | ||
|
|
5350078543 | ||
|
|
404715e02d | ||
|
|
31dde394eb | ||
|
|
8ff2627e8e | ||
|
|
f79ad91a9a | ||
|
|
ff6a79bca3 | ||
|
|
e4d6092939 | ||
|
|
9d998d27c5 | ||
|
|
d6aca6fa00 | ||
|
|
7c34c45983 | ||
|
|
dd15472da7 | ||
|
|
b03712a30e | ||
|
|
d025ba2793 | ||
|
|
e5e09c9b51 | ||
|
|
5b8d1df349 | ||
|
|
fa092c0162 | ||
|
|
08c1359a27 | ||
|
|
dba355eccd | ||
|
|
2ad927b677 | ||
|
|
315f672254 | ||
|
|
7a51407f6d | ||
|
|
783c2b6b03 | ||
|
|
a64e1b2aa6 | ||
|
|
440516e95f | ||
|
|
31f6d13cd8 | ||
|
|
dc6108575c | ||
|
|
6c7316408b | ||
|
|
9aecc69461 | ||
|
|
949be436a6 | ||
|
|
cb91891f22 | ||
|
|
8795f0c8c4 | ||
|
|
fbb5bb2f05 | ||
|
|
ba309fe6e5 | ||
|
|
fee5f959fd | ||
|
|
f9a5f4955c | ||
|
|
325b362727 | ||
|
|
75065f29f7 | ||
|
|
ed874fe3ea | ||
|
|
502e6b0ce5 | ||
|
|
516380f979 | ||
|
|
6885761f87 | ||
|
|
a4762fe65c | ||
|
|
bee407ea34 | ||
|
|
db71673722 | ||
|
|
9ecbbf09cc | ||
|
|
b6b14f4957 | ||
|
|
6e0d6dcac5 | ||
|
|
47a0ebdc91 | ||
|
|
f3a61007a7 | ||
|
|
4a56d6bf1c | ||
|
|
517d4dc6f5 | ||
|
|
722f1c4430 | ||
|
|
f4d1b87f73 | ||
|
|
f64f875806 | ||
|
|
7ae09fa1fe | ||
|
|
8a29dfc3fa | ||
|
|
1015188c4e | ||
|
|
f0bc2a3645 | ||
|
|
82f46464f3 | ||
|
|
d336d920e8 | ||
|
|
2f61dd1c41 | ||
|
|
10ffd5c1ab | ||
|
|
7e99be12b0 | ||
|
|
d37a790b57 | ||
|
|
7ea8509dfe | ||
|
|
783c0c7c7b | ||
|
|
68b0775e4b | ||
|
|
682334d844 | ||
|
|
75981491a7 | ||
|
|
7cc805b203 | ||
|
|
8cdd3a0abb | ||
|
|
571c630d93 | ||
|
|
fa1021df59 | ||
|
|
9acbb2203c | ||
|
|
c770bcbf96 | ||
|
|
caace05ba6 | ||
|
|
c65dc44f20 | ||
|
|
3ea624e1db | ||
|
|
c9c58074fa | ||
|
|
aa687afd37 | ||
|
|
8cb69c1482 | ||
|
|
a68326490d | ||
|
|
ab763c38d9 | ||
|
|
970557660e | ||
|
|
fa8548fe34 | ||
|
|
ac88c1a8f1 | ||
|
|
f2893097a7 | ||
|
|
c0cba7b376 | ||
|
|
87d1058de3 | ||
|
|
85e65da492 | ||
|
|
ce97fa87e7 | ||
|
|
10ffdb2766 | ||
|
|
fec49e5609 | ||
|
|
28fdd992c9 | ||
|
|
048d3c8386 | ||
|
|
50e3d6ff37 | ||
|
|
37b80f0ce3 | ||
|
|
7d37b02cff | ||
|
|
c6556b8442 | ||
|
|
5685c376cb | ||
|
|
2ce64e1bf5 | ||
|
|
7d03a41e3e | ||
|
|
35b68db847 | ||
|
|
0b21fce94e | ||
|
|
abaf36a2de | ||
|
|
26440d94f1 | ||
|
|
205b61e4cf | ||
|
|
fc5fc5bbee | ||
|
|
c4171a01bd | ||
|
|
32669cb07f | ||
|
|
7018c381ee | ||
|
|
b02677ee21 | ||
|
|
63edc60753 | ||
|
|
065d01bcf6 | ||
|
|
42b15e8bbe | ||
|
|
e8195b78ba | ||
|
|
1d7d7cf9a0 | ||
|
|
979575c311 | ||
|
|
be12739342 | ||
|
|
7f7acd8bde | ||
|
|
f5e2899275 | ||
|
|
bee34a3222 | ||
|
|
5b00cb1e64 | ||
|
|
6bb9de5a96 | ||
|
|
b977050caf | ||
|
|
3f63732c31 | ||
|
|
211660eb3d | ||
|
|
0c709f431f | ||
|
|
9062bf14b6 | ||
|
|
47ebb6ae6c | ||
|
|
b1e3f88704 | ||
|
|
bc439cc47f | ||
|
|
1bba5d5c94 | ||
|
|
d159d49700 | ||
|
|
7ef7ef03dd | ||
|
|
808b758cd7 | ||
|
|
ff6d66b96e | ||
|
|
da82b14307 | ||
|
|
7b5e18d94b | ||
|
|
72974c85c8 | ||
|
|
28627a81dc | ||
|
|
bbfe6b66ef | ||
|
|
bce0e3ebf6 | ||
|
|
bf46c241d0 | ||
|
|
287e32aaed | ||
|
|
aa11a5deb8 | ||
|
|
194d63acd8 | ||
|
|
46d640cd86 | ||
|
|
51783a45e6 | ||
|
|
2679c58892 | ||
|
|
2a5af37075 | ||
|
|
e529d3ecc9 | ||
|
|
e8f9ecc7d9 | ||
|
|
aa3723d2bd | ||
|
|
bbbda44218 | ||
|
|
f99aea5cb1 | ||
|
|
65c003eef8 | ||
|
|
959843ee9c | ||
|
|
c16632d99a | ||
|
|
3d066ea1b8 | ||
|
|
e163730118 | ||
|
|
3c8838af08 | ||
|
|
ac5394a1dc | ||
|
|
2e74d91ddc | ||
|
|
2d82c4ada4 | ||
|
|
03f0ca965e | ||
|
|
a527dcd95b | ||
|
|
de29abb05c | ||
|
|
cb7efcb188 | ||
|
|
bf4f22b203 | ||
|
|
7f3a467a66 | ||
|
|
72ef621f9d | ||
|
|
73452f758c | ||
|
|
049e1b2679 | ||
|
|
4631123231 | ||
|
|
c86bcb4dd6 | ||
|
|
a6280661ee | ||
|
|
ca7709a284 | ||
|
|
384c06f2e9 | ||
|
|
774c11781d | ||
|
|
7a692898e4 | ||
|
|
2f1971ea8f | ||
|
|
ce13de7d6c | ||
|
|
d51ad77ab4 | ||
|
|
a9b289626e | ||
|
|
8a542f2ce8 | ||
|
|
14a2c9d48f | ||
|
|
37fef7e4f8 | ||
|
|
b83ab85fd9 | ||
|
|
d424e1e9ff | ||
|
|
aaa52bd767 | ||
|
|
56254e728c | ||
|
|
284159aadc | ||
|
|
29593f0161 | ||
|
|
deca217544 | ||
|
|
c09212de81 | ||
|
|
db8d93d308 | ||
|
|
4c96106666 | ||
|
|
8e849ea6f8 | ||
|
|
82d80d2ead | ||
|
|
a5da55d0f7 | ||
|
|
702e6c8080 | ||
|
|
46e957ab7e | ||
|
|
c27f8a5c1e | ||
|
|
8e835f2f6b | ||
|
|
35e8a027ad | ||
|
|
94642f9066 | ||
|
|
2dd5de4373 | ||
|
|
793e80490c | ||
|
|
727a1f4ddd | ||
|
|
6a752a56ff | ||
|
|
32466e3804 | ||
|
|
a530503c0c | ||
|
|
7ba4af7e2e | ||
|
|
c9c05ad2a8 | ||
|
|
ab1df3a1d0 | ||
|
|
56a60772a4 | ||
|
|
63e6731207 | ||
|
|
8b53ded53a | ||
|
|
d5075d706c | ||
|
|
03927b0a68 | ||
|
|
0d88b8012b | ||
|
|
a583c21136 | ||
|
|
b8ec63cf8c | ||
|
|
4b9afdf53a | ||
|
|
788d1650a2 | ||
|
|
8f58886a21 | ||
|
|
94b3bb2391 | ||
|
|
c8f5fb4d03 | ||
|
|
070a21a9eb | ||
|
|
5698ff9c4c | ||
|
|
47b562b032 | ||
|
|
69234db848 | ||
|
|
ebc132ea65 | ||
|
|
0899eeddc0 | ||
|
|
cf8ff2cf86 | ||
|
|
ee9dc789af | ||
|
|
570598f556 | ||
|
|
e8c3546433 | ||
|
|
1062776762 | ||
|
|
584379b502 | ||
|
|
87e1c45c05 | ||
|
|
9447afe49c | ||
|
|
fc5b1ae3e2 | ||
|
|
13735d0475 | ||
|
|
6b3a783ce9 | ||
|
|
3b66bba92e | ||
|
|
a2e01f8a53 | ||
|
|
34da9a9655 | ||
|
|
df8d73ae43 | ||
|
|
aa3a93b6a0 | ||
|
|
17bfa35f77 | ||
|
|
59797a5c9a | ||
|
|
61fe2923e4 | ||
|
|
50b9eca34c | ||
|
|
f5684eba90 | ||
|
|
f32510e10a | ||
|
|
4fb0eeda37 | ||
|
|
b1aeae6772 | ||
|
|
5a6bd38d22 | ||
|
|
97da8e2f2e | ||
|
|
10be150503 | ||
|
|
36aa248556 | ||
|
|
183c22cc84 | ||
|
|
63f4cf3d07 | ||
|
|
eeeae12639 | ||
|
|
0d854c6ea6 | ||
|
|
1e1c4d159b | ||
|
|
4dc9f6948d | ||
|
|
c06e739c9b | ||
|
|
061720bcf0 | ||
|
|
11585327bf | ||
|
|
2793eeb10a | ||
|
|
8fb093b272 | ||
|
|
c90c008f65 | ||
|
|
3d1a7f173c | ||
|
|
ba078f3ff5 | ||
|
|
6d54401d7c | ||
|
|
c9a0897208 | ||
|
|
6a1049bfb7 | ||
|
|
5995ab3f4c | ||
|
|
a4112ebed2 | ||
|
|
289cae4213 | ||
|
|
eaac21cda1 | ||
|
|
3b9b827ebf | ||
|
|
c908beade2 | ||
|
|
abcf030181 | ||
|
|
22a16da09e | ||
|
|
0e31da5e51 | ||
|
|
61e6a03d70 | ||
|
|
3f2119556f | ||
|
|
77493d0d09 | ||
|
|
340c73cbdf | ||
|
|
ea7f4447b2 | ||
|
|
19f3c75a8d | ||
|
|
25ba08abcf | ||
|
|
26be0c7c82 | ||
|
|
b7c5e3b5d5 | ||
|
|
701653a6bd | ||
|
|
2c099c7f0e | ||
|
|
fba53117d8 | ||
|
|
e1bf53d90a | ||
|
|
ecc82739d8 | ||
|
|
d16afa9692 | ||
|
|
9f3ce09e88 | ||
|
|
cbfb1edb79 | ||
|
|
b6c336bf72 | ||
|
|
403e34506e | ||
|
|
83db868542 | ||
|
|
1b6e673b50 | ||
|
|
a0144f093f | ||
|
|
a5d84bf8a9 | ||
|
|
84bb740e62 | ||
|
|
d37482ada1 | ||
|
|
75fc8202ab | ||
|
|
24aff15752 | ||
|
|
338b9928f0 | ||
|
|
b9607b4b8e | ||
|
|
586f241074 | ||
|
|
e298987d9e | ||
|
|
71cc4b5bf2 | ||
|
|
4fdd9feddc | ||
|
|
caf7da1053 | ||
|
|
7d04ba0fc3 | ||
|
|
d98d091c43 | ||
|
|
3ad196c4c7 | ||
|
|
562f320198 | ||
|
|
3e5581e094 | ||
|
|
c4721e1020 | ||
|
|
728f2670f3 | ||
|
|
8dd157d2eb | ||
|
|
2d40d69fa2 | ||
|
|
f2f0d69bce | ||
|
|
812f5045b0 | ||
|
|
9a8e7b11e5 | ||
|
|
e213e695c8 | ||
|
|
e24eea313c | ||
|
|
a3286ebac3 | ||
|
|
95ae23a32c | ||
|
|
d240f3242c | ||
|
|
814f60a512 | ||
|
|
fac6229e43 | ||
|
|
c528d739c8 | ||
|
|
c664be52d7 | ||
|
|
4ac4f44ba7 | ||
|
|
174430e3b5 | ||
|
|
762b21f809 | ||
|
|
9340bf385e | ||
|
|
9f5be52a97 | ||
|
|
9dc5a4fce3 | ||
|
|
fda3cd5fe7 | ||
|
|
6b8469e9a3 | ||
|
|
7dbbe5a7d8 | ||
|
|
2d6fdeb7ad | ||
|
|
4d3a01a5fe | ||
|
|
8cc6756815 | ||
|
|
5967ab75b1 | ||
|
|
69c954760a | ||
|
|
40a4c3ccbd | ||
|
|
aacb9d9570 | ||
|
|
9b6c229b71 | ||
|
|
1da5be2871 | ||
|
|
66dafca61a | ||
|
|
1f22b5b083 | ||
|
|
5be0b7a731 | ||
|
|
b64b5d9103 | ||
|
|
953d78da9e | ||
|
|
ce9e0981a2 | ||
|
|
76e1114a1f | ||
|
|
cfc80b491f | ||
|
|
43ed05d3c2 | ||
|
|
4cf5ce871f | ||
|
|
91ec08df4e | ||
|
|
c79363ef63 | ||
|
|
cc6672198a | ||
|
|
17cdf7c79d | ||
|
|
725f939f35 | ||
|
|
e2df00bb2e | ||
|
|
a9a33c6179 | ||
|
|
8e7eb87a2d | ||
|
|
217004e7a5 | ||
|
|
4d10848984 | ||
|
|
da2c04f681 | ||
|
|
8deb327b3b | ||
|
|
642b01bf0d | ||
|
|
9fd78b1eb1 | ||
|
|
66c09fc44c | ||
|
|
2cb5e1a6c2 | ||
|
|
02ac638bd4 | ||
|
|
323f74c43a | ||
|
|
1e56d17d39 | ||
|
|
16af8e082b | ||
|
|
9215a54c23 | ||
|
|
fab34d3dbb | ||
|
|
03d7330af5 | ||
|
|
346b0c9d68 | ||
|
|
c29359e7a8 | ||
|
|
4b903931bc | ||
|
|
4e0929e71a | ||
|
|
1bad097a13 | ||
|
|
331065eec6 | ||
|
|
f51ba499d5 | ||
|
|
f62ccc2d48 | ||
|
|
cf485aa62e | ||
|
|
6b16a48568 | ||
|
|
ff7cf503ae | ||
|
|
2412a0d502 | ||
|
|
b1612bb1ed | ||
|
|
b8a205f755 | ||
|
|
eab08ea78c | ||
|
|
d7653769b4 | ||
|
|
c72d9695da | ||
|
|
0493f00a7a | ||
|
|
ff757ddc88 | ||
|
|
c4c495948a | ||
|
|
986ee6bac3 | ||
|
|
6ab7e79987 | ||
|
|
c6e35876fa | ||
|
|
eb31accf20 | ||
|
|
ea1ba0f09b | ||
|
|
10911f5b64 | ||
|
|
0eab8e9322 | ||
|
|
fb2602716e | ||
|
|
be68906ae9 | ||
|
|
c53d6d80ba | ||
|
|
1362177352 | ||
|
|
28ab1230e2 | ||
|
|
0784934520 | ||
|
|
79fbf9d47f | ||
|
|
dabdc8e4c5 | ||
|
|
466ad192b0 | ||
|
|
ac59d9e1b6 | ||
|
|
d44245e1e9 | ||
|
|
c50105493a | ||
|
|
2768a62f92 | ||
|
|
5841d0d87d | ||
|
|
2a796051bf | ||
|
|
50286fd173 | ||
|
|
44f0bad2a6 | ||
|
|
611b9c4fd1 | ||
|
|
3f50776062 | ||
|
|
c70d2ad6fa | ||
|
|
9dd0bd604c | ||
|
|
0565519509 | ||
|
|
c282d95be1 | ||
|
|
766286b8bc | ||
|
|
f405c62f1e | ||
|
|
93fbd7b3ba | ||
|
|
ae5cea7f36 | ||
|
|
8b7b6cfbc5 | ||
|
|
f6e988d6fd | ||
|
|
f88f68f248 | ||
|
|
14f2b24b16 | ||
|
|
f2dde98e2f | ||
|
|
90d8ec0e81 | ||
|
|
14cdb531c8 | ||
|
|
3a50320f79 | ||
|
|
597b5e6cfb | ||
|
|
2cd056cfb3 | ||
|
|
be6aab4c40 | ||
|
|
d030df925f | ||
|
|
50756eb94a | ||
|
|
21b3576b66 | ||
|
|
2e5226356b | ||
|
|
21b76d3d2b | ||
|
|
2d252e6459 | ||
|
|
43be363542 | ||
|
|
8cb3e3418a | ||
|
|
cd47ddd539 | ||
|
|
2be1c10522 | ||
|
|
7f26670173 | ||
|
|
d838ce85c3 | ||
|
|
6350f5e6e8 | ||
|
|
10f3690ede | ||
|
|
06daa8bb0e | ||
|
|
34a90f442e | ||
|
|
afe81dcdbe | ||
|
|
24d616672b | ||
|
|
9cfc61cd45 | ||
|
|
487df84b90 | ||
|
|
614c1306f6 | ||
|
|
07dca9bd16 | ||
|
|
1ebcbd5b0e | ||
|
|
32644ddada | ||
|
|
cf3bab996e | ||
|
|
926ffe2581 | ||
|
|
fff3587d99 | ||
|
|
866cf940da | ||
|
|
7868e1527e | ||
|
|
556bfb752a | ||
|
|
a3b08654b4 | ||
|
|
4cf4436169 | ||
|
|
e2acc55819 | ||
|
|
6b29d6b8dc | ||
|
|
b668c4c302 | ||
|
|
646778227a | ||
|
|
a973630cb4 | ||
|
|
4e7375c09c | ||
|
|
881d0652e7 | ||
|
|
0c46993baa | ||
|
|
09b15f4940 | ||
|
|
ebf7be56bb | ||
|
|
727068cc4b | ||
|
|
6f77c6f3f4 | ||
|
|
c5e3e17eae | ||
|
|
effdb70417 | ||
|
|
e28f910c88 | ||
|
|
c84468dbed | ||
|
|
4b9e39ac64 | ||
|
|
6b0c05ee7c | ||
|
|
5c6ec70126 | ||
|
|
2757ef94c9 | ||
|
|
9ba7120011 | ||
|
|
124f9ec44e | ||
|
|
0d229175cd | ||
|
|
10638b6e40 | ||
|
|
134baad56d | ||
|
|
9a5984d750 | ||
|
|
073f42e64a | ||
|
|
096927beed | ||
|
|
4bc76995d1 | ||
|
|
9f41151156 | ||
|
|
6b3bd755b0 | ||
|
|
7e580e6a0b | ||
|
|
047c8eda22 | ||
|
|
f22e5c209c | ||
|
|
8cc3a08871 | ||
|
|
35f6c6cb98 | ||
|
|
814b174f25 | ||
|
|
5fbaf0bc7d | ||
|
|
ba772ab481 | ||
|
|
0a4888a18f | ||
|
|
fedbf2cc44 | ||
|
|
0f68bbac8e | ||
|
|
13e965096b | ||
|
|
92961bb7bf | ||
|
|
340686ba06 | ||
|
|
d8906f508c | ||
|
|
dde53ea4ba | ||
|
|
225ed5b662 | ||
|
|
02857cf2b5 | ||
|
|
118a771980 | ||
|
|
359123564f | ||
|
|
49d2878938 | ||
|
|
f08ea4a9a3 | ||
|
|
f2bc71bc2a | ||
|
|
2b1e40c6c6 | ||
|
|
105c2deeb3 | ||
|
|
c3dbbc9144 | ||
|
|
ca55a9a8a6 | ||
|
|
7ea5af448e | ||
|
|
2f6898142e | ||
|
|
02e0b8cc32 | ||
|
|
b962a4c69b | ||
|
|
ed3e83df67 | ||
|
|
b07bff61f0 | ||
|
|
a7c955055c | ||
|
|
13f33a9d19 | ||
|
|
be3fa6091d | ||
|
|
8ffddf06e4 | ||
|
|
9b7f583b2b | ||
|
|
9fe4f3adea | ||
|
|
74d9f89c09 | ||
|
|
63c36e917e | ||
|
|
184c6ee252 | ||
|
|
1c024afc1b | ||
|
|
51519361e2 | ||
|
|
bd092295a4 | ||
|
|
153c275d74 | ||
|
|
db0dfa1bf1 | ||
|
|
debd13d8c3 | ||
|
|
4b8466e5e5 | ||
|
|
9d9793e1af | ||
|
|
46fafebade | ||
|
|
1dae3d951a | ||
|
|
fb213a1efd | ||
|
|
93deb37c94 | ||
|
|
004a93a841 | ||
|
|
da5be9f01d | ||
|
|
344d0ae3ec | ||
|
|
3639c86adf | ||
|
|
4fc80fd366 | ||
|
|
336ab2d82a | ||
|
|
74a7e67002 | ||
|
|
f76c04b7a6 | ||
|
|
8130487b18 | ||
|
|
83790a5a73 | ||
|
|
1b35f68de9 | ||
|
|
3e912c6198 | ||
|
|
f995595202 | ||
|
|
6264569ca0 | ||
|
|
e868d427dd | ||
|
|
b9cbdb2dc4 | ||
|
|
7ae563867c | ||
|
|
f2f760bda4 | ||
|
|
ed561ad86b | ||
|
|
675861c323 | ||
|
|
ba330a42d6 | ||
|
|
2a9727c6b7 | ||
|
|
5be147e8cc | ||
|
|
eb96edbd31 | ||
|
|
14c85fa975 | ||
|
|
b0e3339370 | ||
|
|
70e502e55d | ||
|
|
ff38a3bbfe | ||
|
|
b5723a6c18 | ||
|
|
89012cd73b | ||
|
|
756e86662b | ||
|
|
27ca3b4b01 | ||
|
|
a5be4c9d0e | ||
|
|
07cc547bab | ||
|
|
f672af9706 | ||
|
|
58b058ab3a | ||
|
|
28c2ca8bf8 | ||
|
|
d7a06dc7a9 | ||
|
|
8970e6a48a | ||
|
|
b8eef181b9 | ||
|
|
fb94d6ae2b | ||
|
|
fa68e392c8 | ||
|
|
9eaa51442f | ||
|
|
68482d712b | ||
|
|
09fc767bb0 | ||
|
|
ea7e6615f2 | ||
|
|
a183ca8661 | ||
|
|
05939a2bbc | ||
|
|
543a372435 | ||
|
|
e795de5562 | ||
|
|
ae6877ce2f | ||
|
|
a0f3e81b11 | ||
|
|
88f52c4902 | ||
|
|
bf8db7725f | ||
|
|
f4d8c3304a | ||
|
|
44556b7f5e | ||
|
|
2e1e95d483 | ||
|
|
ccc24337be | ||
|
|
b15b38868d | ||
|
|
8feca6874a | ||
|
|
ecd3a49d48 | ||
|
|
6de7cd5063 | ||
|
|
f6d7f7d984 | ||
|
|
d5d501875e | ||
|
|
88561c22d3 | ||
|
|
b786576bcb | ||
|
|
21b5f2c96a | ||
|
|
d8f24b442b | ||
|
|
a1c81a63dd | ||
|
|
4d2b535b04 | ||
|
|
3cfbc05bf9 | ||
|
|
895820f14c | ||
|
|
37fc21f3cf | ||
|
|
5e068c3af5 | ||
|
|
e2c192d254 | ||
|
|
e481ed37ce | ||
|
|
4d7c089b09 | ||
|
|
a4dc67cba0 | ||
|
|
03973cc6d4 | ||
|
|
66c301c031 | ||
|
|
e4edc59689 | ||
|
|
f2d9d38c6f | ||
|
|
667ea43b3c | ||
|
|
f3856819fe | ||
|
|
d6bfe7810a | ||
|
|
7a52ae18f1 | ||
|
|
444539b826 | ||
|
|
c8d6425123 | ||
|
|
e50c35d38c | ||
|
|
aa764fbd1c | ||
|
|
2628426084 | ||
|
|
aa6bc8042a | ||
|
|
26a6c9e932 | ||
|
|
0f9376e959 | ||
|
|
ace3e86546 | ||
|
|
d79c6b8f06 | ||
|
|
6538a2e673 | ||
|
|
153d883aeb | ||
|
|
689432f627 | ||
|
|
cd237219e4 | ||
|
|
8589493581 | ||
|
|
ca2e148ad7 | ||
|
|
f7ca44cad8 | ||
|
|
1b2ac38a50 | ||
|
|
f62d25fa5f | ||
|
|
025eec1782 | ||
|
|
846ff46b2e | ||
|
|
f9718bccb9 | ||
|
|
f66f4ffee6 | ||
|
|
a47417ff49 | ||
|
|
c9836cf0f7 | ||
|
|
289b679e3c | ||
|
|
23e019ec83 | ||
|
|
eeffcea69e | ||
|
|
ae10793d0f | ||
|
|
a062bca431 | ||
|
|
6a453bcc8a | ||
|
|
5a2c4919c6 | ||
|
|
09a80ed654 | ||
|
|
72e954b78f | ||
|
|
a1c27aed6a | ||
|
|
0b3a719a95 | ||
|
|
6c9b4a8c5d | ||
|
|
d9babda1b8 | ||
|
|
ea8e1be294 | ||
|
|
44eccd85fd | ||
|
|
a62720b9d8 | ||
|
|
1a9422c3f9 | ||
|
|
5e52b3609c | ||
|
|
8622385e88 | ||
|
|
d0ffaab339 | ||
|
|
347157b999 | ||
|
|
a9f3235fd3 | ||
|
|
4098a5c08e | ||
|
|
dba7a2ee4f | ||
|
|
a5f49550b3 | ||
|
|
5c9a69e0e8 | ||
|
|
2bf32fb3fa | ||
|
|
0de1e2c6fc | ||
|
|
61868d97c4 | ||
|
|
c994c11d8c | ||
|
|
5ad10955be | ||
|
|
95f100f378 | ||
|
|
3d6c93cd6b | ||
|
|
fc25da37c5 | ||
|
|
896bb2187e | ||
|
|
99398bf0da | ||
|
|
827a54435d | ||
|
|
3c9459e489 | ||
|
|
9291f5c9c6 | ||
|
|
0ab5f993c7 | ||
|
|
4f8db487e7 | ||
|
|
0e1765e045 | ||
|
|
ebc411bbbd | ||
|
|
a76d8f0f9f | ||
|
|
f245feb0b0 | ||
|
|
43a90d7b98 | ||
|
|
2e1a9a8df9 | ||
|
|
57bb0da1d6 | ||
|
|
0d2df22074 | ||
|
|
c7173d5e1c | ||
|
|
789eb48698 | ||
|
|
e686fad546 | ||
|
|
4e4f9b6f8b | ||
|
|
f2292fd618 | ||
|
|
7035ead9e7 | ||
|
|
f01f6e94d1 | ||
|
|
f10064ce39 | ||
|
|
b68f06ca83 | ||
|
|
2ce61402bb | ||
|
|
70e9d85a75 | ||
|
|
a461f462d2 | ||
|
|
50ff0d251a | ||
|
|
f6103d3841 | ||
|
|
cb68d19bed | ||
|
|
89d2505a7c | ||
|
|
9ddfc750e5 | ||
|
|
3b80de1747 | ||
|
|
90ea714e48 | ||
|
|
f9e4182624 | ||
|
|
0291cc2ef4 | ||
|
|
caf2e469a6 | ||
|
|
45da2843ee | ||
|
|
8353f928a1 | ||
|
|
9c1a6d042e | ||
|
|
448b25a8b2 | ||
|
|
4c2d4009da | ||
|
|
67f1e07508 | ||
|
|
c49fdf1233 | ||
|
|
7c835bae20 | ||
|
|
ae81cc2644 | ||
|
|
3907b4101a | ||
|
|
aa5ea0e3a1 | ||
|
|
d21043802e | ||
|
|
995bdb3f9e | ||
|
|
8363b4fda7 | ||
|
|
23979f4ce6 | ||
|
|
28b5f39b84 | ||
|
|
47ce2398a4 | ||
|
|
0a83d8e6a0 | ||
|
|
aa215f2a5a | ||
|
|
b03a6a5327 | ||
|
|
ca36a6fe41 | ||
|
|
bd6285c8b1 | ||
|
|
00cfdc7d92 | ||
|
|
5e2dc14dd5 | ||
|
|
c5f2890cbe | ||
|
|
36aaca997a | ||
|
|
e9f7c61113 | ||
|
|
2373b94d3e | ||
|
|
f131e31949 | ||
|
|
8fd55a210a | ||
|
|
678650beaf | ||
|
|
e09386be44 | ||
|
|
75db2867dc | ||
|
|
80e37df012 | ||
|
|
1f404bb622 | ||
|
|
54078087e5 | ||
|
|
23b8df1c36 | ||
|
|
65395516b0 | ||
|
|
849308e28d | ||
|
|
4d98a64000 | ||
|
|
0c8fdfca7d | ||
|
|
fd928e8d12 | ||
|
|
2a1fe99a29 | ||
|
|
4fa4ba6301 | ||
|
|
48b3959cfb | ||
|
|
9bbff744e9 | ||
|
|
19b0c266f9 | ||
|
|
fead940d10 | ||
|
|
687e17ac52 | ||
|
|
b1c85dcb74 | ||
|
|
a15aad9f9c | ||
|
|
06a1a8690d | ||
|
|
42b9b6426a | ||
|
|
332f0118a2 | ||
|
|
6ed709d6e6 | ||
|
|
7a461c1684 | ||
|
|
7cf171671d | ||
|
|
ebee94fb11 | ||
|
|
61e8becd38 | ||
|
|
a78caa2976 | ||
|
|
c54f7c81c4 | ||
|
|
85840872ab | ||
|
|
d582c30f6e | ||
|
|
392f5f914a | ||
|
|
799d25925a | ||
|
|
4431d50635 | ||
|
|
e120e9a78e | ||
|
|
b6e379d14e | ||
|
|
9a86034162 | ||
|
|
8456c8b47b | ||
|
|
bb656ce44b | ||
|
|
3c2a3898e1 | ||
|
|
c265bd6c4d | ||
|
|
aff65083cc | ||
|
|
aff8cd478c | ||
|
|
bce2a63772 | ||
|
|
3f9d2601b4 | ||
|
|
04bfd52fba | ||
|
|
87dd890eb0 | ||
|
|
a5c0b48b57 | ||
|
|
5d38693b4d | ||
|
|
a4773d259d | ||
|
|
f9b6b1bf76 | ||
|
|
ef106f3232 | ||
|
|
c0b0df34d2 | ||
|
|
d15cc7cc47 | ||
|
|
6336d38a3e | ||
|
|
6a9d2ba653 | ||
|
|
34a8d4a57d | ||
|
|
ffc666eaaa | ||
|
|
c45aab7cef | ||
|
|
eefff148e9 | ||
|
|
05f7578928 | ||
|
|
2ebb2d8f0e | ||
|
|
afe2935c9d | ||
|
|
380c7b7720 | ||
|
|
8657226594 | ||
|
|
41da48f5ff | ||
|
|
405aa906c5 | ||
|
|
1c507a47d2 | ||
|
|
f48a7df80f | ||
|
|
5f73f09836 | ||
|
|
f63dd75f08 | ||
|
|
cc55335a8d | ||
|
|
b5875f3a0a | ||
|
|
cb8333a48f | ||
|
|
f412f4ca88 | ||
|
|
941f30d1ea | ||
|
|
97afa502c5 | ||
|
|
9ae9ea18e1 | ||
|
|
0bf2abaa4c | ||
|
|
924f281536 | ||
|
|
1fc5dacd87 | ||
|
|
5c877de2c2 | ||
|
|
751b95d4af | ||
|
|
e0d5ba9915 | ||
|
|
b8a6946661 | ||
|
|
e5fac08d1d | ||
|
|
df5b7c7d0d | ||
|
|
27649f7d4c | ||
|
|
350dea6228 | ||
|
|
aef6b7712c | ||
|
|
642bcfcdea | ||
|
|
e625d8aabc | ||
|
|
5888ecbdcd | ||
|
|
e2a76056b8 | ||
|
|
a98498eb06 | ||
|
|
8366c8d2a7 | ||
|
|
ed8d441a02 | ||
|
|
f1fb265119 | ||
|
|
6c628094ce | ||
|
|
a60c52e2f0 | ||
|
|
ac2e1709f8 | ||
|
|
db88183a23 | ||
|
|
c7d55ad858 | ||
|
|
06a4e6c323 | ||
|
|
d1de89f387 | ||
|
|
bbba01da92 | ||
|
|
25dbf62274 | ||
|
|
ed6851863b | ||
|
|
ba924e295e | ||
|
|
0828065a62 | ||
|
|
68c789dceb | ||
|
|
6424084502 | ||
|
|
4abea18afe | ||
|
|
0a3c4f131e | ||
|
|
f5e1077e20 | ||
|
|
44d1c3fd2f | ||
|
|
e345161763 | ||
|
|
64d7c87591 | ||
|
|
1fae3baaa3 | ||
|
|
38103aaac5 | ||
|
|
cc25b22f11 | ||
|
|
e6dbeda18e | ||
|
|
8437d45866 | ||
|
|
0bb89de821 | ||
|
|
905cad56d8 | ||
|
|
65eeb70eb3 | ||
|
|
266744f640 | ||
|
|
23d6739580 | ||
|
|
5c9970c786 | ||
|
|
3eae716a2d | ||
|
|
c57b13d922 | ||
|
|
17fb419fb1 | ||
|
|
598d0e216a | ||
|
|
7bbe926232 | ||
|
|
2e848a7c9a | ||
|
|
437225b43e | ||
|
|
d39229713f | ||
|
|
93911be1b9 | ||
|
|
b74055478c | ||
|
|
8614c4db73 | ||
|
|
215d39fc54 | ||
|
|
c4e5a130ee | ||
|
|
630072b574 | ||
|
|
5261a3e845 | ||
|
|
0096a91a57 | ||
|
|
56699a9f89 | ||
|
|
31ff1372ae | ||
|
|
3afb1922bb | ||
|
|
83c0a8b047 | ||
|
|
6699bd47b5 | ||
|
|
34223b8d4f | ||
|
|
5befe1f019 | ||
|
|
87f86e72f4 | ||
|
|
53b7eba31a | ||
|
|
12c12a8ad1 | ||
|
|
897cc7d355 | ||
|
|
2e5c56205c | ||
|
|
bc5ff37e37 | ||
|
|
20341a381f | ||
|
|
926b945846 | ||
|
|
aa877a73ba | ||
|
|
b28208d1bf | ||
|
|
9bd97383bd | ||
|
|
522c7b2f9d | ||
|
|
1833c0acbc | ||
|
|
c5644ee3f9 | ||
|
|
447566fe14 | ||
|
|
9692c34f6c | ||
|
|
37c450f1e1 | ||
|
|
a003e396c5 | ||
|
|
996f61efe1 | ||
|
|
40cdcf8b06 | ||
|
|
5947364846 | ||
|
|
9470107bba | ||
|
|
54b945511b | ||
|
|
acfaa0041e | ||
|
|
aeed2dbc3e | ||
|
|
0c6befe8a5 | ||
|
|
bdcb26edae | ||
|
|
e091667b42 | ||
|
|
d35b14f4cc | ||
|
|
87996c6811 | ||
|
|
a880c733c8 | ||
|
|
ca10dfeb5f | ||
|
|
91f55a637b | ||
|
|
8ae43cfd14 | ||
|
|
83d9513c4a | ||
|
|
1c76d43e44 | ||
|
|
1036ce0fa5 | ||
|
|
3dbab68f17 | ||
|
|
5896cebeaa | ||
|
|
fbe629154d | ||
|
|
364136213b | ||
|
|
136b663cef | ||
|
|
803f11bebb | ||
|
|
7c8036807a | ||
|
|
84ccca0e98 | ||
|
|
74efdb95e8 | ||
|
|
10e45ac493 | ||
|
|
60befdb36e | ||
|
|
59f99ea9bb | ||
|
|
1a894abcff | ||
|
|
4934fc8809 | ||
|
|
18cc6a184f | ||
|
|
0a08765d73 | ||
|
|
355c7437ed | ||
|
|
3c55c2d777 | ||
|
|
94806ad0b3 | ||
|
|
6840259734 | ||
|
|
a1fc48f2a6 | ||
|
|
400e3d21f9 | ||
|
|
8f3daad502 | ||
|
|
b0395933de | ||
|
|
f8f2ab9cba | ||
|
|
ae5f5375da | ||
|
|
ab5f1e712b | ||
|
|
4532ca97fa | ||
|
|
5a9ef57f78 | ||
|
|
8791f382b3 | ||
|
|
abdef67ccc | ||
|
|
33494c4f4b | ||
|
|
daad975f5d | ||
|
|
18c00f0a4b | ||
|
|
e7f46b4fbe | ||
|
|
74827cd8cf | ||
|
|
5ffe1893cd | ||
|
|
f24618e8df | ||
|
|
0e5b32ef13 | ||
|
|
0493a321d2 | ||
|
|
38b6c12153 | ||
|
|
74d4b8e0b9 | ||
|
|
f843d34234 | ||
|
|
95b2bf3645 | ||
|
|
121ac4f1de | ||
|
|
ec8550d587 | ||
|
|
e403c419e5 | ||
|
|
4b0d587fe1 | ||
|
|
ebd356c7bd | ||
|
|
507093dbad | ||
|
|
4cfdc77015 | ||
|
|
9096cacba8 | ||
|
|
607336d3ce | ||
|
|
6383fc3575 | ||
|
|
a5576ddbf3 | ||
|
|
e2a70873b8 | ||
|
|
23c7340afe | ||
|
|
380b56a89d | ||
|
|
8e09f3478f | ||
|
|
c1ce51eb12 | ||
|
|
9aeb773169 | ||
|
|
091c13ff41 | ||
|
|
ef0bab0c6e | ||
|
|
70bd16adf6 | ||
|
|
96a713afeb | ||
|
|
bf3615fb32 | ||
|
|
0f56b1c943 | ||
|
|
d541572882 | ||
|
|
ecfdc377ec | ||
|
|
fa67e90767 | ||
|
|
81b72d5481 | ||
|
|
ef6028e933 | ||
|
|
5d41fe4a35 | ||
|
|
1dc6cec1aa | ||
|
|
9378668e52 | ||
|
|
eb96ead80e | ||
|
|
9403fbaf81 | ||
|
|
79190f313d | ||
|
|
4c124284b6 | ||
|
|
6d892179c8 | ||
|
|
61675c20d8 | ||
|
|
4aae878db8 | ||
|
|
918884bd11 | ||
|
|
8799f9079b | ||
|
|
0b471cfd06 | ||
|
|
7b39a12396 | ||
|
|
57a53b4b6c | ||
|
|
f6d0b3368f | ||
|
|
0fe7bdf849 | ||
|
|
a26dc39a6d | ||
|
|
e45cfe7d0c | ||
|
|
efefa8caf5 | ||
|
|
cc13db9b1f | ||
|
|
f339544256 | ||
|
|
1a05bcb295 | ||
|
|
190e26276a | ||
|
|
bb33760e87 | ||
|
|
9e105b4983 | ||
|
|
8dcf70408d | ||
|
|
9d6d1825c7 | ||
|
|
1a4923cdce | ||
|
|
316e440390 | ||
|
|
7d66019220 | ||
|
|
f98a6fb665 | ||
|
|
dbdc7279c4 | ||
|
|
7726705b5c | ||
|
|
34b7e8815a | ||
|
|
8ac2b58a44 | ||
|
|
fe97f0929b | ||
|
|
6eec353c2b | ||
|
|
2b4c3b8d1f | ||
|
|
df99b37c4d | ||
|
|
ab6f3fcf8e | ||
|
|
ca6f656e1b | ||
|
|
88798b1a9e | ||
|
|
c197270125 | ||
|
|
dc344d4658 | ||
|
|
b4864831e0 | ||
|
|
476dffff13 | ||
|
|
389ee974f3 | ||
|
|
0d15eceacb | ||
|
|
b69fbdda9a | ||
|
|
d3746e0119 | ||
|
|
230af9cafa | ||
|
|
4db63d113c | ||
|
|
008583396d | ||
|
|
33a33e3c71 | ||
|
|
d312d753e9 | ||
|
|
02310d4af6 | ||
|
|
0e6d8c4e25 | ||
|
|
55315fca80 | ||
|
|
4eef9e780f | ||
|
|
7bfc3562af | ||
|
|
5b0b0d6d36 | ||
|
|
cb64072f7b | ||
|
|
c5b6da7201 | ||
|
|
f1d4818045 | ||
|
|
76b49f6985 | ||
|
|
094d9193b9 | ||
|
|
3053a9b6a0 | ||
|
|
47bf0ef591 | ||
|
|
e2aa2709ac | ||
|
|
9a6d478eb1 | ||
|
|
4f37e7dc3c | ||
|
|
2a4ba8d349 | ||
|
|
85bd7a63c6 | ||
|
|
138d57143a | ||
|
|
464a228106 | ||
|
|
2b92a039bb | ||
|
|
f190ee951c | ||
|
|
68cc75cada | ||
|
|
b4e324ec0e | ||
|
|
32fe2e7974 | ||
|
|
713513aacc | ||
|
|
b4ffca56a3 | ||
|
|
f2168774a5 | ||
|
|
febc00d357 | ||
|
|
01a8c507e5 | ||
|
|
bf7982cc2e | ||
|
|
2e9689886b | ||
|
|
2003b34036 | ||
|
|
e1995b5c70 | ||
|
|
3890acabc4 | ||
|
|
ba6c0d0423 | ||
|
|
882e7a845e | ||
|
|
ca56d3fc23 | ||
|
|
49b1e76585 | ||
|
|
80f81685d1 | ||
|
|
21dead3125 | ||
|
|
1521d08285 | ||
|
|
59b2e31add | ||
|
|
b5feb3fd66 | ||
|
|
7785e6ebd2 | ||
|
|
c561d71dc0 | ||
|
|
2cfb697867 | ||
|
|
c680ff006e | ||
|
|
333103f50e | ||
|
|
517385fb63 | ||
|
|
ee8ab58d64 | ||
|
|
b967acda58 | ||
|
|
d81ca5f919 | ||
|
|
07adf64aec | ||
|
|
fbb98e1aec | ||
|
|
2fdf927704 | ||
|
|
4b84656133 | ||
|
|
97c136d043 | ||
|
|
79bf44b3f5 | ||
|
|
ddd8d4aeb2 | ||
|
|
bfcb6f577f | ||
|
|
2b137b43e6 | ||
|
|
6d74493491 | ||
|
|
6f4271c054 | ||
|
|
f24054100e | ||
|
|
6e98649607 | ||
|
|
b2108ff2d0 | ||
|
|
8949ebf041 | ||
|
|
576801cd32 | ||
|
|
2f2b12811f | ||
|
|
d8ea3a9035 | ||
|
|
45c3b3987b | ||
|
|
93720fffd4 | ||
|
|
61ad6a2b88 | ||
|
|
c9d5b3c0ff | ||
|
|
d51bf735c4 | ||
|
|
22c388ab18 | ||
|
|
d5f831301f | ||
|
|
dcab37a148 | ||
|
|
60b2da3671 | ||
|
|
5c1b5816d4 | ||
|
|
7a0a45e9d2 | ||
|
|
70f72a78f6 | ||
|
|
e056c9c135 | ||
|
|
c754b5ae18 | ||
|
|
481fafc11d | ||
|
|
7d927b0e28 | ||
|
|
c314b07136 | ||
|
|
16fe13bf4a | ||
|
|
d19eda7e08 | ||
|
|
6f0a136727 | ||
|
|
e2e101e4fb | ||
|
|
74f03202b7 | ||
|
|
3d19e92059 | ||
|
|
bfff125cc5 | ||
|
|
e90baf3ca6 | ||
|
|
f3b277aeef | ||
|
|
76096747b6 | ||
|
|
4c6ef32d72 | ||
|
|
a8e12e624d | ||
|
|
88a43bfc28 | ||
|
|
3b268fe3cc | ||
|
|
4c72d43a8a | ||
|
|
0a5f8527b2 | ||
|
|
9f1b84d6f2 | ||
|
|
babcbcbcea | ||
|
|
823a6017fe | ||
|
|
f034aef2ae | ||
|
|
bf38bd5a1d | ||
|
|
b922809c9d | ||
|
|
05b0bda8bb | ||
|
|
0aa3aa1b8d | ||
|
|
d4febb4e84 | ||
|
|
21090eaa39 | ||
|
|
d0ea59c568 | ||
|
|
a292bc77ba | ||
|
|
98d5e0b56d | ||
|
|
7ca1cfab1a | ||
|
|
2e7ce38552 | ||
|
|
0ef3a2472d | ||
|
|
b97f095de4 | ||
|
|
10e2b35483 | ||
|
|
16920a89f3 | ||
|
|
1a5b9de82e | ||
|
|
4ef183fee6 | ||
|
|
2115ce6606 | ||
|
|
61d1b733f7 | ||
|
|
4978edb8be | ||
|
|
51f7aba807 | ||
|
|
b9b143e4e7 | ||
|
|
0e7596a205 | ||
|
|
8c401cf01b | ||
|
|
6782e6a532 | ||
|
|
4386bd93c3 | ||
|
|
72b3c10ebd | ||
|
|
62cec2a31c | ||
|
|
0c442622af | ||
|
|
bf3c4bc588 | ||
|
|
d98dd83369 | ||
|
|
21ecf309bb | ||
|
|
4bb4012d87 | ||
|
|
10fd8eb709 | ||
|
|
b1cc1db967 | ||
|
|
77d8bae2c2 | ||
|
|
7274d43645 | ||
|
|
3eeee1b08d | ||
|
|
64b2a32c9a | ||
|
|
4ced1e5075 | ||
|
|
8de15c9d0d | ||
|
|
31d716bd0c | ||
|
|
3da6b3930b | ||
|
|
900fc1cb46 | ||
|
|
deb87f1d4c | ||
|
|
ed44d23afb | ||
|
|
8baf7f3f6a | ||
|
|
d2d4fa29e4 | ||
|
|
0c56cd63bd | ||
|
|
c9cf84f2f4 | ||
|
|
0966369723 | ||
|
|
4f6c3d52b3 | ||
|
|
97f8ab5c51 | ||
|
|
8805f1e4d6 | ||
|
|
3ae57e0ca9 | ||
|
|
a8e4301f23 | ||
|
|
68bc78d00b | ||
|
|
1dc9e74df4 | ||
|
|
a69cee03e5 | ||
|
|
bf15ad3bba | ||
|
|
bb3f50f967 | ||
|
|
1042e19845 | ||
|
|
85830d5076 | ||
|
|
c053bebccd | ||
|
|
d6d6ae8af2 | ||
|
|
6d8b0e3a5d | ||
|
|
cfd7f1571b | ||
|
|
f31c04d92a | ||
|
|
89b58ec3af | ||
|
|
ab0d66c2ef | ||
|
|
9774865d4a | ||
|
|
3817a0c2a1 | ||
|
|
5215bdc035 | ||
|
|
8061d306dd | ||
|
|
30f68759ff | ||
|
|
3f0b595085 | ||
|
|
0c9ce6258c | ||
|
|
7da17ba21e | ||
|
|
7b23d79dc2 | ||
|
|
415314a90d | ||
|
|
0f7e2ad11a | ||
|
|
26d232c567 | ||
|
|
efa48a7e39 | ||
|
|
022642f4d5 | ||
|
|
e6e2f04a10 | ||
|
|
f7e21dbe5c | ||
|
|
f593802a51 | ||
|
|
f545e6eb27 | ||
|
|
1778d82bc3 | ||
|
|
03587d7035 | ||
|
|
6663788612 | ||
|
|
ac2cb773df | ||
|
|
b70b3ec85b | ||
|
|
1e69b8c41d | ||
|
|
d5aa1a4880 | ||
|
|
de0658eaab | ||
|
|
939c28b74b | ||
|
|
c10d628a45 | ||
|
|
92830172f9 | ||
|
|
431af2c0dd | ||
|
|
97ca8b7ada | ||
|
|
f3a7c233b3 | ||
|
|
928abf7094 | ||
|
|
2cace0008e | ||
|
|
db9c20f3dd | ||
|
|
e1a1aef990 | ||
|
|
23cf6ebc89 | ||
|
|
55c279cc7e | ||
|
|
4e89f90c4f | ||
|
|
bd0eb81c1b | ||
|
|
a77a0d98e0 | ||
|
|
e5037fc9f9 | ||
|
|
7ac2022159 | ||
|
|
bc41a15eba | ||
|
|
8aa158c1e0 | ||
|
|
1f6f4d9c49 | ||
|
|
3686a27c19 | ||
|
|
1bcc311738 | ||
|
|
2335d3879e | ||
|
|
209934ad67 | ||
|
|
35200a1ee5 | ||
|
|
6c4977ee78 | ||
|
|
5482a57c45 | ||
|
|
18914978d5 | ||
|
|
36750ab900 | ||
|
|
c5f6a690de | ||
|
|
9611f80a39 | ||
|
|
eb2d68fc28 | ||
|
|
937d346676 | ||
|
|
7565843fbe | ||
|
|
6740ec464c | ||
|
|
314e1e4bfe | ||
|
|
45d68d89a9 | ||
|
|
1d5194a138 | ||
|
|
05043f30dc | ||
|
|
cd549937c5 | ||
|
|
efdea07b7b | ||
|
|
06d4998d87 | ||
|
|
02b566055e | ||
|
|
c312dbaac1 | ||
|
|
b6dcb2f4c0 | ||
|
|
a85d3f2573 | ||
|
|
0ca3fb5af0 | ||
|
|
2a4d78d9bf | ||
|
|
d9e199092d | ||
|
|
02bbb46d2e | ||
|
|
13ffdc6dd2 | ||
|
|
c8c2c4d376 | ||
|
|
01f7343781 | ||
|
|
3acc244692 | ||
|
|
094068e4ff | ||
|
|
ec958697e2 | ||
|
|
208e8f8247 | ||
|
|
3d4890a28b | ||
|
|
fe4362f459 | ||
|
|
81d3ad2d35 | ||
|
|
ffb8c3e53c | ||
|
|
2d4d2374e3 | ||
|
|
09f31a9278 | ||
|
|
80b0a3cdec | ||
|
|
c533bfc83d | ||
|
|
8fa053f7c7 | ||
|
|
b152bb26e3 | ||
|
|
a0816b04e5 | ||
|
|
0819517902 | ||
|
|
55ea8c82e9 | ||
|
|
ffbbf88de4 | ||
|
|
e2ff49825f | ||
|
|
7f325827c4 | ||
|
|
cae9ccfda1 | ||
|
|
248ae7d4d5 | ||
|
|
7f08bbe938 | ||
|
|
81b2f2114d | ||
|
|
5eee430be3 | ||
|
|
623edf3bc9 | ||
|
|
bd4a224051 | ||
|
|
870e84a700 | ||
|
|
8d4fae24ea | ||
|
|
7a84daf3f7 | ||
|
|
7968279bc2 | ||
|
|
258be40285 | ||
|
|
b2ae30eba1 | ||
|
|
daaba1dbc0 | ||
|
|
a3c6ed4dd2 | ||
|
|
e4255ed712 | ||
|
|
5d510f1cf4 | ||
|
|
1819bd910a | ||
|
|
43eecdbb3f | ||
|
|
108c1bcac4 | ||
|
|
4b7e5864d4 | ||
|
|
fb1d2abbfa | ||
|
|
0c290e65ef | ||
|
|
5487fad2ce | ||
|
|
d41f930f69 | ||
|
|
595b2619fd | ||
|
|
26d305d866 | ||
|
|
c9d95ff161 | ||
|
|
9cc592b564 | ||
|
|
ff48422ec0 | ||
|
|
a26c5f85c3 | ||
|
|
727436e1cf | ||
|
|
d1c57a1872 | ||
|
|
b7c021af8c | ||
|
|
7149b509d7 | ||
|
|
45e7111dda | ||
|
|
9fc69db9eb | ||
|
|
2ba314d9d9 | ||
|
|
f35660c8e2 | ||
|
|
68b1fe8631 | ||
|
|
4242c86d40 | ||
|
|
ef4dc3cbc9 | ||
|
|
8daa7561fa | ||
|
|
2cc3dfc2ce | ||
|
|
459800568a | ||
|
|
3a35b84b03 | ||
|
|
79cfa52bf9 | ||
|
|
a0e8fe5848 | ||
|
|
2dae5bccb2 | ||
|
|
8e867ab0c0 | ||
|
|
1b2c88fe38 | ||
|
|
f3bee5ff3f | ||
|
|
196d7e8f72 | ||
|
|
16596c18fb | ||
|
|
7ea3a87bfc | ||
|
|
a57905b6cd | ||
|
|
f9c592ca22 | ||
|
|
aecac0ef85 | ||
|
|
ca315c51a0 | ||
|
|
45c8858140 | ||
|
|
06e45bff24 | ||
|
|
2635a658d0 | ||
|
|
f48a98f691 | ||
|
|
3badda95c1 | ||
|
|
364ccc05d5 | ||
|
|
d09fedf208 | ||
|
|
7936f8730f | ||
|
|
6c0dfc4356 | ||
|
|
d9af8c31a2 | ||
|
|
ca375314f0 | ||
|
|
5266d4d79c | ||
|
|
1cb0826de0 | ||
|
|
89e3178ea3 | ||
|
|
3b5d9d6cee | ||
|
|
ce4ed19029 | ||
|
|
01a502339c | ||
|
|
642d0e6f74 | ||
|
|
d9e659deb0 | ||
|
|
830fe7f9b8 | ||
|
|
3e8c247c05 | ||
|
|
16880074fa | ||
|
|
19c74ce9fa | ||
|
|
56ef0dad9c | ||
|
|
8d99808821 | ||
|
|
1cb08fdecc | ||
|
|
e8952d7e02 | ||
|
|
18fad9c9d9 | ||
|
|
89a0a94f3e | ||
|
|
0859cf30f8 | ||
|
|
a0fe02a560 | ||
|
|
3156f7dacd | ||
|
|
c3958bf042 | ||
|
|
facc5f8aa7 | ||
|
|
8170257c26 | ||
|
|
489e37b2a1 | ||
|
|
4899e0d2d5 | ||
|
|
762f9c4b23 | ||
|
|
6d3dac0ec1 | ||
|
|
f684815272 | ||
|
|
8e04218c95 | ||
|
|
23cb45454b | ||
|
|
7fc9a161b1 | ||
|
|
95a5473051 | ||
|
|
66ceb573dc | ||
|
|
5f8223ebb5 | ||
|
|
51146d4152 | ||
|
|
3334281949 | ||
|
|
8e85d9ac00 | ||
|
|
e1c69a6250 | ||
|
|
edd9a18257 | ||
|
|
65f993677f | ||
|
|
bc775140bb | ||
|
|
4b2bd6e18f | ||
|
|
c36a810bcb | ||
|
|
a994bbc36b | ||
|
|
c3238f4d0b | ||
|
|
632d26e398 | ||
|
|
214cc8b810 | ||
|
|
8f218141f4 | ||
|
|
3676304751 | ||
|
|
c605fd57aa | ||
|
|
4599f6919c | ||
|
|
8ad20c0db3 | ||
|
|
638a69e5f0 | ||
|
|
9fa6b1ebe1 | ||
|
|
5930e2d221 | ||
|
|
fdd96975fb | ||
|
|
de6dd77046 | ||
|
|
1b6ad8413e | ||
|
|
6096d572f3 | ||
|
|
badcd64b62 | ||
|
|
a7b8b52dbd | ||
|
|
d89f0f51df | ||
|
|
be358f3f2e | ||
|
|
f122da1485 | ||
|
|
0dda4728b6 | ||
|
|
45fd95e02b | ||
|
|
91aa2d7f6f | ||
|
|
a96b7d2a80 | ||
|
|
8f9cea54c5 | ||
|
|
045558bede | ||
|
|
58124ebaab | ||
|
|
0c87dd5624 | ||
|
|
b87f986a49 | ||
|
|
c6a6035bb9 | ||
|
|
1ef12f0645 | ||
|
|
ef3ec33ba3 | ||
|
|
c82ef1ee8f | ||
|
|
23b8a60242 | ||
|
|
ac9511165e | ||
|
|
9d70851eb9 | ||
|
|
759dfb28ce | ||
|
|
ff356b1f21 | ||
|
|
b2a6c1bc68 | ||
|
|
76549d0a4a | ||
|
|
e5c72cae83 | ||
|
|
bf47df46c9 | ||
|
|
0ef42870e5 | ||
|
|
da8a6a4c2b | ||
|
|
988007a8c9 | ||
|
|
710439e83c | ||
|
|
80a0a3d4fb | ||
|
|
43299aea10 | ||
|
|
f5aea766a7 | ||
|
|
c5308e3f2f | ||
|
|
2b8e662f81 | ||
|
|
0a6d849435 | ||
|
|
a0106fe5d8 | ||
|
|
cee1b8a64a | ||
|
|
4e2ba71d59 | ||
|
|
fb2bdfb9ee | ||
|
|
72785f6740 | ||
|
|
a94a05fac9 | ||
|
|
430368de97 | ||
|
|
7bfb499549 | ||
|
|
9bc477e1b6 | ||
|
|
f84ac18472 | ||
|
|
cd515a2e54 | ||
|
|
c73c8fdc47 | ||
|
|
e755a32b23 | ||
|
|
d4d1768575 | ||
|
|
0a5745c559 | ||
|
|
b24959205b | ||
|
|
d69f297c05 | ||
|
|
3c8e331809 | ||
|
|
d169471e8c | ||
|
|
56453f6b5c | ||
|
|
dac2e8c79e | ||
|
|
ccc96bc610 | ||
|
|
654371cb6a | ||
|
|
1af8d873bb | ||
|
|
b7a0e23309 | ||
|
|
4a0f868941 | ||
|
|
448073cdd6 | ||
|
|
ad79ec7b1f | ||
|
|
e194854c6d | ||
|
|
d01d033209 | ||
|
|
06c4aca490 | ||
|
|
885d57138a | ||
|
|
9e2a770a26 | ||
|
|
942b699bb9 | ||
|
|
c9d03a8094 | ||
|
|
d015538bb4 | ||
|
|
90d6c5c5bb | ||
|
|
387ce4b6fa | ||
|
|
7943b13891 | ||
|
|
50a7cd19b4 | ||
|
|
53e9335bb0 | ||
|
|
e5cb70972e | ||
|
|
0d84871037 | ||
|
|
1d37745c0c | ||
|
|
ad9ade7849 | ||
|
|
c1e2ee32b4 | ||
|
|
1588d2734c | ||
|
|
50dda4263f | ||
|
|
a8f2239495 | ||
|
|
c42636b0ee | ||
|
|
54b2c8bd7e | ||
|
|
d01a21a867 | ||
|
|
5d43052c05 | ||
|
|
4109ab1590 | ||
|
|
f6eabd695b | ||
|
|
24d9dacfd9 | ||
|
|
66d51a9eb1 | ||
|
|
302df75d83 | ||
|
|
11b7e637e9 | ||
|
|
135c92bd85 | ||
|
|
c15c26a233 | ||
|
|
5d94760cce | ||
|
|
79517a0ba3 | ||
|
|
64295e3541 | ||
|
|
cc2816aaf5 | ||
|
|
4a2fcb9deb | ||
|
|
7f27580f1b | ||
|
|
94d0915004 | ||
|
|
88db99e593 | ||
|
|
593b25a5cd | ||
|
|
5c58bf44c0 | ||
|
|
73ae6cf164 | ||
|
|
7749319c75 | ||
|
|
73037b86ac | ||
|
|
d50ba1259c | ||
|
|
962261fee7 | ||
|
|
4dea2ef1a4 | ||
|
|
aa12eb4ed4 | ||
|
|
8a75363784 | ||
|
|
01dd982587 | ||
|
|
62cf83921b | ||
|
|
73d4025256 | ||
|
|
3405ffd8d8 | ||
|
|
e03f1597a0 | ||
|
|
c5644e0e32 | ||
|
|
bf14b7da9a | ||
|
|
0c8fb376db | ||
|
|
17acdcc4d5 | ||
|
|
654357f5ce | ||
|
|
fbebdd3055 | ||
|
|
83e76c6b53 | ||
|
|
adf12b6084 | ||
|
|
047c6a93a3 | ||
|
|
bf4c33325c | ||
|
|
be1a4548e6 | ||
|
|
d8cd2afd12 | ||
|
|
6ff3f8df87 | ||
|
|
95c4a87ccc | ||
|
|
206f094dd4 | ||
|
|
a05a20440e | ||
|
|
ff12421d60 | ||
|
|
2cbd6e85c6 | ||
|
|
1fc50a59f5 | ||
|
|
9c9401ce2f | ||
|
|
f732a84a7c | ||
|
|
efe7e469ce | ||
|
|
ed136c9d8b | ||
|
|
60e2722a21 | ||
|
|
4fab07b4da | ||
|
|
d07c68bd9a | ||
|
|
2738169a9d | ||
|
|
490b65dfe2 | ||
|
|
38ebe28923 | ||
|
|
4ed7e29896 | ||
|
|
122b8c2a84 | ||
|
|
98c91a01e3 | ||
|
|
a7cd16c159 | ||
|
|
5ca86b87f5 | ||
|
|
25a163cdeb | ||
|
|
3a63f6775a | ||
|
|
d65257c7b0 | ||
|
|
465945f8a8 | ||
|
|
a0de60e179 | ||
|
|
b48682012d | ||
|
|
e624cb31bd | ||
|
|
20e43951e5 | ||
|
|
576802a1d6 | ||
|
|
23a3d48611 | ||
|
|
b6ec0a3526 | ||
|
|
ef6a038451 | ||
|
|
0354685e35 | ||
|
|
ba2b792916 | ||
|
|
44768e92ad | ||
|
|
0e8bdf8299 | ||
|
|
09298d7457 | ||
|
|
e8d80e16ba | ||
|
|
e461982a31 | ||
|
|
c896f6d0d7 | ||
|
|
9a9b38a8c3 | ||
|
|
b26b52cca8 | ||
|
|
b5ee997da9 | ||
|
|
046ffd8648 | ||
|
|
d7e7823606 | ||
|
|
2d3493a225 | ||
|
|
a3b08c0016 | ||
|
|
d9c0f52846 | ||
|
|
a96482b186 | ||
|
|
10e78785cd | ||
|
|
da56397b39 | ||
|
|
abc05b4485 | ||
|
|
09fd0baf78 | ||
|
|
d7deb938c5 | ||
|
|
68834df271 | ||
|
|
8a3c276e66 | ||
|
|
6a043649f5 | ||
|
|
019af7bd3a | ||
|
|
4f2f67d5b1 | ||
|
|
2a59ae294d | ||
|
|
6d586bde6c | ||
|
|
9510bba3b0 | ||
|
|
eb559f7b6a | ||
|
|
64dbd9abdf | ||
|
|
dfd41385b1 | ||
|
|
2b797fcd54 | ||
|
|
5cd557ef9d | ||
|
|
8baab2de37 | ||
|
|
c266cff956 | ||
|
|
53affa3303 | ||
|
|
ec772c5d46 | ||
|
|
7b5a7e10a9 | ||
|
|
188f1fcff8 | ||
|
|
39c346df10 | ||
|
|
490e829083 | ||
|
|
846128a791 | ||
|
|
6bad2daa62 | ||
|
|
4c91d08cea | ||
|
|
2442d0e910 | ||
|
|
7c13194d5a | ||
|
|
0ae7bbd34d | ||
|
|
0b2654f6b1 | ||
|
|
42d49bde86 | ||
|
|
d2b4a6fd50 | ||
|
|
7f172964f6 | ||
|
|
b8b8d70c7f | ||
|
|
969695f318 | ||
|
|
7ec701a816 | ||
|
|
c96b81206d | ||
|
|
5f8356741e | ||
|
|
3987d5e5a0 | ||
|
|
fcb56db224 | ||
|
|
873754c6ca | ||
|
|
12465f840a | ||
|
|
e8c9d2db10 | ||
|
|
a8b4f38865 | ||
|
|
27bd193708 | ||
|
|
c56ddce2f6 | ||
|
|
5d2f9f9f0b | ||
|
|
864aba9f4e | ||
|
|
4d27399ce3 | ||
|
|
76c54ffdef | ||
|
|
c873e9dd68 | ||
|
|
ce99357ebe | ||
|
|
562de3514a | ||
|
|
128a8f3b48 | ||
|
|
1839b85d97 | ||
|
|
f0f154cd10 | ||
|
|
624bff3036 | ||
|
|
1d2950b4a7 | ||
|
|
9072a018dd | ||
|
|
2a997d94bf | ||
|
|
2741e94a72 | ||
|
|
7c660ee556 | ||
|
|
51b850aa85 | ||
|
|
b29e94005d | ||
|
|
ddd506fde7 | ||
|
|
20310cb109 | ||
|
|
11177d37ea | ||
|
|
da006a1d6e | ||
|
|
451b0382ea | ||
|
|
950f250d66 | ||
|
|
01913d2b14 | ||
|
|
e0b19a6383 | ||
|
|
48289845df | ||
|
|
454f2dabbd | ||
|
|
8891d9aa4d | ||
|
|
49d59fc116 | ||
|
|
8c92c50f9a | ||
|
|
75d45ae988 | ||
|
|
d5e1d5db9c | ||
|
|
9ce9d9b7fc | ||
|
|
e9edc7b205 | ||
|
|
56822d9424 | ||
|
|
2c480bee9a | ||
|
|
3983838694 | ||
|
|
1e74ff8a85 | ||
|
|
8c47bf9dd3 | ||
|
|
3a26383c4d | ||
|
|
634976cdde | ||
|
|
bc21f5955f | ||
|
|
e72eb35cc2 | ||
|
|
fbe4e64e44 | ||
|
|
be301dc090 | ||
|
|
250af7f247 | ||
|
|
10577cd1e5 | ||
|
|
62593f60c5 | ||
|
|
89ed8c2173 | ||
|
|
321dd252ea | ||
|
|
19325d552a | ||
|
|
43f8ec46cc | ||
|
|
cb9f78540a | ||
|
|
0a15088572 | ||
|
|
ec68338a20 | ||
|
|
173b35f77d | ||
|
|
f47831c339 | ||
|
|
e7d4f63884 | ||
|
|
4e81083bb4 | ||
|
|
0446a5ae73 | ||
|
|
a2aec4340d | ||
|
|
fda68c114c | ||
|
|
de5c55160b | ||
|
|
d0ac6345c2 | ||
|
|
d9911f4314 | ||
|
|
92bd29ebf1 | ||
|
|
00ac1f7ec9 | ||
|
|
12641ab0c0 | ||
|
|
629b5ff171 | ||
|
|
6ec5858561 | ||
|
|
ab7550947a | ||
|
|
f2078f6d5b | ||
|
|
1a440e3a83 | ||
|
|
b8acce115f | ||
|
|
577ba9b397 | ||
|
|
5f199432f0 | ||
|
|
8b10fc497d | ||
|
|
e80d09aa17 | ||
|
|
4c1e12921a | ||
|
|
1824a9a38e | ||
|
|
3e59535a53 | ||
|
|
8c174cd548 | ||
|
|
2a4d1c978e | ||
|
|
cc91a6d96f | ||
|
|
96ace89789 | ||
|
|
d226477852 | ||
|
|
eddb5fa91e | ||
|
|
c92e00c21f | ||
|
|
f07d29bc8a | ||
|
|
f7b4cbc94a | ||
|
|
d226a4a0b7 | ||
|
|
c62659cdbc | ||
|
|
4831e9705c | ||
|
|
fce74a710f | ||
|
|
39641f05b9 | ||
|
|
2a23537dbd | ||
|
|
ca3b9f253d | ||
|
|
1a0abfbc06 | ||
|
|
6e9149afd4 | ||
|
|
0a7bf4db47 | ||
|
|
8a790fd0b3 | ||
|
|
fd3dab35cc | ||
|
|
5fd179524f | ||
|
|
f8a7beb001 | ||
|
|
02903cc98c | ||
|
|
7c5993b1cd | ||
|
|
a6f78134c0 | ||
|
|
21d4f49d31 | ||
|
|
3f4bd13091 | ||
|
|
6125288e95 | ||
|
|
7f91c9e63e | ||
|
|
ec319f950f | ||
|
|
217ddfe98d | ||
|
|
57578a3aa3 | ||
|
|
9a7e21778e | ||
|
|
cdb42b33ce | ||
|
|
4b47bfb5db | ||
|
|
18deb8b4f2 | ||
|
|
e58ebb8656 | ||
|
|
05d7b79d07 | ||
|
|
d5c43e66fa | ||
|
|
5f3b17af64 | ||
|
|
7bec2b8417 | ||
|
|
70396240c3 | ||
|
|
ac79d8ed3e | ||
|
|
02de91f7f2 | ||
|
|
77fd296095 | ||
|
|
1cc3dd5ee4 | ||
|
|
41ad5d12a3 | ||
|
|
b26c960450 | ||
|
|
7ea12ddd61 | ||
|
|
4e1e47d9e5 | ||
|
|
3ccc8d9508 | ||
|
|
123e6b7de4 | ||
|
|
1caacaacf0 | ||
|
|
0b754ec65d | ||
|
|
647bb501d1 | ||
|
|
47c3d5ed23 | ||
|
|
b693d13144 | ||
|
|
a5ada6f487 | ||
|
|
60351f2677 | ||
|
|
1e2f038ef5 | ||
|
|
708e17162c | ||
|
|
60c60b4db1 | ||
|
|
a09c67772c | ||
|
|
028e3a6c35 | ||
|
|
a72d7652af | ||
|
|
76ad7f24ee | ||
|
|
1ac689b886 | ||
|
|
f32399454c | ||
|
|
f2665262f7 | ||
|
|
fee68cf333 | ||
|
|
1d737409ec | ||
|
|
897af615f9 | ||
|
|
ea6597c3ad | ||
|
|
5a31d2e817 | ||
|
|
09f1966e5f | ||
|
|
ad649aba48 | ||
|
|
c7edc36106 | ||
|
|
57d4ccfdfd | ||
|
|
550f9de41a | ||
|
|
f3aab569ca | ||
|
|
2f9e510f4f | ||
|
|
1d7fd8ac96 | ||
|
|
ccb4e967cf | ||
|
|
08f5af68f0 | ||
|
|
d51b87e80a | ||
|
|
c2edbdc487 | ||
|
|
5bc854bedc | ||
|
|
3e889ee06c | ||
|
|
9c338a5c81 | ||
|
|
d13b4f6698 | ||
|
|
cd0933522d | ||
|
|
dc599bbc63 | ||
|
|
4b82e90ffb | ||
|
|
36de881041 | ||
|
|
e0f80c9f91 | ||
|
|
55f6fe6d3a | ||
|
|
42f314669f | ||
|
|
9d273acd42 | ||
|
|
7fbe2754a4 | ||
|
|
c0043e5098 | ||
|
|
756e15fe19 | ||
|
|
d9218134e2 | ||
|
|
c66ba370d5 | ||
|
|
6fc0b2ecfb | ||
|
|
7b938b246a | ||
|
|
45cb98c8de | ||
|
|
0f8ea92a53 | ||
|
|
ac57e7ced2 | ||
|
|
513dc2fcc5 | ||
|
|
3c10ba4511 | ||
|
|
6f9d8ed01b | ||
|
|
3977cec408 | ||
|
|
7949ffe41e | ||
|
|
bd9e68e69f | ||
|
|
ccf689ffd5 | ||
|
|
b95b49bd19 | ||
|
|
dc8209837c | ||
|
|
582daffd7f | ||
|
|
30715c2512 | ||
|
|
51aea367c3 | ||
|
|
34e31f3d78 | ||
|
|
e250628174 | ||
|
|
eee968ce56 | ||
|
|
52f806ff94 | ||
|
|
58ebd8cc59 | ||
|
|
d08d1c9127 | ||
|
|
1a307f3093 | ||
|
|
dc18c012ed | ||
|
|
c46a82420d | ||
|
|
e002c05350 | ||
|
|
6401ed71ae | ||
|
|
621bfde961 | ||
|
|
5b5c06179c | ||
|
|
b187babd20 | ||
|
|
05e49bbeab | ||
|
|
c88c6a9b63 | ||
|
|
8e795cc2aa | ||
|
|
ad5bac6598 | ||
|
|
0d468a8f48 | ||
|
|
71dae29077 | ||
|
|
d1b26b72e3 | ||
|
|
64a4799c8c | ||
|
|
e5d9c26868 | ||
|
|
5deccd7833 | ||
|
|
e1f64e2476 | ||
|
|
cf5499375e | ||
|
|
a3736fc06e | ||
|
|
68a03c2134 | ||
|
|
dce8cf1af2 | ||
|
|
29d118a19a | ||
|
|
ec50b97aa8 | ||
|
|
fbf672288f | ||
|
|
47e8cfd91e | ||
|
|
9968485cdd | ||
|
|
6ea037cc47 | ||
|
|
c5f0be126e | ||
|
|
8db34a4d7f | ||
|
|
732ff67076 | ||
|
|
e3190a4ca9 | ||
|
|
f2c849703a | ||
|
|
e901307d8d | ||
|
|
e038467c89 | ||
|
|
776582e019 | ||
|
|
018daa8837 | ||
|
|
10b733d215 | ||
|
|
97ec65cccd | ||
|
|
d2d7b3b348 | ||
|
|
b0052eae05 | ||
|
|
ed692ffba3 | ||
|
|
242bc3624a | ||
|
|
18121a99ca | ||
|
|
93c2c13f96 | ||
|
|
b11fd250c1 | ||
|
|
93857b690a | ||
|
|
34d88db0b2 | ||
|
|
dcad550a2f | ||
|
|
816e4d533d | ||
|
|
53adf7a793 | ||
|
|
0ed9dc4e93 | ||
|
|
9eba7923a7 | ||
|
|
3263f6fefc | ||
|
|
a27dd17cef | ||
|
|
52f9d5f0aa | ||
|
|
6783b22ad0 | ||
|
|
a3c1c314d0 | ||
|
|
d01f3b094b | ||
|
|
8492e87d29 | ||
|
|
923b64eb48 | ||
|
|
89883e2078 | ||
|
|
23907e6cb1 | ||
|
|
c6c414c382 | ||
|
|
c349652a27 | ||
|
|
f9731a76ec | ||
|
|
7dd159add2 | ||
|
|
1ad04bf8bf | ||
|
|
6064d12af3 | ||
|
|
9b7820a09d | ||
|
|
e5fdee272b | ||
|
|
a80e4ef0ea | ||
|
|
ab0bd908ec | ||
|
|
9c7fcfbe3f | ||
|
|
1bbaa5ba22 | ||
|
|
b22423e2d5 | ||
|
|
5730b15f01 | ||
|
|
8305fd5f82 | ||
|
|
0518b08ca6 | ||
|
|
8e75d8c39a | ||
|
|
6beb527058 | ||
|
|
b9e3931e80 | ||
|
|
7945126e86 | ||
|
|
408a67f34f | ||
|
|
0dac2a74d3 | ||
|
|
9896570e32 | ||
|
|
9639ab7f1e | ||
|
|
778d1afda0 | ||
|
|
02c22b850b | ||
|
|
bd035e1c3d | ||
|
|
b923b1e31d | ||
|
|
14f448f4c7 | ||
|
|
cd8e9e59fa | ||
|
|
4a6847da8d | ||
|
|
c5a3832eae | ||
|
|
7f75d250c7 | ||
|
|
95dbc20350 | ||
|
|
f846b87590 | ||
|
|
e3764bef37 | ||
|
|
d7c5c24ce4 | ||
|
|
60ba7be319 | ||
|
|
2215dd7bd7 | ||
|
|
07ce9c41bf | ||
|
|
24564f1faa | ||
|
|
ba11971513 | ||
|
|
803737011a | ||
|
|
8ad9f2681c | ||
|
|
b46c3036d8 | ||
|
|
02ed7d6059 | ||
|
|
2c2acae50d | ||
|
|
ac438fbd7d | ||
|
|
feffbc9330 | ||
|
|
908404ab62 | ||
|
|
618abd6320 | ||
|
|
79087f6942 | ||
|
|
9a43f0d54c | ||
|
|
79af7c22d9 | ||
|
|
0b911a5caa | ||
|
|
aae837f642 | ||
|
|
5887e8c8c4 | ||
|
|
974a7ff3f5 | ||
|
|
938fa00469 | ||
|
|
679faf5149 | ||
|
|
7e45233c7d | ||
|
|
2ed69ef602 | ||
|
|
192a08b5bf | ||
|
|
763547f465 | ||
|
|
b3e08b2cf4 | ||
|
|
d1d6797d3e | ||
|
|
4b094b2156 | ||
|
|
562cdc12d1 | ||
|
|
8f562215b0 | ||
|
|
8a478e4616 | ||
|
|
f75de6af82 | ||
|
|
a061f3339e | ||
|
|
724c417f09 | ||
|
|
9c23d03d8d | ||
|
|
b8740c008b | ||
|
|
588c613043 | ||
|
|
1dc166f0f8 | ||
|
|
82103e6a39 | ||
|
|
33bce67a4e | ||
|
|
aeb3c8b8b9 | ||
|
|
24c00b0985 | ||
|
|
a25646a129 | ||
|
|
0c73aff0a2 | ||
|
|
3ed1fee7ce | ||
|
|
b269bda52b | ||
|
|
2ab0ff8aea | ||
|
|
46a36f766f | ||
|
|
276d5097c1 | ||
|
|
ec980edf56 | ||
|
|
89dead79c4 | ||
|
|
e7f849184c | ||
|
|
c61cd350ee | ||
|
|
ea353ac3ba | ||
|
|
74c89ce06e | ||
|
|
42354ee5d5 | ||
|
|
e4d0fdaa56 | ||
|
|
ebb5c53c3a | ||
|
|
676892cb00 | ||
|
|
e09da5cb54 | ||
|
|
027c43c99c | ||
|
|
f5b937667a | ||
|
|
f36229bd95 | ||
|
|
3c9e6054b5 | ||
|
|
284fb5458e | ||
|
|
bf7b53a2a6 | ||
|
|
07c6f2a20b | ||
|
|
514947ba49 | ||
|
|
f3fbf6bd89 | ||
|
|
0f227e8317 | ||
|
|
905aa7cdc4 | ||
|
|
25cc118890 | ||
|
|
72a4f8a9a1 | ||
|
|
e898e6bf82 | ||
|
|
ad9d7931f5 | ||
|
|
fb3c577601 | ||
|
|
02b7cd71c5 | ||
|
|
98e930bd46 | ||
|
|
3481161616 | ||
|
|
a3352ac1dc | ||
|
|
214eb0caa5 | ||
|
|
3a30c00dae | ||
|
|
d971dff593 | ||
|
|
1eef996701 | ||
|
|
dcae7fc541 | ||
|
|
aeb2e235e5 | ||
|
|
ff856d2f20 | ||
|
|
a15c2c5d86 | ||
|
|
c37d13af84 | ||
|
|
bf443265ff | ||
|
|
9ebe38e59d | ||
|
|
a85cc6aa77 | ||
|
|
6683a9cf76 | ||
|
|
e3e0702813 | ||
|
|
d8942a3359 | ||
|
|
4d25634b66 | ||
|
|
717940d969 | ||
|
|
912146b1c9 | ||
|
|
70b6c024bf | ||
|
|
047f08b482 | ||
|
|
4ce3817d28 | ||
|
|
8910412068 | ||
|
|
c61ed150b7 | ||
|
|
1bbb86d304 | ||
|
|
2a76f1decd | ||
|
|
f3548daede | ||
|
|
efdadfd7c5 | ||
|
|
e82507ca4e | ||
|
|
d67db32015 | ||
|
|
56014962d4 | ||
|
|
df3e8ce937 | ||
|
|
7d9c0b76fc | ||
|
|
8a6bea64bc | ||
|
|
114022d18a | ||
|
|
a85af553b8 | ||
|
|
1cf5a0c948 | ||
|
|
6643b4188a | ||
|
|
bc11689f35 | ||
|
|
0339a4f963 | ||
|
|
6a39f48a9e | ||
|
|
b3232b42db | ||
|
|
c1a29b08ac | ||
|
|
75f6cc4319 | ||
|
|
bcbe207515 | ||
|
|
29039fd039 | ||
|
|
90005c8237 | ||
|
|
cdc0aa658a | ||
|
|
dd42819a2f | ||
|
|
88560d06a1 | ||
|
|
cb8a465605 | ||
|
|
fe13a85c0f | ||
|
|
89b6be91a3 | ||
|
|
6aca908462 | ||
|
|
634718d6b4 | ||
|
|
2fca028161 | ||
|
|
5e8d28abba | ||
|
|
bc78460f63 | ||
|
|
e228ba963d | ||
|
|
63927fc1fa | ||
|
|
cbcfe50eb5 | ||
|
|
7da95bd28a | ||
|
|
5444889715 | ||
|
|
3e13a1feed | ||
|
|
7e4c416bc1 | ||
|
|
882b559d3a | ||
|
|
610fd2ac67 | ||
|
|
f383ebb718 | ||
|
|
cb94d43092 | ||
|
|
8812a45607 | ||
|
|
37a374000c | ||
|
|
ac17f116be | ||
|
|
ecf709cbba | ||
|
|
225aa7fa6a | ||
|
|
46b16237b6 | ||
|
|
7a387b9a9f | ||
|
|
3c5e9ddd4e | ||
|
|
02851d7587 | ||
|
|
1b3652e135 | ||
|
|
17ccaab792 | ||
|
|
c9576dcdbe | ||
|
|
7acdc0a606 | ||
|
|
f552f24e6e | ||
|
|
79d13eb6cb | ||
|
|
41974b8c75 | ||
|
|
729cc4330e | ||
|
|
27d510d1b7 | ||
|
|
8a293f45fa | ||
|
|
44bc09b007 | ||
|
|
b58d58ef29 | ||
|
|
ba12331a11 | ||
|
|
b88b04515e | ||
|
|
ba9a0c0b2e | ||
|
|
9237174026 | ||
|
|
fd9a8fd2b1 | ||
|
|
7b59ce61bb | ||
|
|
1ae55e5872 | ||
|
|
6489230e68 | ||
|
|
c05f411ba0 | ||
|
|
b30de01b12 | ||
|
|
79e1096eca | ||
|
|
f1ae5817eb | ||
|
|
a906d7f02f | ||
|
|
50fb373655 | ||
|
|
29d1aa0146 | ||
|
|
fa4009821e | ||
|
|
e5503c51b4 | ||
|
|
ccb68088a8 | ||
|
|
0f07b04627 | ||
|
|
bfc6274cd8 | ||
|
|
c5c0d2060c | ||
|
|
dde2b4a879 | ||
|
|
df21a067ff | ||
|
|
8c49f76534 | ||
|
|
96a9575049 | ||
|
|
0a9368fc70 | ||
|
|
84f7966a0b | ||
|
|
8f7dea698e | ||
|
|
9e7e0a456d | ||
|
|
64b47a29cf | ||
|
|
cbfe8b8232 | ||
|
|
583838e2c2 | ||
|
|
7bfb73dacf | ||
|
|
350e942b6a | ||
|
|
ed82f388e6 | ||
|
|
b5f624a10f | ||
|
|
700c53e60a | ||
|
|
2ed99ba245 | ||
|
|
472c5f542f | ||
|
|
1636187e26 | ||
|
|
4d640dac2a | ||
|
|
7bf11df3b8 | ||
|
|
ddf2aa38cc | ||
|
|
e8c544c774 | ||
|
|
82af922b40 | ||
|
|
446e5fd665 | ||
|
|
7847982a57 | ||
|
|
86a7f96a46 | ||
|
|
da01ea997d | ||
|
|
59aa40e2b0 | ||
|
|
6fb5fa1c52 | ||
|
|
64df22def8 | ||
|
|
bbe403fb40 | ||
|
|
3547a4042c | ||
|
|
46ea2291fe | ||
|
|
66c2c7f789 | ||
|
|
78c06bdd22 | ||
|
|
afd69e4afd | ||
|
|
ff7ff3b55b | ||
|
|
14e2c76799 | ||
|
|
677e3585c9 | ||
|
|
e349facd65 | ||
|
|
7b5e8a9661 | ||
|
|
3f314d8355 | ||
|
|
325117114a | ||
|
|
e1d445ab50 | ||
|
|
b1b72d2d33 | ||
|
|
bb5e520a79 | ||
|
|
fd0069cb0e | ||
|
|
52ee861d3a | ||
|
|
42075e74ad | ||
|
|
1e87aedbb8 | ||
|
|
5221e09b67 | ||
|
|
0972782553 | ||
|
|
425c746b87 | ||
|
|
db2d0df2c4 | ||
|
|
da1397ff76 | ||
|
|
c009fc5d72 | ||
|
|
993b4c92b0 | ||
|
|
b26bc5c7f4 | ||
|
|
e4cc15d19e | ||
|
|
e5c2022f71 | ||
|
|
b343c24a9f | ||
|
|
21e3778e69 | ||
|
|
f4f6e74ea2 | ||
|
|
c887f54740 | ||
|
|
1179731959 | ||
|
|
91e833cdaf | ||
|
|
84e5f30c70 | ||
|
|
b007b66b15 | ||
|
|
8b05455545 | ||
|
|
a44ca91409 | ||
|
|
84235fe479 |
2
.dir-locals.el
Normal file
2
.dir-locals.el
Normal file
@@ -0,0 +1,2 @@
|
||||
((c++-mode . ((indent-tabs-mode . t)))
|
||||
(c-mode . ((mode . c++))))
|
||||
39
.gitignore
vendored
39
.gitignore
vendored
@@ -1,15 +1,21 @@
|
||||
# i2pd
|
||||
obj/*.o
|
||||
*.o
|
||||
router.info
|
||||
router.keys
|
||||
i2p
|
||||
libi2pd.so
|
||||
netDb
|
||||
/i2pd
|
||||
/libi2pd.a
|
||||
/libi2pdclient.a
|
||||
i2pd.exe
|
||||
|
||||
|
||||
# Autotools
|
||||
autom4te.cache
|
||||
.deps
|
||||
stamp-h1
|
||||
Makefile
|
||||
#Makefile
|
||||
config.h
|
||||
config.h.in~
|
||||
config.log
|
||||
@@ -229,3 +235,32 @@ pip-log.txt
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
|
||||
# Sphinx
|
||||
docs/_build
|
||||
/androidIdea/
|
||||
|
||||
# Doxygen
|
||||
docs/generated
|
||||
|
||||
# emacs files
|
||||
*~
|
||||
*\#*
|
||||
|
||||
# gdb files
|
||||
.gdb_history
|
||||
|
||||
# cmake makefile
|
||||
build/Makefile
|
||||
|
||||
# debian stuff
|
||||
.pc/
|
||||
|
||||
# qt
|
||||
|
||||
qt/i2pd_qt/*.ui.autosave
|
||||
qt/i2pd_qt/*.ui.bk*
|
||||
qt/i2pd_qt/*.ui_*
|
||||
|
||||
#unknown android stuff
|
||||
android/libs/
|
||||
|
||||
54
.travis.yml
Normal file
54
.travis.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
language: cpp
|
||||
cache:
|
||||
apt: true
|
||||
os:
|
||||
- linux
|
||||
#- osx
|
||||
dist: trusty
|
||||
sudo: required
|
||||
compiler:
|
||||
- g++
|
||||
- clang++
|
||||
env:
|
||||
global:
|
||||
- MAKEFLAGS="-j 2"
|
||||
matrix:
|
||||
- BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes
|
||||
- BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no
|
||||
- BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes
|
||||
- BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no
|
||||
matrix:
|
||||
exclude:
|
||||
- os: osx
|
||||
env: BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes
|
||||
- os: osx
|
||||
env: BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no
|
||||
- os: linux
|
||||
compiler: clang++
|
||||
env: BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes
|
||||
- os: linux
|
||||
compiler: clang++
|
||||
env: BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- build-essential
|
||||
- cmake
|
||||
- g++
|
||||
- clang
|
||||
- libboost-chrono-dev
|
||||
- libboost-date-time-dev
|
||||
- libboost-filesystem-dev
|
||||
- libboost-program-options-dev
|
||||
- libboost-system-dev
|
||||
- libboost-thread-dev
|
||||
- libminiupnpc-dev
|
||||
- libssl-dev
|
||||
before_install:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libressl miniupnpc ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated boost || brew upgrade boost ; fi
|
||||
script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "cmake" ]]; then cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_UPNP=${UPNP} && make ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "make" ]]; then make USE_UPNP=${MAKE_UPNP} ; fi
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make HOMEBREW=1 USE_UPNP=${MAKE_UPNP} ; fi
|
||||
314
AddressBook.cpp
314
AddressBook.cpp
@@ -1,314 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <fstream>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "base64.h"
|
||||
#include "util.h"
|
||||
#include "Identity.h"
|
||||
#include "Log.h"
|
||||
#include "AddressBook.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
|
||||
class AddressBookFilesystemStorage: public AddressBookStorage
|
||||
{
|
||||
public:
|
||||
|
||||
AddressBookFilesystemStorage ();
|
||||
bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const;
|
||||
void AddAddress (const i2p::data::IdentityEx& address);
|
||||
void RemoveAddress (const i2p::data::IdentHash& ident);
|
||||
|
||||
int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
|
||||
int Save (const std::map<std::string, i2p::data::IdentHash>& addresses);
|
||||
|
||||
private:
|
||||
|
||||
boost::filesystem::path GetPath () const { return i2p::util::filesystem::GetDefaultDataDir() / "addressbook"; };
|
||||
|
||||
};
|
||||
|
||||
AddressBookFilesystemStorage::AddressBookFilesystemStorage ()
|
||||
{
|
||||
auto path = GetPath ();
|
||||
if (!boost::filesystem::exists (path))
|
||||
{
|
||||
// Create directory is necessary
|
||||
if (!boost::filesystem::create_directory (path))
|
||||
LogPrint (eLogError, "Failed to create addressbook directory");
|
||||
}
|
||||
}
|
||||
|
||||
bool AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const
|
||||
{
|
||||
auto filename = GetPath () / (ident.ToBase32() + ".b32");
|
||||
std::ifstream f(filename.c_str (), std::ifstream::binary);
|
||||
if (f.is_open ())
|
||||
{
|
||||
f.seekg (0,std::ios::end);
|
||||
size_t len = f.tellg ();
|
||||
if (len < i2p::data::DEFAULT_IDENTITY_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "File ", filename, " is too short. ", len);
|
||||
return false;
|
||||
}
|
||||
f.seekg(0, std::ios::beg);
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
f.read((char *)buf, len);
|
||||
address.FromBuffer (buf, len);
|
||||
delete[] buf;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddressBookFilesystemStorage::AddAddress (const i2p::data::IdentityEx& address)
|
||||
{
|
||||
auto filename = GetPath () / (address.GetIdentHash ().ToBase32() + ".b32");
|
||||
std::ofstream f (filename.c_str (), std::ofstream::binary | std::ofstream::out);
|
||||
if (f.is_open ())
|
||||
{
|
||||
size_t len = address.GetFullLen ();
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
address.ToBuffer (buf, len);
|
||||
f.write ((char *)buf, len);
|
||||
delete[] buf;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Can't open file ", filename);
|
||||
}
|
||||
|
||||
void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident)
|
||||
{
|
||||
auto filename = GetPath () / (ident.ToBase32() + ".b32");
|
||||
if (boost::filesystem::exists (filename))
|
||||
boost::filesystem::remove (filename);
|
||||
}
|
||||
|
||||
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
|
||||
{
|
||||
int num = 0;
|
||||
auto filename = GetPath () / "addresses.csv";
|
||||
std::ifstream f (filename.c_str (), std::ofstream::in); // in text mode
|
||||
if (f.is_open ())
|
||||
{
|
||||
addresses.clear ();
|
||||
while (!f.eof ())
|
||||
{
|
||||
std::string s;
|
||||
getline(f, s);
|
||||
if (!s.length())
|
||||
continue; // skip empty line
|
||||
|
||||
size_t pos = s.find(',');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string name = s.substr(0, pos++);
|
||||
std::string addr = s.substr(pos);
|
||||
|
||||
i2p::data::IdentHash ident;
|
||||
ident.FromBase32 (addr);
|
||||
addresses[name] = ident;
|
||||
num++;
|
||||
}
|
||||
}
|
||||
LogPrint (eLogInfo, num, " addresses loaded");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, filename, " not found");
|
||||
return num;
|
||||
}
|
||||
|
||||
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
|
||||
{
|
||||
int num = 0;
|
||||
auto filename = GetPath () / "addresses.csv";
|
||||
std::ofstream f (filename.c_str (), std::ofstream::out); // in text mode
|
||||
if (f.is_open ())
|
||||
{
|
||||
for (auto it: addresses)
|
||||
{
|
||||
f << it.first << "," << it.second.ToBase32 () << std::endl;
|
||||
num++;
|
||||
}
|
||||
LogPrint (eLogInfo, num, " addresses save");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Can't open file ", filename);
|
||||
return num;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
AddressBook::AddressBook (): m_IsLoaded (false), m_IsDowloading (false)
|
||||
{
|
||||
}
|
||||
|
||||
AddressBook::~AddressBook ()
|
||||
{
|
||||
if (m_Storage)
|
||||
{
|
||||
m_Storage->Save (m_Addresses);
|
||||
delete m_Storage;
|
||||
}
|
||||
}
|
||||
|
||||
AddressBookStorage * AddressBook::CreateStorage ()
|
||||
{
|
||||
return new AddressBookFilesystemStorage ();
|
||||
}
|
||||
|
||||
bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident)
|
||||
{
|
||||
auto pos = address.find(".b32.i2p");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
Base32ToByteStream (address.c_str(), pos, ident, 32);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = address.find (".i2p");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
auto identHash = FindAddress (address);
|
||||
if (identHash)
|
||||
{
|
||||
ident = *identHash;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// if not .b32 we assume full base64 address
|
||||
i2p::data::IdentityEx dest;
|
||||
if (!dest.FromBase64 (address))
|
||||
return false;
|
||||
ident = dest.GetIdentHash ();
|
||||
return true;
|
||||
}
|
||||
|
||||
const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address)
|
||||
{
|
||||
if (!m_IsLoaded)
|
||||
LoadHosts ();
|
||||
if (m_IsLoaded)
|
||||
{
|
||||
auto it = m_Addresses.find (address);
|
||||
if (it != m_Addresses.end ())
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AddressBook::InsertAddress (const std::string& address, const std::string& base64)
|
||||
{
|
||||
i2p::data::IdentityEx ident;
|
||||
ident.FromBase64 (base64);
|
||||
if (!m_Storage)
|
||||
m_Storage = CreateStorage ();
|
||||
m_Storage->AddAddress (ident);
|
||||
m_Addresses[address] = ident.GetIdentHash ();
|
||||
LogPrint (address,"->",ident.GetIdentHash ().ToBase32 (), ".b32.i2p added");
|
||||
}
|
||||
|
||||
void AddressBook::InsertAddress (const i2p::data::IdentityEx& address)
|
||||
{
|
||||
if (!m_Storage)
|
||||
m_Storage = CreateStorage ();
|
||||
m_Storage->AddAddress (address);
|
||||
}
|
||||
|
||||
bool AddressBook::GetAddress (const std::string& address, i2p::data::IdentityEx& identity)
|
||||
{
|
||||
if (!m_Storage)
|
||||
m_Storage = CreateStorage ();
|
||||
i2p::data::IdentHash ident;
|
||||
if (!GetIdentHash (address, ident)) return false;
|
||||
return m_Storage->GetAddress (ident, identity);
|
||||
}
|
||||
|
||||
void AddressBook::LoadHostsFromI2P ()
|
||||
{
|
||||
std::string content;
|
||||
int http_code = i2p::util::http::httpRequestViaI2pProxy("http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt", content);
|
||||
if (http_code == 200)
|
||||
{
|
||||
std::ofstream f_save(i2p::util::filesystem::GetFullPath("hosts.txt").c_str(), std::ofstream::out);
|
||||
if (f_save.is_open())
|
||||
{
|
||||
f_save << content;
|
||||
f_save.close();
|
||||
}
|
||||
else
|
||||
LogPrint("Can't write hosts.txt");
|
||||
m_IsLoaded = false;
|
||||
}
|
||||
else
|
||||
LogPrint ("Failed to download hosts.txt");
|
||||
m_IsDowloading = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void AddressBook::LoadHosts ()
|
||||
{
|
||||
if (!m_Storage)
|
||||
m_Storage = CreateStorage ();
|
||||
int numAddresses = m_Storage->Load (m_Addresses);
|
||||
if (numAddresses > 0)
|
||||
{
|
||||
m_IsLoaded = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise try hosts.txt
|
||||
std::ifstream f (i2p::util::filesystem::GetFullPath ("hosts.txt").c_str (), std::ofstream::in); // in text mode
|
||||
if (!f.is_open ())
|
||||
{
|
||||
LogPrint ("hosts.txt not found. Try to load...");
|
||||
if (!m_IsDowloading)
|
||||
{
|
||||
m_IsDowloading = true;
|
||||
std::thread load_hosts(&AddressBook::LoadHostsFromI2P, this);
|
||||
load_hosts.detach();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::string s;
|
||||
while (!f.eof ())
|
||||
{
|
||||
getline(f, s);
|
||||
|
||||
if (!s.length())
|
||||
continue; // skip empty line
|
||||
|
||||
size_t pos = s.find('=');
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string name = s.substr(0, pos++);
|
||||
std::string addr = s.substr(pos);
|
||||
|
||||
i2p::data::IdentityEx ident;
|
||||
ident.FromBase64(addr);
|
||||
m_Addresses[name] = ident.GetIdentHash ();
|
||||
m_Storage->AddAddress (ident);
|
||||
numAddresses++;
|
||||
}
|
||||
}
|
||||
LogPrint (numAddresses, " addresses loaded");
|
||||
m_Storage->Save (m_Addresses);
|
||||
m_IsLoaded = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
#ifndef ADDRESS_BOOK_H__
|
||||
#define ADDRESS_BOOK_H__
|
||||
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "base64.h"
|
||||
#include "util.h"
|
||||
#include "Identity.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class AddressBookStorage // interface for storage
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~AddressBookStorage () {};
|
||||
virtual bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const = 0;
|
||||
virtual void AddAddress (const i2p::data::IdentityEx& address) = 0;
|
||||
virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0;
|
||||
|
||||
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
|
||||
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
|
||||
};
|
||||
|
||||
class AddressBook
|
||||
{
|
||||
public:
|
||||
|
||||
AddressBook ();
|
||||
~AddressBook ();
|
||||
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
|
||||
bool GetAddress (const std::string& address, i2p::data::IdentityEx& identity);
|
||||
const i2p::data::IdentHash * FindAddress (const std::string& address);
|
||||
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
|
||||
void InsertAddress (const i2p::data::IdentityEx& address);
|
||||
|
||||
private:
|
||||
|
||||
AddressBookStorage * CreateStorage ();
|
||||
|
||||
private:
|
||||
|
||||
void LoadHosts ();
|
||||
void LoadHostsFromI2P ();
|
||||
|
||||
std::map<std::string, i2p::data::IdentHash> m_Addresses;
|
||||
AddressBookStorage * m_Storage;
|
||||
bool m_IsLoaded, m_IsDowloading;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
249
ChangeLog
Normal file
249
ChangeLog
Normal file
@@ -0,0 +1,249 @@
|
||||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.18.0] - 2018-01-30
|
||||
### Added
|
||||
- Show tunnel nicknames for I2CP destination in WebUI
|
||||
- Re-create HTTP and SOCKS proxy by tunnel reload
|
||||
- Graceful shutdown as soon as no more transit tunnels
|
||||
### Changed
|
||||
- Regenerate shared local destination by tunnel reload
|
||||
- Use transient local destination by default if not specified
|
||||
- Return correct code if pid file can't be created
|
||||
- Timing and number of attempts for adressbook requests
|
||||
- Certificates list
|
||||
### Fixed
|
||||
- Malformed addressbook subsctiption request
|
||||
- Build with boost 1.66
|
||||
- Few race conditions for SAM
|
||||
- Check LeaseSet's signature before update
|
||||
|
||||
## [2.17.0] - 2017-12-04
|
||||
### Added
|
||||
- Reseed through HTTP and SOCKS proxy
|
||||
- Show status of client services through web console
|
||||
- Change log level through web connsole
|
||||
- transient keys for tunnels
|
||||
- i2p.streaming.initialAckDelay parameter
|
||||
- CRYPTO_TYPE for SAM destination
|
||||
- signature and crypto type for newkeys BOB command
|
||||
### Changed
|
||||
- Correct publication of ECIES destinations
|
||||
- Disable RSA signatures completely
|
||||
### Fixed
|
||||
- CVE-2017-17066
|
||||
- Possible buffer overflow for RSA-4096
|
||||
- Shutdown from web console for Windows
|
||||
- Web console page layout
|
||||
## [2.16.0] - 2017-11-13
|
||||
### Added
|
||||
- https and "Connect" method for HTTP proxy
|
||||
- outproxy for HTTP proxy
|
||||
- initial support of ECIES crypto
|
||||
- NTCP soft and hard descriptors limits
|
||||
- Support full timestamps in logs
|
||||
### Changed
|
||||
- Faster implmentation of GOST R 34.11 hash
|
||||
- Reject routers with RSA signtures
|
||||
- Reload config and shudown from Windows GUI
|
||||
- Update tunnels address(destination) without restart
|
||||
### Fixed
|
||||
- BOB crashes if destination is not set
|
||||
- Correct SAM tunnel name
|
||||
- QT GUI issues
|
||||
|
||||
## [2.15.0] - 2017-08-17
|
||||
### Added
|
||||
- QT GUI
|
||||
- Ability to add and remove I2P tunnels without restart
|
||||
- Ability to disable SOCKS outproxy option
|
||||
### Changed
|
||||
- Strip-out Accept-* hedaers in HTTP proxy
|
||||
- Don't run peer test if nat=false
|
||||
- Separate output of NTCP and SSU sessions in Transports tab
|
||||
### Fixed
|
||||
- Handle lines with comments in hosts.txt file for address book
|
||||
- Run router with empty netdb for testnet
|
||||
- Skip expired introducers by iexp
|
||||
|
||||
## [2.14.0] - 2017-06-01
|
||||
### Added
|
||||
- Transit traffic bandwidth limitation
|
||||
- NTCP connections through HTTP and SOCKS proxies
|
||||
- Ability to disable address helper for HTTP proxy
|
||||
### Changed
|
||||
- Reseed servers list
|
||||
- Minimal required version is 4.0 for Android
|
||||
### Fixed
|
||||
- Ignore comments in addressbook feed
|
||||
|
||||
## [2.13.0] - 2017-04-06
|
||||
### Added
|
||||
- Persist local destination's tags
|
||||
- GOST signature types 9 and 10
|
||||
- Exploratory tunnels configuration
|
||||
### Changed
|
||||
- Reseed servers list
|
||||
- Inactive NTCP sockets get closed faster
|
||||
- Some EdDSA speed up
|
||||
### Fixed
|
||||
- Multiple acceptors for SAM
|
||||
- Follow on data after STREAM CREATE for SAM
|
||||
- Memory leaks
|
||||
|
||||
## [2.12.0] - 2017-02-14
|
||||
### Added
|
||||
- Additional HTTP and SOCKS proxy tunnels
|
||||
- Reseed from ZIP archive
|
||||
- Some stats in a main window for Windows version
|
||||
### Changed
|
||||
- Reseed servers list
|
||||
- MTU of 1488 for ipv6
|
||||
- Android and Mac OS X versions use OpenSSL 1.1
|
||||
- New logo for Android
|
||||
### Fixed
|
||||
- Multiple memory leaks
|
||||
- Incomptibility of some EdDSA private keys with Java
|
||||
- Clock skew for Windows XP
|
||||
- Occasional crashes with I2PSnark
|
||||
|
||||
## [2.11.0] - 2016-12-18
|
||||
### Added
|
||||
- Websockets support
|
||||
- Reseed through a floodfill
|
||||
- Tunnel configuration for HTTP and SOCKS proxy
|
||||
- Zero-hops tunnels for destinations
|
||||
- Multiple acceptors for SAM
|
||||
### Changed
|
||||
- Reseed servers list
|
||||
- DHT uses AVX if applicable
|
||||
- New logo
|
||||
- LeaseSet lookups
|
||||
### Fixed
|
||||
- HTTP Proxy connection reset for Windows
|
||||
- Crash upon SAM session termination
|
||||
- Can't connect to a destination for a longer time after restart
|
||||
- Mass packet loss for UDP tunnels
|
||||
|
||||
## [2.10.2] - 2016-12-04
|
||||
### Fixed
|
||||
- Fixes UPnP discovery bug, producing excessive CPU usage
|
||||
- Fixes sudden SSU thread stop for Windows.
|
||||
|
||||
## [2.10.1] - 2016-11-07
|
||||
### Fixed
|
||||
- Fixed some performance issues for Windows and Android
|
||||
|
||||
## [2.10.0] - 2016-10-17
|
||||
### Added
|
||||
- Datagram i2p tunnels
|
||||
- Unique local addresses for server tunnels
|
||||
- Configurable list of reseed servers and initial addressbook
|
||||
- Configurable netid
|
||||
- Initial iOS support
|
||||
|
||||
### Changed
|
||||
- Reduced file descriptiors usage
|
||||
- Strict reseed checks enabled by default
|
||||
|
||||
## Fixed
|
||||
- Multiple fixes in I2CP and BOB implementations
|
||||
|
||||
## [2.9.0] - 2016-08-12
|
||||
### Changed
|
||||
- Proxy refactoring & speedup
|
||||
- Transmission-I2P support
|
||||
- Graceful shutdown for Windows
|
||||
- Android without QT
|
||||
- Reduced number of timers in SSU
|
||||
- ipv6 peer test support
|
||||
- Reseed from SU3 file
|
||||
|
||||
## [2.8.0] - 2016-06-20
|
||||
### Added
|
||||
- Basic Android support
|
||||
- I2CP implementation
|
||||
- 'doxygen' target
|
||||
|
||||
### Changed
|
||||
- I2PControl refactoring & fixes (proper jsonrpc responses on errors)
|
||||
- boost::regex no more needed
|
||||
|
||||
### Fixed
|
||||
- initscripts: added openrc one, in sysv-ish make I2PD_PORT optional
|
||||
- properly close NTCP sessions (memleak)
|
||||
|
||||
## [2.7.0] - 2016-05-18
|
||||
### Added
|
||||
- Precomputed El-Gamal/DH tables
|
||||
- Configurable limit of transit tunnels
|
||||
|
||||
### Changed
|
||||
- Speed-up of asymmetric crypto for non-x64 platforms
|
||||
- Refactoring of web-console
|
||||
|
||||
## [2.6.0] - 2016-03-31
|
||||
### Added
|
||||
- Graceful shutdown on SIGINT
|
||||
- Numeric bandwidth limits (was: by router class)
|
||||
- Jumpservices in web-console
|
||||
- Logging to syslog
|
||||
- Tray icon for windows application
|
||||
|
||||
### Changed
|
||||
- Logs refactoring
|
||||
- Improved statistics in web-console
|
||||
|
||||
### Deprecated:
|
||||
- Renamed main/tunnels config files (will use old, if found, but emits warning)
|
||||
|
||||
## [2.5.1] - 2016-03-10
|
||||
### Fixed
|
||||
- Doesn't create ~/.i2pd dir if missing
|
||||
|
||||
## [2.5.0] - 2016-03-04
|
||||
### Added
|
||||
- IRC server tunnels
|
||||
- SOCKS outproxy support
|
||||
- Support for gzipped addressbook updates
|
||||
- Support for router families
|
||||
|
||||
### Changed
|
||||
- Shared RTT/RTO between streams
|
||||
- Filesystem work refactoring
|
||||
|
||||
## [2.4.0] - 2016-02-03
|
||||
### Added
|
||||
- X-I2P-* headers for server http-tunnels
|
||||
- I2CP options for I2P tunnels
|
||||
- Show I2P tunnels in webconsole
|
||||
|
||||
### Changed
|
||||
- Refactoring of cmdline/config parsing
|
||||
|
||||
## [2.3.0] - 2016-01-12
|
||||
### Added
|
||||
- Support for new router bandwidth class codes (P and X)
|
||||
- I2PControl supports external webui
|
||||
- Added --pidfile and --notransit parameters
|
||||
- Ability to specify signature type for i2p tunnel
|
||||
|
||||
### Changed
|
||||
- Fixed multiple floodfill-related bugs
|
||||
- New webconsole layout
|
||||
|
||||
## [2.2.0] - 2015-12-22
|
||||
### Added
|
||||
- Ability to connect to router without ip via introducer
|
||||
|
||||
### Changed
|
||||
- Persist temporary encryption keys for local destinations
|
||||
- Performance improvements for EdDSA
|
||||
- New addressbook structure
|
||||
|
||||
## [2.1.0] - 2015-11-12
|
||||
### Added
|
||||
- Implementation of EdDSA
|
||||
|
||||
### Changed
|
||||
- EdDSA is default signature type for new RouterInfos
|
||||
@@ -1,223 +0,0 @@
|
||||
#include <fstream>
|
||||
#include "util.h"
|
||||
#include "Log.h"
|
||||
#include "Identity.h"
|
||||
#include "ClientContext.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
ClientContext context;
|
||||
|
||||
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
|
||||
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_IrcTunnel (nullptr),
|
||||
m_ServerTunnel (nullptr), m_SamBridge (nullptr), m_BOBCommandChannel (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
ClientContext::~ClientContext ()
|
||||
{
|
||||
delete m_HttpProxy;
|
||||
delete m_SocksProxy;
|
||||
delete m_IrcTunnel;
|
||||
delete m_ServerTunnel;
|
||||
delete m_SamBridge;
|
||||
delete m_BOBCommandChannel;
|
||||
}
|
||||
|
||||
void ClientContext::Start ()
|
||||
{
|
||||
if (!m_SharedLocalDestination)
|
||||
{
|
||||
m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA
|
||||
m_Destinations[m_SharedLocalDestination->GetIdentity ().GetIdentHash ()] = m_SharedLocalDestination;
|
||||
m_SharedLocalDestination->Start ();
|
||||
}
|
||||
|
||||
m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446));
|
||||
m_HttpProxy->Start();
|
||||
LogPrint("HTTP Proxy started");
|
||||
m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447));
|
||||
m_SocksProxy->Start();
|
||||
LogPrint("SOCKS Proxy Started");
|
||||
std::string ircDestination = i2p::util::config::GetArg("-ircdest", "");
|
||||
if (ircDestination.length () > 0) // ircdest is presented
|
||||
{
|
||||
ClientDestination * localDestination = nullptr;
|
||||
std::string ircKeys = i2p::util::config::GetArg("-irckeys", "");
|
||||
if (ircKeys.length () > 0)
|
||||
localDestination = LoadLocalDestination (ircKeys, false);
|
||||
m_IrcTunnel = new I2PClientTunnel (m_SocksProxy->GetService (), ircDestination,
|
||||
i2p::util::config::GetArg("-ircport", 6668), localDestination);
|
||||
m_IrcTunnel->Start ();
|
||||
LogPrint("IRC tunnel started");
|
||||
}
|
||||
std::string eepKeys = i2p::util::config::GetArg("-eepkeys", "");
|
||||
if (eepKeys.length () > 0) // eepkeys file is presented
|
||||
{
|
||||
auto localDestination = LoadLocalDestination (eepKeys, true);
|
||||
m_ServerTunnel = new I2PServerTunnel (m_SocksProxy->GetService (),
|
||||
i2p::util::config::GetArg("-eephost", "127.0.0.1"), i2p::util::config::GetArg("-eepport", 80),
|
||||
localDestination);
|
||||
m_ServerTunnel->Start ();
|
||||
LogPrint("Server tunnel started");
|
||||
}
|
||||
int samPort = i2p::util::config::GetArg("-samport", 0);
|
||||
if (samPort)
|
||||
{
|
||||
m_SamBridge = new SAMBridge (samPort);
|
||||
m_SamBridge->Start ();
|
||||
LogPrint("SAM bridge started");
|
||||
}
|
||||
int bobPort = i2p::util::config::GetArg("-bobport", 0);
|
||||
if (bobPort)
|
||||
{
|
||||
m_BOBCommandChannel = new BOBCommandChannel (bobPort);
|
||||
m_BOBCommandChannel->Start ();
|
||||
LogPrint("BOB command channel started");
|
||||
}
|
||||
}
|
||||
|
||||
void ClientContext::Stop ()
|
||||
{
|
||||
m_HttpProxy->Stop();
|
||||
delete m_HttpProxy;
|
||||
m_HttpProxy = nullptr;
|
||||
LogPrint("HTTP Proxy stoped");
|
||||
m_SocksProxy->Stop();
|
||||
delete m_SocksProxy;
|
||||
m_SocksProxy = nullptr;
|
||||
LogPrint("SOCKS Proxy stoped");
|
||||
if (m_IrcTunnel)
|
||||
{
|
||||
m_IrcTunnel->Stop ();
|
||||
delete m_IrcTunnel;
|
||||
m_IrcTunnel = nullptr;
|
||||
LogPrint("IRC tunnel stoped");
|
||||
}
|
||||
if (m_ServerTunnel)
|
||||
{
|
||||
m_ServerTunnel->Stop ();
|
||||
delete m_ServerTunnel;
|
||||
m_ServerTunnel = nullptr;
|
||||
LogPrint("Server tunnel stoped");
|
||||
}
|
||||
if (m_SamBridge)
|
||||
{
|
||||
m_SamBridge->Stop ();
|
||||
delete m_SamBridge;
|
||||
m_SamBridge = nullptr;
|
||||
LogPrint("SAM brdige stoped");
|
||||
}
|
||||
if (m_BOBCommandChannel)
|
||||
{
|
||||
m_BOBCommandChannel->Stop ();
|
||||
delete m_BOBCommandChannel;
|
||||
m_BOBCommandChannel = nullptr;
|
||||
LogPrint("BOB command channel stoped");
|
||||
}
|
||||
|
||||
for (auto it: m_Destinations)
|
||||
{
|
||||
it.second->Stop ();
|
||||
delete it.second;
|
||||
}
|
||||
m_Destinations.clear ();
|
||||
m_SharedLocalDestination = 0; // deleted through m_Destination
|
||||
}
|
||||
|
||||
ClientDestination * ClientContext::LoadLocalDestination (const std::string& filename, bool isPublic)
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
std::string fullPath = i2p::util::filesystem::GetFullPath (filename);
|
||||
std::ifstream s(fullPath.c_str (), std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
{
|
||||
s.seekg (0, std::ios::end);
|
||||
size_t len = s.tellg();
|
||||
s.seekg (0, std::ios::beg);
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
s.read ((char *)buf, len);
|
||||
keys.FromBuffer (buf, len);
|
||||
delete[] buf;
|
||||
LogPrint ("Local address ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p loaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Can't open file ", fullPath, " Creating new one");
|
||||
keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_DSA_SHA1);
|
||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
||||
size_t len = keys.GetFullLen ();
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
len = keys.ToBuffer (buf, len);
|
||||
f.write ((char *)buf, len);
|
||||
delete[] buf;
|
||||
|
||||
LogPrint ("New private keys file ", fullPath, " for ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p created");
|
||||
}
|
||||
|
||||
auto localDestination = new ClientDestination (keys, isPublic);
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
||||
localDestination->Start ();
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
ClientDestination * ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
|
||||
auto localDestination = new ClientDestination (keys, isPublic, params);
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
||||
localDestination->Start ();
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
void ClientContext::DeleteLocalDestination (ClientDestination * destination)
|
||||
{
|
||||
if (!destination) return;
|
||||
auto it = m_Destinations.find (destination->GetIdentHash ());
|
||||
if (it != m_Destinations.end ())
|
||||
{
|
||||
auto d = it->second;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
m_Destinations.erase (it);
|
||||
}
|
||||
d->Stop ();
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
ClientDestination * ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
|
||||
if (it != m_Destinations.end ())
|
||||
{
|
||||
LogPrint ("Local destination ", keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p exists");
|
||||
if (!it->second->IsRunning ())
|
||||
{
|
||||
it->second->Start ();
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
auto localDestination = new ClientDestination (keys, isPublic, params);
|
||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
||||
m_Destinations[keys.GetPublic ().GetIdentHash ()] = localDestination;
|
||||
localDestination->Start ();
|
||||
return localDestination;
|
||||
}
|
||||
|
||||
ClientDestination * ClientContext::FindLocalDestination (const i2p::data::IdentHash& destination) const
|
||||
{
|
||||
auto it = m_Destinations.find (destination);
|
||||
if (it != m_Destinations.end ())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
#ifndef CLIENT_CONTEXT_H__
|
||||
#define CLIENT_CONTEXT_H__
|
||||
|
||||
#include <mutex>
|
||||
#include "Destination.h"
|
||||
#include "HTTPProxy.h"
|
||||
#include "SOCKS.h"
|
||||
#include "I2PTunnel.h"
|
||||
#include "SAM.h"
|
||||
#include "BOB.h"
|
||||
#include "AddressBook.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class ClientContext
|
||||
{
|
||||
public:
|
||||
|
||||
ClientContext ();
|
||||
~ClientContext ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
ClientDestination * GetSharedLocalDestination () const { return m_SharedLocalDestination; };
|
||||
ClientDestination * CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1,
|
||||
const std::map<std::string, std::string> * params = nullptr); // transient
|
||||
ClientDestination * CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
||||
const std::map<std::string, std::string> * params = nullptr);
|
||||
void DeleteLocalDestination (ClientDestination * destination);
|
||||
ClientDestination * FindLocalDestination (const i2p::data::IdentHash& destination) const;
|
||||
ClientDestination * LoadLocalDestination (const std::string& filename, bool isPublic);
|
||||
|
||||
AddressBook& GetAddressBook () { return m_AddressBook; };
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_DestinationsMutex;
|
||||
std::map<i2p::data::IdentHash, ClientDestination *> m_Destinations;
|
||||
ClientDestination * m_SharedLocalDestination;
|
||||
|
||||
AddressBook m_AddressBook;
|
||||
|
||||
i2p::proxy::HTTPProxy * m_HttpProxy;
|
||||
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
||||
I2PClientTunnel * m_IrcTunnel;
|
||||
I2PServerTunnel * m_ServerTunnel;
|
||||
SAMBridge * m_SamBridge;
|
||||
BOBCommandChannel * m_BOBCommandChannel;
|
||||
|
||||
public:
|
||||
// for HTTP
|
||||
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
|
||||
};
|
||||
|
||||
extern ClientContext context;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,73 +0,0 @@
|
||||
#include <inttypes.h>
|
||||
#include "CryptoConst.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
const uint8_t elgp_[256]=
|
||||
{
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
||||
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
||||
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
||||
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
|
||||
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
|
||||
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
|
||||
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
|
||||
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
|
||||
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
|
||||
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
|
||||
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
|
||||
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
|
||||
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
|
||||
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
|
||||
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
|
||||
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
const uint8_t dsap_[128]=
|
||||
{
|
||||
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
|
||||
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
|
||||
0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23,
|
||||
0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c,
|
||||
0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8,
|
||||
0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c,
|
||||
0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11,
|
||||
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
|
||||
};
|
||||
|
||||
const uint8_t dsaq_[20]=
|
||||
{
|
||||
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
|
||||
0x68, 0x40, 0x46, 0xb7
|
||||
};
|
||||
|
||||
const uint8_t dsag_[128]=
|
||||
{
|
||||
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
|
||||
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
|
||||
0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2,
|
||||
0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52,
|
||||
0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc,
|
||||
0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a,
|
||||
0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c,
|
||||
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
|
||||
};
|
||||
|
||||
const CryptoConstants& GetCryptoConstants ()
|
||||
{
|
||||
static CryptoConstants cryptoConstants =
|
||||
{
|
||||
{elgp_, 256}, // elgp
|
||||
{2}, // elgg
|
||||
{dsap_, 128}, // dsap
|
||||
{dsaq_, 20}, // dsaq
|
||||
{dsag_, 128} // dsag
|
||||
};
|
||||
return cryptoConstants;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
#ifndef CRYPTO_CONST_H__
|
||||
#define CRYPTO_CONST_H__
|
||||
|
||||
#include <cryptopp/integer.h>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
struct CryptoConstants
|
||||
{
|
||||
// DH/ElGamal
|
||||
const CryptoPP::Integer elgp;
|
||||
const CryptoPP::Integer elgg;
|
||||
|
||||
// DSA
|
||||
const CryptoPP::Integer dsap;
|
||||
const CryptoPP::Integer dsaq;
|
||||
const CryptoPP::Integer dsag;
|
||||
};
|
||||
|
||||
const CryptoConstants& GetCryptoConstants ();
|
||||
|
||||
// DH/ElGamal
|
||||
#define elgp GetCryptoConstants ().elgp
|
||||
#define elgg GetCryptoConstants ().elgg
|
||||
|
||||
// DSA
|
||||
#define dsap GetCryptoConstants ().dsap
|
||||
#define dsaq GetCryptoConstants ().dsaq
|
||||
#define dsag GetCryptoConstants ().dsag
|
||||
|
||||
// RSA
|
||||
const int rsae = 65537;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
138
Daemon.cpp
138
Daemon.cpp
@@ -1,138 +0,0 @@
|
||||
#include <thread>
|
||||
|
||||
#include "Daemon.h"
|
||||
|
||||
#include "Log.h"
|
||||
#include "base64.h"
|
||||
#include "version.h"
|
||||
#include "Transports.h"
|
||||
#include "NTCPSession.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Tunnel.h"
|
||||
#include "NetDb.h"
|
||||
#include "Garlic.h"
|
||||
#include "util.h"
|
||||
#include "Streaming.h"
|
||||
#include "Destination.h"
|
||||
#include "HTTPServer.h"
|
||||
#include "ClientContext.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
class Daemon_Singleton::Daemon_Singleton_Private
|
||||
{
|
||||
public:
|
||||
Daemon_Singleton_Private() : httpServer(nullptr)
|
||||
{};
|
||||
~Daemon_Singleton_Private()
|
||||
{
|
||||
delete httpServer;
|
||||
};
|
||||
|
||||
i2p::util::HTTPServer *httpServer;
|
||||
};
|
||||
|
||||
Daemon_Singleton::Daemon_Singleton() : running(1), d(*new Daemon_Singleton_Private()) {};
|
||||
Daemon_Singleton::~Daemon_Singleton() {
|
||||
delete &d;
|
||||
};
|
||||
|
||||
bool Daemon_Singleton::IsService () const
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return i2p::util::config::GetArg("-service", 0);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Daemon_Singleton::init(int argc, char* argv[])
|
||||
{
|
||||
i2p::util::config::OptionParser(argc, argv);
|
||||
i2p::context.Init ();
|
||||
|
||||
LogPrint("\n\n\n\ni2pd starting\n");
|
||||
LogPrint("Version ", VERSION);
|
||||
LogPrint("data directory: ", i2p::util::filesystem::GetDataDir().string());
|
||||
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
|
||||
|
||||
isDaemon = i2p::util::config::GetArg("-daemon", 0);
|
||||
isLogging = i2p::util::config::GetArg("-log", 1);
|
||||
|
||||
int port = i2p::util::config::GetArg("-port", 0);
|
||||
if (port)
|
||||
i2p::context.UpdatePort (port);
|
||||
const char * host = i2p::util::config::GetCharArg("-host", "");
|
||||
if (host && host[0])
|
||||
i2p::context.UpdateAddress (boost::asio::ip::address::from_string (host));
|
||||
|
||||
if (i2p::util::config::GetArg("-unreachable", 0))
|
||||
i2p::context.SetUnreachable ();
|
||||
|
||||
i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0));
|
||||
|
||||
LogPrint("CMD parameters:");
|
||||
for (int i = 0; i < argc; ++i)
|
||||
LogPrint(i, " ", argv[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon_Singleton::start()
|
||||
{
|
||||
// initialize log
|
||||
if (isLogging)
|
||||
{
|
||||
if (isDaemon)
|
||||
{
|
||||
std::string logfile_path = IsService () ? "/var/log" : i2p::util::filesystem::GetDataDir().string();
|
||||
#ifndef _WIN32
|
||||
logfile_path.append("/i2pd.log");
|
||||
#else
|
||||
logfile_path.append("\\i2pd.log");
|
||||
#endif
|
||||
StartLog (logfile_path);
|
||||
}
|
||||
else
|
||||
StartLog (""); // write to stdout
|
||||
}
|
||||
|
||||
d.httpServer = new i2p::util::HTTPServer(i2p::util::config::GetArg("-httpport", 7070));
|
||||
d.httpServer->Start();
|
||||
LogPrint("HTTP Server started");
|
||||
i2p::data::netdb.Start();
|
||||
LogPrint("NetDB started");
|
||||
i2p::transport::transports.Start();
|
||||
LogPrint("Transports started");
|
||||
i2p::tunnel::tunnels.Start();
|
||||
LogPrint("Tunnels started");
|
||||
i2p::client::context.Start ();
|
||||
LogPrint("Client started");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon_Singleton::stop()
|
||||
{
|
||||
LogPrint("Shutdown started.");
|
||||
i2p::client::context.Stop();
|
||||
LogPrint("Client stoped");
|
||||
i2p::tunnel::tunnels.Stop();
|
||||
LogPrint("Tunnels stoped");
|
||||
i2p::transport::transports.Stop();
|
||||
LogPrint("Transports stoped");
|
||||
i2p::data::netdb.Stop();
|
||||
LogPrint("NetDB stoped");
|
||||
d.httpServer->Stop();
|
||||
LogPrint("HTTP Server stoped");
|
||||
StopLog ();
|
||||
|
||||
delete d.httpServer; d.httpServer = nullptr;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
71
Daemon.h
71
Daemon.h
@@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define Daemon i2p::util::DaemonWin32::Instance()
|
||||
#else
|
||||
#define Daemon i2p::util::DaemonLinux::Instance()
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
class Daemon_Singleton_Private;
|
||||
class Daemon_Singleton
|
||||
{
|
||||
public:
|
||||
virtual bool init(int argc, char* argv[]);
|
||||
virtual bool start();
|
||||
virtual bool stop();
|
||||
|
||||
int isLogging;
|
||||
int isDaemon;
|
||||
|
||||
int running;
|
||||
|
||||
protected:
|
||||
Daemon_Singleton();
|
||||
virtual ~Daemon_Singleton();
|
||||
|
||||
bool IsService () const;
|
||||
|
||||
// d-pointer for httpServer, httpProxy, etc.
|
||||
class Daemon_Singleton_Private;
|
||||
Daemon_Singleton_Private &d;
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
class DaemonWin32 : public Daemon_Singleton
|
||||
{
|
||||
public:
|
||||
static DaemonWin32& Instance()
|
||||
{
|
||||
static DaemonWin32 instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
virtual bool init(int argc, char* argv[]);
|
||||
virtual bool start();
|
||||
virtual bool stop();
|
||||
};
|
||||
#else
|
||||
class DaemonLinux : public Daemon_Singleton
|
||||
{
|
||||
public:
|
||||
static DaemonLinux& Instance()
|
||||
{
|
||||
static DaemonLinux instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
virtual bool start();
|
||||
virtual bool stop();
|
||||
private:
|
||||
std::string pidfile;
|
||||
int pidFilehandle;
|
||||
|
||||
};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
118
DaemonLinux.cpp
118
DaemonLinux.cpp
@@ -1,118 +0,0 @@
|
||||
#include "Daemon.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "Log.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
void handle_signal(int sig)
|
||||
{
|
||||
switch (sig)
|
||||
{
|
||||
case SIGHUP:
|
||||
if (i2p::util::config::GetArg("daemon", 0) == 1)
|
||||
{
|
||||
static bool first=true;
|
||||
if (first)
|
||||
{
|
||||
first=false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
LogPrint("Reloading config.");
|
||||
i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGTERM:
|
||||
case SIGINT:
|
||||
Daemon.running = 0; // Exit loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
bool DaemonLinux::start()
|
||||
{
|
||||
if (isDaemon == 1)
|
||||
{
|
||||
pid_t pid;
|
||||
pid = fork();
|
||||
if (pid > 0) // parent
|
||||
::exit (EXIT_SUCCESS);
|
||||
|
||||
if (pid < 0) // error
|
||||
return false;
|
||||
|
||||
// child
|
||||
umask(0);
|
||||
int sid = setsid();
|
||||
if (sid < 0)
|
||||
{
|
||||
LogPrint("Error, could not create process group.");
|
||||
return false;
|
||||
}
|
||||
chdir(i2p::util::filesystem::GetDataDir().string().c_str());
|
||||
|
||||
// close stdin/stdout/stderr descriptors
|
||||
::close (0);
|
||||
::open ("/dev/null", O_RDWR);
|
||||
::close (1);
|
||||
::open ("/dev/null", O_RDWR);
|
||||
::close (2);
|
||||
::open ("/dev/null", O_RDWR);
|
||||
}
|
||||
|
||||
// Pidfile
|
||||
pidfile = IsService () ? "/var/run" : i2p::util::filesystem::GetDataDir().string();
|
||||
pidfile.append("/i2pd.pid");
|
||||
pidFilehandle = open(pidfile.c_str(), O_RDWR | O_CREAT, 0600);
|
||||
if (pidFilehandle == -1)
|
||||
{
|
||||
LogPrint("Error, could not create pid file (", pidfile, ")\nIs an instance already running?");
|
||||
return false;
|
||||
}
|
||||
if (lockf(pidFilehandle, F_TLOCK, 0) == -1)
|
||||
{
|
||||
LogPrint("Error, could not lock pid file (", pidfile, ")\nIs an instance already running?");
|
||||
return false;
|
||||
}
|
||||
char pid[10];
|
||||
sprintf(pid, "%d\n", getpid());
|
||||
write(pidFilehandle, pid, strlen(pid));
|
||||
|
||||
// Signal handler
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = handle_signal;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigaction(SIGHUP, &sa, 0);
|
||||
sigaction(SIGABRT, &sa, 0);
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
|
||||
return Daemon_Singleton::start();
|
||||
}
|
||||
|
||||
bool DaemonLinux::stop()
|
||||
{
|
||||
close(pidFilehandle);
|
||||
unlink(pidfile.c_str());
|
||||
|
||||
return Daemon_Singleton::stop();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,83 +0,0 @@
|
||||
#include "Daemon.h"
|
||||
#include "util.h"
|
||||
#include "Log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#include "./Win32/Win32Service.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
bool DaemonWin32::init(int argc, char* argv[])
|
||||
{
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
SetConsoleOutputCP(1251);
|
||||
setlocale(LC_ALL, "Russian");
|
||||
|
||||
if (!Daemon_Singleton::init(argc, argv)) return false;
|
||||
if (I2PService::isService())
|
||||
isDaemon = 1;
|
||||
else
|
||||
isDaemon = 0;
|
||||
|
||||
std::string serviceControl = i2p::util::config::GetArg("-service", "none");
|
||||
if (serviceControl == "install")
|
||||
{
|
||||
InstallService(
|
||||
SERVICE_NAME, // Name of service
|
||||
SERVICE_DISPLAY_NAME, // Name to display
|
||||
SERVICE_START_TYPE, // Service start type
|
||||
SERVICE_DEPENDENCIES, // Dependencies
|
||||
SERVICE_ACCOUNT, // Service running account
|
||||
SERVICE_PASSWORD // Password of the account
|
||||
);
|
||||
exit(0);
|
||||
}
|
||||
else if (serviceControl == "remove")
|
||||
{
|
||||
UninstallService(SERVICE_NAME);
|
||||
exit(0);
|
||||
}
|
||||
else if (serviceControl != "none")
|
||||
{
|
||||
printf(" --service=install to install the service.\n");
|
||||
printf(" --service=remove to remove the service.\n");
|
||||
}
|
||||
|
||||
if (isDaemon == 1)
|
||||
{
|
||||
LogPrint("Service session");
|
||||
I2PService service(SERVICE_NAME);
|
||||
if (!I2PService::Run(service))
|
||||
{
|
||||
LogPrint("Service failed to run w/err 0x%08lx\n", GetLastError());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
else
|
||||
LogPrint("User session");
|
||||
|
||||
return true;
|
||||
}
|
||||
bool DaemonWin32::start()
|
||||
{
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
SetConsoleOutputCP(1251);
|
||||
setlocale(LC_ALL, "Russian");
|
||||
|
||||
return Daemon_Singleton::start();
|
||||
}
|
||||
|
||||
bool DaemonWin32::stop()
|
||||
{
|
||||
return Daemon_Singleton::stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
135
Datagram.cpp
135
Datagram.cpp
@@ -1,135 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <cryptopp/sha.h>
|
||||
#include <cryptopp/gzip.h>
|
||||
#include "Log.h"
|
||||
#include "TunnelBase.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Destination.h"
|
||||
#include "Datagram.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace datagram
|
||||
{
|
||||
DatagramDestination::DatagramDestination (i2p::client::ClientDestination& owner):
|
||||
m_Owner (owner), m_Receiver (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void DatagramDestination::SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::LeaseSet& remote)
|
||||
{
|
||||
uint8_t buf[MAX_DATAGRAM_SIZE];
|
||||
auto identityLen = m_Owner.GetIdentity ().ToBuffer (buf, MAX_DATAGRAM_SIZE);
|
||||
uint8_t * signature = buf + identityLen;
|
||||
auto signatureLen = m_Owner.GetIdentity ().GetSignatureLen ();
|
||||
uint8_t * buf1 = signature + signatureLen;
|
||||
size_t headerLen = identityLen + signatureLen;
|
||||
|
||||
memcpy (buf1, payload, len);
|
||||
if (m_Owner.GetIdentity ().GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
uint8_t hash[32];
|
||||
CryptoPP::SHA256().CalculateDigest (hash, buf1, len);
|
||||
m_Owner.Sign (hash, 32, signature);
|
||||
}
|
||||
else
|
||||
m_Owner.Sign (buf1, len, signature);
|
||||
|
||||
m_Owner.GetService ().post (std::bind (&DatagramDestination::SendMsg, this,
|
||||
CreateDataMessage (buf, len + headerLen), remote));
|
||||
}
|
||||
|
||||
void DatagramDestination::SendMsg (I2NPMessage * msg, const i2p::data::LeaseSet& remote)
|
||||
{
|
||||
auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
auto leases = remote.GetNonExpiredLeases ();
|
||||
if (!leases.empty () && outboundTunnel)
|
||||
{
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1);
|
||||
auto garlic = m_Owner.WrapMessage (remote, msg, true);
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeTunnel,
|
||||
leases[i].tunnelGateway, leases[i].tunnelID,
|
||||
garlic
|
||||
});
|
||||
outboundTunnel->SendTunnelDataMsg (msgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outboundTunnel)
|
||||
LogPrint (eLogWarning, "Failed to send datagram. All leases expired");
|
||||
else
|
||||
LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels");
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDatagram (const uint8_t * buf, size_t len)
|
||||
{
|
||||
i2p::data::IdentityEx identity;
|
||||
size_t identityLen = identity.FromBuffer (buf, len);
|
||||
const uint8_t * signature = buf + identityLen;
|
||||
size_t headerLen = identityLen + identity.GetSignatureLen ();
|
||||
|
||||
bool verified = false;
|
||||
if (identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
uint8_t hash[32];
|
||||
CryptoPP::SHA256().CalculateDigest (hash, buf + headerLen, len - headerLen);
|
||||
verified = identity.Verify (hash, 32, signature);
|
||||
}
|
||||
else
|
||||
verified = identity.Verify (buf + headerLen, len - headerLen, signature);
|
||||
|
||||
if (verified)
|
||||
{
|
||||
if (m_Receiver != nullptr)
|
||||
m_Receiver (identity, buf + headerLen, len -headerLen);
|
||||
else
|
||||
LogPrint (eLogWarning, "Receiver for datagram is not set");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram signature verification failed");
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// unzip it
|
||||
CryptoPP::Gunzip decompressor;
|
||||
decompressor.Put (buf, len);
|
||||
decompressor.MessageEnd();
|
||||
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
|
||||
auto uncompressedLen = decompressor.MaxRetrievable ();
|
||||
if (uncompressedLen <= MAX_DATAGRAM_SIZE)
|
||||
{
|
||||
decompressor.Get (uncompressed, uncompressedLen);
|
||||
HandleDatagram (uncompressed, uncompressedLen);
|
||||
}
|
||||
else
|
||||
LogPrint ("Received datagram size ", uncompressedLen, " exceeds max size");
|
||||
|
||||
}
|
||||
|
||||
I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage ();
|
||||
CryptoPP::Gzip compressor; // default level
|
||||
compressor.Put (payload, len);
|
||||
compressor.MessageEnd();
|
||||
int size = compressor.MaxRetrievable ();
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
*(uint32_t *)buf = htobe32 (size); // length
|
||||
buf += 4;
|
||||
compressor.Get (buf, size);
|
||||
memset (buf + 4, 0, 4); // source and destination are zeroes
|
||||
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
|
||||
msg->len += size + 4;
|
||||
FillI2NPMessageHeader (msg, eI2NPData);
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
49
Datagram.h
49
Datagram.h
@@ -1,49 +0,0 @@
|
||||
#ifndef DATAGRAM_H__
|
||||
#define DATAGRAM_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <functional>
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class ClientDestination;
|
||||
}
|
||||
namespace datagram
|
||||
{
|
||||
const size_t MAX_DATAGRAM_SIZE = 32768;
|
||||
class DatagramDestination
|
||||
{
|
||||
typedef std::function<void (const i2p::data::IdentityEx& ident, const uint8_t *, size_t)> Receiver;
|
||||
|
||||
public:
|
||||
|
||||
DatagramDestination (i2p::client::ClientDestination& owner);
|
||||
~DatagramDestination () {};
|
||||
|
||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::LeaseSet& remote);
|
||||
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
|
||||
|
||||
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
|
||||
void ResetReceiver () { m_Receiver = nullptr; };
|
||||
|
||||
private:
|
||||
|
||||
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len);
|
||||
void SendMsg (I2NPMessage * msg, const i2p::data::LeaseSet& remote);
|
||||
void HandleDatagram (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
i2p::client::ClientDestination& m_Owner;
|
||||
Receiver m_Receiver;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
354
Destination.cpp
354
Destination.cpp
@@ -1,354 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <cryptopp/dh.h>
|
||||
#include "Log.h"
|
||||
#include "util.h"
|
||||
#include "NetDb.h"
|
||||
#include "Destination.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
||||
const std::map<std::string, std::string> * params):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||
m_Keys (keys), m_LeaseSet (nullptr), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
||||
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service)
|
||||
{
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
|
||||
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
||||
if (params)
|
||||
{
|
||||
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int len = boost::lexical_cast<int>(it->second);
|
||||
if (len > 0)
|
||||
{
|
||||
inboundTunnelLen = len;
|
||||
LogPrint (eLogInfo, "Inbound tunnel length set to ", len);
|
||||
}
|
||||
}
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int len = boost::lexical_cast<int>(it->second);
|
||||
if (len > 0)
|
||||
{
|
||||
outboundTunnelLen = len;
|
||||
LogPrint (eLogInfo, "Outbound tunnel length set to ", len);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen);
|
||||
if (m_IsPublic)
|
||||
LogPrint (eLogInfo, "Local address ", GetIdentHash ().ToBase32 (), ".b32.i2p created");
|
||||
m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
|
||||
}
|
||||
|
||||
ClientDestination::~ClientDestination ()
|
||||
{
|
||||
Stop ();
|
||||
for (auto it: m_RemoteLeaseSets)
|
||||
delete it.second;
|
||||
if (m_Pool)
|
||||
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
|
||||
}
|
||||
|
||||
void ClientDestination::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("Destination: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::Start ()
|
||||
{
|
||||
m_Pool->SetLocalDestination (this);
|
||||
m_Pool->SetActive (true);
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&ClientDestination::Run, this));
|
||||
m_StreamingDestination->Start ();
|
||||
}
|
||||
|
||||
void ClientDestination::Stop ()
|
||||
{
|
||||
m_StreamingDestination->Stop ();
|
||||
if (m_DatagramDestination)
|
||||
{
|
||||
auto d = m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
delete d;
|
||||
}
|
||||
if (m_Pool)
|
||||
{
|
||||
m_Pool->SetLocalDestination (nullptr);
|
||||
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
|
||||
}
|
||||
m_IsRunning = false;
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
const i2p::data::LeaseSet * ClientDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
|
||||
{
|
||||
auto it = m_RemoteLeaseSets.find (ident);
|
||||
if (it != m_RemoteLeaseSets.end ())
|
||||
{
|
||||
if (it->second->HasNonExpiredLeases ())
|
||||
return it->second;
|
||||
else
|
||||
{
|
||||
LogPrint ("All leases of remote LeaseSet expired. Request it");
|
||||
i2p::data::netdb.RequestDestination (ident, true, m_Pool);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ls = i2p::data::netdb.FindLeaseSet (ident);
|
||||
if (ls)
|
||||
{
|
||||
ls = new i2p::data::LeaseSet (*ls);
|
||||
m_RemoteLeaseSets[ident] = ls;
|
||||
return ls;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const i2p::data::LeaseSet * ClientDestination::GetLeaseSet ()
|
||||
{
|
||||
if (!m_Pool) return nullptr;
|
||||
if (!m_LeaseSet)
|
||||
UpdateLeaseSet ();
|
||||
return m_LeaseSet;
|
||||
}
|
||||
|
||||
void ClientDestination::UpdateLeaseSet ()
|
||||
{
|
||||
auto newLeaseSet = new i2p::data::LeaseSet (*m_Pool);
|
||||
if (!m_LeaseSet)
|
||||
m_LeaseSet = newLeaseSet;
|
||||
else
|
||||
{
|
||||
// TODO: implement it better
|
||||
*m_LeaseSet = *newLeaseSet;
|
||||
delete newLeaseSet;
|
||||
}
|
||||
}
|
||||
|
||||
bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint8_t k[32], t[32];
|
||||
} data;
|
||||
memcpy (data.k, key, 32);
|
||||
memcpy (data.t, tag, 32);
|
||||
m_Service.post ([this,data](void)
|
||||
{
|
||||
this->AddSessionKey (data.k, data.t);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void ClientDestination::ProcessGarlicMessage (I2NPMessage * msg)
|
||||
{
|
||||
m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, this, msg));
|
||||
}
|
||||
|
||||
void ClientDestination::ProcessDeliveryStatusMessage (I2NPMessage * msg)
|
||||
{
|
||||
m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, this, msg));
|
||||
}
|
||||
|
||||
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
|
||||
{
|
||||
I2NPHeader * header = (I2NPHeader *)buf;
|
||||
switch (header->typeID)
|
||||
{
|
||||
case eI2NPData:
|
||||
HandleDataMessage (buf + sizeof (I2NPHeader), be16toh (header->size));
|
||||
break;
|
||||
case eI2NPDatabaseStore:
|
||||
HandleDatabaseStoreMessage (buf + sizeof (I2NPHeader), be16toh (header->size));
|
||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from)); // TODO: remove
|
||||
break;
|
||||
default:
|
||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf;
|
||||
size_t offset = sizeof (I2NPDatabaseStoreMsg);
|
||||
if (msg->replyToken) // TODO:
|
||||
offset += 36;
|
||||
if (msg->type == 1) // LeaseSet
|
||||
{
|
||||
LogPrint (eLogDebug, "Remote LeaseSet");
|
||||
auto it = m_RemoteLeaseSets.find (msg->key);
|
||||
if (it != m_RemoteLeaseSets.end ())
|
||||
{
|
||||
it->second->Update (buf + offset, len - offset);
|
||||
LogPrint (eLogDebug, "Remote LeaseSet updated");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "New remote LeaseSet added");
|
||||
m_RemoteLeaseSets[msg->key] = new i2p::data::LeaseSet (buf + offset, len - offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Unexpected client's DatabaseStore type ", msg->type, ". Dropped");
|
||||
}
|
||||
|
||||
void ClientDestination::HandleDeliveryStatusMessage (I2NPMessage * msg)
|
||||
{
|
||||
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
|
||||
uint32_t msgID = be32toh (deliveryStatus->msgID);
|
||||
if (msgID == m_PublishReplyToken)
|
||||
{
|
||||
LogPrint (eLogDebug, "Publishing confirmed");
|
||||
m_ExcludedFloodfills.clear ();
|
||||
m_PublishReplyToken = 0;
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
else
|
||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
|
||||
}
|
||||
|
||||
void ClientDestination::SetLeaseSetUpdated ()
|
||||
{
|
||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
||||
UpdateLeaseSet ();
|
||||
if (m_IsPublic)
|
||||
Publish ();
|
||||
}
|
||||
|
||||
void ClientDestination::Publish ()
|
||||
{
|
||||
if (!m_LeaseSet || !m_Pool)
|
||||
{
|
||||
LogPrint (eLogError, "Can't publish non-existing LeaseSet");
|
||||
return;
|
||||
}
|
||||
if (m_PublishReplyToken)
|
||||
{
|
||||
LogPrint (eLogInfo, "Publishing is pending");
|
||||
return;
|
||||
}
|
||||
auto outbound = m_Pool->GetNextOutboundTunnel ();
|
||||
if (!outbound)
|
||||
{
|
||||
LogPrint ("Can't publish LeaseSet. No outbound tunnels");
|
||||
return;
|
||||
}
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills);
|
||||
if (!floodfill)
|
||||
{
|
||||
LogPrint ("Can't publish LeaseSet. No more floodfills found");
|
||||
m_ExcludedFloodfills.clear ();
|
||||
return;
|
||||
}
|
||||
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
|
||||
LogPrint (eLogDebug, "Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
|
||||
m_PublishReplyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
||||
auto msg = WrapMessage (*floodfill, i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken));
|
||||
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
||||
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
|
||||
this, std::placeholders::_1));
|
||||
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
|
||||
}
|
||||
|
||||
void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
if (m_PublishReplyToken)
|
||||
{
|
||||
LogPrint (eLogWarning, "Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, "seconds. Try again");
|
||||
m_PublishReplyToken = 0;
|
||||
Publish ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t length = be32toh (*(uint32_t *)buf);
|
||||
buf += 4;
|
||||
// we assume I2CP payload
|
||||
switch (buf[9])
|
||||
{
|
||||
case PROTOCOL_TYPE_STREAMING:
|
||||
// streaming protocol
|
||||
if (m_StreamingDestination)
|
||||
m_StreamingDestination->HandleDataMessagePayload (buf, length);
|
||||
else
|
||||
LogPrint ("Missing streaming destination");
|
||||
break;
|
||||
case PROTOCOL_TYPE_DATAGRAM:
|
||||
// datagram protocol
|
||||
if (m_DatagramDestination)
|
||||
m_DatagramDestination->HandleDataMessagePayload (buf, length);
|
||||
else
|
||||
LogPrint ("Missing streaming destination");
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Data: unexpected protocol ", buf[9]);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (const i2p::data::LeaseSet& remote, int port)
|
||||
{
|
||||
if (m_StreamingDestination)
|
||||
return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ClientDestination::AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor)
|
||||
{
|
||||
if (m_StreamingDestination)
|
||||
m_StreamingDestination->SetAcceptor (acceptor);
|
||||
}
|
||||
|
||||
void ClientDestination::StopAcceptingStreams ()
|
||||
{
|
||||
if (m_StreamingDestination)
|
||||
m_StreamingDestination->ResetAcceptor ();
|
||||
}
|
||||
|
||||
bool ClientDestination::IsAcceptingStreams () const
|
||||
{
|
||||
if (m_StreamingDestination)
|
||||
return m_StreamingDestination->IsAcceptorSet ();
|
||||
return false;
|
||||
}
|
||||
|
||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
|
||||
{
|
||||
if (!m_DatagramDestination)
|
||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (*this);
|
||||
return m_DatagramDestination;
|
||||
}
|
||||
}
|
||||
}
|
||||
114
Destination.h
114
Destination.h
@@ -1,114 +0,0 @@
|
||||
#ifndef DESTINATION_H__
|
||||
#define DESTINATION_H__
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "Identity.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Garlic.h"
|
||||
#include "Streaming.h"
|
||||
#include "Datagram.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
|
||||
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
|
||||
const uint8_t PROTOCOL_TYPE_RAW = 18;
|
||||
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
|
||||
|
||||
// I2CP
|
||||
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
|
||||
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
|
||||
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
|
||||
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
|
||||
|
||||
class ClientDestination: public i2p::garlic::GarlicDestination
|
||||
{
|
||||
public:
|
||||
|
||||
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~ClientDestination ();
|
||||
|
||||
virtual void Start ();
|
||||
virtual void Stop ();
|
||||
bool IsRunning () const { return m_IsRunning; };
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; };
|
||||
bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases (); };
|
||||
const i2p::data::LeaseSet * FindLeaseSet (const i2p::data::IdentHash& ident);
|
||||
|
||||
// streaming
|
||||
i2p::stream::StreamingDestination * GetStreamingDestination () const { return m_StreamingDestination; };
|
||||
std::shared_ptr<i2p::stream::Stream> CreateStream (const i2p::data::LeaseSet& remote, int port = 0);
|
||||
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
|
||||
void StopAcceptingStreams ();
|
||||
bool IsAcceptingStreams () const;
|
||||
|
||||
// datagram
|
||||
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
|
||||
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
|
||||
|
||||
// implements LocalDestination
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionPublicKey; };
|
||||
|
||||
// implements GarlicDestination
|
||||
const i2p::data::LeaseSet * GetLeaseSet ();
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from);
|
||||
|
||||
// override GarlicDestination
|
||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
||||
void ProcessGarlicMessage (I2NPMessage * msg);
|
||||
void ProcessDeliveryStatusMessage (I2NPMessage * msg);
|
||||
void SetLeaseSetUpdated ();
|
||||
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void UpdateLeaseSet ();
|
||||
void Publish ();
|
||||
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
||||
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
|
||||
void HandleDeliveryStatusMessage (I2NPMessage * msg);
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
uint8_t m_EncryptionPublicKey[256], m_EncryptionPrivateKey[256];
|
||||
std::map<i2p::data::IdentHash, i2p::data::LeaseSet *> m_RemoteLeaseSets;
|
||||
|
||||
i2p::tunnel::TunnelPool * m_Pool;
|
||||
i2p::data::LeaseSet * m_LeaseSet;
|
||||
bool m_IsPublic;
|
||||
uint32_t m_PublishReplyToken;
|
||||
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
|
||||
|
||||
i2p::stream::StreamingDestination * m_StreamingDestination;
|
||||
i2p::datagram::DatagramDestination * m_DatagramDestination;
|
||||
|
||||
boost::asio::deadline_timer m_PublishConfirmationTimer;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
54
Dockerfile
Normal file
54
Dockerfile
Normal file
@@ -0,0 +1,54 @@
|
||||
FROM alpine:latest
|
||||
|
||||
MAINTAINER Mikal Villa <mikal@sigterm.no>
|
||||
|
||||
ENV GIT_BRANCH="master"
|
||||
ENV I2PD_PREFIX="/opt/i2pd-${GIT_BRANCH}"
|
||||
ENV PATH=${I2PD_PREFIX}/bin:$PATH
|
||||
|
||||
ENV GOSU_VERSION=1.7
|
||||
ENV GOSU_SHASUM="34049cfc713e8b74b90d6de49690fa601dc040021980812b2f1f691534be8a50 /usr/local/bin/gosu"
|
||||
|
||||
RUN mkdir /user && adduser -S -h /user i2pd && chown -R i2pd:nobody /user
|
||||
|
||||
|
||||
#
|
||||
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
|
||||
# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer.
|
||||
#
|
||||
|
||||
# 1. install deps, clone and build.
|
||||
# 2. strip binaries.
|
||||
# 3. Purge all dependencies and other unrelated packages, including build directory.
|
||||
RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool boost-dev build-base openssl-dev openssl git \
|
||||
&& mkdir -p /tmp/build \
|
||||
&& cd /tmp/build && git clone -b ${GIT_BRANCH} https://github.com/PurpleI2P/i2pd.git \
|
||||
&& cd i2pd \
|
||||
&& make -j4 \
|
||||
&& mkdir -p ${I2PD_PREFIX}/bin \
|
||||
&& mv i2pd ${I2PD_PREFIX}/bin/ \
|
||||
&& cd ${I2PD_PREFIX}/bin \
|
||||
&& strip i2pd \
|
||||
&& rm -fr /tmp/build && apk --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \
|
||||
boost-python3 python3 gdbm boost-unit_test_framework boost-python linux-headers boost-prg_exec_monitor \
|
||||
boost-serialization boost-signals boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
|
||||
libtool g++ gcc pkgconfig
|
||||
|
||||
# 2. Adding required libraries to run i2pd to ensure it will run.
|
||||
RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl musl-utils libstdc++
|
||||
|
||||
# Gosu is a replacement for su/sudo in docker and not a backdoor :) See https://github.com/tianon/gosu
|
||||
RUN wget -O /usr/local/bin/gosu https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64 \
|
||||
&& echo "${GOSU_SHASUM}" | sha256sum -c && chmod +x /usr/local/bin/gosu
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
RUN chmod a+x /entrypoint.sh
|
||||
RUN echo "export PATH=${PATH}" >> /etc/profile
|
||||
|
||||
VOLUME [ "/var/lib/i2pd" ]
|
||||
|
||||
EXPOSE 7070 4444 4447 7656 2827 7654 7650
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
|
||||
77
ElGamal.h
77
ElGamal.h
@@ -1,77 +0,0 @@
|
||||
#ifndef EL_GAMAL_H__
|
||||
#define EL_GAMAL_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cryptopp/integer.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/sha.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
|
||||
class ElGamalEncryption
|
||||
{
|
||||
public:
|
||||
|
||||
ElGamalEncryption (const uint8_t * key):
|
||||
y (key, 256), k (rnd, CryptoPP::Integer::One(), elgp-1),
|
||||
a (a_exp_b_mod_c (elgg, k, elgp)), b1 (a_exp_b_mod_c (y, k, elgp))
|
||||
{
|
||||
}
|
||||
|
||||
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false)
|
||||
{
|
||||
// calculate b = b1*m mod p
|
||||
uint8_t m[255];
|
||||
m[0] = 0xFF;
|
||||
memcpy (m+33, data, len);
|
||||
CryptoPP::SHA256().CalculateDigest(m+1, m+33, 222);
|
||||
CryptoPP::Integer b (a_times_b_mod_c (b1, CryptoPP::Integer (m, 255), elgp));
|
||||
|
||||
// copy a and b
|
||||
if (zeroPadding)
|
||||
{
|
||||
encrypted[0] = 0;
|
||||
a.Encode (encrypted + 1, 256);
|
||||
encrypted[257] = 0;
|
||||
b.Encode (encrypted + 258, 256);
|
||||
}
|
||||
else
|
||||
{
|
||||
a.Encode (encrypted, 256);
|
||||
b.Encode (encrypted + 256, 256);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::AutoSeededRandomPool rnd;
|
||||
CryptoPP::Integer y, k, a, b1;
|
||||
bool m_ZeroPadding;
|
||||
};
|
||||
|
||||
inline bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
|
||||
uint8_t * data, bool zeroPadding = false)
|
||||
{
|
||||
CryptoPP::Integer x(key, 256), a(zeroPadding? encrypted +1 : encrypted, 256),
|
||||
b(zeroPadding? encrypted + 258 :encrypted + 256, 256);
|
||||
uint8_t m[255], hash[32];
|
||||
a_times_b_mod_c (b, a_exp_b_mod_c (a, elgp - x - 1, elgp), elgp).Encode (m, 255);
|
||||
CryptoPP::SHA256().CalculateDigest(hash, m+33, 222);
|
||||
for (int i = 0; i < 32; i++)
|
||||
if (hash[i] != m[i+1])
|
||||
{
|
||||
LogPrint ("ElGamal decrypt hash doesn't match");
|
||||
return false;
|
||||
}
|
||||
memcpy (data, m + 33, 222);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
563
Garlic.cpp
563
Garlic.cpp
@@ -1,563 +0,0 @@
|
||||
#include <inttypes.h>
|
||||
#include "I2PEndian.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "RouterContext.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Destination.h"
|
||||
#include "Garlic.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace garlic
|
||||
{
|
||||
GarlicRoutingSession::GarlicRoutingSession (GarlicDestination * owner,
|
||||
const i2p::data::RoutingDestination * destination, int numTags):
|
||||
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
|
||||
m_LeaseSetUpdated (numTags > 0)
|
||||
{
|
||||
// create new session tags and session key
|
||||
m_Rnd.GenerateBlock (m_SessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
}
|
||||
|
||||
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
|
||||
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdated (false)
|
||||
{
|
||||
memcpy (m_SessionKey, sessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
m_SessionTags.push_back (sessionTag);
|
||||
m_SessionTags.back ().creationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
GarlicRoutingSession::~GarlicRoutingSession ()
|
||||
{
|
||||
for (auto it: m_UnconfirmedTagsMsgs)
|
||||
delete it.second;
|
||||
m_UnconfirmedTagsMsgs.clear ();
|
||||
}
|
||||
|
||||
GarlicRoutingSession::UnconfirmedTags * GarlicRoutingSession::GenerateSessionTags ()
|
||||
{
|
||||
auto tags = new UnconfirmedTags (m_NumTags);
|
||||
tags->tagsCreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (int i = 0; i < m_NumTags; i++)
|
||||
{
|
||||
m_Rnd.GenerateBlock (tags->sessionTags[i], 32);
|
||||
tags->sessionTags[i].creationTime = tags->tagsCreationTime;
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto it = m_UnconfirmedTagsMsgs.find (msgID);
|
||||
if (it != m_UnconfirmedTagsMsgs.end ())
|
||||
{
|
||||
UnconfirmedTags * tags = it->second;
|
||||
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
for (int i = 0; i < tags->numTags; i++)
|
||||
m_SessionTags.push_back (tags->sessionTags[i]);
|
||||
}
|
||||
m_UnconfirmedTagsMsgs.erase (it);
|
||||
delete tags;
|
||||
}
|
||||
// delete expired unconfirmed tags
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
{
|
||||
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
delete it->second;
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
I2NPMessage * GarlicRoutingSession::WrapSingleMessage (I2NPMessage * msg)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
||||
size_t len = 0;
|
||||
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
||||
|
||||
// find non-expired tag
|
||||
bool tagFound = false;
|
||||
SessionTag tag;
|
||||
if (m_NumTags > 0)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
while (!m_SessionTags.empty ())
|
||||
{
|
||||
if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
tag = m_SessionTags.front ();
|
||||
m_SessionTags.pop_front (); // use same tag only once
|
||||
tagFound = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
m_SessionTags.pop_front (); // remove expired tag
|
||||
}
|
||||
}
|
||||
// create message
|
||||
if (!tagFound) // new session
|
||||
{
|
||||
LogPrint ("No garlic tags available. Use ElGamal");
|
||||
if (!m_Destination)
|
||||
{
|
||||
LogPrint ("Can't use ElGamal for unknown destination");
|
||||
return nullptr;
|
||||
}
|
||||
// create ElGamal block
|
||||
ElGamalBlock elGamal;
|
||||
memcpy (elGamal.sessionKey, m_SessionKey, 32);
|
||||
m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
|
||||
m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
|
||||
m_Encryption.SetIV (iv);
|
||||
buf += 514;
|
||||
len += 514;
|
||||
}
|
||||
else // existing session
|
||||
{
|
||||
// session tag
|
||||
memcpy (buf, tag, 32);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
CryptoPP::SHA256().CalculateDigest(iv, tag, 32);
|
||||
m_Encryption.SetIV (iv);
|
||||
buf += 32;
|
||||
len += 32;
|
||||
}
|
||||
// AES block
|
||||
len += CreateAESBlock (buf, msg);
|
||||
*(uint32_t *)(m->GetPayload ()) = htobe32 (len);
|
||||
m->len += len + 4;
|
||||
FillI2NPMessageHeader (m, eI2NPGarlic);
|
||||
if (msg)
|
||||
DeleteI2NPMessage (msg);
|
||||
return m;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateAESBlock (uint8_t * buf, const I2NPMessage * msg)
|
||||
{
|
||||
size_t blockSize = 0;
|
||||
bool createNewTags = m_Owner && m_NumTags && ((int)m_SessionTags.size () <= m_NumTags/2);
|
||||
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
|
||||
*(uint16_t *)buf = newTags ? htobe16 (newTags->numTags) : 0; // tag count
|
||||
blockSize += 2;
|
||||
if (newTags) // session tags recreated
|
||||
{
|
||||
for (int i = 0; i < newTags->numTags; i++)
|
||||
{
|
||||
memcpy (buf + blockSize, newTags->sessionTags[i], 32); // tags
|
||||
blockSize += 32;
|
||||
}
|
||||
}
|
||||
uint32_t * payloadSize = (uint32_t *)(buf + blockSize);
|
||||
blockSize += 4;
|
||||
uint8_t * payloadHash = buf + blockSize;
|
||||
blockSize += 32;
|
||||
buf[blockSize] = 0; // flag
|
||||
blockSize++;
|
||||
size_t len = CreateGarlicPayload (buf + blockSize, msg, newTags);
|
||||
*payloadSize = htobe32 (len);
|
||||
CryptoPP::SHA256().CalculateDigest(payloadHash, buf + blockSize, len);
|
||||
blockSize += len;
|
||||
size_t rem = blockSize % 16;
|
||||
if (rem)
|
||||
blockSize += (16-rem); //padding
|
||||
m_Encryption.Encrypt(buf, blockSize, buf);
|
||||
return blockSize;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
||||
uint32_t msgID = m_Rnd.GenerateWord32 ();
|
||||
size_t size = 0;
|
||||
uint8_t * numCloves = payload + size;
|
||||
*numCloves = 0;
|
||||
size++;
|
||||
|
||||
if (m_Owner)
|
||||
{
|
||||
if (newTags) // new session
|
||||
{
|
||||
// clove is DeliveryStatus
|
||||
size += CreateDeliveryStatusClove (payload + size, msgID);
|
||||
if (size > 0) // successive?
|
||||
{
|
||||
(*numCloves)++;
|
||||
m_UnconfirmedTagsMsgs[msgID] = newTags;
|
||||
m_Owner->DeliveryStatusSent (this, msgID);
|
||||
}
|
||||
else
|
||||
LogPrint ("DeliveryStatus clove was not created");
|
||||
}
|
||||
if (m_LeaseSetUpdated)
|
||||
{
|
||||
m_LeaseSetUpdated = false;
|
||||
// clove if our leaseSet must be attached
|
||||
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
|
||||
size += CreateGarlicClove (payload + size, leaseSet, false);
|
||||
DeleteI2NPMessage (leaseSet);
|
||||
(*numCloves)++;
|
||||
}
|
||||
}
|
||||
if (msg) // clove message ifself if presented
|
||||
{
|
||||
size += CreateGarlicClove (payload + size, msg, m_Destination ? m_Destination->IsDestination () : false);
|
||||
(*numCloves)++;
|
||||
}
|
||||
|
||||
memset (payload + size, 0, 3); // certificate of message
|
||||
size += 3;
|
||||
*(uint32_t *)(payload + size) = htobe32 (msgID); // MessageID
|
||||
size += 4;
|
||||
*(uint64_t *)(payload + size) = htobe64 (ts); // Expiration of message
|
||||
size += 8;
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
||||
size_t size = 0;
|
||||
if (isDestination && m_Destination)
|
||||
{
|
||||
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
|
||||
size++;
|
||||
memcpy (buf + size, m_Destination->GetIdentHash (), 32);
|
||||
size += 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
buf[size] = 0;// delivery instructions flag local
|
||||
size++;
|
||||
}
|
||||
|
||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
||||
size += msg->GetLength ();
|
||||
*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
|
||||
size += 4;
|
||||
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
|
||||
size += 8;
|
||||
memset (buf + size, 0, 3); // certificate of clove
|
||||
size += 3;
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t GarlicRoutingSession::CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID)
|
||||
{
|
||||
size_t size = 0;
|
||||
if (m_Owner)
|
||||
{
|
||||
auto leases = m_Owner->GetLeaseSet ()->GetNonExpiredLeases ();
|
||||
if (!leases.empty ())
|
||||
{
|
||||
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
|
||||
size++;
|
||||
uint32_t i = m_Rnd.GenerateWord32 (0, leases.size () - 1);
|
||||
// hash and tunnelID sequence is reversed for Garlic
|
||||
memcpy (buf + size, leases[i].tunnelGateway, 32); // To Hash
|
||||
size += 32;
|
||||
*(uint32_t *)(buf + size) = htobe32 (leases[i].tunnelID); // tunnelID
|
||||
size += 4;
|
||||
// create msg
|
||||
I2NPMessage * msg = CreateDeliveryStatusMsg (msgID);
|
||||
if (m_Owner)
|
||||
{
|
||||
//encrypt
|
||||
uint8_t key[32], tag[32];
|
||||
m_Rnd.GenerateBlock (key, 32); // random session key
|
||||
m_Rnd.GenerateBlock (tag, 32); // random session tag
|
||||
m_Owner->SubmitSessionKey (key, tag);
|
||||
GarlicRoutingSession garlic (key, tag);
|
||||
msg = garlic.WrapSingleMessage (msg);
|
||||
}
|
||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
||||
size += msg->GetLength ();
|
||||
DeleteI2NPMessage (msg);
|
||||
// fill clove
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
||||
*(uint32_t *)(buf + size) = htobe32 (m_Rnd.GenerateWord32 ()); // CloveID
|
||||
size += 4;
|
||||
*(uint64_t *)(buf + size) = htobe64 (ts); // Expiration of clove
|
||||
size += 8;
|
||||
memset (buf + size, 0, 3); // certificate of clove
|
||||
size += 3;
|
||||
}
|
||||
else
|
||||
LogPrint ("All tunnels of local LeaseSet expired");
|
||||
}
|
||||
else
|
||||
LogPrint ("Missing local LeaseSet");
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
GarlicDestination::~GarlicDestination ()
|
||||
{
|
||||
for (auto it: m_Sessions)
|
||||
delete it.second;
|
||||
m_Sessions.clear ();
|
||||
}
|
||||
|
||||
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||
{
|
||||
if (key)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
|
||||
decryption->SetKey (key);
|
||||
m_Tags[SessionTag(tag, ts)] = decryption;
|
||||
}
|
||||
}
|
||||
|
||||
bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||
{
|
||||
AddSessionKey (key, tag);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleGarlicMessage (I2NPMessage * msg)
|
||||
{
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
uint32_t length = be32toh (*(uint32_t *)buf);
|
||||
buf += 4; // length
|
||||
auto it = m_Tags.find (SessionTag(buf));
|
||||
if (it != m_Tags.end ())
|
||||
{
|
||||
// tag found. Use AES
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
CryptoPP::SHA256().CalculateDigest(iv, buf, 32);
|
||||
it->second->SetIV (iv);
|
||||
it->second->Decrypt (buf + 32, length - 32, buf + 32);
|
||||
HandleAESBlock (buf + 32, length - 32, it->second, msg->from);
|
||||
m_Tags.erase (it); // tag might be used only once
|
||||
}
|
||||
else
|
||||
{
|
||||
// tag not found. Use ElGamal
|
||||
ElGamalBlock elGamal;
|
||||
if (i2p::crypto::ElGamalDecrypt (GetEncryptionPrivateKey (), buf, (uint8_t *)&elGamal, true))
|
||||
{
|
||||
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
|
||||
decryption->SetKey (elGamal.sessionKey);
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
|
||||
decryption->SetIV (iv);
|
||||
decryption->Decrypt(buf + 514, length - 514, buf + 514);
|
||||
HandleAESBlock (buf + 514, length - 514, decryption, msg->from);
|
||||
}
|
||||
else
|
||||
LogPrint ("Failed to decrypt garlic");
|
||||
}
|
||||
DeleteI2NPMessage (msg);
|
||||
|
||||
// cleanup expired tags
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts > m_LastTagsCleanupTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
if (m_LastTagsCleanupTime)
|
||||
{
|
||||
int numExpiredTags = 0;
|
||||
for (auto it = m_Tags.begin (); it != m_Tags.end ();)
|
||||
{
|
||||
if (ts > it->first.creationTime + INCOMING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
numExpiredTags++;
|
||||
it = m_Tags.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
LogPrint (numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
|
||||
}
|
||||
m_LastTagsCleanupTime = ts;
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
|
||||
i2p::tunnel::InboundTunnel * from)
|
||||
{
|
||||
uint16_t tagCount = be16toh (*(uint16_t *)buf);
|
||||
buf += 2; len -= 2;
|
||||
if (tagCount > 0)
|
||||
{
|
||||
if (tagCount*32 > len)
|
||||
{
|
||||
LogPrint (eLogError, "Tag count ", tagCount, " exceeds length ", len);
|
||||
return ;
|
||||
}
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (int i = 0; i < tagCount; i++)
|
||||
m_Tags[SessionTag(buf + i*32, ts)] = decryption;
|
||||
}
|
||||
buf += tagCount*32;
|
||||
len -= tagCount*32;
|
||||
uint32_t payloadSize = be32toh (*(uint32_t *)buf);
|
||||
if (payloadSize > len)
|
||||
{
|
||||
LogPrint (eLogError, "Unexpected payload size ", payloadSize);
|
||||
return;
|
||||
}
|
||||
buf += 4;
|
||||
uint8_t * payloadHash = buf;
|
||||
buf += 32;// payload hash.
|
||||
if (*buf) // session key?
|
||||
buf += 32; // new session key
|
||||
buf++; // flag
|
||||
|
||||
// payload
|
||||
uint8_t hash[32];
|
||||
CryptoPP::SHA256().CalculateDigest(hash, buf, payloadSize);
|
||||
if (memcmp (hash, payloadHash, 32)) // payload hash doesn't match
|
||||
{
|
||||
LogPrint ("Wrong payload hash");
|
||||
return;
|
||||
}
|
||||
HandleGarlicPayload (buf, payloadSize, from);
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
|
||||
{
|
||||
int numCloves = buf[0];
|
||||
LogPrint (numCloves," cloves");
|
||||
buf++;
|
||||
for (int i = 0; i < numCloves; i++)
|
||||
{
|
||||
// delivery instructions
|
||||
uint8_t flag = buf[0];
|
||||
buf++; // flag
|
||||
if (flag & 0x80) // encrypted?
|
||||
{
|
||||
// TODO: implement
|
||||
LogPrint ("Clove encrypted");
|
||||
buf += 32;
|
||||
}
|
||||
GarlicDeliveryType deliveryType = (GarlicDeliveryType)((flag >> 5) & 0x03);
|
||||
switch (deliveryType)
|
||||
{
|
||||
case eGarlicDeliveryTypeLocal:
|
||||
LogPrint ("Garlic type local");
|
||||
HandleI2NPMessage (buf, len, from);
|
||||
break;
|
||||
case eGarlicDeliveryTypeDestination:
|
||||
LogPrint ("Garlic type destination");
|
||||
buf += 32; // destination. check it later or for multiple destinations
|
||||
HandleI2NPMessage (buf, len, from);
|
||||
break;
|
||||
case eGarlicDeliveryTypeTunnel:
|
||||
{
|
||||
LogPrint ("Garlic type tunnel");
|
||||
// gwHash and gwTunnel sequence is reverted
|
||||
uint8_t * gwHash = buf;
|
||||
buf += 32;
|
||||
uint32_t gwTunnel = be32toh (*(uint32_t *)buf);
|
||||
buf += 4;
|
||||
i2p::tunnel::OutboundTunnel * tunnel = nullptr;
|
||||
if (from && from->GetTunnelPool ())
|
||||
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
if (tunnel) // we have send it through an outbound tunnel
|
||||
{
|
||||
I2NPMessage * msg = CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from);
|
||||
tunnel->SendTunnelDataMsg (gwHash, gwTunnel, msg);
|
||||
}
|
||||
else
|
||||
LogPrint ("No outbound tunnels available for garlic clove");
|
||||
break;
|
||||
}
|
||||
case eGarlicDeliveryTypeRouter:
|
||||
LogPrint ("Garlic type router not supported");
|
||||
buf += 32;
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Unknow garlic delivery type ", (int)deliveryType);
|
||||
}
|
||||
buf += GetI2NPMessageLength (buf); // I2NP
|
||||
buf += 4; // CloveID
|
||||
buf += 8; // Date
|
||||
buf += 3; // Certificate
|
||||
}
|
||||
}
|
||||
|
||||
I2NPMessage * GarlicDestination::WrapMessage (const i2p::data::RoutingDestination& destination,
|
||||
I2NPMessage * msg, bool attachLeaseSet)
|
||||
{
|
||||
if (attachLeaseSet) // we should maintain this session
|
||||
{
|
||||
auto session = GetRoutingSession (destination, 32); // 32 tags by default
|
||||
return session->WrapSingleMessage (msg);
|
||||
}
|
||||
else // one time session
|
||||
{
|
||||
GarlicRoutingSession session (this, &destination, 0); // don't use tag if no LeaseSet
|
||||
return session.WrapSingleMessage (msg);
|
||||
}
|
||||
}
|
||||
|
||||
GarlicRoutingSession * GarlicDestination::GetRoutingSession (
|
||||
const i2p::data::RoutingDestination& destination, int numTags)
|
||||
{
|
||||
auto it = m_Sessions.find (destination.GetIdentHash ());
|
||||
GarlicRoutingSession * session = nullptr;
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
if (!session)
|
||||
{
|
||||
session = new GarlicRoutingSession (this, &destination, numTags);
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
m_Sessions[destination.GetIdentHash ()] = session;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID)
|
||||
{
|
||||
m_CreatedSessions[msgID] = session;
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleDeliveryStatusMessage (I2NPMessage * msg)
|
||||
{
|
||||
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
|
||||
uint32_t msgID = be32toh (deliveryStatus->msgID);
|
||||
{
|
||||
auto it = m_CreatedSessions.find (msgID);
|
||||
if (it != m_CreatedSessions.end ())
|
||||
{
|
||||
it->second->TagsConfirmed (msgID);
|
||||
m_CreatedSessions.erase (it);
|
||||
LogPrint ("Garlic message ", msgID, " acknowledged");
|
||||
}
|
||||
}
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
|
||||
void GarlicDestination::SetLeaseSetUpdated ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
for (auto it: m_Sessions)
|
||||
it.second->SetLeaseSetUpdated ();
|
||||
}
|
||||
|
||||
void GarlicDestination::ProcessGarlicMessage (I2NPMessage * msg)
|
||||
{
|
||||
HandleGarlicMessage (msg);
|
||||
}
|
||||
|
||||
void GarlicDestination::ProcessDeliveryStatusMessage (I2NPMessage * msg)
|
||||
{
|
||||
HandleDeliveryStatusMessage (msg);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
148
Garlic.h
148
Garlic.h
@@ -1,148 +0,0 @@
|
||||
#ifndef GARLIC_H__
|
||||
#define GARLIC_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include "aes.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Queue.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace garlic
|
||||
{
|
||||
|
||||
enum GarlicDeliveryType
|
||||
{
|
||||
eGarlicDeliveryTypeLocal = 0,
|
||||
eGarlicDeliveryTypeDestination = 1,
|
||||
eGarlicDeliveryTypeRouter = 2,
|
||||
eGarlicDeliveryTypeTunnel = 3
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
struct ElGamalBlock
|
||||
{
|
||||
uint8_t sessionKey[32];
|
||||
uint8_t preIV[32];
|
||||
uint8_t padding[158];
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
const int INCOMING_TAGS_EXPIRATION_TIMEOUT = 960; // 16 minutes
|
||||
const int OUTGOING_TAGS_EXPIRATION_TIMEOUT = 720; // 12 minutes
|
||||
|
||||
struct SessionTag: public i2p::data::Tag<32>
|
||||
{
|
||||
SessionTag (const uint8_t * buf, uint32_t ts = 0): Tag<32>(buf), creationTime (ts) {};
|
||||
SessionTag () = default;
|
||||
SessionTag (const SessionTag& ) = default;
|
||||
SessionTag& operator= (const SessionTag& ) = default;
|
||||
#ifndef _WIN32
|
||||
SessionTag (SessionTag&& ) = default;
|
||||
SessionTag& operator= (SessionTag&& ) = default;
|
||||
#endif
|
||||
uint32_t creationTime; // seconds since epoch
|
||||
};
|
||||
|
||||
class GarlicDestination;
|
||||
class GarlicRoutingSession
|
||||
{
|
||||
struct UnconfirmedTags
|
||||
{
|
||||
UnconfirmedTags (int n): numTags (n), tagsCreationTime (0) { sessionTags = new SessionTag[numTags]; };
|
||||
~UnconfirmedTags () { delete[] sessionTags; };
|
||||
int numTags;
|
||||
SessionTag * sessionTags;
|
||||
uint32_t tagsCreationTime;
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
GarlicRoutingSession (GarlicDestination * owner, const i2p::data::RoutingDestination * destination, int numTags);
|
||||
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
|
||||
~GarlicRoutingSession ();
|
||||
I2NPMessage * WrapSingleMessage (I2NPMessage * msg);
|
||||
void TagsConfirmed (uint32_t msgID);
|
||||
|
||||
void SetLeaseSetUpdated () { m_LeaseSetUpdated = true; };
|
||||
|
||||
private:
|
||||
|
||||
size_t CreateAESBlock (uint8_t * buf, const I2NPMessage * msg);
|
||||
size_t CreateGarlicPayload (uint8_t * payload, const I2NPMessage * msg, UnconfirmedTags * newTags);
|
||||
size_t CreateGarlicClove (uint8_t * buf, const I2NPMessage * msg, bool isDestination);
|
||||
size_t CreateDeliveryStatusClove (uint8_t * buf, uint32_t msgID);
|
||||
|
||||
UnconfirmedTags * GenerateSessionTags ();
|
||||
|
||||
private:
|
||||
|
||||
GarlicDestination * m_Owner;
|
||||
const i2p::data::RoutingDestination * m_Destination;
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
std::list<SessionTag> m_SessionTags;
|
||||
int m_NumTags;
|
||||
std::map<uint32_t, UnconfirmedTags *> m_UnconfirmedTagsMsgs;
|
||||
bool m_LeaseSetUpdated;
|
||||
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
CryptoPP::AutoSeededRandomPool m_Rnd;
|
||||
};
|
||||
|
||||
class GarlicDestination: public i2p::data::LocalDestination
|
||||
{
|
||||
public:
|
||||
|
||||
GarlicDestination (): m_LastTagsCleanupTime (0) {};
|
||||
~GarlicDestination ();
|
||||
|
||||
GarlicRoutingSession * GetRoutingSession (const i2p::data::RoutingDestination& destination, int numTags);
|
||||
I2NPMessage * WrapMessage (const i2p::data::RoutingDestination& destination,
|
||||
I2NPMessage * msg, bool attachLeaseSet = false);
|
||||
|
||||
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
|
||||
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
|
||||
void DeliveryStatusSent (GarlicRoutingSession * session, uint32_t msgID);
|
||||
|
||||
virtual void ProcessGarlicMessage (I2NPMessage * msg);
|
||||
virtual void ProcessDeliveryStatusMessage (I2NPMessage * msg);
|
||||
virtual void SetLeaseSetUpdated ();
|
||||
|
||||
virtual const i2p::data::LeaseSet * GetLeaseSet () = 0; // TODO
|
||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
void HandleGarlicMessage (I2NPMessage * msg);
|
||||
void HandleDeliveryStatusMessage (I2NPMessage * msg);
|
||||
|
||||
private:
|
||||
|
||||
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
|
||||
i2p::tunnel::InboundTunnel * from);
|
||||
void HandleGarlicPayload (uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from);
|
||||
|
||||
private:
|
||||
|
||||
// outgoing sessions
|
||||
std::mutex m_SessionsMutex;
|
||||
std::map<i2p::data::IdentHash, GarlicRoutingSession *> m_Sessions;
|
||||
// incoming
|
||||
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
|
||||
uint32_t m_LastTagsCleanupTime;
|
||||
// DeliveryStatus
|
||||
std::map<uint32_t, GarlicRoutingSession *> m_CreatedSessions; // msgID -> session
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,87 +0,0 @@
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include "ClientContext.h"
|
||||
#include "HTTPProxy.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace proxy
|
||||
{
|
||||
void HTTPProxyConnection::parseHeaders(const std::string& h, std::vector<header>& hm) {
|
||||
std::string str (h);
|
||||
std::string::size_type idx;
|
||||
std::string t;
|
||||
int i = 0;
|
||||
while( (idx=str.find ("\r\n")) != std::string::npos) {
|
||||
t=str.substr (0,idx);
|
||||
str.erase (0,idx+2);
|
||||
if (t == "")
|
||||
break;
|
||||
idx=t.find(": ");
|
||||
if (idx == std::string::npos)
|
||||
{
|
||||
std::cout << "Bad header line: " << t << std::endl;
|
||||
break;
|
||||
}
|
||||
LogPrint ("Name: ", t.substr (0,idx), " Value: ", t.substr (idx+2));
|
||||
hm[i].name = t.substr (0,idx);
|
||||
hm[i].value = t.substr (idx+2);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPProxyConnection::ExtractRequest(request& r)
|
||||
{
|
||||
std::string requestString = m_Buffer;
|
||||
int idx=requestString.find(" ");
|
||||
std::string method = requestString.substr(0,idx);
|
||||
requestString = requestString.substr(idx+1);
|
||||
idx=requestString.find(" ");
|
||||
std::string requestUrl = requestString.substr(0,idx);
|
||||
LogPrint("method is: ", method, "\nRequest is: ", requestUrl);
|
||||
std::string server="";
|
||||
std::string port="80";
|
||||
boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)");
|
||||
boost::smatch m;
|
||||
std::string path;
|
||||
if(boost::regex_search(requestUrl, m, rHTTP, boost::match_extra)) {
|
||||
server=m[1].str();
|
||||
if(m[2].str() != "") {
|
||||
port=m[3].str();
|
||||
}
|
||||
path=m[4].str();
|
||||
}
|
||||
LogPrint("server is: ",server, " port is: ", port, "\n path is: ",path);
|
||||
r.uri = path;
|
||||
r.method = method;
|
||||
r.host = server;
|
||||
r.port = boost::lexical_cast<int>(port);
|
||||
}
|
||||
|
||||
|
||||
void HTTPProxyConnection::RunRequest()
|
||||
{
|
||||
request r;
|
||||
ExtractRequest(r);
|
||||
parseHeaders(m_Buffer, r.headers);
|
||||
size_t addressHelperPos = r.uri.find ("i2paddresshelper");
|
||||
if (addressHelperPos != std::string::npos)
|
||||
{
|
||||
// jump service
|
||||
size_t addressPos = r.uri.find ("=", addressHelperPos);
|
||||
if (addressPos != std::string::npos)
|
||||
{
|
||||
LogPrint ("Jump service for ", r.host, " found. Inserting to address book");
|
||||
auto base64 = r.uri.substr (addressPos + 1);
|
||||
i2p::client::context.GetAddressBook ().InsertAddress (r.host, base64);
|
||||
}
|
||||
}
|
||||
|
||||
LogPrint("Requesting ", r.host, ":", r.port, " with path ", r.uri, " and method ", r.method);
|
||||
SendToAddress (r.host, r.port, m_Buffer, m_BufferLen);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
42
HTTPProxy.h
42
HTTPProxy.h
@@ -1,42 +0,0 @@
|
||||
#ifndef HTTP_PROXY_H__
|
||||
#define HTTP_PROXY_H__
|
||||
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/array.hpp>
|
||||
|
||||
#include "HTTPServer.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace proxy
|
||||
{
|
||||
class HTTPProxyConnection : public i2p::util::HTTPConnection
|
||||
{
|
||||
public:
|
||||
HTTPProxyConnection (boost::asio::ip::tcp::socket * socket): HTTPConnection(socket) { };
|
||||
|
||||
protected:
|
||||
void RunRequest();
|
||||
void parseHeaders(const std::string& h, std::vector<header>& hm);
|
||||
void ExtractRequest(request& r);
|
||||
};
|
||||
|
||||
class HTTPProxy : public i2p::util::HTTPServer
|
||||
{
|
||||
public:
|
||||
HTTPProxy (int port): HTTPServer(port) {};
|
||||
|
||||
private:
|
||||
void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket)
|
||||
{
|
||||
new HTTPProxyConnection(m_NewSocket);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
1007
HTTPServer.cpp
1007
HTTPServer.cpp
File diff suppressed because it is too large
Load Diff
136
HTTPServer.h
136
HTTPServer.h
@@ -1,136 +0,0 @@
|
||||
#ifndef HTTP_SERVER_H__
|
||||
#define HTTP_SERVER_H__
|
||||
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include "LeaseSet.h"
|
||||
#include "Streaming.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
class HTTPConnection
|
||||
{
|
||||
protected:
|
||||
|
||||
struct header
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
struct request
|
||||
{
|
||||
std::string method;
|
||||
std::string uri;
|
||||
std::string host;
|
||||
int port;
|
||||
int http_version_major;
|
||||
int http_version_minor;
|
||||
std::vector<header> headers;
|
||||
};
|
||||
|
||||
struct reply
|
||||
{
|
||||
std::vector<header> headers;
|
||||
std::string content;
|
||||
|
||||
std::vector<boost::asio::const_buffer> to_buffers (int status);
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
HTTPConnection (boost::asio::ip::tcp::socket * socket):
|
||||
m_Socket (socket), m_Timer (socket->get_io_service ()),
|
||||
m_Stream (nullptr), m_BufferLen (0) { Receive (); };
|
||||
virtual ~HTTPConnection() { delete m_Socket; }
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
void Receive ();
|
||||
void HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void AsyncStreamReceive ();
|
||||
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleWriteReply(const boost::system::error_code& ecode);
|
||||
void HandleWrite (const boost::system::error_code& ecode);
|
||||
void SendReply (const std::string& content, int status = 200);
|
||||
|
||||
void HandleRequest (const std::string& address);
|
||||
void HandleCommand (const std::string& command, std::stringstream& s);
|
||||
void ShowTransports (std::stringstream& s);
|
||||
void ShowTunnels (std::stringstream& s);
|
||||
void ShowTransitTunnels (std::stringstream& s);
|
||||
void ShowLocalDestinations (std::stringstream& s);
|
||||
void ShowLocalDestination (const std::string& b32, std::stringstream& s);
|
||||
void StartAcceptingTunnels (std::stringstream& s);
|
||||
void StopAcceptingTunnels (std::stringstream& s);
|
||||
void FillContent (std::stringstream& s);
|
||||
std::string ExtractAddress ();
|
||||
void ExtractParams (const std::string& str, std::map<std::string, std::string>& params);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
boost::asio::ip::tcp::socket * m_Socket;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
||||
size_t m_BufferLen;
|
||||
request m_Request;
|
||||
reply m_Reply;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void RunRequest ();
|
||||
void HandleDestinationRequest(const std::string& address, const std::string& uri);
|
||||
void SendToAddress (const std::string& address, int port, const char * buf, size_t len);
|
||||
void HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
|
||||
i2p::data::IdentHash destination, int port, const char * buf, size_t len);
|
||||
void SendToDestination (const i2p::data::LeaseSet * remote, int port, const char * buf, size_t len);
|
||||
|
||||
public:
|
||||
|
||||
static const std::string itoopieImage;
|
||||
static const std::string itoopieFavicon;
|
||||
};
|
||||
|
||||
class HTTPServer
|
||||
{
|
||||
public:
|
||||
|
||||
HTTPServer (int port);
|
||||
virtual ~HTTPServer ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void Accept ();
|
||||
void HandleAccept(const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::ip::tcp::socket * m_NewSocket;
|
||||
|
||||
protected:
|
||||
virtual void CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
597
I2NPProtocol.cpp
597
I2NPProtocol.cpp
@@ -1,597 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <atomic>
|
||||
#include "I2PEndian.h"
|
||||
#include <cryptopp/sha.h>
|
||||
#include <cryptopp/gzip.h>
|
||||
#include "ElGamal.h"
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
#include "NetDb.h"
|
||||
#include "Tunnel.h"
|
||||
#include "base64.h"
|
||||
#include "Transports.h"
|
||||
#include "Garlic.h"
|
||||
#include "I2NPProtocol.h"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
I2NPMessage * NewI2NPMessage ()
|
||||
{
|
||||
return new I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE>();
|
||||
}
|
||||
|
||||
I2NPMessage * NewI2NPShortMessage ()
|
||||
{
|
||||
return new I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE>();
|
||||
}
|
||||
|
||||
I2NPMessage * NewI2NPMessage (size_t len)
|
||||
{
|
||||
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
|
||||
}
|
||||
|
||||
void DeleteI2NPMessage (I2NPMessage * msg)
|
||||
{
|
||||
delete msg;
|
||||
}
|
||||
|
||||
static std::atomic<uint32_t> I2NPmsgID(0); // TODO: create class
|
||||
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID)
|
||||
{
|
||||
I2NPHeader * header = msg->GetHeader ();
|
||||
header->typeID = msgType;
|
||||
if (replyMsgID) // for tunnel creation
|
||||
header->msgID = htobe32 (replyMsgID);
|
||||
else
|
||||
{
|
||||
header->msgID = htobe32 (I2NPmsgID);
|
||||
I2NPmsgID++;
|
||||
}
|
||||
header->expiration = htobe64 (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
|
||||
int len = msg->GetLength () - sizeof (I2NPHeader);
|
||||
header->size = htobe16 (len);
|
||||
uint8_t hash[32];
|
||||
CryptoPP::SHA256().CalculateDigest(hash, msg->GetPayload (), len);
|
||||
header->chks = hash[0];
|
||||
}
|
||||
|
||||
void RenewI2NPMessageHeader (I2NPMessage * msg)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
I2NPHeader * header = msg->GetHeader ();
|
||||
header->msgID = htobe32 (I2NPmsgID);
|
||||
I2NPmsgID++;
|
||||
header->expiration = htobe64 (i2p::util::GetMillisecondsSinceEpoch () + 5000);
|
||||
}
|
||||
}
|
||||
|
||||
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage (len);
|
||||
memcpy (msg->GetPayload (), buf, len);
|
||||
msg->len += len;
|
||||
FillI2NPMessageHeader (msg, msgType, replyMsgID);
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage ();
|
||||
memcpy (msg->GetBuffer (), buf, len);
|
||||
msg->len = msg->offset + len;
|
||||
msg->from = from;
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID)
|
||||
{
|
||||
I2NPDeliveryStatusMsg msg;
|
||||
if (msgID)
|
||||
{
|
||||
msg.msgID = htobe32 (msgID);
|
||||
msg.timestamp = htobe64 (i2p::util::GetMillisecondsSinceEpoch ());
|
||||
}
|
||||
else // for SSU establishment
|
||||
{
|
||||
msg.msgID = htobe32 (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
|
||||
msg.timestamp = htobe64 (2); // netID = 2
|
||||
}
|
||||
return CreateI2NPMessage (eI2NPDeliveryStatus, (uint8_t *)&msg, sizeof (msg));
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
||||
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers,
|
||||
bool encryption, i2p::tunnel::TunnelPool * pool)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPMessage ();
|
||||
uint8_t * buf = m->GetPayload ();
|
||||
memcpy (buf, key, 32); // key
|
||||
buf += 32;
|
||||
memcpy (buf, from, 32); // from
|
||||
buf += 32;
|
||||
if (replyTunnelID)
|
||||
{
|
||||
*buf = encryption ? 0x03: 0x01; // set delivery flag
|
||||
*(uint32_t *)(buf+1) = htobe32 (replyTunnelID);
|
||||
buf += 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
encryption = false; // encryption can we set for tunnels only
|
||||
*buf = 0; // flag
|
||||
buf++;
|
||||
}
|
||||
|
||||
if (exploratory)
|
||||
{
|
||||
*(uint16_t *)buf = htobe16 (1); // one exlude record
|
||||
buf += 2;
|
||||
// reply with non-floodfill routers only
|
||||
memset (buf, 0, 32);
|
||||
buf += 32;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (excludedPeers)
|
||||
{
|
||||
int cnt = excludedPeers->size ();
|
||||
*(uint16_t *)buf = htobe16 (cnt);
|
||||
buf += 2;
|
||||
for (auto& it: *excludedPeers)
|
||||
{
|
||||
memcpy (buf, it, 32);
|
||||
buf += 32;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to exclude
|
||||
*(uint16_t *)buf = htobe16 (0);
|
||||
buf += 2;
|
||||
}
|
||||
}
|
||||
if (encryption)
|
||||
{
|
||||
// session key and tag for reply
|
||||
auto& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
rnd.GenerateBlock (buf, 32); // key
|
||||
buf[32] = 1; // 1 tag
|
||||
rnd.GenerateBlock (buf + 33, 32); // tag
|
||||
if (pool && pool->GetLocalDestination ())
|
||||
pool->GetLocalDestination ()->SubmitSessionKey (buf, buf + 33); // introduce new key-tag to garlic engine
|
||||
else
|
||||
LogPrint ("Destination for encrypteed reply not specified");
|
||||
buf += 65;
|
||||
}
|
||||
m->len += (buf - m->GetPayload ());
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseLookup);
|
||||
return m;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
|
||||
const i2p::data::RouterInfo * floodfill)
|
||||
{
|
||||
I2NPMessage * m = NewI2NPShortMessage ();
|
||||
uint8_t * buf = m->GetPayload ();
|
||||
size_t len = 0;
|
||||
memcpy (buf, ident, 32);
|
||||
len += 32;
|
||||
buf[len] = floodfill ? 1 : 0; // 1 router for now
|
||||
len++;
|
||||
if (floodfill)
|
||||
{
|
||||
memcpy (buf + len, floodfill->GetIdentHash (), 32);
|
||||
len += 32;
|
||||
}
|
||||
memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
|
||||
len += 32;
|
||||
m->len += len;
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseSearchReply);
|
||||
return m;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router)
|
||||
{
|
||||
if (!router) // we send own RouterInfo
|
||||
router = &context.GetRouterInfo ();
|
||||
|
||||
I2NPMessage * m = NewI2NPShortMessage ();
|
||||
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)m->GetPayload ();
|
||||
|
||||
memcpy (msg->key, router->GetIdentHash (), 32);
|
||||
msg->type = 0;
|
||||
msg->replyToken = 0;
|
||||
|
||||
CryptoPP::Gzip compressor;
|
||||
compressor.Put (router->GetBuffer (), router->GetBufferLen ());
|
||||
compressor.MessageEnd();
|
||||
auto size = compressor.MaxRetrievable ();
|
||||
uint8_t * buf = m->GetPayload () + sizeof (I2NPDatabaseStoreMsg);
|
||||
*(uint16_t *)buf = htobe16 (size); // size
|
||||
buf += 2;
|
||||
// TODO: check if size doesn't exceed buffer
|
||||
compressor.Get (buf, size);
|
||||
m->len += sizeof (I2NPDatabaseStoreMsg) + 2 + size; // payload size
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken)
|
||||
{
|
||||
if (!leaseSet) return nullptr;
|
||||
I2NPMessage * m = NewI2NPShortMessage ();
|
||||
uint8_t * payload = m->GetPayload ();
|
||||
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)payload;
|
||||
memcpy (msg->key, leaseSet->GetIdentHash (), 32);
|
||||
msg->type = 1; // LeaseSet
|
||||
msg->replyToken = htobe32 (replyToken);
|
||||
size_t size = sizeof (I2NPDatabaseStoreMsg);
|
||||
if (replyToken)
|
||||
{
|
||||
auto leases = leaseSet->GetNonExpiredLeases ();
|
||||
if (leases.size () > 0)
|
||||
{
|
||||
*(uint32_t *)(payload + size) = htobe32 (leases[0].tunnelID);
|
||||
size += 4; // reply tunnelID
|
||||
memcpy (payload + size, leases[0].tunnelGateway, 32);
|
||||
size += 32; // reply tunnel gateway
|
||||
}
|
||||
else
|
||||
msg->replyToken = 0;
|
||||
}
|
||||
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
|
||||
size += leaseSet->GetBufferLen ();
|
||||
m->len += size;
|
||||
FillI2NPMessageHeader (m, eI2NPDatabaseStore);
|
||||
return m;
|
||||
}
|
||||
|
||||
I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
|
||||
const uint8_t * ourIdent, uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey,
|
||||
const uint8_t * replyKey, const uint8_t * replyIV, uint32_t nextMessageID,
|
||||
bool isGateway, bool isEndpoint)
|
||||
{
|
||||
I2NPBuildRequestRecordClearText clearText;
|
||||
clearText.receiveTunnel = htobe32 (receiveTunnelID);
|
||||
clearText.nextTunnel = htobe32(nextTunnelID);
|
||||
memcpy (clearText.layerKey, layerKey, 32);
|
||||
memcpy (clearText.ivKey, ivKey, 32);
|
||||
memcpy (clearText.replyKey, replyKey, 32);
|
||||
memcpy (clearText.replyIV, replyIV, 16);
|
||||
clearText.flag = 0;
|
||||
if (isGateway) clearText.flag |= 0x80;
|
||||
if (isEndpoint) clearText.flag |= 0x40;
|
||||
memcpy (clearText.ourIdent, ourIdent, 32);
|
||||
memcpy (clearText.nextIdent, nextIdent, 32);
|
||||
clearText.requestTime = htobe32 (i2p::util::GetHoursSinceEpoch ());
|
||||
clearText.nextMessageID = htobe32(nextMessageID);
|
||||
return clearText;
|
||||
}
|
||||
|
||||
void EncryptBuildRequestRecord (const i2p::data::RouterInfo& router,
|
||||
const I2NPBuildRequestRecordClearText& clearText,
|
||||
I2NPBuildRequestRecordElGamalEncrypted& record)
|
||||
{
|
||||
router.GetElGamalEncryption ()->Encrypt ((uint8_t *)&clearText, sizeof(clearText), record.encrypted);
|
||||
memcpy (record.toPeer, (const uint8_t *)router.GetIdentHash (), 16);
|
||||
}
|
||||
|
||||
bool HandleBuildRequestRecords (int num, I2NPBuildRequestRecordElGamalEncrypted * records, I2NPBuildRequestRecordClearText& clearText)
|
||||
{
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
if (!memcmp (records[i].toPeer, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
||||
{
|
||||
LogPrint ("Record ",i," is ours");
|
||||
|
||||
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), records[i].encrypted, (uint8_t *)&clearText);
|
||||
// replace record to reply
|
||||
I2NPBuildResponseRecord * reply = (I2NPBuildResponseRecord *)(records + i);
|
||||
if (i2p::context.AcceptsTunnels ())
|
||||
{
|
||||
i2p::tunnel::TransitTunnel * transitTunnel =
|
||||
i2p::tunnel::CreateTransitTunnel (
|
||||
be32toh (clearText.receiveTunnel),
|
||||
clearText.nextIdent, be32toh (clearText.nextTunnel),
|
||||
clearText.layerKey, clearText.ivKey,
|
||||
clearText.flag & 0x80, clearText.flag & 0x40);
|
||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
||||
reply->ret = 0;
|
||||
}
|
||||
else
|
||||
reply->ret = 30; // always reject with bandwidth reason (30)
|
||||
|
||||
//TODO: fill filler
|
||||
CryptoPP::SHA256().CalculateDigest(reply->hash, reply->padding, sizeof (reply->padding) + 1); // + 1 byte of ret
|
||||
// encrypt reply
|
||||
i2p::crypto::CBCEncryption encryption;
|
||||
for (int j = 0; j < num; j++)
|
||||
{
|
||||
encryption.SetKey (clearText.replyKey);
|
||||
encryption.SetIV (clearText.replyIV);
|
||||
encryption.Encrypt((uint8_t *)(records + j), sizeof (records[j]), (uint8_t *)(records + j));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
||||
{
|
||||
int num = buf[0];
|
||||
LogPrint ("VariableTunnelBuild ", num, " records");
|
||||
|
||||
i2p::tunnel::Tunnel * tunnel = i2p::tunnel::tunnels.GetPendingTunnel (replyMsgID);
|
||||
if (tunnel)
|
||||
{
|
||||
// endpoint of inbound tunnel
|
||||
LogPrint ("VariableTunnelBuild reply for tunnel ", tunnel->GetTunnelID ());
|
||||
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||
{
|
||||
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||
i2p::tunnel::tunnels.AddInboundTunnel (static_cast<i2p::tunnel::InboundTunnel *>(tunnel));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(buf+1);
|
||||
I2NPBuildRequestRecordClearText clearText;
|
||||
if (HandleBuildRequestRecords (num, records, clearText))
|
||||
{
|
||||
if (clearText.flag & 0x40) // we are endpoint of outboud tunnel
|
||||
{
|
||||
// so we send it to reply tunnel
|
||||
transports.SendMessage (clearText.nextIdent,
|
||||
CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel),
|
||||
eI2NPVariableTunnelBuildReply, buf, len,
|
||||
be32toh (clearText.nextMessageID)));
|
||||
}
|
||||
else
|
||||
transports.SendMessage (clearText.nextIdent,
|
||||
CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len, be32toh (clearText.nextMessageID)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
|
||||
{
|
||||
I2NPBuildRequestRecordClearText clearText;
|
||||
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, (I2NPBuildRequestRecordElGamalEncrypted *)buf, clearText))
|
||||
{
|
||||
if (clearText.flag & 0x40) // we are endpoint of outbound tunnel
|
||||
{
|
||||
// so we send it to reply tunnel
|
||||
transports.SendMessage (clearText.nextIdent,
|
||||
CreateTunnelGatewayMsg (be32toh (clearText.nextTunnel),
|
||||
eI2NPTunnelBuildReply, buf, len,
|
||||
be32toh (clearText.nextMessageID)));
|
||||
}
|
||||
else
|
||||
transports.SendMessage (clearText.nextIdent,
|
||||
CreateI2NPMessage (eI2NPTunnelBuild, buf, len, be32toh (clearText.nextMessageID)));
|
||||
}
|
||||
}
|
||||
|
||||
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
||||
{
|
||||
LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID);
|
||||
i2p::tunnel::Tunnel * tunnel = i2p::tunnel::tunnels.GetPendingTunnel (replyMsgID);
|
||||
if (tunnel)
|
||||
{
|
||||
// reply for outbound tunnel
|
||||
if (tunnel->HandleTunnelBuildResponse (buf, len))
|
||||
{
|
||||
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been created");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateEstablished);
|
||||
i2p::tunnel::tunnels.AddOutboundTunnel (static_cast<i2p::tunnel::OutboundTunnel *>(tunnel));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Outbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("Pending tunnel for message ", replyMsgID, " not found");
|
||||
}
|
||||
|
||||
|
||||
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage ();
|
||||
memcpy (msg->GetPayload (), buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
|
||||
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelData);
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage ();
|
||||
memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
|
||||
*(uint32_t *)(msg->GetPayload ()) = htobe32 (tunnelID);
|
||||
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelData);
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage (len);
|
||||
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
|
||||
header->tunnelID = htobe32 (tunnelID);
|
||||
header->length = htobe16 (len);
|
||||
memcpy (msg->GetPayload () + sizeof (TunnelGatewayHeader), buf, len);
|
||||
msg->len += sizeof (TunnelGatewayHeader) + len;
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg)
|
||||
{
|
||||
if (msg->offset >= sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader))
|
||||
{
|
||||
// message is capable to be used without copying
|
||||
TunnelGatewayHeader * header = (TunnelGatewayHeader *)(msg->GetBuffer () - sizeof (TunnelGatewayHeader));
|
||||
header->tunnelID = htobe32 (tunnelID);
|
||||
int len = msg->GetLength ();
|
||||
header->length = htobe16 (len);
|
||||
msg->offset -= (sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader));
|
||||
msg->len = msg->offset + sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader) +len;
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelGateway);
|
||||
return msg;
|
||||
}
|
||||
else
|
||||
{
|
||||
I2NPMessage * msg1 = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
|
||||
DeleteI2NPMessage (msg);
|
||||
return msg1;
|
||||
}
|
||||
}
|
||||
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
|
||||
const uint8_t * buf, size_t len, uint32_t replyMsgID)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPMessage (len);
|
||||
size_t gatewayMsgOffset = sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader);
|
||||
msg->offset += gatewayMsgOffset;
|
||||
msg->len += gatewayMsgOffset;
|
||||
memcpy (msg->GetPayload (), buf, len);
|
||||
msg->len += len;
|
||||
FillI2NPMessageHeader (msg, msgType, replyMsgID); // create content message
|
||||
len = msg->GetLength ();
|
||||
msg->offset -= gatewayMsgOffset;
|
||||
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
|
||||
header->tunnelID = htobe32 (tunnelID);
|
||||
header->length = htobe16 (len);
|
||||
FillI2NPMessageHeader (msg, eI2NPTunnelGateway); // gateway message
|
||||
return msg;
|
||||
}
|
||||
|
||||
void HandleTunnelGatewayMsg (I2NPMessage * msg)
|
||||
{
|
||||
TunnelGatewayHeader * header = (TunnelGatewayHeader *)msg->GetPayload ();
|
||||
uint32_t tunnelID = be32toh(header->tunnelID);
|
||||
uint16_t len = be16toh(header->length);
|
||||
// we make payload as new I2NP message to send
|
||||
msg->offset += sizeof (I2NPHeader) + sizeof (TunnelGatewayHeader);
|
||||
msg->len = msg->offset + len;
|
||||
LogPrint ("TunnelGateway of ", (int)len, " bytes for tunnel ", (unsigned int)tunnelID, ". Msg type ", (int)msg->GetHeader()->typeID);
|
||||
if (msg->GetHeader()->typeID == eI2NPDatabaseStore ||
|
||||
msg->GetHeader()->typeID == eI2NPDatabaseSearchReply)
|
||||
{
|
||||
// transit DatabaseStore my contain new/updated RI
|
||||
// or DatabaseSearchReply with new routers
|
||||
auto ds = NewI2NPMessage ();
|
||||
*ds = *msg;
|
||||
i2p::data::netdb.PostI2NPMsg (ds);
|
||||
}
|
||||
i2p::tunnel::TransitTunnel * tunnel = i2p::tunnel::tunnels.GetTransitTunnel (tunnelID);
|
||||
if (tunnel)
|
||||
tunnel->SendTunnelDataMsg (msg);
|
||||
else
|
||||
{
|
||||
LogPrint ("Tunnel ", (unsigned int)tunnelID, " not found");
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
|
||||
size_t GetI2NPMessageLength (const uint8_t * msg)
|
||||
{
|
||||
I2NPHeader * header = (I2NPHeader *)msg;
|
||||
return be16toh (header->size) + sizeof (I2NPHeader);
|
||||
}
|
||||
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len)
|
||||
{
|
||||
I2NPHeader * header = (I2NPHeader *)msg;
|
||||
uint32_t msgID = be32toh (header->msgID);
|
||||
LogPrint ("I2NP msg received len=", len,", type=", (int)header->typeID, ", msgID=", (unsigned int)msgID);
|
||||
|
||||
uint8_t * buf = msg + sizeof (I2NPHeader);
|
||||
int size = be16toh (header->size);
|
||||
switch (header->typeID)
|
||||
{
|
||||
case eI2NPVariableTunnelBuild:
|
||||
LogPrint ("VariableTunnelBuild");
|
||||
HandleVariableTunnelBuildMsg (msgID, buf, size);
|
||||
break;
|
||||
case eI2NPVariableTunnelBuildReply:
|
||||
LogPrint ("VariableTunnelBuildReply");
|
||||
HandleVariableTunnelBuildReplyMsg (msgID, buf, size);
|
||||
break;
|
||||
case eI2NPTunnelBuild:
|
||||
LogPrint ("TunnelBuild");
|
||||
HandleTunnelBuildMsg (buf, size);
|
||||
break;
|
||||
case eI2NPTunnelBuildReply:
|
||||
LogPrint ("TunnelBuildReply");
|
||||
// TODO:
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Unexpected message ", (int)header->typeID);
|
||||
}
|
||||
}
|
||||
|
||||
void HandleI2NPMessage (I2NPMessage * msg)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
switch (msg->GetHeader ()->typeID)
|
||||
{
|
||||
case eI2NPTunnelData:
|
||||
LogPrint ("TunnelData");
|
||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
||||
break;
|
||||
case eI2NPTunnelGateway:
|
||||
LogPrint ("TunnelGateway");
|
||||
HandleTunnelGatewayMsg (msg);
|
||||
break;
|
||||
case eI2NPGarlic:
|
||||
LogPrint ("Garlic");
|
||||
if (msg->from)
|
||||
{
|
||||
if (msg->from->GetTunnelPool ())
|
||||
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Local destination for garlic doesn't exist anymore");
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
i2p::context.ProcessGarlicMessage (msg);
|
||||
break;
|
||||
case eI2NPDatabaseStore:
|
||||
case eI2NPDatabaseSearchReply:
|
||||
case eI2NPDatabaseLookup:
|
||||
// forward to netDb
|
||||
i2p::data::netdb.PostI2NPMsg (msg);
|
||||
break;
|
||||
case eI2NPDeliveryStatus:
|
||||
LogPrint ("DeliveryStatus");
|
||||
if (msg->from && msg->from->GetTunnelPool ())
|
||||
msg->from->GetTunnelPool ()->ProcessDeliveryStatus (msg);
|
||||
else
|
||||
i2p::context.ProcessDeliveryStatusMessage (msg);
|
||||
break;
|
||||
default:
|
||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
217
I2NPProtocol.h
217
I2NPProtocol.h
@@ -1,217 +0,0 @@
|
||||
#ifndef I2NP_PROTOCOL_H__
|
||||
#define I2NP_PROTOCOL_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <string.h>
|
||||
#include "I2PEndian.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "LeaseSet.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
#pragma pack (1)
|
||||
|
||||
struct I2NPHeader
|
||||
{
|
||||
uint8_t typeID;
|
||||
uint32_t msgID;
|
||||
uint64_t expiration;
|
||||
uint16_t size;
|
||||
uint8_t chks;
|
||||
};
|
||||
|
||||
struct I2NPHeaderShort
|
||||
{
|
||||
uint8_t typeID;
|
||||
uint32_t shortExpiration;
|
||||
};
|
||||
|
||||
struct I2NPDatabaseStoreMsg
|
||||
{
|
||||
uint8_t key[32];
|
||||
uint8_t type;
|
||||
uint32_t replyToken;
|
||||
};
|
||||
|
||||
struct I2NPDeliveryStatusMsg
|
||||
{
|
||||
uint32_t msgID;
|
||||
uint64_t timestamp;
|
||||
};
|
||||
|
||||
struct I2NPBuildRequestRecordClearText
|
||||
{
|
||||
uint32_t receiveTunnel;
|
||||
uint8_t ourIdent[32];
|
||||
uint32_t nextTunnel;
|
||||
uint8_t nextIdent[32];
|
||||
uint8_t layerKey[32];
|
||||
uint8_t ivKey[32];
|
||||
uint8_t replyKey[32];
|
||||
uint8_t replyIV[16];
|
||||
uint8_t flag;
|
||||
uint32_t requestTime;
|
||||
uint32_t nextMessageID;
|
||||
uint8_t filler[29];
|
||||
};
|
||||
|
||||
struct I2NPBuildResponseRecord
|
||||
{
|
||||
uint8_t hash[32];
|
||||
uint8_t padding[495];
|
||||
uint8_t ret;
|
||||
};
|
||||
|
||||
struct I2NPBuildRequestRecordElGamalEncrypted
|
||||
{
|
||||
uint8_t toPeer[16];
|
||||
uint8_t encrypted[512];
|
||||
};
|
||||
|
||||
struct TunnelGatewayHeader
|
||||
{
|
||||
uint32_t tunnelID;
|
||||
uint16_t length;
|
||||
};
|
||||
|
||||
|
||||
#pragma pack ()
|
||||
|
||||
enum I2NPMessageType
|
||||
{
|
||||
eI2NPDatabaseStore = 1,
|
||||
eI2NPDatabaseLookup = 2,
|
||||
eI2NPDatabaseSearchReply = 3,
|
||||
eI2NPDeliveryStatus = 10,
|
||||
eI2NPGarlic = 11,
|
||||
eI2NPTunnelData = 18,
|
||||
eI2NPTunnelGateway = 19,
|
||||
eI2NPData = 20,
|
||||
eI2NPTunnelBuild = 21,
|
||||
eI2NPTunnelBuildReply = 22,
|
||||
eI2NPVariableTunnelBuild = 23,
|
||||
eI2NPVariableTunnelBuildReply = 24
|
||||
};
|
||||
|
||||
const int NUM_TUNNEL_BUILD_RECORDS = 8;
|
||||
|
||||
namespace tunnel
|
||||
{
|
||||
class InboundTunnel;
|
||||
class TunnelPool;
|
||||
}
|
||||
|
||||
const size_t I2NP_MAX_MESSAGE_SIZE = 32768;
|
||||
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 2400;
|
||||
struct I2NPMessage
|
||||
{
|
||||
uint8_t * buf;
|
||||
size_t len, offset, maxLen;
|
||||
i2p::tunnel::InboundTunnel * from;
|
||||
|
||||
I2NPMessage (): buf (nullptr),len (sizeof (I2NPHeader) + 2),
|
||||
offset(2), maxLen (0), from (nullptr) {};
|
||||
// reserve 2 bytes for NTCP header
|
||||
I2NPHeader * GetHeader () { return (I2NPHeader *)GetBuffer (); };
|
||||
uint8_t * GetPayload () { return GetBuffer () + sizeof(I2NPHeader); };
|
||||
uint8_t * GetBuffer () { return buf + offset; };
|
||||
const uint8_t * GetBuffer () const { return buf + offset; };
|
||||
size_t GetLength () const { return len - offset; };
|
||||
void Align (size_t alignment)
|
||||
{
|
||||
size_t rem = ((size_t)GetBuffer ()) % alignment;
|
||||
if (rem)
|
||||
{
|
||||
offset += (alignment - rem);
|
||||
len += (alignment - rem);
|
||||
}
|
||||
}
|
||||
|
||||
I2NPMessage& operator=(const I2NPMessage& other)
|
||||
{
|
||||
memcpy (buf + offset, other.buf + other.offset, other.GetLength ());
|
||||
len = offset + other.GetLength ();
|
||||
from = other.from;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// for SSU only
|
||||
uint8_t * GetSSUHeader () { return buf + offset + sizeof(I2NPHeader) - sizeof(I2NPHeaderShort); };
|
||||
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
|
||||
{
|
||||
I2NPHeaderShort ssu = *(I2NPHeaderShort *)GetSSUHeader ();
|
||||
I2NPHeader * header = GetHeader ();
|
||||
header->typeID = ssu.typeID;
|
||||
header->msgID = htobe32 (msgID);
|
||||
header->expiration = htobe64 (be32toh (ssu.shortExpiration)*1000LL);
|
||||
header->size = htobe16 (len - offset - sizeof (I2NPHeader));
|
||||
header->chks = 0;
|
||||
}
|
||||
uint32_t ToSSU () // return msgID
|
||||
{
|
||||
I2NPHeader header = *GetHeader ();
|
||||
I2NPHeaderShort * ssu = (I2NPHeaderShort *)GetSSUHeader ();
|
||||
ssu->typeID = header.typeID;
|
||||
ssu->shortExpiration = htobe32 (be64toh (header.expiration)/1000LL);
|
||||
len = offset + sizeof (I2NPHeaderShort) + be16toh (header.size);
|
||||
return be32toh (header.msgID);
|
||||
}
|
||||
};
|
||||
|
||||
template<int sz>
|
||||
struct I2NPMessageBuffer: public I2NPMessage
|
||||
{
|
||||
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; };
|
||||
uint8_t m_Buffer[sz];
|
||||
};
|
||||
|
||||
I2NPMessage * NewI2NPMessage ();
|
||||
I2NPMessage * NewI2NPShortMessage ();
|
||||
I2NPMessage * NewI2NPMessage (size_t len);
|
||||
void DeleteI2NPMessage (I2NPMessage * msg);
|
||||
void FillI2NPMessageHeader (I2NPMessage * msg, I2NPMessageType msgType, uint32_t replyMsgID = 0);
|
||||
void RenewI2NPMessageHeader (I2NPMessage * msg);
|
||||
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);
|
||||
I2NPMessage * CreateI2NPMessage (const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from = nullptr);
|
||||
|
||||
I2NPMessage * CreateDeliveryStatusMsg (uint32_t msgID);
|
||||
I2NPMessage * CreateDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
||||
uint32_t replyTunnelID, bool exploratory = false,
|
||||
std::set<i2p::data::IdentHash> * excludedPeers = nullptr, bool encryption = false,
|
||||
i2p::tunnel::TunnelPool * pool = nullptr);
|
||||
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, const i2p::data::RouterInfo * floodfill);
|
||||
|
||||
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::RouterInfo * router = nullptr);
|
||||
I2NPMessage * CreateDatabaseStoreMsg (const i2p::data::LeaseSet * leaseSet, uint32_t replyToken = 0);
|
||||
|
||||
I2NPBuildRequestRecordClearText CreateBuildRequestRecord (
|
||||
const uint8_t * ourIdent, uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey,
|
||||
const uint8_t * replyKey, const uint8_t * replyIV, uint32_t nextMessageID,
|
||||
bool isGateway, bool isEndpoint);
|
||||
void EncryptBuildRequestRecord (const i2p::data::RouterInfo& router,
|
||||
const I2NPBuildRequestRecordClearText& clearText,
|
||||
I2NPBuildRequestRecordElGamalEncrypted& record);
|
||||
|
||||
bool HandleBuildRequestRecords (int num, I2NPBuildRequestRecordElGamalEncrypted * records, I2NPBuildRequestRecordClearText& clearText);
|
||||
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
|
||||
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len);
|
||||
void HandleTunnelBuildMsg (uint8_t * buf, size_t len);
|
||||
|
||||
I2NPMessage * CreateTunnelDataMsg (const uint8_t * buf);
|
||||
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
|
||||
|
||||
void HandleTunnelGatewayMsg (I2NPMessage * msg);
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,
|
||||
const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
|
||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessage * msg);
|
||||
|
||||
size_t GetI2NPMessageLength (const uint8_t * msg);
|
||||
void HandleI2NPMessage (uint8_t * msg, size_t len);
|
||||
void HandleI2NPMessage (I2NPMessage * msg);
|
||||
}
|
||||
|
||||
#endif
|
||||
48
I2PEndian.h
48
I2PEndian.h
@@ -1,48 +0,0 @@
|
||||
#ifndef I2PENDIAN_H__
|
||||
#define I2PENDIAN_H__
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD_kernel__)
|
||||
#include <endian.h>
|
||||
#elif __FreeBSD__
|
||||
#include <sys/endian.h>
|
||||
#elif defined(__APPLE__) && defined(__MACH__)
|
||||
|
||||
#include <libkern/OSByteOrder.h>
|
||||
|
||||
#define htobe16(x) OSSwapHostToBigInt16(x)
|
||||
#define htole16(x) OSSwapHostToLittleInt16(x)
|
||||
#define be16toh(x) OSSwapBigToHostInt16(x)
|
||||
#define le16toh(x) OSSwapLittleToHostInt16(x)
|
||||
|
||||
#define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
#define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
#define be32toh(x) OSSwapBigToHostInt32(x)
|
||||
#define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
|
||||
#define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
#define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
#define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
#define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
|
||||
#else
|
||||
#include <cstdint>
|
||||
uint16_t htobe16(uint16_t int16);
|
||||
uint32_t htobe32(uint32_t int32);
|
||||
uint64_t htobe64(uint64_t int64);
|
||||
|
||||
uint16_t be16toh(uint16_t big16);
|
||||
uint32_t be32toh(uint32_t big32);
|
||||
uint64_t be64toh(uint64_t big64);
|
||||
|
||||
// assume LittleEndine
|
||||
#define htole16
|
||||
#define htole32
|
||||
#define htole64
|
||||
#define le16toh
|
||||
#define le32toh
|
||||
#define le64toh
|
||||
|
||||
#endif
|
||||
|
||||
#endif // I2PENDIAN_H__
|
||||
|
||||
300
I2PTunnel.cpp
300
I2PTunnel.cpp
@@ -1,300 +0,0 @@
|
||||
#include "base64.h"
|
||||
#include "Log.h"
|
||||
#include "NetDb.h"
|
||||
#include "Destination.h"
|
||||
#include "ClientContext.h"
|
||||
#include "I2PTunnel.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner,
|
||||
boost::asio::ip::tcp::socket * socket, const i2p::data::LeaseSet * leaseSet):
|
||||
m_Socket (socket), m_Owner (owner), m_RemoteEndpoint (socket->remote_endpoint ()),
|
||||
m_IsQuiet (true)
|
||||
{
|
||||
m_Stream = m_Owner->GetLocalDestination ()->CreateStream (*leaseSet);
|
||||
}
|
||||
|
||||
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
|
||||
m_Socket (socket), m_Stream (stream), m_Owner (owner), m_RemoteEndpoint (target), m_IsQuiet (quiet)
|
||||
{
|
||||
}
|
||||
|
||||
I2PTunnelConnection::~I2PTunnelConnection ()
|
||||
{
|
||||
delete m_Socket;
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
|
||||
{
|
||||
if (msg)
|
||||
m_Stream->Send (msg, len); // connect and send
|
||||
else
|
||||
m_Stream->Send (m_Buffer, 0); // connect
|
||||
StreamReceive ();
|
||||
Receive ();
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Connect ()
|
||||
{
|
||||
if (m_Socket)
|
||||
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Terminate ()
|
||||
{
|
||||
if (m_Stream)
|
||||
{
|
||||
m_Stream->Close ();
|
||||
m_Stream.reset ();
|
||||
}
|
||||
m_Socket->close ();
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveConnection (shared_from_this ());
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Receive ()
|
||||
{
|
||||
m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
||||
std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("I2PTunnel read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Stream)
|
||||
m_Stream->Send (m_Buffer, bytes_transferred);
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("I2PTunnel write error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
StreamReceive ();
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::StreamReceive ()
|
||||
{
|
||||
if (m_Stream)
|
||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
||||
std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
I2P_TUNNEL_CONNECTION_MAX_IDLE);
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("I2PTunnel stream read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
|
||||
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("I2PTunnel connect error: ", ecode.message ());
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("I2PTunnel connected");
|
||||
if (m_IsQuiet)
|
||||
StreamReceive ();
|
||||
else
|
||||
{
|
||||
// send destination first like received from I2P
|
||||
std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 ();
|
||||
dest += "\n";
|
||||
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
|
||||
HandleStreamReceive (boost::system::error_code (), dest.size ());
|
||||
}
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnel::AddConnection (std::shared_ptr<I2PTunnelConnection> conn)
|
||||
{
|
||||
m_Connections.insert (conn);
|
||||
}
|
||||
|
||||
void I2PTunnel::RemoveConnection (std::shared_ptr<I2PTunnelConnection> conn)
|
||||
{
|
||||
m_Connections.erase (conn);
|
||||
}
|
||||
|
||||
void I2PTunnel::ClearConnections ()
|
||||
{
|
||||
m_Connections.clear ();
|
||||
}
|
||||
|
||||
I2PClientTunnel::I2PClientTunnel (boost::asio::io_service& service, const std::string& destination,
|
||||
int port, ClientDestination * localDestination):
|
||||
I2PTunnel (service, localDestination ? localDestination :
|
||||
i2p::client::context.CreateNewLocalDestination (false, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256)),
|
||||
m_Acceptor (service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
|
||||
m_Timer (service), m_Destination (destination), m_DestinationIdentHash (nullptr),
|
||||
m_RemoteLeaseSet (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
I2PClientTunnel::~I2PClientTunnel ()
|
||||
{
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void I2PClientTunnel::Start ()
|
||||
{
|
||||
i2p::data::IdentHash identHash;
|
||||
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
|
||||
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
|
||||
if (!m_DestinationIdentHash)
|
||||
LogPrint ("I2PTunnel unknown destination ", m_Destination);
|
||||
m_Acceptor.listen ();
|
||||
Accept ();
|
||||
}
|
||||
|
||||
void I2PClientTunnel::Stop ()
|
||||
{
|
||||
m_Acceptor.close();
|
||||
m_Timer.cancel ();
|
||||
ClearConnections ();
|
||||
m_DestinationIdentHash = nullptr;
|
||||
}
|
||||
|
||||
void I2PClientTunnel::Accept ()
|
||||
{
|
||||
auto newSocket = new boost::asio::ip::tcp::socket (GetService ());
|
||||
m_Acceptor.async_accept (*newSocket, std::bind (&I2PClientTunnel::HandleAccept, this,
|
||||
std::placeholders::_1, newSocket));
|
||||
}
|
||||
|
||||
void I2PClientTunnel::HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
if (!m_DestinationIdentHash)
|
||||
{
|
||||
i2p::data::IdentHash identHash;
|
||||
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
|
||||
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
|
||||
}
|
||||
if (m_DestinationIdentHash)
|
||||
{
|
||||
// try to get a LeaseSet
|
||||
m_RemoteLeaseSet = GetLocalDestination ()->FindLeaseSet (*m_DestinationIdentHash);
|
||||
if (m_RemoteLeaseSet && m_RemoteLeaseSet->HasNonExpiredLeases ())
|
||||
CreateConnection (socket);
|
||||
else
|
||||
{
|
||||
i2p::data::netdb.RequestDestination (*m_DestinationIdentHash, true, GetLocalDestination ()->GetTunnelPool ());
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds (I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT));
|
||||
m_Timer.async_wait (std::bind (&I2PClientTunnel::HandleDestinationRequestTimer,
|
||||
this, std::placeholders::_1, socket));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Remote destination ", m_Destination, " not found");
|
||||
delete socket;
|
||||
}
|
||||
|
||||
Accept ();
|
||||
}
|
||||
else
|
||||
delete socket;
|
||||
}
|
||||
|
||||
void I2PClientTunnel::HandleDestinationRequestTimer (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
if (m_DestinationIdentHash)
|
||||
{
|
||||
m_RemoteLeaseSet = GetLocalDestination ()->FindLeaseSet (*m_DestinationIdentHash);
|
||||
CreateConnection (socket);
|
||||
return;
|
||||
}
|
||||
}
|
||||
delete socket;
|
||||
}
|
||||
|
||||
void I2PClientTunnel::CreateConnection (boost::asio::ip::tcp::socket * socket)
|
||||
{
|
||||
if (m_RemoteLeaseSet) // leaseSet found
|
||||
{
|
||||
LogPrint ("New I2PTunnel connection");
|
||||
auto connection = std::make_shared<I2PTunnelConnection>(this, socket, m_RemoteLeaseSet);
|
||||
AddConnection (connection);
|
||||
connection->I2PConnect ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("LeaseSet for I2PTunnel destination not found");
|
||||
delete socket;
|
||||
}
|
||||
}
|
||||
|
||||
I2PServerTunnel::I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
|
||||
ClientDestination * localDestination): I2PTunnel (service, localDestination),
|
||||
m_Endpoint (boost::asio::ip::address::from_string (address), port)
|
||||
{
|
||||
}
|
||||
|
||||
void I2PServerTunnel::Start ()
|
||||
{
|
||||
Accept ();
|
||||
}
|
||||
|
||||
void I2PServerTunnel::Stop ()
|
||||
{
|
||||
ClearConnections ();
|
||||
}
|
||||
|
||||
void I2PServerTunnel::Accept ()
|
||||
{
|
||||
auto localDestination = GetLocalDestination ();
|
||||
if (localDestination)
|
||||
localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
|
||||
else
|
||||
LogPrint ("Local destination not set for server tunnel");
|
||||
}
|
||||
|
||||
void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
|
||||
AddConnection (conn);
|
||||
conn->Connect ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
I2PTunnel.h
129
I2PTunnel.h
@@ -1,129 +0,0 @@
|
||||
#ifndef I2PTUNNEL_H__
|
||||
#define I2PTUNNEL_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "Destination.h"
|
||||
#include "Streaming.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
|
||||
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
|
||||
class I2PTunnel;
|
||||
class I2PTunnelConnection: public std::enable_shared_from_this<I2PTunnelConnection>
|
||||
{
|
||||
public:
|
||||
|
||||
I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket,
|
||||
const i2p::data::LeaseSet * leaseSet); // to I2P
|
||||
I2PTunnelConnection (I2PTunnel * owner, std::shared_ptr<i2p::stream::Stream> stream, boost::asio::ip::tcp::socket * socket,
|
||||
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
|
||||
~I2PTunnelConnection ();
|
||||
|
||||
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
|
||||
void Connect ();
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
|
||||
void Receive ();
|
||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleWrite (const boost::system::error_code& ecode);
|
||||
|
||||
void StreamReceive ();
|
||||
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleConnect (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
|
||||
boost::asio::ip::tcp::socket * m_Socket;
|
||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
||||
I2PTunnel * m_Owner;
|
||||
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
|
||||
bool m_IsQuiet; // don't send destination
|
||||
};
|
||||
|
||||
class I2PTunnel
|
||||
{
|
||||
public:
|
||||
|
||||
I2PTunnel (boost::asio::io_service& service, ClientDestination * localDestination):
|
||||
m_Service (service), m_LocalDestination (localDestination) {};
|
||||
virtual ~I2PTunnel () { ClearConnections (); };
|
||||
|
||||
void AddConnection (std::shared_ptr<I2PTunnelConnection> conn);
|
||||
void RemoveConnection (std::shared_ptr<I2PTunnelConnection> conn);
|
||||
void ClearConnections ();
|
||||
ClientDestination * GetLocalDestination () { return m_LocalDestination; };
|
||||
void SetLocalDestination (ClientDestination * dest) { m_LocalDestination = dest; };
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service& m_Service;
|
||||
ClientDestination * m_LocalDestination;
|
||||
std::set<std::shared_ptr<I2PTunnelConnection> > m_Connections;
|
||||
};
|
||||
|
||||
class I2PClientTunnel: public I2PTunnel
|
||||
{
|
||||
public:
|
||||
|
||||
I2PClientTunnel (boost::asio::io_service& service, const std::string& destination, int port,
|
||||
ClientDestination * localDestination = nullptr);
|
||||
~I2PClientTunnel ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
private:
|
||||
|
||||
void Accept ();
|
||||
void HandleAccept (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
|
||||
void HandleDestinationRequestTimer (const boost::system::error_code& ecode, boost::asio::ip::tcp::socket * socket);
|
||||
void CreateConnection (boost::asio::ip::tcp::socket * socket);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
std::string m_Destination;
|
||||
const i2p::data::IdentHash * m_DestinationIdentHash;
|
||||
const i2p::data::LeaseSet * m_RemoteLeaseSet;
|
||||
};
|
||||
|
||||
class I2PServerTunnel: public I2PTunnel
|
||||
{
|
||||
public:
|
||||
|
||||
I2PServerTunnel (boost::asio::io_service& service, const std::string& address, int port,
|
||||
ClientDestination * localDestination);
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
private:
|
||||
|
||||
void Accept ();
|
||||
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::ip::tcp::endpoint m_Endpoint;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
548
Identity.cpp
548
Identity.cpp
@@ -1,548 +0,0 @@
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <cryptopp/sha.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "base64.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Identity.h"
|
||||
#include "I2PEndian.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
Identity& Identity::operator=(const Keys& keys)
|
||||
{
|
||||
// copy public and signing keys together
|
||||
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
|
||||
memset (&certificate, 0, sizeof (certificate));
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
|
||||
{
|
||||
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
|
||||
return DEFAULT_IDENTITY_SIZE;
|
||||
}
|
||||
|
||||
IdentHash Identity::Hash () const
|
||||
{
|
||||
IdentHash hash;
|
||||
CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE);
|
||||
return hash;
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx ():
|
||||
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type)
|
||||
{
|
||||
memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey));
|
||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
size_t excessLen = 0;
|
||||
uint8_t * excessBuf = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
|
||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
|
||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
||||
{
|
||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
||||
excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128
|
||||
excessBuf = new uint8_t[excessLen];
|
||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
{
|
||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
||||
excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256 - 128
|
||||
excessBuf = new uint8_t[excessLen];
|
||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
{
|
||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
||||
excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384 - 128
|
||||
excessBuf = new uint8_t[excessLen];
|
||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
{
|
||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
||||
excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512 - 128
|
||||
excessBuf = new uint8_t[excessLen];
|
||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint ("Signing key type ", (int)type, " is not supported");
|
||||
}
|
||||
m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length
|
||||
// fill certificate
|
||||
m_StandardIdentity.certificate.type = CERTIFICATE_TYPE_KEY;
|
||||
m_StandardIdentity.certificate.length = htobe16 (m_ExtendedLen);
|
||||
// fill extended buffer
|
||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
||||
*(uint16_t *)m_ExtendedBuffer = htobe16 (type);
|
||||
*(uint16_t *)(m_ExtendedBuffer + 2) = htobe16 (CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
if (excessLen && excessBuf)
|
||||
{
|
||||
memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen);
|
||||
delete[] excessBuf;
|
||||
}
|
||||
// calculate ident hash
|
||||
uint8_t * buf = new uint8_t[GetFullLen ()];
|
||||
ToBuffer (buf, GetFullLen ());
|
||||
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
|
||||
delete[] buf;
|
||||
}
|
||||
else // DSA-SHA1
|
||||
{
|
||||
memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey));
|
||||
memset (&m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
|
||||
m_IdentHash = m_StandardIdentity.Hash ();
|
||||
m_ExtendedLen = 0;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
}
|
||||
CreateVerifier ();
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
|
||||
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
FromBuffer (buf, len);
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const IdentityEx& other):
|
||||
m_Verifier (nullptr), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
IdentityEx::~IdentityEx ()
|
||||
{
|
||||
delete m_Verifier;
|
||||
delete[] m_ExtendedBuffer;
|
||||
}
|
||||
|
||||
IdentityEx& IdentityEx::operator=(const IdentityEx& other)
|
||||
{
|
||||
memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
||||
m_IdentHash = other.m_IdentHash;
|
||||
|
||||
delete[] m_ExtendedBuffer;
|
||||
m_ExtendedLen = other.m_ExtendedLen;
|
||||
if (m_ExtendedLen > 0)
|
||||
{
|
||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
||||
memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen);
|
||||
}
|
||||
else
|
||||
m_ExtendedBuffer = nullptr;
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
IdentityEx& IdentityEx::operator=(const Identity& standard)
|
||||
{
|
||||
m_StandardIdentity = standard;
|
||||
m_IdentHash = m_StandardIdentity.Hash ();
|
||||
|
||||
delete[] m_ExtendedBuffer;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
m_ExtendedLen = 0;
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (len < DEFAULT_IDENTITY_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "Identity buffer length ", len, " is too small");
|
||||
return 0;
|
||||
}
|
||||
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
|
||||
|
||||
delete[] m_ExtendedBuffer;
|
||||
if (m_StandardIdentity.certificate.length)
|
||||
{
|
||||
m_ExtendedLen = be16toh (m_StandardIdentity.certificate.length);
|
||||
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
|
||||
{
|
||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
||||
memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ExtendedLen = 0;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
}
|
||||
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
|
||||
|
||||
delete m_Verifier;
|
||||
m_Verifier = nullptr;
|
||||
|
||||
return GetFullLen ();
|
||||
}
|
||||
|
||||
size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const
|
||||
{
|
||||
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
||||
if (m_ExtendedLen > 0 && m_ExtendedBuffer)
|
||||
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
|
||||
return GetFullLen ();
|
||||
}
|
||||
|
||||
size_t IdentityEx::FromBase64(const std::string& s)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 1024);
|
||||
return FromBuffer (buf, len);
|
||||
}
|
||||
|
||||
std::string IdentityEx::ToBase64 () const
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
char str[1536];
|
||||
size_t l = ToBuffer (buf, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, 1536);
|
||||
str[l1] = 0;
|
||||
return std::string (str);
|
||||
}
|
||||
|
||||
size_t IdentityEx::GetSigningPublicKeyLen () const
|
||||
{
|
||||
if (!m_Verifier) CreateVerifier ();
|
||||
if (m_Verifier)
|
||||
return m_Verifier->GetPublicKeyLen ();
|
||||
return 128;
|
||||
}
|
||||
|
||||
size_t IdentityEx::GetSigningPrivateKeyLen () const
|
||||
{
|
||||
if (!m_Verifier) CreateVerifier ();
|
||||
if (m_Verifier)
|
||||
return m_Verifier->GetPrivateKeyLen ();
|
||||
return GetSignatureLen ()/2;
|
||||
}
|
||||
|
||||
size_t IdentityEx::GetSignatureLen () const
|
||||
{
|
||||
if (!m_Verifier) CreateVerifier ();
|
||||
if (m_Verifier)
|
||||
return m_Verifier->GetSignatureLen ();
|
||||
return 40;
|
||||
}
|
||||
bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
if (!m_Verifier) CreateVerifier ();
|
||||
if (m_Verifier)
|
||||
return m_Verifier->Verify (buf, len, signature);
|
||||
return false;
|
||||
}
|
||||
|
||||
SigningKeyType IdentityEx::GetSigningKeyType () const
|
||||
{
|
||||
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
||||
return be16toh (*(const uint16_t *)m_ExtendedBuffer); // signing key
|
||||
return SIGNING_KEY_TYPE_DSA_SHA1;
|
||||
}
|
||||
|
||||
CryptoKeyType IdentityEx::GetCryptoKeyType () const
|
||||
{
|
||||
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
||||
return be16toh (*(const uint16_t *)(m_ExtendedBuffer + 2)); // crypto key
|
||||
return CRYPTO_KEY_TYPE_ELGAMAL;
|
||||
}
|
||||
|
||||
void IdentityEx::CreateVerifier () const
|
||||
{
|
||||
auto keyType = GetSigningKeyType ();
|
||||
switch (keyType)
|
||||
{
|
||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
||||
m_Verifier = new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
|
||||
m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
{
|
||||
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
|
||||
m_Verifier = new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
||||
{
|
||||
uint8_t signingKey[i2p::crypto::ECDSAP521_KEY_LENGTH];
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier = new i2p::crypto::ECDSAP521Verifier (signingKey);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
{
|
||||
uint8_t signingKey[i2p::crypto::RSASHA2562048_KEY_LENGTH];
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier = new i2p::crypto:: RSASHA2562048Verifier (signingKey);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
{
|
||||
uint8_t signingKey[i2p::crypto::RSASHA3843072_KEY_LENGTH];
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier = new i2p::crypto:: RSASHA3843072Verifier (signingKey);
|
||||
break;
|
||||
}
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
{
|
||||
uint8_t signingKey[i2p::crypto::RSASHA5124096_KEY_LENGTH];
|
||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
||||
size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128
|
||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
||||
m_Verifier = new i2p::crypto:: RSASHA5124096Verifier (signingKey);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogPrint ("Signing key type ", (int)keyType, " is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
void IdentityEx::DropVerifier ()
|
||||
{
|
||||
auto verifier = m_Verifier;
|
||||
m_Verifier = nullptr; // TODO: make this atomic
|
||||
delete verifier;
|
||||
}
|
||||
|
||||
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
|
||||
{
|
||||
m_Public = Identity (keys);
|
||||
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
|
||||
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public.GetSigningPrivateKeyLen ());
|
||||
delete m_Signer;
|
||||
m_Signer = nullptr;
|
||||
CreateSigner ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other)
|
||||
{
|
||||
m_Public = other.m_Public;
|
||||
memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256
|
||||
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public.GetSigningPrivateKeyLen ());
|
||||
delete m_Signer;
|
||||
m_Signer = nullptr;
|
||||
CreateSigner ();
|
||||
return *this;
|
||||
}
|
||||
|
||||
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
|
||||
{
|
||||
size_t ret = m_Public.FromBuffer (buf, len);
|
||||
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
|
||||
ret += 256;
|
||||
size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen ();
|
||||
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
delete m_Signer;
|
||||
m_Signer = nullptr;
|
||||
CreateSigner ();
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
|
||||
{
|
||||
size_t ret = m_Public.ToBuffer (buf, len);
|
||||
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256
|
||||
ret += 256;
|
||||
size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen ();
|
||||
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t PrivateKeys::FromBase64(const std::string& s)
|
||||
{
|
||||
uint8_t * buf = new uint8_t[s.length ()];
|
||||
size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ());
|
||||
size_t ret = FromBuffer (buf, l);
|
||||
delete[] buf;
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string PrivateKeys::ToBase64 () const
|
||||
{
|
||||
uint8_t * buf = new uint8_t[GetFullLen ()];
|
||||
char * str = new char[GetFullLen ()*2];
|
||||
size_t l = ToBuffer (buf, GetFullLen ());
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2);
|
||||
str[l1] = 0;
|
||||
delete[] buf;
|
||||
std::string ret(str);
|
||||
delete[] str;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
if (m_Signer)
|
||||
m_Signer->Sign (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
|
||||
}
|
||||
|
||||
void PrivateKeys::CreateSigner ()
|
||||
{
|
||||
switch (m_Public.GetSigningKeyType ())
|
||||
{
|
||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
||||
m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
m_Signer = new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
||||
m_Signer = new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
m_Signer = new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
m_Signer = new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
m_Signer = new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey);
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Signing key type ", (int)m_Public.GetSigningKeyType (), " is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type)
|
||||
{
|
||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
PrivateKeys keys;
|
||||
auto& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
// signature
|
||||
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
|
||||
switch (type)
|
||||
{
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
||||
i2p::crypto::CreateECDSAP384RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
||||
i2p::crypto::CreateECDSAP521RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
||||
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
||||
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
||||
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
|
||||
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
|
||||
}
|
||||
// encryption
|
||||
uint8_t publicKey[256];
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey);
|
||||
// identity
|
||||
keys.m_Public = IdentityEx (publicKey, signingPublicKey, type);
|
||||
|
||||
keys.CreateSigner ();
|
||||
return keys;
|
||||
}
|
||||
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
|
||||
}
|
||||
|
||||
Keys CreateRandomKeys ()
|
||||
{
|
||||
Keys keys;
|
||||
auto& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
// encryption
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(rnd, keys.privateKey, keys.publicKey);
|
||||
// signing
|
||||
i2p::crypto::CreateDSARandomKeys (rnd, keys.signingPrivateKey, keys.signingKey);
|
||||
return keys;
|
||||
}
|
||||
|
||||
IdentHash CreateRoutingKey (const IdentHash& ident)
|
||||
{
|
||||
uint8_t buf[41]; // ident + yyyymmdd
|
||||
memcpy (buf, (const uint8_t *)ident, 32);
|
||||
time_t t = time (nullptr);
|
||||
struct tm tm;
|
||||
#ifdef _WIN32
|
||||
gmtime_s(&tm, &t);
|
||||
sprintf_s((char *)(buf + 32), 9, "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||
#else
|
||||
gmtime_r(&t, &tm);
|
||||
sprintf((char *)(buf + 32), "%04i%02i%02i", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||
#endif
|
||||
IdentHash key;
|
||||
CryptoPP::SHA256().CalculateDigest((uint8_t *)key, buf, 40);
|
||||
return key;
|
||||
}
|
||||
|
||||
XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
|
||||
{
|
||||
XORMetric m;
|
||||
const uint64_t * hash1 = key1.GetLL (), * hash2 = key2.GetLL ();
|
||||
m.metric_ll[0] = hash1[0] ^ hash2[0];
|
||||
m.metric_ll[1] = hash1[1] ^ hash2[1];
|
||||
m.metric_ll[2] = hash1[2] ^ hash2[2];
|
||||
m.metric_ll[3] = hash1[3] ^ hash2[3];
|
||||
return m;
|
||||
}
|
||||
}
|
||||
}
|
||||
354
LICENSE
354
LICENSE
@@ -1,339 +1,27 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
Copyright (c) 2013-2015, The PurpleI2P Project
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
All rights reserved.
|
||||
|
||||
Preamble
|
||||
Redistribution and use in source and binary forms, with or without modification, are
|
||||
permitted provided that the following conditions are met:
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of
|
||||
conditions and the following disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
i2p router for Linux written on C++
|
||||
Copyright (C) 2013 orignal
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
|
||||
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
128
LeaseSet.cpp
128
LeaseSet.cpp
@@ -1,128 +0,0 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "NetDb.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "LeaseSet.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
|
||||
LeaseSet::LeaseSet (const uint8_t * buf, int len)
|
||||
{
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
ReadFromBuffer ();
|
||||
}
|
||||
|
||||
LeaseSet::LeaseSet (const i2p::tunnel::TunnelPool& pool)
|
||||
{
|
||||
// header
|
||||
const i2p::data::LocalDestination * localDestination = pool.GetLocalDestination ();
|
||||
if (!localDestination)
|
||||
{
|
||||
m_BufferLen = 0;
|
||||
LogPrint (eLogError, "Destination for local LeaseSet doesn't exist");
|
||||
return;
|
||||
}
|
||||
m_BufferLen = localDestination->GetIdentity ().ToBuffer (m_Buffer, MAX_LS_BUFFER_SIZE);
|
||||
memcpy (m_Buffer + m_BufferLen, localDestination->GetEncryptionPublicKey (), 256);
|
||||
m_BufferLen += 256;
|
||||
auto signingKeyLen = localDestination->GetIdentity ().GetSigningPublicKeyLen ();
|
||||
memset (m_Buffer + m_BufferLen, 0, signingKeyLen);
|
||||
m_BufferLen += signingKeyLen;
|
||||
auto tunnels = pool.GetInboundTunnels (5); // 5 tunnels maximum
|
||||
m_Buffer[m_BufferLen] = tunnels.size (); // num leases
|
||||
m_BufferLen++;
|
||||
// leases
|
||||
for (auto it: tunnels)
|
||||
{
|
||||
Lease * lease = (Lease *)(m_Buffer + m_BufferLen);
|
||||
memcpy (lease->tunnelGateway, it->GetNextIdentHash (), 32);
|
||||
lease->tunnelID = htobe32 (it->GetNextTunnelID ());
|
||||
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - 60; // 1 minute before expiration
|
||||
ts *= 1000; // in milliseconds
|
||||
lease->endDate = htobe64 (ts);
|
||||
m_BufferLen += sizeof (Lease);
|
||||
}
|
||||
// signature
|
||||
localDestination->Sign (m_Buffer, m_BufferLen, m_Buffer + m_BufferLen);
|
||||
m_BufferLen += localDestination->GetIdentity ().GetSignatureLen ();
|
||||
LogPrint ("Local LeaseSet of ", tunnels.size (), " leases created");
|
||||
|
||||
ReadFromBuffer ();
|
||||
}
|
||||
|
||||
void LeaseSet::Update (const uint8_t * buf, int len)
|
||||
{
|
||||
m_Leases.clear ();
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
ReadFromBuffer ();
|
||||
}
|
||||
|
||||
void LeaseSet::ReadFromBuffer ()
|
||||
{
|
||||
size_t size = m_Identity.FromBuffer (m_Buffer, m_BufferLen);
|
||||
memcpy (m_EncryptionKey, m_Buffer + size, 256);
|
||||
size += 256; // encryption key
|
||||
size += m_Identity.GetSigningPublicKeyLen (); // unused signing key
|
||||
uint8_t num = m_Buffer[size];
|
||||
size++; // num
|
||||
LogPrint ("LeaseSet num=", (int)num);
|
||||
|
||||
// process leases
|
||||
const uint8_t * leases = m_Buffer + size;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
Lease lease = *(Lease *)leases;
|
||||
lease.tunnelID = be32toh (lease.tunnelID);
|
||||
lease.endDate = be64toh (lease.endDate);
|
||||
m_Leases.push_back (lease);
|
||||
leases += sizeof (Lease);
|
||||
|
||||
// check if lease's gateway is in our netDb
|
||||
if (!netdb.FindRouter (lease.tunnelGateway))
|
||||
{
|
||||
// if not found request it
|
||||
LogPrint ("Lease's tunnel gateway not found. Requested");
|
||||
netdb.RequestDestination (lease.tunnelGateway);
|
||||
}
|
||||
}
|
||||
|
||||
// verify
|
||||
if (!m_Identity.Verify (m_Buffer, leases - m_Buffer, leases))
|
||||
LogPrint ("LeaseSet verification failed");
|
||||
}
|
||||
|
||||
const std::vector<Lease> LeaseSet::GetNonExpiredLeases () const
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
std::vector<Lease> leases;
|
||||
for (auto& it: m_Leases)
|
||||
if (ts < it.endDate)
|
||||
leases.push_back (it);
|
||||
return leases;
|
||||
}
|
||||
|
||||
bool LeaseSet::HasExpiredLeases () const
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto& it: m_Leases)
|
||||
if (ts >= it.endDate) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LeaseSet::HasNonExpiredLeases () const
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto& it: m_Leases)
|
||||
if (ts < it.endDate) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
78
LeaseSet.h
78
LeaseSet.h
@@ -1,78 +0,0 @@
|
||||
#ifndef LEASE_SET_H__
|
||||
#define LEASE_SET_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
|
||||
namespace tunnel
|
||||
{
|
||||
class TunnelPool;
|
||||
}
|
||||
|
||||
namespace data
|
||||
{
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct Lease
|
||||
{
|
||||
uint8_t tunnelGateway[32];
|
||||
uint32_t tunnelID;
|
||||
uint64_t endDate;
|
||||
|
||||
bool operator< (const Lease& other) const
|
||||
{
|
||||
if (endDate != other.endDate)
|
||||
return endDate > other.endDate;
|
||||
else
|
||||
return tunnelID < other.tunnelID;
|
||||
}
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
const int MAX_LS_BUFFER_SIZE = 3072;
|
||||
class LeaseSet: public RoutingDestination
|
||||
{
|
||||
public:
|
||||
|
||||
LeaseSet (const uint8_t * buf, int len);
|
||||
LeaseSet (const LeaseSet& ) = default;
|
||||
LeaseSet (const i2p::tunnel::TunnelPool& pool);
|
||||
LeaseSet& operator=(const LeaseSet& ) = default;
|
||||
void Update (const uint8_t * buf, int len);
|
||||
const IdentityEx& GetIdentity () const { return m_Identity; };
|
||||
|
||||
const uint8_t * GetBuffer () const { return m_Buffer; };
|
||||
size_t GetBufferLen () const { return m_BufferLen; };
|
||||
|
||||
// implements RoutingDestination
|
||||
const IdentHash& GetIdentHash () const { return m_Identity.GetIdentHash (); };
|
||||
const std::vector<Lease>& GetLeases () const { return m_Leases; };
|
||||
const std::vector<Lease> GetNonExpiredLeases () const;
|
||||
bool HasExpiredLeases () const;
|
||||
bool HasNonExpiredLeases () const;
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
|
||||
bool IsDestination () const { return true; };
|
||||
|
||||
private:
|
||||
|
||||
void ReadFromBuffer ();
|
||||
|
||||
private:
|
||||
|
||||
std::vector<Lease> m_Leases;
|
||||
IdentityEx m_Identity;
|
||||
uint8_t m_EncryptionKey[256];
|
||||
uint8_t m_Buffer[MAX_LS_BUFFER_SIZE];
|
||||
size_t m_BufferLen;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
43
Log.cpp
43
Log.cpp
@@ -1,43 +0,0 @@
|
||||
#include "Log.h"
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
||||
Log * g_Log = nullptr;
|
||||
|
||||
static const char * g_LogLevelStr[eNumLogLevels] =
|
||||
{
|
||||
"error", // eLogError
|
||||
"warn", // eLogWarning
|
||||
"info", // eLogInfo
|
||||
"debug" // eLogDebug
|
||||
};
|
||||
|
||||
void LogMsg::Process()
|
||||
{
|
||||
output << boost::posix_time::second_clock::local_time().time_of_day () <<
|
||||
"/" << g_LogLevelStr[level] << " - ";
|
||||
output << s.str();
|
||||
}
|
||||
|
||||
void Log::Flush ()
|
||||
{
|
||||
if (m_LogStream)
|
||||
m_LogStream->flush();
|
||||
}
|
||||
|
||||
void Log::SetLogFile (const std::string& fullFilePath)
|
||||
{
|
||||
auto logFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
|
||||
if (logFile->is_open ())
|
||||
{
|
||||
SetLogStream (logFile);
|
||||
LogPrint("Logging to file ", fullFilePath, " enabled.");
|
||||
}
|
||||
else
|
||||
delete logFile;
|
||||
}
|
||||
|
||||
void Log::SetLogStream (std::ostream * logStream)
|
||||
{
|
||||
if (m_LogStream) delete m_LogStream;
|
||||
m_LogStream = logStream;
|
||||
}
|
||||
117
Log.h
117
Log.h
@@ -1,117 +0,0 @@
|
||||
#ifndef LOG_H__
|
||||
#define LOG_H__
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include "Queue.h"
|
||||
|
||||
enum LogLevel
|
||||
{
|
||||
eLogError = 0,
|
||||
eLogWarning,
|
||||
eLogInfo,
|
||||
eLogDebug,
|
||||
eNumLogLevels
|
||||
};
|
||||
|
||||
struct LogMsg
|
||||
{
|
||||
std::stringstream s;
|
||||
std::ostream& output;
|
||||
LogLevel level;
|
||||
|
||||
LogMsg (std::ostream& o = std::cout, LogLevel l = eLogInfo): output (o), level (l) {};
|
||||
|
||||
void Process();
|
||||
};
|
||||
|
||||
class Log: public i2p::util::MsgQueue<LogMsg>
|
||||
{
|
||||
public:
|
||||
|
||||
Log (): m_LogStream (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); };
|
||||
~Log () { delete m_LogStream; };
|
||||
|
||||
void SetLogFile (const std::string& fullFilePath);
|
||||
void SetLogStream (std::ostream * logStream);
|
||||
std::ostream * GetLogStream () const { return m_LogStream; };
|
||||
|
||||
private:
|
||||
|
||||
void Flush ();
|
||||
|
||||
private:
|
||||
|
||||
std::ostream * m_LogStream;
|
||||
};
|
||||
|
||||
extern Log * g_Log;
|
||||
|
||||
inline void StartLog (const std::string& fullFilePath)
|
||||
{
|
||||
if (!g_Log)
|
||||
{
|
||||
g_Log = new Log ();
|
||||
if (fullFilePath.length () > 0)
|
||||
g_Log->SetLogFile (fullFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
inline void StartLog (std::ostream * s)
|
||||
{
|
||||
if (!g_Log)
|
||||
{
|
||||
g_Log = new Log ();
|
||||
if (s)
|
||||
g_Log->SetLogStream (s);
|
||||
}
|
||||
}
|
||||
|
||||
inline void StopLog ()
|
||||
{
|
||||
if (g_Log)
|
||||
{
|
||||
delete g_Log;
|
||||
g_Log = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TValue>
|
||||
void LogPrint (std::stringstream& s, TValue arg)
|
||||
{
|
||||
s << arg;
|
||||
}
|
||||
|
||||
template<typename TValue, typename... TArgs>
|
||||
void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
|
||||
{
|
||||
LogPrint (s, arg);
|
||||
LogPrint (s, args...);
|
||||
}
|
||||
|
||||
template<typename... TArgs>
|
||||
void LogPrint (LogLevel level, TArgs... args)
|
||||
{
|
||||
LogMsg * msg = (g_Log && g_Log->GetLogStream ()) ? new LogMsg (*g_Log->GetLogStream (), level) :
|
||||
new LogMsg (std::cout, level);
|
||||
LogPrint (msg->s, args...);
|
||||
msg->s << std::endl;
|
||||
if (g_Log)
|
||||
g_Log->Put (msg);
|
||||
else
|
||||
{
|
||||
msg->Process ();
|
||||
delete msg;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... TArgs>
|
||||
void LogPrint (TArgs... args)
|
||||
{
|
||||
LogPrint (eLogInfo, args...);
|
||||
}
|
||||
|
||||
#endif
|
||||
131
Makefile
131
Makefile
@@ -1,52 +1,127 @@
|
||||
UNAME := $(shell uname -s)
|
||||
SYS := $(shell $(CXX) -dumpmachine)
|
||||
SHLIB := libi2pd.so
|
||||
I2PD := i2p
|
||||
ARLIB := libi2pd.a
|
||||
SHLIB_CLIENT := libi2pdclient.so
|
||||
ARLIB_CLIENT := libi2pdclient.a
|
||||
I2PD := i2pd
|
||||
GREP := grep
|
||||
DEPS := obj/make.dep
|
||||
|
||||
LIB_SRC_DIR := libi2pd
|
||||
LIB_CLIENT_SRC_DIR := libi2pd_client
|
||||
DAEMON_SRC_DIR := daemon
|
||||
|
||||
include filelist.mk
|
||||
|
||||
USE_AESNI := yes
|
||||
USE_STATIC := no
|
||||
USE_AESNI := yes
|
||||
USE_AVX := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
include Makefile.osx
|
||||
else ifeq ($(UNAME),FreeBSD)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
include Makefile.bsd
|
||||
else ifeq ($(UNAME),Linux)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
include Makefile.linux
|
||||
else # win32
|
||||
DAEMON_SRC += DaemonWin32.cpp
|
||||
ifeq ($(WEBSOCKETS),1)
|
||||
NEEDED_CXXFLAGS += -DWITH_EVENTS
|
||||
endif
|
||||
|
||||
all: obj $(SHLIB) $(I2PD)
|
||||
ifneq (, $(findstring darwin, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
ifeq ($(HOMEBREW),1)
|
||||
include Makefile.homebrew
|
||||
else
|
||||
include Makefile.osx
|
||||
endif
|
||||
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.bsd
|
||||
else ifneq (, $(findstring linux, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.linux
|
||||
else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS)))
|
||||
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32Service.cpp Win32/Win32App.cpp
|
||||
include Makefile.mingw
|
||||
else # not supported
|
||||
$(error Not supported platform)
|
||||
endif
|
||||
|
||||
obj:
|
||||
mkdir -p obj
|
||||
ifeq ($(USE_MESHNET),yes)
|
||||
NEEDED_CXXFLAGS += -DMESHNET
|
||||
endif
|
||||
|
||||
obj/%.o : %.cpp %.h
|
||||
$(CXX) $(CXXFLAGS) $(INCFLAGS) -c -o $@ $<
|
||||
NEEDED_CXXFLAGS += -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR)
|
||||
|
||||
# weaker rule for building files without headers
|
||||
obj/%.o : %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(INCFLAGS) -c -o $@ $<
|
||||
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
|
||||
|
||||
$(I2PD): $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||
$(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS)
|
||||
mk_obj_dir:
|
||||
@mkdir -p obj
|
||||
@mkdir -p obj/Win32
|
||||
@mkdir -p obj/$(LIB_SRC_DIR)
|
||||
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
|
||||
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
||||
|
||||
api: mk_obj_dir $(SHLIB) $(ARLIB)
|
||||
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
|
||||
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||
## For example, when adding 'hardening flags' to the build
|
||||
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
|
||||
deps: mk_obj_dir
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
|
||||
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
|
||||
|
||||
obj/%.o: %.cpp
|
||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
|
||||
|
||||
# '-' is 'ignore if missing' on first run
|
||||
-include $(DEPS)
|
||||
|
||||
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
|
||||
$(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS)
|
||||
|
||||
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||
$(CXX) -o $@ $^ $(LDFLAGS) $(LDLIBS) -shared
|
||||
ifneq ($(USE_STATIC),yes)
|
||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
||||
endif
|
||||
|
||||
$(SHLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
||||
|
||||
$(ARLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||
$(AR) -r $@ $^
|
||||
|
||||
$(ARLIB_CLIENT): $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||
$(AR) -r $@ $^
|
||||
|
||||
clean:
|
||||
rm -fr obj $(I2PD) $(SHLIB)
|
||||
$(RM) -r obj
|
||||
$(RM) -r docs/generated
|
||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 master)
|
||||
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
|
||||
strip $^
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||
BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
|
||||
dist:
|
||||
git archive --format=tar.gz -9 --worktree-attributes \
|
||||
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
|
||||
|
||||
last-dist:
|
||||
git archive --format=tar.gz -9 --worktree-attributes \
|
||||
--prefix=i2pd_$(LATEST_TAG)/ $(BRANCH) -o ../i2pd_$(LATEST_TAG).orig.tar.gz
|
||||
|
||||
doxygen:
|
||||
doxygen -s docs/Doxyfile
|
||||
|
||||
.PHONY: all
|
||||
.PHONY: clean
|
||||
.PHONY: deps
|
||||
.PHONY: doxygen
|
||||
.PHONY: dist
|
||||
.PHONY: api
|
||||
.PHONY: api_client
|
||||
.PHONY: mk_obj_dir
|
||||
|
||||
13
Makefile.bsd
13
Makefile.bsd
@@ -1,5 +1,12 @@
|
||||
CXX = g++
|
||||
CXXFLAGS = -g -Wall -O2 -std=c++11
|
||||
CXX = clang++
|
||||
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
|
||||
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
|
||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||
## For example, when adding 'hardening flags' to the build
|
||||
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
NEEDED_CXXFLAGS = -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
|
||||
45
Makefile.homebrew
Normal file
45
Makefile.homebrew
Normal file
@@ -0,0 +1,45 @@
|
||||
# root directory holding homebrew
|
||||
BREWROOT = /usr/local
|
||||
BOOSTROOT = ${BREWROOT}/opt/boost
|
||||
SSLROOT = ${BREWROOT}/opt/libressl
|
||||
UPNPROOT = ${BREWROOT}/opt/miniupnpc
|
||||
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX -Wno-overloaded-virtual
|
||||
INCFLAGS = -I${SSLROOT}/include -I${BOOSTROOT}/include
|
||||
|
||||
ifndef TRAVIS
|
||||
CXX = clang++
|
||||
endif
|
||||
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_date_time.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a -lpthread
|
||||
else
|
||||
LDFLAGS = -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -ldl
|
||||
CXXFLAGS += -DUSE_UPNP
|
||||
INCFLAGS += -I${UPNPROOT}/include
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
|
||||
else
|
||||
LDFLAGS += -L${UPNPROOT}/lib
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
||||
endif
|
||||
|
||||
# OSX Notes
|
||||
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
|
||||
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
|
||||
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
|
||||
# note from psi: 2009 macbook does not have aesni
|
||||
#ifeq ($(USE_AESNI),yes)
|
||||
# CXXFLAGS += -maes -DAESNI
|
||||
#endif
|
||||
|
||||
# Disabled, since it will be the default make rule. I think its better
|
||||
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
|
||||
#install: all
|
||||
# test -d ${PREFIX} || mkdir -p ${PREFIX}/
|
||||
# cp -r i2p ${PREFIX}/
|
||||
@@ -1,43 +1,72 @@
|
||||
CXXFLAGS = -g -Wall -fPIC
|
||||
INCFLAGS =
|
||||
# set defaults instead redefine
|
||||
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
|
||||
INCFLAGS ?=
|
||||
|
||||
# detect proper flag for c++11 support by gcc
|
||||
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
|
||||
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||
## For example, when adding 'hardening flags' to the build
|
||||
## (e.g. -fstack-protector-strong -Wformat -Werror=format-security), we do not want to remove
|
||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
||||
## custom FLAGS to work at build-time.
|
||||
|
||||
# detect proper flag for c++11 support by compilers
|
||||
CXXVER := $(shell $(CXX) -dumpversion)
|
||||
ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # >= 4.10
|
||||
CXXFLAGS += -std=c++11
|
||||
ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
|
||||
CXXFLAGS += -std=c++11
|
||||
NEEDED_CXXFLAGS += -std=c++11 -D_GLIBCXX_USE_NANOSLEEP=1
|
||||
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
|
||||
CXXFLAGS += -std=c++0x
|
||||
else ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||
CXXFLAGS += -std=c++11
|
||||
NEEDED_CXXFLAGS += -std=c++0x
|
||||
else ifeq ($(shell expr match ${CXXVER} "[5-7]\.[0-9]"),3) # gcc >= 5.0
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else ifeq ($(shell expr match ${CXXVER} "7"),1) # gcc 7 ubuntu
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else # not supported
|
||||
$(error Compiler too old)
|
||||
$(error Compiler too old)
|
||||
endif
|
||||
|
||||
NEEDED_CXXFLAGS += -fPIC
|
||||
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LIBDIR := /usr/lib
|
||||
LDLIBS = $(LIBDIR)/libboost_system.a
|
||||
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
||||
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
||||
LDLIBS += $(LIBDIR)/libboost_regex.a
|
||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||
LDLIBS += $(LIBDIR)/libcryptopp.a
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
|
||||
USE_AESNI := no
|
||||
# NOTE: on glibc you will get this warning:
|
||||
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
||||
# the shared libraries from the glibc version used for linking
|
||||
LIBDIR := /usr/lib
|
||||
LDLIBS = $(LIBDIR)/libboost_system.a
|
||||
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
||||
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||
LDLIBS += $(LIBDIR)/libssl.a
|
||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||
LDLIBS += $(LIBDIR)/libz.a
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt -ldl
|
||||
USE_AESNI := no
|
||||
else
|
||||
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
# UPNP Support (miniupnpc 1.5 and higher)
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
CXXFLAGS += -DUSE_UPNP
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS += $(LIBDIR)/libminiupnpc.a
|
||||
else
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
||||
endif
|
||||
|
||||
GREP = fgrep
|
||||
IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(GREP) -c "64")
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
ifeq ($(IS_64),1)
|
||||
#check if AES-NI is supported by CPU
|
||||
ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0)
|
||||
CXXFLAGS += -maes -DAESNI
|
||||
else
|
||||
$(warning "AESNI support enabled requested but not supported by this CPU")
|
||||
CPU_FLAGS += -maes -DAESNI
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AVX),yes)
|
||||
#check if AVX supported by CPU
|
||||
ifneq ($(shell $(GREP) -c avx /proc/cpuinfo),0)
|
||||
CPU_FLAGS += -mavx
|
||||
endif
|
||||
endif
|
||||
|
||||
54
Makefile.mingw
Normal file
54
Makefile.mingw
Normal file
@@ -0,0 +1,54 @@
|
||||
USE_WIN32_APP=yes
|
||||
CXX = g++
|
||||
WINDRES = windres
|
||||
CXXFLAGS = -Os -D_MT -DWIN32 -D_WINDOWS -DWIN32_LEAN_AND_MEAN
|
||||
NEEDED_CXXFLAGS = -std=c++11
|
||||
BOOST_SUFFIX = -mt
|
||||
INCFLAGS = -Idaemon -I.
|
||||
LDFLAGS = -s -Wl,-rpath,/usr/local/lib -Wl,-Bstatic -static-libgcc -static-libstdc++
|
||||
|
||||
# UPNP Support
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
CXXFLAGS += -DUSE_UPNP -DMINIUPNP_STATICLIB
|
||||
LDLIBS = -lminiupnpc
|
||||
endif
|
||||
|
||||
LDLIBS += \
|
||||
-lboost_system$(BOOST_SUFFIX) \
|
||||
-lboost_date_time$(BOOST_SUFFIX) \
|
||||
-lboost_filesystem$(BOOST_SUFFIX) \
|
||||
-lboost_program_options$(BOOST_SUFFIX) \
|
||||
-lssl \
|
||||
-lcrypto \
|
||||
-lz \
|
||||
-lwsock32 \
|
||||
-lws2_32 \
|
||||
-lgdi32 \
|
||||
-liphlpapi \
|
||||
-lstdc++ \
|
||||
-lpthread
|
||||
|
||||
ifeq ($(USE_WIN32_APP), yes)
|
||||
CXXFLAGS += -DWIN32_APP
|
||||
LDFLAGS += -mwindows
|
||||
DAEMON_RC += Win32/Resource.rc
|
||||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||
endif
|
||||
|
||||
# don't change following line to ifeq ($(USE_AESNI),yes) !!!
|
||||
ifeq ($(USE_AESNI),1)
|
||||
CPU_FLAGS += -maes -DAESNI
|
||||
else
|
||||
CPU_FLAGS += -msse
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AVX),1)
|
||||
CPU_FLAGS += -mavx
|
||||
endif
|
||||
|
||||
ifeq ($(USE_ASLR),yes)
|
||||
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
|
||||
endif
|
||||
|
||||
obj/%.o : %.rc
|
||||
$(WINDRES) -i $< -o $@
|
||||
42
Makefile.osx
42
Makefile.osx
@@ -1,17 +1,37 @@
|
||||
CXX = clang++
|
||||
CXXFLAGS = -g -Wall -std=c++11 -lstdc++ -DCRYPTOPP_DISABLE_ASM
|
||||
CXXFLAGS = -Os -Wall -std=c++11 -DMAC_OSX
|
||||
#CXXFLAGS = -g -O2 -Wall -std=c++11
|
||||
INCFLAGS = -I/usr/local/include
|
||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
||||
|
||||
# OSX Notes
|
||||
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
|
||||
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
|
||||
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
CXXFLAGS += -maes -DAESNI
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
||||
else
|
||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
install: all
|
||||
mkdir -p ${PREFIX}/
|
||||
cp -r i2p ${PREFIX}/
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -ldl
|
||||
CXXFLAGS += -DUSE_UPNP
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS += /usr/local/lib/libminiupnpc.a
|
||||
else
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AESNI),1)
|
||||
CXXFLAGS += -maes -DAESNI
|
||||
else
|
||||
CXXFLAGS += -msse
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AVX),1)
|
||||
CXXFLAGS += -mavx
|
||||
endif
|
||||
|
||||
# Disabled, since it will be the default make rule. I think its better
|
||||
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
|
||||
#install: all
|
||||
# test -d ${PREFIX} || mkdir -p ${PREFIX}/
|
||||
# cp -r i2p ${PREFIX}/
|
||||
|
||||
659
NTCPSession.cpp
659
NTCPSession.cpp
@@ -1,659 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "I2PEndian.h"
|
||||
#include <cryptopp/dh.h>
|
||||
#include "base64.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Transports.h"
|
||||
#include "NetDb.h"
|
||||
#include "NTCPSession.h"
|
||||
|
||||
using namespace i2p::crypto;
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
NTCPSession::NTCPSession (boost::asio::io_service& service, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
|
||||
TransportSession (in_RemoteRouter), m_Socket (service),
|
||||
m_TerminationTimer (service), m_IsEstablished (false), m_ReceiveBufferOffset (0),
|
||||
m_NextMessage (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0)
|
||||
{
|
||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
||||
m_Establisher = new Establisher;
|
||||
}
|
||||
|
||||
NTCPSession::~NTCPSession ()
|
||||
{
|
||||
delete m_Establisher;
|
||||
if (m_NextMessage)
|
||||
i2p::DeleteI2NPMessage (m_NextMessage);
|
||||
for (auto it :m_DelayedMessages)
|
||||
i2p::DeleteI2NPMessage (it);
|
||||
m_DelayedMessages.clear ();
|
||||
}
|
||||
|
||||
void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key)
|
||||
{
|
||||
CryptoPP::DH dh (elgp, elgg);
|
||||
uint8_t sharedKey[256];
|
||||
if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey))
|
||||
{
|
||||
LogPrint (eLogError, "Couldn't create shared key");
|
||||
Terminate ();
|
||||
return;
|
||||
};
|
||||
|
||||
uint8_t * aesKey = key;
|
||||
if (sharedKey[0] & 0x80)
|
||||
{
|
||||
aesKey[0] = 0;
|
||||
memcpy (aesKey + 1, sharedKey, 31);
|
||||
}
|
||||
else if (sharedKey[0])
|
||||
memcpy (aesKey, sharedKey, 32);
|
||||
else
|
||||
{
|
||||
// find first non-zero byte
|
||||
uint8_t * nonZero = sharedKey + 1;
|
||||
while (!*nonZero)
|
||||
{
|
||||
nonZero++;
|
||||
if (nonZero - sharedKey > 32)
|
||||
{
|
||||
LogPrint (eLogWarning, "First 32 bytes of shared key is all zeros. Ignored");
|
||||
return;
|
||||
}
|
||||
}
|
||||
memcpy (aesKey, nonZero, 32);
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::Terminate ()
|
||||
{
|
||||
m_IsEstablished = false;
|
||||
m_Socket.close ();
|
||||
int numDelayed = 0;
|
||||
for (auto it :m_DelayedMessages)
|
||||
{
|
||||
// try to send them again
|
||||
if (m_RemoteRouter)
|
||||
transports.SendMessage (m_RemoteRouter->GetIdentHash (), it);
|
||||
numDelayed++;
|
||||
}
|
||||
m_DelayedMessages.clear ();
|
||||
if (numDelayed > 0)
|
||||
LogPrint (eLogWarning, "NTCP session ", numDelayed, " not sent");
|
||||
// TODO: notify tunnels
|
||||
transports.RemoveNTCPSession (shared_from_this ());
|
||||
LogPrint ("NTCP session terminated");
|
||||
}
|
||||
|
||||
void NTCPSession::Connected ()
|
||||
{
|
||||
m_IsEstablished = true;
|
||||
|
||||
delete m_Establisher;
|
||||
m_Establisher = nullptr;
|
||||
|
||||
delete m_DHKeysPair;
|
||||
m_DHKeysPair = nullptr;
|
||||
|
||||
SendTimeSyncMessage ();
|
||||
SendI2NPMessage (CreateDatabaseStoreMsg ()); // we tell immediately who we are
|
||||
|
||||
if (!m_DelayedMessages.empty ())
|
||||
{
|
||||
for (auto it :m_DelayedMessages)
|
||||
SendI2NPMessage (it);
|
||||
m_DelayedMessages.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::ClientLogin ()
|
||||
{
|
||||
if (!m_DHKeysPair)
|
||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
||||
// send Phase1
|
||||
const uint8_t * x = m_DHKeysPair->publicKey;
|
||||
memcpy (m_Establisher->phase1.pubKey, x, 256);
|
||||
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase1.HXxorHI, x, 256);
|
||||
const uint8_t * ident = m_RemoteIdentity.GetIdentHash ();
|
||||
for (int i = 0; i < 32; i++)
|
||||
m_Establisher->phase1.HXxorHI[i] ^= ident[i];
|
||||
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void NTCPSession::ServerLogin ()
|
||||
{
|
||||
// receive Phase1
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "Couldn't send Phase 1 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Phase 1 sent: ", bytes_transferred);
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase2Received, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Phase 1 read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Phase 1 received: ", bytes_transferred);
|
||||
// verify ident
|
||||
uint8_t digest[32];
|
||||
CryptoPP::SHA256().CalculateDigest(digest, m_Establisher->phase1.pubKey, 256);
|
||||
const uint8_t * ident = i2p::context.GetRouterInfo ().GetIdentHash ();
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i])
|
||||
{
|
||||
LogPrint (eLogError, "Wrong ident");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SendPhase2 ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::SendPhase2 ()
|
||||
{
|
||||
if (!m_DHKeysPair)
|
||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
||||
const uint8_t * y = m_DHKeysPair->publicKey;
|
||||
memcpy (m_Establisher->phase2.pubKey, y, 256);
|
||||
uint8_t xy[512];
|
||||
memcpy (xy, m_Establisher->phase1.pubKey, 256);
|
||||
memcpy (xy + 256, y, 256);
|
||||
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase2.encrypted.hxy, xy, 512);
|
||||
uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ());
|
||||
m_Establisher->phase2.encrypted.timestamp = tsB;
|
||||
// TODO: fill filler
|
||||
|
||||
i2p::crypto::AESKey aesKey;
|
||||
CreateAESKey (m_Establisher->phase1.pubKey, aesKey);
|
||||
m_Encryption.SetKey (aesKey);
|
||||
m_Encryption.SetIV (y + 240);
|
||||
m_Decryption.SetKey (aesKey);
|
||||
m_Decryption.SetIV (m_Establisher->phase1.HXxorHI + 16);
|
||||
|
||||
m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase2Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsB));
|
||||
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "Couldn't send Phase 2 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Phase 2 sent: ", bytes_transferred);
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase3Received, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, tsB));
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Phase 2 read error: ", ecode.message (), ". Wrong ident assumed");
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
// this RI is not valid
|
||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true);
|
||||
transports.ReuseDHKeysPair (m_DHKeysPair);
|
||||
m_DHKeysPair = nullptr;
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Phase 2 received: ", bytes_transferred);
|
||||
|
||||
i2p::crypto::AESKey aesKey;
|
||||
CreateAESKey (m_Establisher->phase2.pubKey, aesKey);
|
||||
m_Decryption.SetKey (aesKey);
|
||||
m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240);
|
||||
m_Encryption.SetKey (aesKey);
|
||||
m_Encryption.SetIV (m_Establisher->phase1.HXxorHI + 16);
|
||||
|
||||
m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
|
||||
// verify
|
||||
uint8_t xy[512], hxy[32];
|
||||
memcpy (xy, m_DHKeysPair->publicKey, 256);
|
||||
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
|
||||
CryptoPP::SHA256().CalculateDigest(hxy, xy, 512);
|
||||
if (memcmp (hxy, m_Establisher->phase2.encrypted.hxy, 32))
|
||||
{
|
||||
LogPrint (eLogError, "Incorrect hash");
|
||||
transports.ReuseDHKeysPair (m_DHKeysPair);
|
||||
m_DHKeysPair = nullptr;
|
||||
Terminate ();
|
||||
return ;
|
||||
}
|
||||
SendPhase3 ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::SendPhase3 ()
|
||||
{
|
||||
auto keys = i2p::context.GetPrivateKeys ();
|
||||
uint8_t * buf = m_ReceiveBuffer;
|
||||
*(uint16_t *)buf = htobe16 (keys.GetPublic ().GetFullLen ());
|
||||
buf += 2;
|
||||
buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE);
|
||||
uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ());
|
||||
*(uint32_t *)buf = tsA;
|
||||
buf += 4;
|
||||
size_t signatureLen = keys.GetPublic ().GetSignatureLen ();
|
||||
size_t len = (buf - m_ReceiveBuffer) + signatureLen;
|
||||
size_t paddingSize = len & 0x0F; // %16
|
||||
if (paddingSize > 0)
|
||||
{
|
||||
paddingSize = 16 - paddingSize;
|
||||
// TODO: fill padding with random data
|
||||
buf += paddingSize;
|
||||
len += paddingSize;
|
||||
}
|
||||
|
||||
SignedData s;
|
||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
||||
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident
|
||||
s.Insert (tsA); // tsA
|
||||
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
|
||||
s.Sign (keys, buf);
|
||||
|
||||
m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer);
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, len), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase3Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsA));
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "Couldn't send Phase 3 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Phase 3 sent: ", bytes_transferred);
|
||||
// wait for phase4
|
||||
auto signatureLen = m_RemoteIdentity.GetSignatureLen ();
|
||||
size_t paddingSize = signatureLen & 0x0F; // %16
|
||||
if (paddingSize > 0) signatureLen += (16 - paddingSize);
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase4Received, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, tsA));
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Phase 3 read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Phase 3 received: ", bytes_transferred);
|
||||
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
|
||||
uint8_t * buf = m_ReceiveBuffer;
|
||||
uint16_t size = be16toh (*(uint16_t *)buf);
|
||||
m_RemoteIdentity.FromBuffer (buf + 2, size);
|
||||
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen ();
|
||||
size_t paddingLen = expectedSize & 0x0F;
|
||||
if (paddingLen) paddingLen = (16 - paddingLen);
|
||||
if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE)
|
||||
{
|
||||
// we need more bytes for Phase3
|
||||
expectedSize += paddingLen;
|
||||
LogPrint (eLogDebug, "Wait for ", expectedSize, " more bytes for Phase3");
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, tsB, paddingLen));
|
||||
}
|
||||
else
|
||||
HandlePhase3 (tsB, paddingLen);
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Phase 3 extra read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Phase 3 extra received: ", bytes_transferred);
|
||||
m_Decryption.Decrypt (m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, bytes_transferred, m_ReceiveBuffer+ NTCP_DEFAULT_PHASE3_SIZE);
|
||||
HandlePhase3 (tsB, paddingLen);
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen)
|
||||
{
|
||||
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/;
|
||||
uint32_t tsA = *(uint32_t *)buf;
|
||||
buf += 4;
|
||||
buf += paddingLen;
|
||||
|
||||
SignedData s;
|
||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
||||
s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident
|
||||
s.Insert (tsA); // tsA
|
||||
s.Insert (tsB); // tsB
|
||||
if (!s.Verify (m_RemoteIdentity, buf))
|
||||
{
|
||||
LogPrint (eLogError, "signature verification failed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
|
||||
SendPhase4 (tsA, tsB);
|
||||
}
|
||||
|
||||
void NTCPSession::SendPhase4 (uint32_t tsA, uint32_t tsB)
|
||||
{
|
||||
SignedData s;
|
||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
||||
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident
|
||||
s.Insert (tsA); // tsA
|
||||
s.Insert (tsB); // tsB
|
||||
auto keys = i2p::context.GetPrivateKeys ();
|
||||
auto signatureLen = keys.GetPublic ().GetSignatureLen ();
|
||||
s.Sign (keys, m_ReceiveBuffer);
|
||||
size_t paddingSize = signatureLen & 0x0F; // %16
|
||||
if (paddingSize > 0) signatureLen += (16 - paddingSize);
|
||||
m_Encryption.Encrypt (m_ReceiveBuffer, signatureLen, m_ReceiveBuffer);
|
||||
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase4Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "Couldn't send Phase 4 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Phase 4 sent: ", bytes_transferred);
|
||||
LogPrint ("NTCP server session connected");
|
||||
transports.AddNTCPSession (shared_from_this ());
|
||||
|
||||
Connected ();
|
||||
m_ReceiveBufferOffset = 0;
|
||||
m_NextMessage = nullptr;
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Phase 4 read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
// this router doesn't like us
|
||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Phase 4 received: ", bytes_transferred);
|
||||
m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
|
||||
|
||||
// verify signature
|
||||
SignedData s;
|
||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
||||
s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident
|
||||
s.Insert (tsA); // tsA
|
||||
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
|
||||
|
||||
if (!s.Verify (m_RemoteIdentity, m_ReceiveBuffer))
|
||||
{
|
||||
LogPrint (eLogError, "signature verification failed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
LogPrint ("NTCP session connected");
|
||||
Connected ();
|
||||
|
||||
m_ReceiveBufferOffset = 0;
|
||||
m_NextMessage = nullptr;
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::Receive ()
|
||||
{
|
||||
m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, NTCP_BUFFER_SIZE - m_ReceiveBufferOffset),
|
||||
std::bind(&NTCPSession::HandleReceived, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "Read error: ", ecode.message ());
|
||||
//if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NumReceivedBytes += bytes_transferred;
|
||||
m_ReceiveBufferOffset += bytes_transferred;
|
||||
|
||||
if (m_ReceiveBufferOffset >= 16)
|
||||
{
|
||||
uint8_t * nextBlock = m_ReceiveBuffer;
|
||||
while (m_ReceiveBufferOffset >= 16)
|
||||
{
|
||||
if (!DecryptNextBlock (nextBlock)) // 16 bytes
|
||||
{
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
nextBlock += 16;
|
||||
m_ReceiveBufferOffset -= 16;
|
||||
}
|
||||
if (m_ReceiveBufferOffset > 0)
|
||||
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
|
||||
}
|
||||
|
||||
ScheduleTermination (); // reset termination timer
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
bool NTCPSession::DecryptNextBlock (const uint8_t * encrypted) // 16 bytes
|
||||
{
|
||||
if (!m_NextMessage) // new message, header expected
|
||||
{
|
||||
m_NextMessage = i2p::NewI2NPMessage ();
|
||||
m_NextMessageOffset = 0;
|
||||
|
||||
m_Decryption.Decrypt (encrypted, m_NextMessage->buf);
|
||||
uint16_t dataSize = be16toh (*(uint16_t *)m_NextMessage->buf);
|
||||
if (dataSize)
|
||||
{
|
||||
// new message
|
||||
if (dataSize > NTCP_MAX_MESSAGE_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP data size ", dataSize, " exceeds max size");
|
||||
i2p::DeleteI2NPMessage (m_NextMessage);
|
||||
m_NextMessage = nullptr;
|
||||
return false;
|
||||
}
|
||||
m_NextMessageOffset += 16;
|
||||
m_NextMessage->offset = 2; // size field
|
||||
m_NextMessage->len = dataSize + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// timestamp
|
||||
LogPrint ("Timestamp");
|
||||
i2p::DeleteI2NPMessage (m_NextMessage);
|
||||
m_NextMessage = nullptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else // message continues
|
||||
{
|
||||
m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset);
|
||||
m_NextMessageOffset += 16;
|
||||
}
|
||||
|
||||
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
|
||||
{
|
||||
// we have a complete I2NP message
|
||||
i2p::HandleI2NPMessage (m_NextMessage);
|
||||
m_NextMessage = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NTCPSession::Send (i2p::I2NPMessage * msg)
|
||||
{
|
||||
uint8_t * sendBuffer;
|
||||
int len;
|
||||
|
||||
if (msg)
|
||||
{
|
||||
// regular I2NP
|
||||
if (msg->offset < 2)
|
||||
{
|
||||
LogPrint (eLogError, "Malformed I2NP message");
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
sendBuffer = msg->GetBuffer () - 2;
|
||||
len = msg->GetLength ();
|
||||
*((uint16_t *)sendBuffer) = htobe16 (len);
|
||||
}
|
||||
else
|
||||
{
|
||||
// prepare timestamp
|
||||
sendBuffer = m_TimeSyncBuffer;
|
||||
len = 4;
|
||||
*((uint16_t *)sendBuffer) = 0;
|
||||
*((uint32_t *)(sendBuffer + 2)) = htobe32 (time (0));
|
||||
}
|
||||
int rem = (len + 6) & 0x0F; // %16
|
||||
int padding = 0;
|
||||
if (rem > 0) padding = 16 - rem;
|
||||
// TODO: fill padding
|
||||
m_Adler.CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding);
|
||||
|
||||
int l = len + padding + 6;
|
||||
m_Encryption.Encrypt(sendBuffer, l, sendBuffer);
|
||||
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (sendBuffer, l), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msg));
|
||||
}
|
||||
|
||||
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg)
|
||||
{
|
||||
if (msg)
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogWarning, "Couldn't send msg: ", ecode.message ());
|
||||
// we shouldn't call Terminate () here, because HandleReceive takes care
|
||||
// TODO: 'delete this' statement in Terminate () must be eliminated later
|
||||
// Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_NumSentBytes += bytes_transferred;
|
||||
ScheduleTermination (); // reset termination timer
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::SendTimeSyncMessage ()
|
||||
{
|
||||
Send (nullptr);
|
||||
}
|
||||
|
||||
void NTCPSession::SendI2NPMessage (I2NPMessage * msg)
|
||||
{
|
||||
if (msg)
|
||||
{
|
||||
if (m_IsEstablished)
|
||||
Send (msg);
|
||||
else
|
||||
m_DelayedMessages.push_back (msg);
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::ScheduleTermination ()
|
||||
{
|
||||
m_TerminationTimer.cancel ();
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT));
|
||||
m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void NTCPSession::HandleTerminationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint ("No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds");
|
||||
//Terminate ();
|
||||
m_Socket.close ();// invoke Terminate () from HandleReceive
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
133
NTCPSession.h
133
NTCPSession.h
@@ -1,133 +0,0 @@
|
||||
#ifndef NTCP_SESSION_H__
|
||||
#define NTCP_SESSION_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <cryptopp/modes.h>
|
||||
#include <cryptopp/aes.h>
|
||||
#include <cryptopp/adler32.h>
|
||||
#include "aes.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TransportSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
|
||||
#pragma pack(1)
|
||||
struct NTCPPhase1
|
||||
{
|
||||
uint8_t pubKey[256];
|
||||
uint8_t HXxorHI[32];
|
||||
};
|
||||
|
||||
struct NTCPPhase2
|
||||
{
|
||||
uint8_t pubKey[256];
|
||||
struct
|
||||
{
|
||||
uint8_t hxy[32];
|
||||
uint32_t timestamp;
|
||||
uint8_t filler[12];
|
||||
} encrypted;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
|
||||
const size_t NTCP_BUFFER_SIZE = 1040; // fits one tunnel message (1028)
|
||||
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
|
||||
const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448
|
||||
|
||||
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
|
||||
{
|
||||
public:
|
||||
|
||||
NTCPSession (boost::asio::io_service& service, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
|
||||
~NTCPSession ();
|
||||
void Terminate ();
|
||||
|
||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
||||
bool IsEstablished () const { return m_IsEstablished; };
|
||||
|
||||
void ClientLogin ();
|
||||
void ServerLogin ();
|
||||
void SendI2NPMessage (I2NPMessage * msg);
|
||||
|
||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
|
||||
protected:
|
||||
|
||||
void Connected ();
|
||||
void SendTimeSyncMessage ();
|
||||
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
|
||||
|
||||
private:
|
||||
|
||||
void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key);
|
||||
|
||||
// client
|
||||
void SendPhase3 ();
|
||||
void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
|
||||
void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
|
||||
|
||||
//server
|
||||
void SendPhase2 ();
|
||||
void SendPhase4 (uint32_t tsA, uint32_t tsB);
|
||||
void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
|
||||
void HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
|
||||
void HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen);
|
||||
void HandlePhase3 (uint32_t tsB, size_t paddingLen);
|
||||
void HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
|
||||
// common
|
||||
void Receive ();
|
||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
bool DecryptNextBlock (const uint8_t * encrypted);
|
||||
|
||||
void Send (i2p::I2NPMessage * msg);
|
||||
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, i2p::I2NPMessage * msg);
|
||||
|
||||
|
||||
// timer
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::ip::tcp::socket m_Socket;
|
||||
boost::asio::deadline_timer m_TerminationTimer;
|
||||
bool m_IsEstablished;
|
||||
|
||||
i2p::crypto::CBCDecryption m_Decryption;
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
CryptoPP::Adler32 m_Adler;
|
||||
|
||||
struct Establisher
|
||||
{
|
||||
NTCPPhase1 phase1;
|
||||
NTCPPhase2 phase2;
|
||||
} * m_Establisher;
|
||||
|
||||
i2p::crypto::AESAlignedBuffer<NTCP_BUFFER_SIZE + 16> m_ReceiveBuffer;
|
||||
i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer;
|
||||
int m_ReceiveBufferOffset;
|
||||
|
||||
i2p::I2NPMessage * m_NextMessage;
|
||||
std::list<i2p::I2NPMessage *> m_DelayedMessages;
|
||||
size_t m_NextMessageOffset;
|
||||
|
||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
922
NetDb.cpp
922
NetDb.cpp
@@ -1,922 +0,0 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cryptopp/gzip.h>
|
||||
#include "base64.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Transports.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.h"
|
||||
#include "Reseed.h"
|
||||
#include "util.h"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
I2NPMessage * RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
|
||||
const i2p::tunnel::InboundTunnel * replyTunnel)
|
||||
{
|
||||
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination,
|
||||
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
|
||||
&m_ExcludedPeers, m_IsLeaseSet, m_Pool);
|
||||
if (m_IsLeaseSet) // wrap lookup message into garlic
|
||||
{
|
||||
if (m_Pool && m_Pool->GetLocalDestination ())
|
||||
msg = m_Pool->GetLocalDestination ()->WrapMessage (*router, msg);
|
||||
else
|
||||
LogPrint ("Can't create garlic message without destination");
|
||||
}
|
||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
||||
m_LastRouter = router;
|
||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
return msg;
|
||||
}
|
||||
|
||||
I2NPMessage * RequestedDestination::CreateRequestMessage (const IdentHash& floodfill)
|
||||
{
|
||||
I2NPMessage * msg = i2p::CreateDatabaseLookupMsg (m_Destination,
|
||||
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
|
||||
m_ExcludedPeers.insert (floodfill);
|
||||
m_LastRouter = nullptr;
|
||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
return msg;
|
||||
}
|
||||
|
||||
void RequestedDestination::ClearExcludedPeers ()
|
||||
{
|
||||
m_ExcludedPeers.clear ();
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
const char NetDb::m_NetDbPath[] = "/netDb";
|
||||
#else
|
||||
const char NetDb::m_NetDbPath[] = "\\netDb";
|
||||
#endif
|
||||
NetDb netdb;
|
||||
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (0)
|
||||
{
|
||||
}
|
||||
|
||||
NetDb::~NetDb ()
|
||||
{
|
||||
Stop ();
|
||||
for (auto l:m_LeaseSets)
|
||||
delete l.second;
|
||||
for (auto r:m_RequestedDestinations)
|
||||
delete r.second;
|
||||
}
|
||||
|
||||
void NetDb::Start ()
|
||||
{
|
||||
Load (m_NetDbPath);
|
||||
if (m_RouterInfos.size () < 100) // reseed if # of router less than 100
|
||||
{
|
||||
Reseeder reseeder;
|
||||
reseeder.LoadCertificates (); // we need certificates for SU3 verification
|
||||
|
||||
// try SU3 first
|
||||
int reseedRetries = 0;
|
||||
while (m_RouterInfos.size () < 100 && reseedRetries < 10)
|
||||
{
|
||||
reseeder.ReseedNowSU3();
|
||||
reseedRetries++;
|
||||
}
|
||||
|
||||
// if still not enough download .dat files
|
||||
reseedRetries = 0;
|
||||
while (m_RouterInfos.size () < 100 && reseedRetries < 10)
|
||||
{
|
||||
reseeder.reseedNow();
|
||||
reseedRetries++;
|
||||
Load (m_NetDbPath);
|
||||
}
|
||||
}
|
||||
m_Thread = new std::thread (std::bind (&NetDb::Run, this));
|
||||
}
|
||||
|
||||
void NetDb::Stop ()
|
||||
{
|
||||
if (m_Thread)
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Queue.WakeUp ();
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::Run ()
|
||||
{
|
||||
uint32_t lastSave = 0, lastPublish = 0;
|
||||
m_IsRunning = true;
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
I2NPMessage * msg = m_Queue.GetNextWithTimeout (15000); // 15 sec
|
||||
if (msg)
|
||||
{
|
||||
while (msg)
|
||||
{
|
||||
switch (msg->GetHeader ()->typeID)
|
||||
{
|
||||
case eI2NPDatabaseStore:
|
||||
LogPrint ("DatabaseStore");
|
||||
HandleDatabaseStoreMsg (msg);
|
||||
break;
|
||||
case eI2NPDatabaseSearchReply:
|
||||
LogPrint ("DatabaseSearchReply");
|
||||
HandleDatabaseSearchReplyMsg (msg);
|
||||
break;
|
||||
case eI2NPDatabaseLookup:
|
||||
LogPrint ("DatabaseLookup");
|
||||
HandleDatabaseLookupMsg (msg);
|
||||
break;
|
||||
default: // WTF?
|
||||
LogPrint ("NetDb: unexpected message type ", msg->GetHeader ()->typeID);
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
}
|
||||
msg = m_Queue.Get ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_IsRunning) break;
|
||||
// if no new DatabaseStore coming, explore it
|
||||
auto numRouters = m_RouterInfos.size ();
|
||||
Explore (numRouters < 1500 ? 5 : 1);
|
||||
}
|
||||
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts - lastSave >= 60) // save routers, manage leasesets and validate subscriptions every minute
|
||||
{
|
||||
if (lastSave)
|
||||
{
|
||||
SaveUpdated (m_NetDbPath);
|
||||
ManageLeaseSets ();
|
||||
}
|
||||
lastSave = ts;
|
||||
}
|
||||
if (ts - lastPublish >= 600) // publish every 10 minutes
|
||||
{
|
||||
Publish ();
|
||||
lastPublish = ts;
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("NetDb: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::AddRouterInfo (const uint8_t * buf, int len)
|
||||
{
|
||||
IdentityEx identity;
|
||||
if (identity.FromBuffer (buf, len))
|
||||
AddRouterInfo (identity.GetIdentHash (), buf, len);
|
||||
}
|
||||
|
||||
void NetDb::AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len)
|
||||
{
|
||||
DeleteRequestedDestination (ident);
|
||||
auto r = FindRouter (ident);
|
||||
if (r)
|
||||
{
|
||||
auto ts = r->GetTimestamp ();
|
||||
r->Update (buf, len);
|
||||
if (r->GetTimestamp () > ts)
|
||||
LogPrint ("RouterInfo updated");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("New RouterInfo added");
|
||||
auto newRouter = std::make_shared<RouterInfo> (buf, len);
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
m_RouterInfos[newRouter->GetIdentHash ()] = newRouter;
|
||||
}
|
||||
if (newRouter->IsFloodfill ())
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
m_Floodfills.push_back (newRouter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len,
|
||||
i2p::tunnel::InboundTunnel * from)
|
||||
{
|
||||
DeleteRequestedDestination (ident);
|
||||
if (!from) // unsolicited LS must be received directly
|
||||
{
|
||||
auto it = m_LeaseSets.find(ident);
|
||||
if (it != m_LeaseSets.end ())
|
||||
{
|
||||
it->second->Update (buf, len);
|
||||
LogPrint ("LeaseSet updated");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("New LeaseSet added");
|
||||
m_LeaseSets[ident] = new LeaseSet (buf, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RouterInfo> NetDb::FindRouter (const IdentHash& ident) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
auto it = m_RouterInfos.find (ident);
|
||||
if (it != m_RouterInfos.end ())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LeaseSet * NetDb::FindLeaseSet (const IdentHash& destination) const
|
||||
{
|
||||
auto it = m_LeaseSets.find (destination);
|
||||
if (it != m_LeaseSets.end ())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NetDb::SetUnreachable (const IdentHash& ident, bool unreachable)
|
||||
{
|
||||
auto it = m_RouterInfos.find (ident);
|
||||
if (it != m_RouterInfos.end ())
|
||||
return it->second->SetUnreachable (unreachable);
|
||||
}
|
||||
|
||||
// TODO: Move to reseed and/or scheduled tasks. (In java version, scheduler fix this as well as sort RIs.)
|
||||
bool NetDb::CreateNetDb(boost::filesystem::path directory)
|
||||
{
|
||||
LogPrint (directory.string(), " doesn't exist, trying to create it.");
|
||||
if (!boost::filesystem::create_directory (directory))
|
||||
{
|
||||
LogPrint("Failed to create directory ", directory.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
// list of chars might appear in base64 string
|
||||
const char * chars = GetBase64SubstitutionTable (); // 64 bytes
|
||||
boost::filesystem::path suffix;
|
||||
for (int i = 0; i < 64; i++)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
suffix = std::string ("/r") + chars[i];
|
||||
#else
|
||||
suffix = std::string ("\\r") + chars[i];
|
||||
#endif
|
||||
if (!boost::filesystem::create_directory( boost::filesystem::path (directory / suffix) )) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void NetDb::Load (const char * directory)
|
||||
{
|
||||
boost::filesystem::path p (i2p::util::filesystem::GetDataDir());
|
||||
p /= (directory);
|
||||
if (!boost::filesystem::exists (p))
|
||||
{
|
||||
// seems netDb doesn't exist yet
|
||||
if (!CreateNetDb(p)) return;
|
||||
}
|
||||
// make sure we cleanup netDb from previous attempts
|
||||
m_RouterInfos.clear ();
|
||||
m_Floodfills.clear ();
|
||||
|
||||
// load routers now
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
int numRouters = 0;
|
||||
boost::filesystem::directory_iterator end;
|
||||
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
|
||||
{
|
||||
if (boost::filesystem::is_directory (it->status()))
|
||||
{
|
||||
for (boost::filesystem::directory_iterator it1 (it->path ()); it1 != end; ++it1)
|
||||
{
|
||||
#if BOOST_VERSION > 10500
|
||||
const std::string& fullPath = it1->path().string();
|
||||
#else
|
||||
const std::string& fullPath = it1->path();
|
||||
#endif
|
||||
auto r = std::make_shared<RouterInfo>(fullPath);
|
||||
if (!r->IsUnreachable () && (!r->UsesIntroducer () || ts < r->GetTimestamp () + 3600*1000LL)) // 1 hour
|
||||
{
|
||||
r->DeleteBuffer ();
|
||||
m_RouterInfos[r->GetIdentHash ()] = r;
|
||||
if (r->IsFloodfill ())
|
||||
m_Floodfills.push_back (r);
|
||||
numRouters++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (boost::filesystem::exists (fullPath))
|
||||
boost::filesystem::remove (fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LogPrint (numRouters, " routers loaded");
|
||||
LogPrint (m_Floodfills.size (), " floodfills loaded");
|
||||
}
|
||||
|
||||
void NetDb::SaveUpdated (const char * directory)
|
||||
{
|
||||
auto GetFilePath = [](const char * directory, const RouterInfo * routerInfo)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return std::string (directory) + "/r" +
|
||||
routerInfo->GetIdentHashBase64 ()[0] + "/routerInfo-" +
|
||||
#else
|
||||
return std::string (directory) + "\\r" +
|
||||
routerInfo->GetIdentHashBase64 ()[0] + "\\routerInfo-" +
|
||||
#endif
|
||||
routerInfo->GetIdentHashBase64 () + ".dat";
|
||||
};
|
||||
|
||||
boost::filesystem::path p (i2p::util::filesystem::GetDataDir());
|
||||
p /= (directory);
|
||||
#if BOOST_VERSION > 10500
|
||||
const char * fullDirectory = p.string().c_str ();
|
||||
#else
|
||||
const char * fullDirectory = p.c_str ();
|
||||
#endif
|
||||
int count = 0, deletedCount = 0;
|
||||
auto total = m_RouterInfos.size ();
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
for (auto it: m_RouterInfos)
|
||||
{
|
||||
if (it.second->IsUpdated ())
|
||||
{
|
||||
it.second->SaveToFile (GetFilePath(fullDirectory, it.second.get ()));
|
||||
it.second->SetUpdated (false);
|
||||
it.second->DeleteBuffer ();
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RouterInfo expires after 1 hour if uses introducer
|
||||
if ((it.second->UsesIntroducer () && ts > it.second->GetTimestamp () + 3600*1000LL) // 1 hour
|
||||
// RouterInfo expires in 72 hours if more than 300
|
||||
|| (total > 300 && ts > it.second->GetTimestamp () + 3*24*3600*1000LL)) // 3 days
|
||||
{
|
||||
total--;
|
||||
it.second->SetUnreachable (true);
|
||||
}
|
||||
|
||||
if (it.second->IsUnreachable ())
|
||||
{
|
||||
// delete RI file
|
||||
if (boost::filesystem::exists (GetFilePath (fullDirectory, it.second.get ())))
|
||||
{
|
||||
boost::filesystem::remove (GetFilePath (fullDirectory, it.second.get ()));
|
||||
deletedCount++;
|
||||
}
|
||||
// delete from floodfills list
|
||||
if (it.second->IsFloodfill ())
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
m_Floodfills.remove (it.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0)
|
||||
LogPrint (count," new/updated routers saved");
|
||||
if (deletedCount > 0)
|
||||
{
|
||||
LogPrint (deletedCount," routers deleted");
|
||||
// clean up RouterInfos table
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
for (auto it = m_RouterInfos.begin (); it != m_RouterInfos.end ();)
|
||||
{
|
||||
if (it->second->IsUnreachable ())
|
||||
it = m_RouterInfos.erase (it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::RequestDestination (const IdentHash& destination, bool isLeaseSet, i2p::tunnel::TunnelPool * pool)
|
||||
{
|
||||
if (isLeaseSet) // we request LeaseSet through tunnels
|
||||
{
|
||||
i2p::tunnel::OutboundTunnel * outbound = pool ? pool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
if (outbound)
|
||||
{
|
||||
i2p::tunnel::InboundTunnel * inbound = pool ? pool->GetNextInboundTunnel () :i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
if (inbound)
|
||||
{
|
||||
RequestedDestination * dest = CreateRequestedDestination (destination, true, false, pool);
|
||||
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
|
||||
if (floodfill)
|
||||
{
|
||||
// DatabaseLookup message
|
||||
outbound->SendTunnelDataMsg (
|
||||
{
|
||||
i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
floodfill->GetIdentHash (), 0,
|
||||
dest->CreateRequestMessage (floodfill, inbound)
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
LogPrint ("No more floodfills found");
|
||||
}
|
||||
else
|
||||
LogPrint ("No inbound tunnels found");
|
||||
}
|
||||
else
|
||||
LogPrint ("No outbound tunnels found");
|
||||
}
|
||||
else // RouterInfo is requested directly
|
||||
{
|
||||
RequestedDestination * dest = CreateRequestedDestination (destination, false, false, pool);
|
||||
auto floodfill = GetClosestFloodfill (destination, dest->GetExcludedPeers ());
|
||||
if (floodfill)
|
||||
transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::HandleDatabaseStoreMsg (I2NPMessage * m)
|
||||
{
|
||||
const uint8_t * buf = m->GetPayload ();
|
||||
size_t len = be16toh (m->GetHeader ()->size);
|
||||
I2NPDatabaseStoreMsg * msg = (I2NPDatabaseStoreMsg *)buf;
|
||||
size_t offset = sizeof (I2NPDatabaseStoreMsg);
|
||||
if (msg->replyToken)
|
||||
offset += 36;
|
||||
if (msg->type)
|
||||
{
|
||||
LogPrint ("LeaseSet");
|
||||
AddLeaseSet (msg->key, buf + offset, len - offset, m->from);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("RouterInfo");
|
||||
size_t size = be16toh (*(uint16_t *)(buf + offset));
|
||||
if (size > 2048)
|
||||
{
|
||||
LogPrint ("Invalid RouterInfo length ", (int)size);
|
||||
return;
|
||||
}
|
||||
offset += 2;
|
||||
CryptoPP::Gunzip decompressor;
|
||||
decompressor.Put (buf + offset, size);
|
||||
decompressor.MessageEnd();
|
||||
uint8_t uncompressed[2048];
|
||||
size_t uncomressedSize = decompressor.MaxRetrievable ();
|
||||
decompressor.Get (uncompressed, uncomressedSize);
|
||||
AddRouterInfo (msg->key, uncompressed, uncomressedSize);
|
||||
}
|
||||
i2p::DeleteI2NPMessage (m);
|
||||
}
|
||||
|
||||
void NetDb::HandleDatabaseSearchReplyMsg (I2NPMessage * msg)
|
||||
{
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
char key[48];
|
||||
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
|
||||
key[l] = 0;
|
||||
int num = buf[32]; // num
|
||||
LogPrint ("DatabaseSearchReply for ", key, " num=", num);
|
||||
auto it = m_RequestedDestinations.find (IdentHash (buf));
|
||||
if (it != m_RequestedDestinations.end ())
|
||||
{
|
||||
RequestedDestination * dest = it->second;
|
||||
bool deleteDest = true;
|
||||
if (num > 0)
|
||||
{
|
||||
auto pool = dest ? dest->GetTunnelPool () : nullptr;
|
||||
auto outbound = pool ? pool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
auto inbound = pool ? pool->GetNextInboundTunnel () : i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
if (!dest->IsExploratory ())
|
||||
{
|
||||
// reply to our destination. Try other floodfills
|
||||
if (outbound && inbound )
|
||||
{
|
||||
auto count = dest->GetExcludedPeers ().size ();
|
||||
if (count < 7)
|
||||
{
|
||||
auto nextFloodfill = GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
|
||||
if (nextFloodfill)
|
||||
{
|
||||
if (!dest->IsLeaseSet ())
|
||||
{
|
||||
// tell floodfill about us
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
nextFloodfill->GetIdentHash (), 0,
|
||||
CreateDatabaseStoreMsg ()
|
||||
});
|
||||
}
|
||||
|
||||
// request destination
|
||||
LogPrint ("Try ", key, " at ", count, " floodfill ", nextFloodfill->GetIdentHash ().ToBase64 ());
|
||||
auto msg = dest->CreateRequestMessage (nextFloodfill, inbound);
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
nextFloodfill->GetIdentHash (), 0, msg
|
||||
});
|
||||
deleteDest = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (key, " was not found on 7 floodfills");
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
uint8_t * router = buf + 33 + i*32;
|
||||
char peerHash[48];
|
||||
int l1 = i2p::data::ByteStreamToBase64 (router, 32, peerHash, 48);
|
||||
peerHash[l1] = 0;
|
||||
LogPrint (i,": ", peerHash);
|
||||
|
||||
if (dest->IsExploratory ())
|
||||
{
|
||||
auto r = FindRouter (router);
|
||||
if (!r || i2p::util::GetMillisecondsSinceEpoch () > r->GetTimestamp () + 3600*1000LL)
|
||||
{
|
||||
// router with ident not found or too old (1 hour)
|
||||
LogPrint ("Found new/outdated router. Requesting RouterInfo ...");
|
||||
if (outbound && inbound && dest->GetLastRouter ())
|
||||
{
|
||||
RequestedDestination * d1 = CreateRequestedDestination (router, false, false, pool);
|
||||
auto msg = d1->CreateRequestMessage (dest->GetLastRouter (), inbound);
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
dest->GetLastRouter ()->GetIdentHash (), 0, msg
|
||||
});
|
||||
}
|
||||
else
|
||||
RequestDestination (router, false, pool);
|
||||
}
|
||||
else
|
||||
LogPrint ("Bayan");
|
||||
}
|
||||
else
|
||||
{
|
||||
auto r = FindRouter (router);
|
||||
// do we have that floodfill router in our database?
|
||||
if (!r)
|
||||
{
|
||||
// request router
|
||||
LogPrint ("Found new floodfill. Request it");
|
||||
RequestDestination (router, false, pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outbound && msgs.size () > 0)
|
||||
outbound->SendTunnelDataMsg (msgs);
|
||||
if (deleteDest)
|
||||
{
|
||||
// no more requests for the destinationation. delete it
|
||||
delete it->second;
|
||||
m_RequestedDestinations.erase (it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no more requests for detination possible. delete it
|
||||
delete it->second;
|
||||
m_RequestedDestinations.erase (it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Requested destination for ", key, " not found");
|
||||
// it might contain new routers
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
IdentHash router (buf + 33 + i*32);
|
||||
if (!FindRouter (router))
|
||||
{
|
||||
LogPrint ("New router ", router.ToBase64 (), " found. Request it");
|
||||
RequestDestination (router);
|
||||
}
|
||||
}
|
||||
}
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
|
||||
void NetDb::HandleDatabaseLookupMsg (I2NPMessage * msg)
|
||||
{
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
char key[48];
|
||||
int l = i2p::data::ByteStreamToBase64 (buf, 32, key, 48);
|
||||
key[l] = 0;
|
||||
LogPrint ("DatabaseLookup for ", key, " recieved");
|
||||
uint8_t flag = buf[64];
|
||||
uint8_t * excluded = buf + 65;
|
||||
uint32_t replyTunnelID = 0;
|
||||
if (flag & 0x01) //reply to tunnel
|
||||
{
|
||||
replyTunnelID = be32toh (*(uint32_t *)(buf + 64));
|
||||
excluded += 4;
|
||||
}
|
||||
uint16_t numExcluded = be16toh (*(uint16_t *)excluded);
|
||||
excluded += 2;
|
||||
if (numExcluded > 512)
|
||||
{
|
||||
LogPrint ("Number of excluded peers", numExcluded, " exceeds 512");
|
||||
numExcluded = 0; // TODO:
|
||||
}
|
||||
|
||||
I2NPMessage * replyMsg = nullptr;
|
||||
|
||||
{
|
||||
auto router = FindRouter (buf);
|
||||
if (router)
|
||||
{
|
||||
LogPrint ("Requested RouterInfo ", key, " found");
|
||||
router->LoadBuffer ();
|
||||
if (router->GetBuffer ())
|
||||
replyMsg = CreateDatabaseStoreMsg (router.get ());
|
||||
}
|
||||
}
|
||||
if (!replyMsg)
|
||||
{
|
||||
auto leaseSet = FindLeaseSet (buf);
|
||||
if (leaseSet) // we don't send back our LeaseSets
|
||||
{
|
||||
LogPrint ("Requested LeaseSet ", key, " found");
|
||||
replyMsg = CreateDatabaseStoreMsg (leaseSet);
|
||||
}
|
||||
}
|
||||
if (!replyMsg)
|
||||
{
|
||||
LogPrint ("Requested ", key, " not found. ", numExcluded, " excluded");
|
||||
std::set<IdentHash> excludedRouters;
|
||||
for (int i = 0; i < numExcluded; i++)
|
||||
{
|
||||
// TODO: check for all zeroes (exploratory)
|
||||
excludedRouters.insert (excluded);
|
||||
excluded += 32;
|
||||
}
|
||||
replyMsg = CreateDatabaseSearchReply (buf, GetClosestFloodfill (buf, excludedRouters).get ());
|
||||
}
|
||||
else
|
||||
excluded += numExcluded*32; // we don't care about exluded
|
||||
|
||||
if (replyMsg)
|
||||
{
|
||||
if (replyTunnelID)
|
||||
{
|
||||
// encryption might be used though tunnel only
|
||||
if (flag & 0x02) // encrypted reply requested
|
||||
{
|
||||
uint8_t * sessionKey = excluded;
|
||||
uint8_t numTags = sessionKey[32];
|
||||
if (numTags > 0)
|
||||
{
|
||||
uint8_t * sessionTag = sessionKey + 33; // take first tag
|
||||
i2p::garlic::GarlicRoutingSession garlic (sessionKey, sessionTag);
|
||||
replyMsg = garlic.WrapSingleMessage (replyMsg);
|
||||
}
|
||||
}
|
||||
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : nullptr;
|
||||
if (outbound)
|
||||
outbound->SendTunnelDataMsg (buf+32, replyTunnelID, replyMsg);
|
||||
else
|
||||
transports.SendMessage (buf+32, i2p::CreateTunnelGatewayMsg (replyTunnelID, replyMsg));
|
||||
}
|
||||
else
|
||||
transports.SendMessage (buf+32, replyMsg);
|
||||
}
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
|
||||
void NetDb::Explore (int numDestinations)
|
||||
{
|
||||
// clean up previous exploratories
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
|
||||
{
|
||||
if (it->second->IsExploratory () || ts > it->second->GetCreationTime () + 60) // no response for 1 minute
|
||||
{
|
||||
delete it->second;
|
||||
it = m_RequestedDestinations.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
// new requests
|
||||
auto exploratoryPool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
||||
auto outbound = exploratoryPool ? exploratoryPool->GetNextOutboundTunnel () : i2p::tunnel::tunnels.GetNextOutboundTunnel ();
|
||||
auto inbound = exploratoryPool ? exploratoryPool->GetNextInboundTunnel () : i2p::tunnel::tunnels.GetNextInboundTunnel ();
|
||||
bool throughTunnels = outbound && inbound;
|
||||
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
uint8_t randomHash[32];
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
std::set<const RouterInfo *> floodfills;
|
||||
LogPrint ("Exploring new ", numDestinations, " routers ...");
|
||||
for (int i = 0; i < numDestinations; i++)
|
||||
{
|
||||
rnd.GenerateBlock (randomHash, 32);
|
||||
RequestedDestination * dest = CreateRequestedDestination (IdentHash (randomHash), false, true, exploratoryPool);
|
||||
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
|
||||
if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once
|
||||
{
|
||||
floodfills.insert (floodfill.get ());
|
||||
if (throughTunnels)
|
||||
{
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
floodfill->GetIdentHash (), 0,
|
||||
CreateDatabaseStoreMsg () // tell floodfill about us
|
||||
});
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeRouter,
|
||||
floodfill->GetIdentHash (), 0,
|
||||
dest->CreateRequestMessage (floodfill, inbound) // explore
|
||||
});
|
||||
}
|
||||
else
|
||||
i2p::transport::transports.SendMessage (floodfill->GetIdentHash (), dest->CreateRequestMessage (floodfill->GetIdentHash ()));
|
||||
}
|
||||
else
|
||||
DeleteRequestedDestination (dest);
|
||||
}
|
||||
if (throughTunnels && msgs.size () > 0)
|
||||
outbound->SendTunnelDataMsg (msgs);
|
||||
}
|
||||
|
||||
void NetDb::Publish ()
|
||||
{
|
||||
std::set<IdentHash> excluded; // TODO: fill up later
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
auto floodfill = GetClosestFloodfill (i2p::context.GetRouterInfo ().GetIdentHash (), excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
LogPrint ("Publishing our RouterInfo to ", floodfill->GetIdentHashAbbreviation ());
|
||||
transports.SendMessage (floodfill->GetIdentHash (), CreateDatabaseStoreMsg ());
|
||||
excluded.insert (floodfill->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RequestedDestination * NetDb::CreateRequestedDestination (const IdentHash& dest,
|
||||
bool isLeaseSet, bool isExploratory, i2p::tunnel::TunnelPool * pool)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
||||
auto it = m_RequestedDestinations.find (dest);
|
||||
if (it == m_RequestedDestinations.end ()) // not exist yet
|
||||
{
|
||||
RequestedDestination * d = new RequestedDestination (dest, isLeaseSet, isExploratory, pool);
|
||||
m_RequestedDestinations[dest] = d;
|
||||
return d;
|
||||
}
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
bool NetDb::DeleteRequestedDestination (const IdentHash& dest)
|
||||
{
|
||||
auto it = m_RequestedDestinations.find (dest);
|
||||
if (it != m_RequestedDestinations.end ())
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
||||
delete it->second;
|
||||
m_RequestedDestinations.erase (it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void NetDb::DeleteRequestedDestination (RequestedDestination * dest)
|
||||
{
|
||||
if (dest)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
||||
m_RequestedDestinations.erase (dest->GetDestination ());
|
||||
delete dest;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter () const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden ();
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router != compatibleWith &&
|
||||
router->IsCompatible (*compatibleWith);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router != compatibleWith &&
|
||||
router->IsCompatible (*compatibleWith) && (router->GetCaps () & RouterInfo::eHighBandwidth);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomRouter (Filter filter) const
|
||||
{
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
uint32_t ind = rnd.GenerateWord32 (0, m_RouterInfos.size () - 1);
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
uint32_t i = 0;
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
for (auto it: m_RouterInfos)
|
||||
{
|
||||
if (i >= ind)
|
||||
{
|
||||
if (!it.second->IsUnreachable () && filter (it.second))
|
||||
return it.second;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
// we couldn't find anything, try second pass
|
||||
ind = 0;
|
||||
}
|
||||
return nullptr; // seems we have too few routers
|
||||
}
|
||||
|
||||
void NetDb::PostI2NPMsg (I2NPMessage * msg)
|
||||
{
|
||||
if (msg) m_Queue.Put (msg);
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetClosestFloodfill (const IdentHash& destination,
|
||||
const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
std::shared_ptr<const RouterInfo> r;
|
||||
XORMetric minMetric;
|
||||
IdentHash destKey = CreateRoutingKey (destination);
|
||||
minMetric.SetMax ();
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
for (auto it: m_Floodfills)
|
||||
{
|
||||
if (!it->IsUnreachable () && !excluded.count (it->GetIdentHash ()))
|
||||
{
|
||||
XORMetric m = destKey ^ it->GetIdentHash ();
|
||||
if (m < minMetric)
|
||||
{
|
||||
minMetric = m;
|
||||
r = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void NetDb::ManageLeaseSets ()
|
||||
{
|
||||
for (auto it = m_LeaseSets.begin (); it != m_LeaseSets.end ();)
|
||||
{
|
||||
if (it->second->HasNonExpiredLeases ()) // all leases expired
|
||||
{
|
||||
LogPrint ("LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
||||
delete it->second;
|
||||
it = m_LeaseSets.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
130
NetDb.h
130
NetDb.h
@@ -1,130 +0,0 @@
|
||||
#ifndef NETDB_H__
|
||||
#define NETDB_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include "Queue.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
class RequestedDestination
|
||||
{
|
||||
public:
|
||||
|
||||
RequestedDestination (const IdentHash& destination, bool isLeaseSet,
|
||||
bool isExploratory = false, i2p::tunnel::TunnelPool * pool = nullptr):
|
||||
m_Destination (destination), m_IsLeaseSet (isLeaseSet), m_IsExploratory (isExploratory),
|
||||
m_Pool (pool), m_CreationTime (0) {};
|
||||
|
||||
const IdentHash& GetDestination () const { return m_Destination; };
|
||||
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
|
||||
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
|
||||
void ClearExcludedPeers ();
|
||||
std::shared_ptr<const RouterInfo> GetLastRouter () const { return m_LastRouter; };
|
||||
i2p::tunnel::TunnelPool * GetTunnelPool () { return m_Pool; };
|
||||
bool IsExploratory () const { return m_IsExploratory; };
|
||||
bool IsLeaseSet () const { return m_IsLeaseSet; };
|
||||
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
|
||||
uint64_t GetCreationTime () const { return m_CreationTime; };
|
||||
I2NPMessage * CreateRequestMessage (std::shared_ptr<const RouterInfo>, const i2p::tunnel::InboundTunnel * replyTunnel);
|
||||
I2NPMessage * CreateRequestMessage (const IdentHash& floodfill);
|
||||
|
||||
private:
|
||||
|
||||
IdentHash m_Destination;
|
||||
bool m_IsLeaseSet, m_IsExploratory;
|
||||
i2p::tunnel::TunnelPool * m_Pool;
|
||||
std::set<IdentHash> m_ExcludedPeers;
|
||||
std::shared_ptr<const RouterInfo> m_LastRouter;
|
||||
uint64_t m_CreationTime;
|
||||
};
|
||||
|
||||
class NetDb
|
||||
{
|
||||
public:
|
||||
|
||||
NetDb ();
|
||||
~NetDb ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
void AddRouterInfo (const uint8_t * buf, int len);
|
||||
void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
|
||||
void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, i2p::tunnel::InboundTunnel * from);
|
||||
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
|
||||
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
|
||||
|
||||
void RequestDestination (const IdentHash& destination, bool isLeaseSet = false,
|
||||
i2p::tunnel::TunnelPool * pool = nullptr);
|
||||
|
||||
void HandleDatabaseStoreMsg (I2NPMessage * msg);
|
||||
void HandleDatabaseSearchReplyMsg (I2NPMessage * msg);
|
||||
void HandleDatabaseLookupMsg (I2NPMessage * msg);
|
||||
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
|
||||
void SetUnreachable (const IdentHash& ident, bool unreachable);
|
||||
|
||||
void PostI2NPMsg (I2NPMessage * msg);
|
||||
|
||||
// for web interface
|
||||
int GetNumRouters () const { return m_RouterInfos.size (); };
|
||||
int GetNumFloodfills () const { return m_Floodfills.size (); };
|
||||
int GetNumLeaseSets () const { return m_LeaseSets.size (); };
|
||||
|
||||
private:
|
||||
|
||||
bool CreateNetDb(boost::filesystem::path directory);
|
||||
void Load (const char * directory);
|
||||
void SaveUpdated (const char * directory);
|
||||
void Run (); // exploratory thread
|
||||
void Explore (int numDestinations);
|
||||
void Publish ();
|
||||
void ManageLeaseSets ();
|
||||
|
||||
RequestedDestination * CreateRequestedDestination (const IdentHash& dest,
|
||||
bool isLeaseSet, bool isExploratory = false, i2p::tunnel::TunnelPool * pool = nullptr);
|
||||
bool DeleteRequestedDestination (const IdentHash& dest); // returns true if found
|
||||
void DeleteRequestedDestination (RequestedDestination * dest);
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
|
||||
|
||||
private:
|
||||
|
||||
std::map<IdentHash, LeaseSet *> m_LeaseSets;
|
||||
mutable std::mutex m_RouterInfosMutex;
|
||||
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
|
||||
mutable std::mutex m_FloodfillsMutex;
|
||||
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
|
||||
std::mutex m_RequestedDestinationsMutex;
|
||||
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
|
||||
|
||||
static const char m_NetDbPath[];
|
||||
};
|
||||
|
||||
extern NetDb netdb;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
143
README.md
143
README.md
@@ -1,85 +1,84 @@
|
||||
i2pd
|
||||
====
|
||||
|
||||
I2P router written in C++
|
||||
[Русская версия](https://github.com/PurpleI2P/i2pd_docs_ru/blob/master/README.md)
|
||||
|
||||
Requirements for Linux/FreeBSD/OSX
|
||||
----------------------------------
|
||||
i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.
|
||||
|
||||
GCC 4.6 or newer, Boost 1.46 or newer, crypto++. Clang can be used instead of
|
||||
GCC.
|
||||
I2P (Invisible Internet Protocol) is a universal anonymous network layer.
|
||||
All communications over I2P are anonymous and end-to-end encrypted, participants
|
||||
don't reveal their real IP addresses.
|
||||
|
||||
Requirements for Windows
|
||||
------------------------
|
||||
I2P client is a software used for building and using anonymous I2P
|
||||
networks. Such networks are commonly used for anonymous peer-to-peer
|
||||
applications (filesharing, cryptocurrencies) and anonymous client-server
|
||||
applications (websites, instant messengers, chat-servers).
|
||||
|
||||
VS2013 (known to work with 12.0.21005.1 or newer), Boost 1.46 or newer,
|
||||
crypto++ 5.62. See Win32/README-Build.txt for instructions on how to build i2pd
|
||||
and its dependencies.
|
||||
I2P allows people from all around the world to communicate and share information
|
||||
without restrictions.
|
||||
|
||||
Build Statuses
|
||||
---------------
|
||||
Features
|
||||
--------
|
||||
|
||||
- Linux x64 - [](https://jenkins.nordcloud.no/job/i2pd-linux/)
|
||||
- Linux ARM - To be added
|
||||
- Mac OS X - To be added
|
||||
- Microsoft VC13 - To be added
|
||||
* Distributed anonymous networking framework
|
||||
* End-to-end encrypted communications
|
||||
* Small footprint, simple dependencies, fast performance
|
||||
* Rich set of APIs for developers of secure applications
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Website](http://i2pd.website)
|
||||
* [Documentation](https://i2pd.readthedocs.io/en/latest/)
|
||||
* [Wiki](https://github.com/PurpleI2P/i2pd/wiki)
|
||||
* [Tickets/Issues](https://github.com/PurpleI2P/i2pd/issues)
|
||||
* [Specifications](https://geti2p.net/spec)
|
||||
* [Twitter](https://twitter.com/hashtag/i2pd)
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
The easiest way to install i2pd is by using
|
||||
[precompiled binaries](https://github.com/PurpleI2P/i2pd/releases/latest).
|
||||
See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build
|
||||
i2pd from source on your OS.
|
||||
|
||||
|
||||
Testing
|
||||
Build instructions:
|
||||
|
||||
* [unix](https://i2pd.readthedocs.io/en/latest/devs/building/unix/)
|
||||
* [windows](https://i2pd.readthedocs.io/en/latest/devs/building/windows/)
|
||||
* [iOS](https://i2pd.readthedocs.io/en/latest/devs/building/ios/)
|
||||
* [android](https://i2pd.readthedocs.io/en/latest/devs/building/android/)
|
||||
|
||||
|
||||
**Supported systems:**
|
||||
|
||||
* GNU/Linux x86/x64 - [](https://travis-ci.org/PurpleI2P/i2pd)
|
||||
* Windows - [](https://ci.appveyor.com/project/PurpleI2P/i2pd)
|
||||
* Mac OS X - [](https://travis-ci.org/PurpleI2P/i2pd)
|
||||
* FreeBSD
|
||||
* Android
|
||||
* iOS
|
||||
|
||||
Using i2pd
|
||||
----------
|
||||
|
||||
See [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/run/) and
|
||||
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/contrib/i2pd.conf).
|
||||
|
||||
Donations
|
||||
---------
|
||||
|
||||
BTC: 3MDoGJW9TLMTCDGrR9bLgWXfm6sjmgy86f
|
||||
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
|
||||
ETH: 0x9e5bac70d20d1079ceaa111127f4fb3bccce379d
|
||||
DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF
|
||||
ZEC: t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ
|
||||
GST: GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
First, build it.
|
||||
|
||||
* $ cd i2pd
|
||||
* $ make
|
||||
|
||||
Next, find out your public ip. (find it for example at http://www.whatismyip.com/)
|
||||
|
||||
Then, run it with:
|
||||
|
||||
$ ./i2p --host=YOUR_PUBLIC_IP
|
||||
|
||||
The client should now reseed by itself.
|
||||
|
||||
To visit an I2P page, you need to find the b32 address of your destination.
|
||||
After that, go to the webconsole and add it behind the url. (Remove http:// from the address)
|
||||
|
||||
This should resulting in for example:
|
||||
http://localhost:7070/4oes3rlgrpbkmzv4lqcfili23h3cvpwslqcfjlk6vvguxyggspwa.b32.i2p
|
||||
|
||||
|
||||
Cmdline options
|
||||
---------------
|
||||
|
||||
* --host= - The external IP
|
||||
* --port= - The port to listen on
|
||||
* --httpport= - The http port to listen on
|
||||
* --log= - Enable or disable logging to file. 1 for yes, 0 for no.
|
||||
* --daemon= - Enable or disable daemon mode. 1 for yes, 0 for no.
|
||||
* --service= - 1 if uses system folders (/var/run/i2pd.pid, /var/log/i2pd.log, /var/lib/i2pd).
|
||||
* --unreachable= - 1 if router is declared as unreachable and works through introducers.
|
||||
* --v6= - 1 if supports communication through ipv6, off by default
|
||||
* --httpproxyport= - The port to listen on (HTTP Proxy)
|
||||
* --socksproxyport= - The port to listen on (SOCKS Proxy)
|
||||
* --ircport= - The local port of IRC tunnel to listen on. 6668 by default
|
||||
* --ircdest= - I2P destination address of IRC server. For example irc.postman.i2p
|
||||
* --irckeys= - optional keys file for local destination
|
||||
* --eepkeys= - File name containing destination keys, for example privKeys.dat.
|
||||
The file will be created if it does not already exist (issue #110).
|
||||
* --eephost= - Address incoming trafic forward to. 127.0.0.1 by default
|
||||
* --eepport= - Port incoming trafic forward to. 80 by default
|
||||
* --samport= - Port of SAM bridge. Usually 7656. SAM is off if not specified
|
||||
* --bobport= - Port of BOB command channel. Usually 2827. BOB is off if not specified
|
||||
* --conf= - Config file (default: ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf)
|
||||
This parameter will be silently ignored if the specified config file does not exist.
|
||||
Options specified on the command line take precedence over those in the config file.
|
||||
|
||||
Config file
|
||||
-----------
|
||||
|
||||
INI-like, syntax is the following : <key> = <value>.
|
||||
All command-line parameters are allowed as keys, for example:
|
||||
|
||||
log = 1
|
||||
v6 = 0
|
||||
ircdest = irc.postman.i2p
|
||||
This project is licensed under the BSD 3-clause license, which can be found in the file
|
||||
LICENSE in the root of the project source code.
|
||||
|
||||
502
Reseed.cpp
502
Reseed.cpp
@@ -1,502 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/asn.h>
|
||||
#include <cryptopp/base64.h>
|
||||
#include <cryptopp/crc.h>
|
||||
#include <cryptopp/zinflate.h>
|
||||
#include "I2PEndian.h"
|
||||
#include "Reseed.h"
|
||||
#include "Log.h"
|
||||
#include "Identity.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "NetDb.h"
|
||||
#include "util.h"
|
||||
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
|
||||
static std::vector<std::string> httpReseedHostList = {
|
||||
"http://193.150.121.66/netDb/",
|
||||
"http://netdb.i2p2.no/",
|
||||
"http://reseed.i2p-projekt.de/",
|
||||
"http://cowpuncher.drollette.com/netdb/",
|
||||
"http://i2p.mooo.com/netDb/",
|
||||
"http://reseed.info/",
|
||||
"http://uk.reseed.i2p2.no/",
|
||||
"http://us.reseed.i2p2.no/",
|
||||
"http://jp.reseed.i2p2.no/",
|
||||
"http://i2p-netdb.innovatio.no/",
|
||||
"http://ieb9oopo.mooo.com"
|
||||
};
|
||||
|
||||
//TODO: Remember to add custom port support. Not all serves on 443
|
||||
static std::vector<std::string> httpsReseedHostList = {
|
||||
"https://193.150.121.66/netDb/",
|
||||
"https://netdb.i2p2.no/",
|
||||
"https://reseed.i2p-projekt.de/",
|
||||
"https://cowpuncher.drollette.com/netdb/",
|
||||
"https://i2p.mooo.com/netDb/",
|
||||
"https://reseed.info/",
|
||||
"https://i2p-netdb.innovatio.no/",
|
||||
"https://ieb9oopo.mooo.com/",
|
||||
"https://ssl.webpack.de/ivae2he9.sg4.e-plaza.de/" // Only HTTPS and SU3 (v2) support
|
||||
};
|
||||
|
||||
//TODO: Implement v2 reseeding. Lightweight zip library is needed.
|
||||
//TODO: Implement SU3, utils.
|
||||
Reseeder::Reseeder()
|
||||
{
|
||||
}
|
||||
|
||||
Reseeder::~Reseeder()
|
||||
{
|
||||
}
|
||||
|
||||
bool Reseeder::reseedNow()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Seems like the best place to try to intercept with SSL
|
||||
/*ssl_server = true;
|
||||
try {
|
||||
// SSL
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LogPrint("Exception in SSL: ", e.what());
|
||||
}*/
|
||||
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())];
|
||||
LogPrint("Reseeding from ", reseedHost);
|
||||
std::string content = i2p::util::http::httpRequest(reseedHost);
|
||||
if (content == "")
|
||||
{
|
||||
LogPrint("Reseed failed");
|
||||
return false;
|
||||
}
|
||||
boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase);
|
||||
boost::sregex_token_iterator i(content.begin(), content.end(), e, 1);
|
||||
boost::sregex_token_iterator j;
|
||||
//TODO: Ugly code, try to clean up.
|
||||
//TODO: Try to reduce N number of variables
|
||||
std::string name;
|
||||
std::string routerInfo;
|
||||
std::string tmpUrl;
|
||||
std::string filename;
|
||||
std::string ignoreFileSuffix = ".su3";
|
||||
boost::filesystem::path root = i2p::util::filesystem::GetDataDir();
|
||||
while (i != j)
|
||||
{
|
||||
name = *i++;
|
||||
if (name.find(ignoreFileSuffix)!=std::string::npos)
|
||||
continue;
|
||||
LogPrint("Downloading ", name);
|
||||
tmpUrl = reseedHost;
|
||||
tmpUrl.append(name);
|
||||
routerInfo = i2p::util::http::httpRequest(tmpUrl);
|
||||
if (routerInfo.size()==0)
|
||||
continue;
|
||||
filename = root.string();
|
||||
#ifndef _WIN32
|
||||
filename += "/netDb/r";
|
||||
#else
|
||||
filename += "\\netDb\\r";
|
||||
#endif
|
||||
filename += name.at(11); // first char in id
|
||||
#ifndef _WIN32
|
||||
filename.append("/");
|
||||
#else
|
||||
filename.append("\\");
|
||||
#endif
|
||||
filename.append(name.c_str());
|
||||
std::ofstream outfile (filename, std::ios::binary);
|
||||
outfile << routerInfo;
|
||||
outfile.close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
//TODO: error reporting
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Reseeder::ReseedNowSU3 ()
|
||||
{
|
||||
CryptoPP::AutoSeededRandomPool rnd;
|
||||
auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1);
|
||||
std::string reseedHost = httpReseedHostList[ind];
|
||||
return ReseedFromSU3 (reseedHost);
|
||||
}
|
||||
|
||||
int Reseeder::ReseedFromSU3 (const std::string& host)
|
||||
{
|
||||
std::string url = host + "i2pseeds.su3";
|
||||
LogPrint (eLogInfo, "Dowloading SU3 from ", host);
|
||||
std::string su3 = i2p::util::http::httpRequest (url);
|
||||
if (su3.length () > 0)
|
||||
{
|
||||
std::stringstream s(su3);
|
||||
return ProcessSU3Stream (s);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SU3 download failed");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int Reseeder::ProcessSU3File (const char * filename)
|
||||
{
|
||||
std::ifstream s(filename, std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
return ProcessSU3Stream (s);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Can't open file ", filename);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const char SU3_MAGIC_NUMBER[]="I2Psu3";
|
||||
const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50;
|
||||
const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50;
|
||||
const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008;
|
||||
int Reseeder::ProcessSU3Stream (std::istream& s)
|
||||
{
|
||||
char magicNumber[7];
|
||||
s.read (magicNumber, 7); // magic number and zero byte 6
|
||||
if (strcmp (magicNumber, SU3_MAGIC_NUMBER))
|
||||
{
|
||||
LogPrint (eLogError, "Unexpected SU3 magic number");
|
||||
return 0;
|
||||
}
|
||||
s.seekg (1, std::ios::cur); // su3 file format version
|
||||
SigningKeyType signatureType;
|
||||
s.read ((char *)&signatureType, 2); // signature type
|
||||
signatureType = be16toh (signatureType);
|
||||
uint16_t signatureLength;
|
||||
s.read ((char *)&signatureLength, 2); // signature length
|
||||
signatureLength = be16toh (signatureLength);
|
||||
s.seekg (1, std::ios::cur); // unused
|
||||
uint8_t versionLength;
|
||||
s.read ((char *)&versionLength, 1); // version length
|
||||
s.seekg (1, std::ios::cur); // unused
|
||||
uint8_t signerIDLength;
|
||||
s.read ((char *)&signerIDLength, 1); // signer ID length
|
||||
uint64_t contentLength;
|
||||
s.read ((char *)&contentLength, 8); // content length
|
||||
contentLength = be64toh (contentLength);
|
||||
s.seekg (1, std::ios::cur); // unused
|
||||
uint8_t fileType;
|
||||
s.read ((char *)&fileType, 1); // file type
|
||||
if (fileType != 0x00) // zip file
|
||||
{
|
||||
LogPrint (eLogError, "Can't handle file type ", (int)fileType);
|
||||
return 0;
|
||||
}
|
||||
s.seekg (1, std::ios::cur); // unused
|
||||
uint8_t contentType;
|
||||
s.read ((char *)&contentType, 1); // content type
|
||||
if (contentType != 0x03) // reseed data
|
||||
{
|
||||
LogPrint (eLogError, "Unexpected content type ", (int)contentType);
|
||||
return 0;
|
||||
}
|
||||
s.seekg (12, std::ios::cur); // unused
|
||||
|
||||
s.seekg (versionLength, std::ios::cur); // skip version
|
||||
char signerID[256];
|
||||
s.read (signerID, signerIDLength); // signerID
|
||||
signerID[signerIDLength] = 0;
|
||||
|
||||
//try to verify signature
|
||||
auto it = m_SigningKeys.find (signerID);
|
||||
if (it != m_SigningKeys.end ())
|
||||
{
|
||||
// TODO: implement all signature types
|
||||
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
|
||||
{
|
||||
size_t pos = s.tellg ();
|
||||
size_t tbsLen = pos + contentLength;
|
||||
uint8_t * tbs = new uint8_t[tbsLen];
|
||||
s.seekg (0, std::ios::beg);
|
||||
s.read ((char *)tbs, tbsLen);
|
||||
uint8_t * signature = new uint8_t[signatureLength];
|
||||
s.read ((char *)signature, signatureLength);
|
||||
// RSA-raw
|
||||
i2p::crypto::RSASHA5124096RawVerifier verifier(it->second);
|
||||
verifier.Update (tbs, tbsLen);
|
||||
if (!verifier.Verify (signature))
|
||||
LogPrint (eLogWarning, "SU3 signature verification failed");
|
||||
delete[] signature;
|
||||
delete[] tbs;
|
||||
s.seekg (pos, std::ios::beg);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Signature type ", signatureType, " is not supported");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Certificate for ", signerID, " not loaded");
|
||||
|
||||
// handle content
|
||||
int numFiles = 0;
|
||||
size_t contentPos = s.tellg ();
|
||||
while (!s.eof ())
|
||||
{
|
||||
uint32_t signature;
|
||||
s.read ((char *)&signature, 4);
|
||||
signature = le32toh (signature);
|
||||
if (signature == ZIP_HEADER_SIGNATURE)
|
||||
{
|
||||
// next local file
|
||||
s.seekg (2, std::ios::cur); // version
|
||||
uint16_t bitFlag;
|
||||
s.read ((char *)&bitFlag, 2);
|
||||
bitFlag = le16toh (bitFlag);
|
||||
uint16_t compressionMethod;
|
||||
s.read ((char *)&compressionMethod, 2);
|
||||
compressionMethod = le16toh (compressionMethod);
|
||||
s.seekg (4, std::ios::cur); // skip fields we don't care about
|
||||
uint32_t crc32, compressedSize, uncompressedSize;
|
||||
s.read ((char *)&crc32, 4);
|
||||
crc32 = le32toh (crc32);
|
||||
s.read ((char *)&compressedSize, 4);
|
||||
compressedSize = le32toh (compressedSize);
|
||||
s.read ((char *)&uncompressedSize, 4);
|
||||
uncompressedSize = le32toh (uncompressedSize);
|
||||
uint16_t fileNameLength, extraFieldLength;
|
||||
s.read ((char *)&fileNameLength, 2);
|
||||
fileNameLength = le16toh (fileNameLength);
|
||||
s.read ((char *)&extraFieldLength, 2);
|
||||
extraFieldLength = le16toh (extraFieldLength);
|
||||
char localFileName[255];
|
||||
s.read (localFileName, fileNameLength);
|
||||
localFileName[fileNameLength] = 0;
|
||||
s.seekg (extraFieldLength, std::ios::cur);
|
||||
// take care about data desriptor if presented
|
||||
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
|
||||
{
|
||||
size_t pos = s.tellg ();
|
||||
if (!FindZipDataDescriptor (s))
|
||||
{
|
||||
LogPrint (eLogError, "SU3 archive data descriptor not found");
|
||||
return numFiles;
|
||||
}
|
||||
|
||||
s.read ((char *)&crc32, 4);
|
||||
crc32 = le32toh (crc32);
|
||||
s.read ((char *)&compressedSize, 4);
|
||||
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
|
||||
s.read ((char *)&uncompressedSize, 4);
|
||||
uncompressedSize = le32toh (uncompressedSize);
|
||||
|
||||
// now we know compressed and uncompressed size
|
||||
s.seekg (pos, std::ios::beg); // back to compressed data
|
||||
}
|
||||
|
||||
LogPrint (eLogDebug, "Proccessing file ", localFileName, " ", compressedSize, " bytes");
|
||||
if (!compressedSize)
|
||||
{
|
||||
LogPrint (eLogWarning, "Unexpected size 0. Skipped");
|
||||
continue;
|
||||
}
|
||||
|
||||
uint8_t * compressed = new uint8_t[compressedSize];
|
||||
s.read ((char *)compressed, compressedSize);
|
||||
if (compressionMethod) // we assume Deflate
|
||||
{
|
||||
CryptoPP::Inflator decompressor;
|
||||
decompressor.Put (compressed, compressedSize);
|
||||
decompressor.MessageEnd();
|
||||
if (decompressor.MaxRetrievable () <= uncompressedSize)
|
||||
{
|
||||
uint8_t * uncompressed = new uint8_t[uncompressedSize];
|
||||
decompressor.Get (uncompressed, uncompressedSize);
|
||||
if (CryptoPP::CRC32().VerifyDigest ((uint8_t *)&crc32, uncompressed, uncompressedSize))
|
||||
{
|
||||
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
|
||||
numFiles++;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "CRC32 verification failed");
|
||||
delete[] uncompressed;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header");
|
||||
}
|
||||
else // no compression
|
||||
{
|
||||
i2p::data::netdb.AddRouterInfo (compressed, compressedSize);
|
||||
numFiles++;
|
||||
}
|
||||
delete[] compressed;
|
||||
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
|
||||
s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE)
|
||||
LogPrint (eLogWarning, "Missing zip central directory header");
|
||||
break; // no more files
|
||||
}
|
||||
size_t end = s.tellg ();
|
||||
if (end - contentPos >= contentLength)
|
||||
break; // we are beyond contentLength
|
||||
}
|
||||
return numFiles;
|
||||
}
|
||||
|
||||
const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 };
|
||||
bool Reseeder::FindZipDataDescriptor (std::istream& s)
|
||||
{
|
||||
size_t nextInd = 0;
|
||||
while (!s.eof ())
|
||||
{
|
||||
uint8_t nextByte;
|
||||
s.read ((char *)&nextByte, 1);
|
||||
if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd])
|
||||
{
|
||||
nextInd++;
|
||||
if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
nextInd = 0;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
const char CERTIFICATE_HEADER[] = "-----BEGIN CERTIFICATE-----";
|
||||
const char CERTIFICATE_FOOTER[] = "-----END CERTIFICATE-----";
|
||||
void Reseeder::LoadCertificate (const std::string& filename)
|
||||
{
|
||||
std::ifstream s(filename, std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
{
|
||||
s.seekg (0, std::ios::end);
|
||||
size_t len = s.tellg ();
|
||||
s.seekg (0, std::ios::beg);
|
||||
char buf[2048];
|
||||
s.read (buf, len);
|
||||
std::string cert (buf, len);
|
||||
// assume file in pem format
|
||||
auto pos1 = cert.find (CERTIFICATE_HEADER);
|
||||
auto pos2 = cert.find (CERTIFICATE_FOOTER);
|
||||
if (pos1 == std::string::npos || pos2 == std::string::npos)
|
||||
{
|
||||
LogPrint (eLogError, "Malformed certificate file");
|
||||
return;
|
||||
}
|
||||
pos1 += strlen (CERTIFICATE_HEADER);
|
||||
pos2 -= pos1;
|
||||
std::string base64 = cert.substr (pos1, pos2);
|
||||
|
||||
CryptoPP::ByteQueue queue;
|
||||
CryptoPP::Base64Decoder decoder; // regular base64 rather than I2P
|
||||
decoder.Attach (new CryptoPP::Redirector (queue));
|
||||
decoder.Put ((const uint8_t *)base64.data(), base64.length());
|
||||
decoder.MessageEnd ();
|
||||
|
||||
// extract X.509
|
||||
CryptoPP::BERSequenceDecoder x509Cert (queue);
|
||||
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
|
||||
// version
|
||||
uint32_t ver;
|
||||
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
|
||||
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
|
||||
// serial
|
||||
CryptoPP::Integer serial;
|
||||
serial.BERDecode(tbsCert);
|
||||
// signature
|
||||
CryptoPP::BERSequenceDecoder signature (tbsCert);
|
||||
signature.SkipAll();
|
||||
|
||||
// issuer
|
||||
std::string name;
|
||||
CryptoPP::BERSequenceDecoder issuer (tbsCert);
|
||||
{
|
||||
CryptoPP::BERSetDecoder c (issuer); c.SkipAll();
|
||||
CryptoPP::BERSetDecoder st (issuer); st.SkipAll();
|
||||
CryptoPP::BERSetDecoder l (issuer); l.SkipAll();
|
||||
CryptoPP::BERSetDecoder o (issuer); o.SkipAll();
|
||||
CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll();
|
||||
CryptoPP::BERSetDecoder cn (issuer);
|
||||
{
|
||||
CryptoPP::BERSequenceDecoder attributes (cn);
|
||||
{
|
||||
CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER);
|
||||
ident.SkipAll ();
|
||||
CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING);
|
||||
}
|
||||
}
|
||||
}
|
||||
issuer.SkipAll();
|
||||
// validity
|
||||
CryptoPP::BERSequenceDecoder validity (tbsCert);
|
||||
validity.SkipAll();
|
||||
// subject
|
||||
CryptoPP::BERSequenceDecoder subject (tbsCert);
|
||||
subject.SkipAll();
|
||||
// public key
|
||||
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
|
||||
{
|
||||
CryptoPP::BERSequenceDecoder ident (publicKey);
|
||||
ident.SkipAll ();
|
||||
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
|
||||
key.Skip (1); // FIXME: probably bug in crypto++
|
||||
CryptoPP::BERSequenceDecoder keyPair (key);
|
||||
CryptoPP::Integer n;
|
||||
n.BERDecode (keyPair);
|
||||
if (name.length () > 0)
|
||||
{
|
||||
PublicKey value;
|
||||
n.Encode (value, 512);
|
||||
m_SigningKeys[name] = value;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Unknown issuer. Skipped");
|
||||
}
|
||||
publicKey.SkipAll();
|
||||
|
||||
tbsCert.SkipAll();
|
||||
x509Cert.SkipAll();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Can't open certificate file ", filename);
|
||||
}
|
||||
|
||||
void Reseeder::LoadCertificates ()
|
||||
{
|
||||
boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed";
|
||||
|
||||
if (!boost::filesystem::exists (reseedDir))
|
||||
{
|
||||
LogPrint (eLogWarning, "Reseed certificates not loaded. ", reseedDir, " doesn't exist");
|
||||
return;
|
||||
}
|
||||
|
||||
int numCertificates = 0;
|
||||
boost::filesystem::directory_iterator end; // empty
|
||||
for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it)
|
||||
{
|
||||
if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt")
|
||||
{
|
||||
LoadCertificate (it->path ().string ());
|
||||
numCertificates++;
|
||||
}
|
||||
}
|
||||
LogPrint (eLogInfo, numCertificates, " certificates loaded");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
45
Reseed.h
45
Reseed.h
@@ -1,45 +0,0 @@
|
||||
#ifndef RESEED_H
|
||||
#define RESEED_H
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
|
||||
class Reseeder
|
||||
{
|
||||
typedef Tag<512> PublicKey;
|
||||
|
||||
public:
|
||||
|
||||
Reseeder();
|
||||
~Reseeder();
|
||||
bool reseedNow(); // depreacted
|
||||
int ReseedNowSU3 ();
|
||||
|
||||
void LoadCertificates ();
|
||||
|
||||
private:
|
||||
|
||||
void LoadCertificate (const std::string& filename);
|
||||
|
||||
int ReseedFromSU3 (const std::string& host);
|
||||
int ProcessSU3File (const char * filename);
|
||||
int ProcessSU3Stream (std::istream& s);
|
||||
|
||||
bool FindZipDataDescriptor (std::istream& s);
|
||||
|
||||
private:
|
||||
|
||||
std::map<std::string, PublicKey> m_SigningKeys;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,211 +0,0 @@
|
||||
#include <fstream>
|
||||
#include <cryptopp/dh.h>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Timestamp.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "util.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
RouterContext context;
|
||||
|
||||
RouterContext::RouterContext ():
|
||||
m_LastUpdateTime (0), m_IsUnreachable (false), m_AcceptsTunnels (true)
|
||||
{
|
||||
}
|
||||
|
||||
void RouterContext::Init ()
|
||||
{
|
||||
if (!Load ())
|
||||
CreateNewRouter ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::CreateNewRouter ()
|
||||
{
|
||||
m_Keys = i2p::data::CreateRandomKeys ();
|
||||
SaveKeys ();
|
||||
NewRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::NewRouterInfo ()
|
||||
{
|
||||
i2p::data::RouterInfo routerInfo;
|
||||
routerInfo.SetRouterIdentity (GetIdentity ());
|
||||
int port = i2p::util::config::GetArg("-port", 0);
|
||||
if (!port)
|
||||
port = m_Rnd.GenerateWord32 (9111, 30777); // I2P network ports range
|
||||
routerInfo.AddSSUAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port, routerInfo.GetIdentHash ());
|
||||
routerInfo.AddNTCPAddress (i2p::util::config::GetCharArg("-host", "127.0.0.1"), port);
|
||||
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
|
||||
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC
|
||||
routerInfo.SetProperty ("coreVersion", I2P_VERSION);
|
||||
routerInfo.SetProperty ("netId", "2");
|
||||
routerInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
routerInfo.SetProperty ("stat_uptime", "90m");
|
||||
routerInfo.CreateBuffer (m_Keys);
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
}
|
||||
|
||||
void RouterContext::UpdateRouterInfo ()
|
||||
{
|
||||
m_RouterInfo.CreateBuffer (m_Keys);
|
||||
m_RouterInfo.SaveToFile (i2p::util::filesystem::GetFullPath (ROUTER_INFO));
|
||||
m_LastUpdateTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
void RouterContext::UpdatePort (int port)
|
||||
{
|
||||
bool updated = false;
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address.port != port)
|
||||
{
|
||||
address.port = port;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
if (updated)
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::UpdateAddress (const boost::asio::ip::address& host)
|
||||
{
|
||||
bool updated = false;
|
||||
for (auto& address : m_RouterInfo.GetAddresses ())
|
||||
{
|
||||
if (address.host != host && address.IsCompatible (host))
|
||||
{
|
||||
address.host = host;
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (updated || ts > m_LastUpdateTime + ROUTER_INFO_UPDATE_INTERVAL)
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
bool RouterContext::AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag)
|
||||
{
|
||||
bool ret = false;
|
||||
auto address = routerInfo.GetSSUAddress ();
|
||||
if (address)
|
||||
{
|
||||
ret = m_RouterInfo.AddIntroducer (address, tag);
|
||||
if (ret)
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void RouterContext::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
|
||||
{
|
||||
if (m_RouterInfo.RemoveIntroducer (e))
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::SetUnreachable ()
|
||||
{
|
||||
m_IsUnreachable = true;
|
||||
// set caps
|
||||
m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B
|
||||
// remove NTCP address
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (size_t i = 0; i < addresses.size (); i++)
|
||||
{
|
||||
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
||||
{
|
||||
addresses.erase (addresses.begin () + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// delete previous introducers
|
||||
for (auto& addr : addresses)
|
||||
addr.introducers.clear ();
|
||||
|
||||
// update
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::SetSupportsV6 (bool supportsV6)
|
||||
{
|
||||
if (supportsV6)
|
||||
m_RouterInfo.EnableV6 ();
|
||||
else
|
||||
m_RouterInfo.DisableV6 ();
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
void RouterContext::UpdateNTCPV6Address (const boost::asio::ip::address& host)
|
||||
{
|
||||
bool updated = false, found = false;
|
||||
int port = 0;
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto& addr : addresses)
|
||||
{
|
||||
if (addr.host.is_v6 () && addr.transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
||||
{
|
||||
if (addr.host != host)
|
||||
{
|
||||
addr.host = host;
|
||||
updated = true;
|
||||
}
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
port = addr.port;
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
// create new address
|
||||
m_RouterInfo.AddNTCPAddress (host.to_string ().c_str (), port);
|
||||
auto mtu = i2p::util::net::GetMTU (host);
|
||||
if (mtu)
|
||||
{
|
||||
LogPrint ("Our v6 MTU=", mtu);
|
||||
if (mtu > 1472) mtu = 1472;
|
||||
}
|
||||
m_RouterInfo.AddSSUAddress (host.to_string ().c_str (), port, GetIdentHash (), mtu ? mtu : 1472); // TODO
|
||||
updated = true;
|
||||
}
|
||||
if (updated)
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
|
||||
bool RouterContext::Load ()
|
||||
{
|
||||
std::ifstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ifstream::binary | std::ofstream::in);
|
||||
if (!fk.is_open ()) return false;
|
||||
|
||||
i2p::data::Keys keys;
|
||||
fk.read ((char *)&keys, sizeof (keys));
|
||||
m_Keys = keys;
|
||||
|
||||
i2p::data::RouterInfo routerInfo(i2p::util::filesystem::GetFullPath (ROUTER_INFO)); // TODO
|
||||
m_RouterInfo.Update (routerInfo.GetBuffer (), routerInfo.GetBufferLen ());
|
||||
m_RouterInfo.SetProperty ("coreVersion", I2P_VERSION);
|
||||
m_RouterInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RouterContext::SaveKeys ()
|
||||
{
|
||||
std::ofstream fk (i2p::util::filesystem::GetFullPath (ROUTER_KEYS).c_str (), std::ofstream::binary | std::ofstream::out);
|
||||
i2p::data::Keys keys;
|
||||
memcpy (keys.privateKey, m_Keys.GetPrivateKey (), sizeof (keys.privateKey));
|
||||
memcpy (keys.signingPrivateKey, m_Keys.GetSigningPrivateKey (), sizeof (keys.signingPrivateKey));
|
||||
auto& ident = GetIdentity ().GetStandardIdentity ();
|
||||
memcpy (keys.publicKey, ident.publicKey, sizeof (keys.publicKey));
|
||||
memcpy (keys.signingKey, ident.signingKey, sizeof (keys.signingKey));
|
||||
fk.write ((char *)&keys, sizeof (keys));
|
||||
}
|
||||
|
||||
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from)
|
||||
{
|
||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
#ifndef ROUTER_CONTEXT_H__
|
||||
#define ROUTER_CONTEXT_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "Garlic.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
const char ROUTER_INFO[] = "router.info";
|
||||
const char ROUTER_KEYS[] = "router.keys";
|
||||
const int ROUTER_INFO_UPDATE_INTERVAL = 1800; // 30 minutes
|
||||
|
||||
class RouterContext: public i2p::garlic::GarlicDestination
|
||||
{
|
||||
public:
|
||||
|
||||
RouterContext ();
|
||||
void Init ();
|
||||
|
||||
i2p::data::RouterInfo& GetRouterInfo () { return m_RouterInfo; };
|
||||
std::shared_ptr<const i2p::data::RouterInfo> GetSharedRouterInfo () const
|
||||
{
|
||||
return std::shared_ptr<const i2p::data::RouterInfo> (&m_RouterInfo,
|
||||
[](const i2p::data::RouterInfo *) {});
|
||||
}
|
||||
CryptoPP::RandomNumberGenerator& GetRandomNumberGenerator () { return m_Rnd; };
|
||||
|
||||
void UpdatePort (int port); // called from Daemon
|
||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
|
||||
bool AddIntroducer (const i2p::data::RouterInfo& routerInfo, uint32_t tag);
|
||||
void RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
bool IsUnreachable () const { return m_IsUnreachable; };
|
||||
void SetUnreachable ();
|
||||
bool AcceptsTunnels () const { return m_AcceptsTunnels; };
|
||||
void SetAcceptsTunnels (bool acceptsTunnels) { m_AcceptsTunnels = acceptsTunnels; };
|
||||
bool SupportsV6 () const { return m_RouterInfo.IsV6 (); };
|
||||
void SetSupportsV6 (bool supportsV6);
|
||||
void UpdateNTCPV6Address (const boost::asio::ip::address& host); // called from NTCP session
|
||||
|
||||
// implements LocalDestination
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_Keys.GetPrivateKey (); };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return GetIdentity ().GetStandardIdentity ().publicKey; };
|
||||
void SetLeaseSetUpdated () {};
|
||||
|
||||
// implements GarlicDestination
|
||||
const i2p::data::LeaseSet * GetLeaseSet () { return nullptr; };
|
||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, i2p::tunnel::InboundTunnel * from);
|
||||
|
||||
private:
|
||||
|
||||
void CreateNewRouter ();
|
||||
void NewRouterInfo ();
|
||||
void UpdateRouterInfo ();
|
||||
bool Load ();
|
||||
void SaveKeys ();
|
||||
|
||||
private:
|
||||
|
||||
i2p::data::RouterInfo m_RouterInfo;
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
CryptoPP::AutoSeededRandomPool m_Rnd;
|
||||
uint64_t m_LastUpdateTime;
|
||||
bool m_IsUnreachable, m_AcceptsTunnels;
|
||||
};
|
||||
|
||||
extern RouterContext context;
|
||||
}
|
||||
|
||||
#endif
|
||||
646
RouterInfo.cpp
646
RouterInfo.cpp
@@ -1,646 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "I2PEndian.h"
|
||||
#include <fstream>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <cryptopp/sha.h>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include "CryptoConst.h"
|
||||
#include "base64.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Log.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
RouterInfo::RouterInfo (const std::string& fullPath):
|
||||
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
|
||||
m_SupportedTransports (0), m_Caps (0)
|
||||
{
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
ReadFromFile ();
|
||||
}
|
||||
|
||||
RouterInfo::RouterInfo (const uint8_t * buf, int len):
|
||||
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
|
||||
{
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
ReadFromBuffer (true);
|
||||
}
|
||||
|
||||
RouterInfo::~RouterInfo ()
|
||||
{
|
||||
delete m_Buffer;
|
||||
}
|
||||
|
||||
void RouterInfo::Update (const uint8_t * buf, int len)
|
||||
{
|
||||
if (!m_Buffer)
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
m_IsUpdated = true;
|
||||
m_IsUnreachable = false;
|
||||
m_SupportedTransports = 0;
|
||||
m_Caps = 0;
|
||||
m_Addresses.clear ();
|
||||
m_Properties.clear ();
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
ReadFromBuffer (true);
|
||||
// don't delete buffer until save to file
|
||||
}
|
||||
|
||||
void RouterInfo::SetRouterIdentity (const IdentityEx& identity)
|
||||
{
|
||||
m_RouterIdentity = identity;
|
||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
bool RouterInfo::LoadFile ()
|
||||
{
|
||||
std::ifstream s(m_FullPath.c_str (), std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
{
|
||||
s.seekg (0,std::ios::end);
|
||||
m_BufferLen = s.tellg ();
|
||||
if (m_BufferLen < 40)
|
||||
{
|
||||
LogPrint(eLogError, "File", m_FullPath, " is malformed");
|
||||
return false;
|
||||
}
|
||||
s.seekg(0, std::ios::beg);
|
||||
if (!m_Buffer)
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
s.read((char *)m_Buffer, m_BufferLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Can't open file ", m_FullPath);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RouterInfo::ReadFromFile ()
|
||||
{
|
||||
if (LoadFile ())
|
||||
ReadFromBuffer (false);
|
||||
}
|
||||
|
||||
void RouterInfo::ReadFromBuffer (bool verifySignature)
|
||||
{
|
||||
size_t identityLen = m_RouterIdentity.FromBuffer (m_Buffer, m_BufferLen);
|
||||
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
|
||||
ReadFromStream (str);
|
||||
if (verifySignature)
|
||||
{
|
||||
// verify signature
|
||||
int l = m_BufferLen - m_RouterIdentity.GetSignatureLen ();
|
||||
if (!m_RouterIdentity.Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
|
||||
{
|
||||
LogPrint (eLogError, "signature verification failed");
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
m_RouterIdentity.DropVerifier ();
|
||||
}
|
||||
}
|
||||
|
||||
void RouterInfo::ReadFromStream (std::istream& s)
|
||||
{
|
||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||
m_Timestamp = be64toh (m_Timestamp);
|
||||
// read addresses
|
||||
uint8_t numAddresses;
|
||||
s.read ((char *)&numAddresses, sizeof (numAddresses));
|
||||
bool introducers = false;
|
||||
for (int i = 0; i < numAddresses; i++)
|
||||
{
|
||||
bool isValidAddress = true;
|
||||
Address address;
|
||||
s.read ((char *)&address.cost, sizeof (address.cost));
|
||||
s.read ((char *)&address.date, sizeof (address.date));
|
||||
char transportStyle[5];
|
||||
ReadString (transportStyle, s);
|
||||
if (!strcmp (transportStyle, "NTCP"))
|
||||
address.transportStyle = eTransportNTCP;
|
||||
else if (!strcmp (transportStyle, "SSU"))
|
||||
address.transportStyle = eTransportSSU;
|
||||
else
|
||||
address.transportStyle = eTransportUnknown;
|
||||
address.port = 0;
|
||||
address.mtu = 0;
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size));
|
||||
size = be16toh (size);
|
||||
while (r < size)
|
||||
{
|
||||
char key[500], value[500];
|
||||
r += ReadString (key, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // =
|
||||
r += ReadString (value, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // ;
|
||||
if (!strcmp (key, "host"))
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
address.host = boost::asio::ip::address::from_string (value, ecode);
|
||||
if (ecode)
|
||||
{
|
||||
// TODO: we should try to resolve address here
|
||||
LogPrint (eLogWarning, "Unexpected address ", value);
|
||||
isValidAddress = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add supported protocol
|
||||
if (address.host.is_v4 ())
|
||||
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
|
||||
else
|
||||
m_SupportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "port"))
|
||||
address.port = boost::lexical_cast<int>(value);
|
||||
else if (!strcmp (key, "mtu"))
|
||||
address.mtu = boost::lexical_cast<int>(value);
|
||||
else if (!strcmp (key, "key"))
|
||||
Base64ToByteStream (value, strlen (value), address.key, 32);
|
||||
else if (!strcmp (key, "caps"))
|
||||
ExtractCaps (value);
|
||||
else if (key[0] == 'i')
|
||||
{
|
||||
// introducers
|
||||
introducers = true;
|
||||
size_t l = strlen(key);
|
||||
unsigned char index = key[l-1] - '0'; // TODO:
|
||||
key[l-1] = 0;
|
||||
if (index >= address.introducers.size ())
|
||||
address.introducers.resize (index + 1);
|
||||
Introducer& introducer = address.introducers.at (index);
|
||||
if (!strcmp (key, "ihost"))
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
introducer.iHost = boost::asio::ip::address::from_string (value, ecode);
|
||||
}
|
||||
else if (!strcmp (key, "iport"))
|
||||
introducer.iPort = boost::lexical_cast<int>(value);
|
||||
else if (!strcmp (key, "itag"))
|
||||
introducer.iTag = boost::lexical_cast<uint32_t>(value);
|
||||
else if (!strcmp (key, "ikey"))
|
||||
Base64ToByteStream (value, strlen (value), introducer.iKey, 32);
|
||||
}
|
||||
}
|
||||
if (isValidAddress)
|
||||
m_Addresses.push_back(address);
|
||||
}
|
||||
// read peers
|
||||
uint8_t numPeers;
|
||||
s.read ((char *)&numPeers, sizeof (numPeers));
|
||||
s.seekg (numPeers*32, std::ios_base::cur); // TODO: read peers
|
||||
// read properties
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size));
|
||||
size = be16toh (size);
|
||||
while (r < size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
char key[500], value[500];
|
||||
// TODO: investigate why properties get read as one long string under Windows
|
||||
// length should not be more than 44
|
||||
#else
|
||||
char key[50], value[50];
|
||||
#endif
|
||||
r += ReadString (key, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // =
|
||||
r += ReadString (value, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // ;
|
||||
m_Properties[key] = value;
|
||||
|
||||
// extract caps
|
||||
if (!strcmp (key, "caps"))
|
||||
ExtractCaps (value);
|
||||
}
|
||||
|
||||
if (!m_SupportedTransports || !m_Addresses.size() || (UsesIntroducer () && !introducers))
|
||||
SetUnreachable (true);
|
||||
}
|
||||
|
||||
void RouterInfo::ExtractCaps (const char * value)
|
||||
{
|
||||
const char * cap = value;
|
||||
while (*cap)
|
||||
{
|
||||
switch (*cap)
|
||||
{
|
||||
case CAPS_FLAG_FLOODFILL:
|
||||
m_Caps |= Caps::eFloodfill;
|
||||
break;
|
||||
case CAPS_FLAG_HIGH_BANDWIDTH1:
|
||||
case CAPS_FLAG_HIGH_BANDWIDTH2:
|
||||
case CAPS_FLAG_HIGH_BANDWIDTH3:
|
||||
m_Caps |= Caps::eHighBandwidth;
|
||||
break;
|
||||
case CAPS_FLAG_HIDDEN:
|
||||
m_Caps |= Caps::eHidden;
|
||||
break;
|
||||
case CAPS_FLAG_REACHABLE:
|
||||
m_Caps |= Caps::eReachable;
|
||||
break;
|
||||
case CAPS_FLAG_UNREACHABLE:
|
||||
m_Caps |= Caps::eUnreachable;
|
||||
break;
|
||||
case CAPS_FLAG_SSU_TESTING:
|
||||
m_Caps |= Caps::eSSUTesting;
|
||||
break;
|
||||
case CAPS_FLAG_SSU_INTRODUCER:
|
||||
m_Caps |= Caps::eSSUIntroducer;
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
cap++;
|
||||
}
|
||||
}
|
||||
|
||||
void RouterInfo::UpdateCapsProperty ()
|
||||
{
|
||||
std::string caps;
|
||||
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH1 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
|
||||
if (m_Caps & eFloodfill) caps += CAPS_FLAG_FLOODFILL; // floodfill
|
||||
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
|
||||
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
|
||||
if (m_Caps & eUnreachable) caps += CAPS_FLAG_UNREACHABLE; // unreachable
|
||||
|
||||
SetProperty ("caps", caps.c_str ());
|
||||
}
|
||||
|
||||
void RouterInfo::WriteToStream (std::ostream& s)
|
||||
{
|
||||
uint64_t ts = htobe64 (m_Timestamp);
|
||||
s.write ((char *)&ts, sizeof (ts));
|
||||
|
||||
// addresses
|
||||
uint8_t numAddresses = m_Addresses.size ();
|
||||
s.write ((char *)&numAddresses, sizeof (numAddresses));
|
||||
for (auto& address : m_Addresses)
|
||||
{
|
||||
s.write ((char *)&address.cost, sizeof (address.cost));
|
||||
s.write ((char *)&address.date, sizeof (address.date));
|
||||
std::stringstream properties;
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
WriteString ("NTCP", s);
|
||||
else if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
WriteString ("SSU", s);
|
||||
// caps
|
||||
WriteString ("caps", properties);
|
||||
properties << '=';
|
||||
std::string caps;
|
||||
if (IsPeerTesting ()) caps += CAPS_FLAG_SSU_TESTING;
|
||||
if (IsIntroducer ()) caps += CAPS_FLAG_SSU_INTRODUCER;
|
||||
WriteString (caps, properties);
|
||||
properties << ';';
|
||||
}
|
||||
else
|
||||
WriteString ("", s);
|
||||
|
||||
WriteString ("host", properties);
|
||||
properties << '=';
|
||||
WriteString (address.host.to_string (), properties);
|
||||
properties << ';';
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
// write introducers if any
|
||||
if (address.introducers.size () > 0)
|
||||
{
|
||||
int i = 0;
|
||||
for (auto introducer: address.introducers)
|
||||
{
|
||||
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
WriteString (introducer.iHost.to_string (), properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (auto introducer: address.introducers)
|
||||
{
|
||||
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
char value[64];
|
||||
size_t l = ByteStreamToBase64 (introducer.iKey, 32, value, 64);
|
||||
value[l] = 0;
|
||||
WriteString (value, properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (auto introducer: address.introducers)
|
||||
{
|
||||
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(introducer.iPort), properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (auto introducer: address.introducers)
|
||||
{
|
||||
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(introducer.iTag), properties);
|
||||
properties << ';';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// write intro key
|
||||
WriteString ("key", properties);
|
||||
properties << '=';
|
||||
char value[64];
|
||||
size_t l = ByteStreamToBase64 (address.key, 32, value, 64);
|
||||
value[l] = 0;
|
||||
WriteString (value, properties);
|
||||
properties << ';';
|
||||
// write mtu
|
||||
if (address.mtu)
|
||||
{
|
||||
WriteString ("mtu", properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(address.mtu), properties);
|
||||
properties << ';';
|
||||
}
|
||||
}
|
||||
WriteString ("port", properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(address.port), properties);
|
||||
properties << ';';
|
||||
|
||||
uint16_t size = htobe16 (properties.str ().size ());
|
||||
s.write ((char *)&size, sizeof (size));
|
||||
s.write (properties.str ().c_str (), properties.str ().size ());
|
||||
}
|
||||
|
||||
// peers
|
||||
uint8_t numPeers = 0;
|
||||
s.write ((char *)&numPeers, sizeof (numPeers));
|
||||
|
||||
// properties
|
||||
std::stringstream properties;
|
||||
for (auto& p : m_Properties)
|
||||
{
|
||||
WriteString (p.first, properties);
|
||||
properties << '=';
|
||||
WriteString (p.second, properties);
|
||||
properties << ';';
|
||||
}
|
||||
uint16_t size = htobe16 (properties.str ().size ());
|
||||
s.write ((char *)&size, sizeof (size));
|
||||
s.write (properties.str ().c_str (), properties.str ().size ());
|
||||
}
|
||||
|
||||
const uint8_t * RouterInfo::LoadBuffer ()
|
||||
{
|
||||
if (!m_Buffer)
|
||||
{
|
||||
if (LoadFile ())
|
||||
LogPrint ("Buffer for ", GetIdentHashAbbreviation (), " loaded from file");
|
||||
}
|
||||
return m_Buffer;
|
||||
}
|
||||
|
||||
void RouterInfo::CreateBuffer (const PrivateKeys& privateKeys)
|
||||
{
|
||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch (); // refresh timstamp
|
||||
std::stringstream s;
|
||||
uint8_t ident[1024];
|
||||
auto identLen = privateKeys.GetPublic ().ToBuffer (ident, 1024);
|
||||
s.write ((char *)ident, identLen);
|
||||
WriteToStream (s);
|
||||
m_BufferLen = s.str ().size ();
|
||||
if (!m_Buffer)
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
memcpy (m_Buffer, s.str ().c_str (), m_BufferLen);
|
||||
// signature
|
||||
privateKeys.Sign ((uint8_t *)m_Buffer, m_BufferLen, (uint8_t *)m_Buffer + m_BufferLen);
|
||||
m_BufferLen += privateKeys.GetPublic ().GetSignatureLen ();
|
||||
}
|
||||
|
||||
void RouterInfo::SaveToFile (const std::string& fullPath)
|
||||
{
|
||||
m_FullPath = fullPath;
|
||||
if (m_Buffer)
|
||||
{
|
||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
||||
f.write ((char *)m_Buffer, m_BufferLen);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Can't save to file");
|
||||
}
|
||||
|
||||
size_t RouterInfo::ReadString (char * str, std::istream& s)
|
||||
{
|
||||
uint8_t len;
|
||||
s.read ((char *)&len, 1);
|
||||
s.read (str, len);
|
||||
str[len] = 0;
|
||||
return len+1;
|
||||
}
|
||||
|
||||
void RouterInfo::WriteString (const std::string& str, std::ostream& s)
|
||||
{
|
||||
uint8_t len = str.size ();
|
||||
s.write ((char *)&len, 1);
|
||||
s.write (str.c_str (), len);
|
||||
}
|
||||
|
||||
void RouterInfo::AddNTCPAddress (const char * host, int port)
|
||||
{
|
||||
Address addr;
|
||||
addr.host = boost::asio::ip::address::from_string (host);
|
||||
addr.port = port;
|
||||
addr.transportStyle = eTransportNTCP;
|
||||
addr.cost = 2;
|
||||
addr.date = 0;
|
||||
addr.mtu = 0;
|
||||
m_Addresses.push_back(addr);
|
||||
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eNTCPV4;
|
||||
}
|
||||
|
||||
void RouterInfo::AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu)
|
||||
{
|
||||
Address addr;
|
||||
addr.host = boost::asio::ip::address::from_string (host);
|
||||
addr.port = port;
|
||||
addr.transportStyle = eTransportSSU;
|
||||
addr.cost = 10; // NTCP should have priority over SSU
|
||||
addr.date = 0;
|
||||
addr.mtu = mtu;
|
||||
memcpy (addr.key, key, 32);
|
||||
m_Addresses.push_back(addr);
|
||||
m_SupportedTransports |= addr.host.is_v6 () ? eNTCPV6 : eSSUV4;
|
||||
m_Caps |= eSSUTesting;
|
||||
m_Caps |= eSSUIntroducer;
|
||||
}
|
||||
|
||||
bool RouterInfo::AddIntroducer (const Address * address, uint32_t tag)
|
||||
{
|
||||
for (auto& addr : m_Addresses)
|
||||
{
|
||||
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
|
||||
{
|
||||
for (auto intro: addr.introducers)
|
||||
if (intro.iTag == tag) return false; // already presented
|
||||
Introducer x;
|
||||
x.iHost = address->host;
|
||||
x.iPort = address->port;
|
||||
x.iTag = tag;
|
||||
memcpy (x.iKey, address->key, 32); // TODO: replace to Tag<32>
|
||||
addr.introducers.push_back (x);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RouterInfo::RemoveIntroducer (const boost::asio::ip::udp::endpoint& e)
|
||||
{
|
||||
for (auto& addr : m_Addresses)
|
||||
{
|
||||
if (addr.transportStyle == eTransportSSU && addr.host.is_v4 ())
|
||||
{
|
||||
for (std::vector<Introducer>::iterator it = addr.introducers.begin (); it != addr.introducers.end (); it++)
|
||||
if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
|
||||
{
|
||||
addr.introducers.erase (it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RouterInfo::SetCaps (uint8_t caps)
|
||||
{
|
||||
m_Caps = caps;
|
||||
UpdateCapsProperty ();
|
||||
}
|
||||
|
||||
void RouterInfo::SetCaps (const char * caps)
|
||||
{
|
||||
SetProperty ("caps", caps);
|
||||
m_Caps = 0;
|
||||
ExtractCaps (caps);
|
||||
}
|
||||
|
||||
void RouterInfo::SetProperty (const char * key, const char * value)
|
||||
{
|
||||
m_Properties[key] = value;
|
||||
}
|
||||
|
||||
const char * RouterInfo::GetProperty (const char * key) const
|
||||
{
|
||||
auto it = m_Properties.find (key);
|
||||
if (it != m_Properties.end ())
|
||||
return it->second.c_str ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsFloodfill () const
|
||||
{
|
||||
return m_Caps & Caps::eFloodfill;
|
||||
}
|
||||
|
||||
bool RouterInfo::IsNTCP (bool v4only) const
|
||||
{
|
||||
if (v4only)
|
||||
return m_SupportedTransports & eNTCPV4;
|
||||
else
|
||||
return m_SupportedTransports & (eNTCPV4 | eNTCPV6);
|
||||
}
|
||||
|
||||
bool RouterInfo::IsSSU (bool v4only) const
|
||||
{
|
||||
if (v4only)
|
||||
return m_SupportedTransports & eSSUV4;
|
||||
else
|
||||
return m_SupportedTransports & (eSSUV4 | eSSUV6);
|
||||
}
|
||||
|
||||
bool RouterInfo::IsV6 () const
|
||||
{
|
||||
return m_SupportedTransports & (eNTCPV6 | eSSUV6);
|
||||
}
|
||||
|
||||
void RouterInfo::EnableV6 ()
|
||||
{
|
||||
if (!IsV6 ())
|
||||
m_SupportedTransports |= eNTCPV6 | eSSUV6;
|
||||
}
|
||||
|
||||
void RouterInfo::DisableV6 ()
|
||||
{
|
||||
if (IsV6 ())
|
||||
{
|
||||
// NTCP
|
||||
m_SupportedTransports &= ~eNTCPV6;
|
||||
for (size_t i = 0; i < m_Addresses.size (); i++)
|
||||
{
|
||||
if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportNTCP &&
|
||||
m_Addresses[i].host.is_v6 ())
|
||||
{
|
||||
m_Addresses.erase (m_Addresses.begin () + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// SSU
|
||||
m_SupportedTransports &= ~eSSUV6;
|
||||
for (size_t i = 0; i < m_Addresses.size (); i++)
|
||||
{
|
||||
if (m_Addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU &&
|
||||
m_Addresses[i].host.is_v6 ())
|
||||
{
|
||||
m_Addresses.erase (m_Addresses.begin () + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool RouterInfo::UsesIntroducer () const
|
||||
{
|
||||
return m_Caps & Caps::eUnreachable; // non-reachable
|
||||
}
|
||||
|
||||
const RouterInfo::Address * RouterInfo::GetNTCPAddress (bool v4only) const
|
||||
{
|
||||
return GetAddress (eTransportNTCP, v4only);
|
||||
}
|
||||
|
||||
const RouterInfo::Address * RouterInfo::GetSSUAddress (bool v4only) const
|
||||
{
|
||||
return GetAddress (eTransportSSU, v4only);
|
||||
}
|
||||
|
||||
const RouterInfo::Address * RouterInfo::GetSSUV6Address () const
|
||||
{
|
||||
return GetAddress (eTransportSSU, false, true);
|
||||
}
|
||||
|
||||
const RouterInfo::Address * RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
|
||||
{
|
||||
for (auto& address : m_Addresses)
|
||||
{
|
||||
if (address.transportStyle == s)
|
||||
{
|
||||
if ((!v4only || address.host.is_v4 ()) && (!v6only || address.host.is_v6 ()))
|
||||
return &address;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
174
RouterInfo.h
174
RouterInfo.h
@@ -1,174 +0,0 @@
|
||||
#ifndef ROUTER_INFO_H__
|
||||
#define ROUTER_INFO_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
const char CAPS_FLAG_FLOODFILL = 'f';
|
||||
const char CAPS_FLAG_HIDDEN = 'H';
|
||||
const char CAPS_FLAG_REACHABLE = 'R';
|
||||
const char CAPS_FLAG_UNREACHABLE = 'U';
|
||||
const char CAPS_FLAG_LOW_BANDWIDTH1 = 'K';
|
||||
const char CAPS_FLAG_LOW_BANDWIDTH2 = 'L';
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH1 = 'M';
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH2 = 'N';
|
||||
const char CAPS_FLAG_HIGH_BANDWIDTH3 = 'O';
|
||||
|
||||
const char CAPS_FLAG_SSU_TESTING = 'B';
|
||||
const char CAPS_FLAG_SSU_INTRODUCER = 'C';
|
||||
|
||||
const int MAX_RI_BUFFER_SIZE = 2048;
|
||||
class RouterInfo: public RoutingDestination
|
||||
{
|
||||
public:
|
||||
|
||||
enum SupportedTranports
|
||||
{
|
||||
eNTCPV4 = 0x01,
|
||||
eNTCPV6 = 0x02,
|
||||
eSSUV4 = 0x04,
|
||||
eSSUV6 = 0x08
|
||||
};
|
||||
|
||||
enum Caps
|
||||
{
|
||||
eFloodfill = 0x01,
|
||||
eHighBandwidth = 0x02,
|
||||
eReachable = 0x04,
|
||||
eSSUTesting = 0x08,
|
||||
eSSUIntroducer = 0x10,
|
||||
eHidden = 0x20,
|
||||
eUnreachable = 0x40
|
||||
};
|
||||
|
||||
enum TransportStyle
|
||||
{
|
||||
eTransportUnknown = 0,
|
||||
eTransportNTCP,
|
||||
eTransportSSU
|
||||
};
|
||||
|
||||
struct Introducer
|
||||
{
|
||||
boost::asio::ip::address iHost;
|
||||
int iPort;
|
||||
Tag<32> iKey;
|
||||
uint32_t iTag;
|
||||
};
|
||||
|
||||
struct Address
|
||||
{
|
||||
TransportStyle transportStyle;
|
||||
boost::asio::ip::address host;
|
||||
int port, mtu;
|
||||
uint64_t date;
|
||||
uint8_t cost;
|
||||
// SSU only
|
||||
Tag<32> key; // intro key for SSU
|
||||
std::vector<Introducer> introducers;
|
||||
|
||||
bool IsCompatible (const boost::asio::ip::address& other) const
|
||||
{
|
||||
return (host.is_v4 () && other.is_v4 ()) ||
|
||||
(host.is_v6 () && other.is_v6 ());
|
||||
}
|
||||
};
|
||||
|
||||
RouterInfo (const std::string& fullPath);
|
||||
RouterInfo (): m_Buffer (nullptr) { };
|
||||
RouterInfo (const RouterInfo& ) = default;
|
||||
RouterInfo& operator=(const RouterInfo& ) = default;
|
||||
RouterInfo (const uint8_t * buf, int len);
|
||||
~RouterInfo ();
|
||||
|
||||
const IdentityEx& GetRouterIdentity () const { return m_RouterIdentity; };
|
||||
void SetRouterIdentity (const IdentityEx& identity);
|
||||
std::string GetIdentHashBase64 () const { return GetIdentHash ().ToBase64 (); };
|
||||
std::string GetIdentHashAbbreviation () const { return GetIdentHash ().ToBase64 ().substr (0, 4); };
|
||||
uint64_t GetTimestamp () const { return m_Timestamp; };
|
||||
std::vector<Address>& GetAddresses () { return m_Addresses; };
|
||||
const Address * GetNTCPAddress (bool v4only = true) const;
|
||||
const Address * GetSSUAddress (bool v4only = true) const;
|
||||
const Address * GetSSUV6Address () const;
|
||||
|
||||
void AddNTCPAddress (const char * host, int port);
|
||||
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
|
||||
bool AddIntroducer (const Address * address, uint32_t tag);
|
||||
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
|
||||
void SetProperty (const char * key, const char * value);
|
||||
const char * GetProperty (const char * key) const;
|
||||
bool IsFloodfill () const;
|
||||
bool IsNTCP (bool v4only = true) const;
|
||||
bool IsSSU (bool v4only = true) const;
|
||||
bool IsV6 () const;
|
||||
void EnableV6 ();
|
||||
void DisableV6 ();
|
||||
bool IsCompatible (const RouterInfo& other) const { return m_SupportedTransports & other.m_SupportedTransports; };
|
||||
bool UsesIntroducer () const;
|
||||
bool IsIntroducer () const { return m_Caps & eSSUIntroducer; };
|
||||
bool IsPeerTesting () const { return m_Caps & eSSUTesting; };
|
||||
bool IsHidden () const { return m_Caps & eHidden; };
|
||||
|
||||
uint8_t GetCaps () const { return m_Caps; };
|
||||
void SetCaps (uint8_t caps);
|
||||
void SetCaps (const char * caps);
|
||||
|
||||
void SetUnreachable (bool unreachable) { m_IsUnreachable = unreachable; };
|
||||
bool IsUnreachable () const { return m_IsUnreachable; };
|
||||
|
||||
const uint8_t * GetBuffer () const { return m_Buffer; };
|
||||
const uint8_t * LoadBuffer (); // load if necessary
|
||||
int GetBufferLen () const { return m_BufferLen; };
|
||||
void CreateBuffer (const PrivateKeys& privateKeys);
|
||||
|
||||
bool IsUpdated () const { return m_IsUpdated; };
|
||||
void SetUpdated (bool updated) { m_IsUpdated = updated; };
|
||||
void SaveToFile (const std::string& fullPath);
|
||||
|
||||
void Update (const uint8_t * buf, int len);
|
||||
void DeleteBuffer () { delete m_Buffer; m_Buffer = nullptr; };
|
||||
|
||||
// implements RoutingDestination
|
||||
const IdentHash& GetIdentHash () const { return m_RouterIdentity.GetIdentHash (); };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_RouterIdentity.GetStandardIdentity ().publicKey; };
|
||||
bool IsDestination () const { return false; };
|
||||
|
||||
|
||||
private:
|
||||
|
||||
bool LoadFile ();
|
||||
void ReadFromFile ();
|
||||
void ReadFromStream (std::istream& s);
|
||||
void ReadFromBuffer (bool verifySignature);
|
||||
void WriteToStream (std::ostream& s);
|
||||
size_t ReadString (char * str, std::istream& s);
|
||||
void WriteString (const std::string& str, std::ostream& s);
|
||||
void ExtractCaps (const char * value);
|
||||
const Address * GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
|
||||
void UpdateCapsProperty ();
|
||||
|
||||
private:
|
||||
|
||||
std::string m_FullPath;
|
||||
IdentityEx m_RouterIdentity;
|
||||
uint8_t * m_Buffer;
|
||||
int m_BufferLen;
|
||||
uint64_t m_Timestamp;
|
||||
std::vector<Address> m_Addresses;
|
||||
std::map<std::string, std::string> m_Properties;
|
||||
bool m_IsUpdated, m_IsUnreachable;
|
||||
uint8_t m_SupportedTransports, m_Caps;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
753
SAM.cpp
753
SAM.cpp
@@ -1,753 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "base64.h"
|
||||
#include "Identity.h"
|
||||
#include "Log.h"
|
||||
#include "NetDb.h"
|
||||
#include "Destination.h"
|
||||
#include "ClientContext.h"
|
||||
#include "SAM.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
SAMSocket::SAMSocket (SAMBridge& owner):
|
||||
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_Timer (m_Owner.GetService ()),
|
||||
m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false), m_Stream (nullptr),
|
||||
m_Session (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SAMSocket::~SAMSocket ()
|
||||
{
|
||||
Terminate ();
|
||||
}
|
||||
|
||||
void SAMSocket::CloseStream ()
|
||||
{
|
||||
if (m_Stream)
|
||||
{
|
||||
m_Stream->Close ();
|
||||
m_Stream.reset ();
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::Terminate ()
|
||||
{
|
||||
CloseStream ();
|
||||
|
||||
switch (m_SocketType)
|
||||
{
|
||||
case eSAMSocketTypeSession:
|
||||
m_Owner.CloseSession (m_ID);
|
||||
break;
|
||||
case eSAMSocketTypeStream:
|
||||
{
|
||||
if (m_Session)
|
||||
m_Session->sockets.remove (shared_from_this ());
|
||||
break;
|
||||
}
|
||||
case eSAMSocketTypeAcceptor:
|
||||
{
|
||||
if (m_Session)
|
||||
{
|
||||
m_Session->sockets.remove (shared_from_this ());
|
||||
m_Session->localDestination->StopAcceptingStreams ();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
;
|
||||
}
|
||||
m_SocketType = eSAMSocketTypeTerminated;
|
||||
m_Socket.close ();
|
||||
}
|
||||
|
||||
void SAMSocket::ReceiveHandshake ()
|
||||
{
|
||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
||||
std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM handshake read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Buffer[bytes_transferred] = 0;
|
||||
LogPrint ("SAM handshake ", m_Buffer);
|
||||
char * separator = strchr (m_Buffer, ' ');
|
||||
if (separator)
|
||||
{
|
||||
separator = strchr (separator + 1, ' ');
|
||||
if (separator)
|
||||
*separator = 0;
|
||||
}
|
||||
|
||||
if (!strcmp (m_Buffer, SAM_HANDSHAKE))
|
||||
{
|
||||
std::string version("3.0");
|
||||
// try to find MIN and MAX, 3.0 if not found
|
||||
if (separator)
|
||||
{
|
||||
separator++;
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (separator, bytes_transferred - (separator - m_Buffer), params);
|
||||
auto it = params.find (SAM_PARAM_MAX);
|
||||
// TODO: check MIN as well
|
||||
if (it != params.end ())
|
||||
version = it->second;
|
||||
}
|
||||
if (version[0] == '3') // we support v3 (3.0 and 3.1) only
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
|
||||
#else
|
||||
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
|
||||
#endif
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (),
|
||||
std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM handshake mismatch");
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM handshake reply send error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
||||
std::bind(&SAMSocket::HandleMessage, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close)
|
||||
{
|
||||
if (!m_IsSilent || m_SocketType == eSAMSocketTypeAcceptor)
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
|
||||
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, close));
|
||||
else
|
||||
{
|
||||
if (close)
|
||||
Terminate ();
|
||||
else
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM reply send error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (close)
|
||||
Terminate ();
|
||||
else
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Buffer[bytes_transferred] = 0;
|
||||
char * eol = strchr (m_Buffer, '\n');
|
||||
if (eol)
|
||||
{
|
||||
*eol = 0;
|
||||
char * separator = strchr (m_Buffer, ' ');
|
||||
if (separator)
|
||||
{
|
||||
separator = strchr (separator + 1, ' ');
|
||||
if (separator)
|
||||
*separator = 0;
|
||||
else
|
||||
separator = eol;
|
||||
|
||||
if (!strcmp (m_Buffer, SAM_SESSION_CREATE))
|
||||
ProcessSessionCreate (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
||||
else if (!strcmp (m_Buffer, SAM_STREAM_CONNECT))
|
||||
ProcessStreamConnect (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
||||
else if (!strcmp (m_Buffer, SAM_STREAM_ACCEPT))
|
||||
ProcessStreamAccept (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
||||
else if (!strcmp (m_Buffer, SAM_DEST_GENERATE))
|
||||
ProcessDestGenerate ();
|
||||
else if (!strcmp (m_Buffer, SAM_NAMING_LOOKUP))
|
||||
ProcessNamingLookup (separator + 1, bytes_transferred - (separator - m_Buffer) - 1);
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM unexpected message ", m_Buffer);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM malformed message ", m_Buffer);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM malformed message ", m_Buffer);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessSessionCreate (char * buf, size_t len)
|
||||
{
|
||||
LogPrint ("SAM session create: ", buf);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, len, params);
|
||||
std::string& style = params[SAM_PARAM_STYLE];
|
||||
std::string& id = params[SAM_PARAM_ID];
|
||||
std::string& destination = params[SAM_PARAM_DESTINATION];
|
||||
m_ID = id;
|
||||
if (m_Owner.FindSession (id))
|
||||
{
|
||||
// session exists
|
||||
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true);
|
||||
return;
|
||||
}
|
||||
|
||||
// create destination
|
||||
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms);
|
||||
if (m_Session)
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeSession;
|
||||
if (m_Session->localDestination->IsReady ())
|
||||
{
|
||||
if (style == SAM_VALUE_DATAGRAM)
|
||||
{
|
||||
auto dest = m_Session->localDestination->CreateDatagramDestination ();
|
||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
SendSessionCreateReplyOk ();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
|
||||
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true);
|
||||
}
|
||||
|
||||
void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
if (m_Session->localDestination->IsReady ())
|
||||
SendSessionCreateReplyOk ();
|
||||
else
|
||||
{
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
|
||||
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::SendSessionCreateReplyOk ()
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
char priv[1024];
|
||||
size_t l = m_Session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
|
||||
priv[l1] = 0;
|
||||
#ifdef _MSC_VER
|
||||
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
||||
#else
|
||||
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
||||
#endif
|
||||
SendMessageReply (m_Buffer, l2, false);
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessStreamConnect (char * buf, size_t len)
|
||||
{
|
||||
LogPrint ("SAM stream connect: ", buf);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, len, params);
|
||||
std::string& id = params[SAM_PARAM_ID];
|
||||
std::string& destination = params[SAM_PARAM_DESTINATION];
|
||||
std::string& silent = params[SAM_PARAM_SILENT];
|
||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
||||
m_ID = id;
|
||||
m_Session = m_Owner.FindSession (id);
|
||||
if (m_Session)
|
||||
{
|
||||
i2p::data::IdentityEx dest;
|
||||
dest.FromBase64 (destination);
|
||||
context.GetAddressBook ().InsertAddress (dest);
|
||||
auto leaseSet = i2p::data::netdb.FindLeaseSet (dest.GetIdentHash ());
|
||||
if (leaseSet)
|
||||
Connect (*leaseSet);
|
||||
else
|
||||
{
|
||||
i2p::data::netdb.RequestDestination (dest.GetIdentHash (), true, m_Session->localDestination->GetTunnelPool ());
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_CONNECT_TIMEOUT));
|
||||
m_Timer.async_wait (std::bind (&SAMSocket::HandleStreamDestinationRequestTimer,
|
||||
shared_from_this (), std::placeholders::_1, dest.GetIdentHash ()));
|
||||
}
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
||||
}
|
||||
|
||||
void SAMSocket::Connect (const i2p::data::LeaseSet& remote)
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeStream;
|
||||
m_Session->sockets.push_back (shared_from_this ());
|
||||
m_Stream = m_Session->localDestination->CreateStream (remote);
|
||||
m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect
|
||||
I2PReceive ();
|
||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
||||
}
|
||||
|
||||
void SAMSocket::HandleStreamDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident)
|
||||
{
|
||||
if (!ecode) // timeout expired
|
||||
{
|
||||
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
Connect (*leaseSet);
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM destination to connect not found");
|
||||
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
|
||||
{
|
||||
LogPrint ("SAM stream accept: ", buf);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, len, params);
|
||||
std::string& id = params[SAM_PARAM_ID];
|
||||
std::string& silent = params[SAM_PARAM_SILENT];
|
||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
||||
m_ID = id;
|
||||
m_Session = m_Owner.FindSession (id);
|
||||
if (m_Session)
|
||||
{
|
||||
if (!m_Session->localDestination->IsAcceptingStreams ())
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeAcceptor;
|
||||
m_Session->sockets.push_back (shared_from_this ());
|
||||
m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
|
||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true);
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessDestGenerate ()
|
||||
{
|
||||
LogPrint ("SAM dest generate");
|
||||
auto localDestination = i2p::client::context.CreateNewLocalDestination ();
|
||||
if (localDestination)
|
||||
{
|
||||
auto priv = localDestination->GetPrivateKeys ().ToBase64 ();
|
||||
auto pub = localDestination->GetIdentity ().ToBase64 ();
|
||||
#ifdef _MSC_VER
|
||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, pub.c_str (), priv.c_str ());
|
||||
#else
|
||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, pub.c_str (), priv.c_str ());
|
||||
#endif
|
||||
SendMessageReply (m_Buffer, len, true);
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_DEST_REPLY_I2P_ERROR, strlen(SAM_DEST_REPLY_I2P_ERROR), true);
|
||||
}
|
||||
|
||||
void SAMSocket::ProcessNamingLookup (char * buf, size_t len)
|
||||
{
|
||||
LogPrint ("SAM naming lookup: ", buf);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, len, params);
|
||||
std::string& name = params[SAM_PARAM_NAME];
|
||||
i2p::data::IdentHash ident;
|
||||
i2p::data::IdentityEx identity;
|
||||
if (name == "ME")
|
||||
SendNamingLookupReply (nullptr);
|
||||
else if (context.GetAddressBook ().GetAddress (name, identity))
|
||||
SendNamingLookupReply (identity);
|
||||
else
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
||||
#else
|
||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY, name.c_str());
|
||||
#endif
|
||||
SendMessageReply (m_Buffer, len, false);
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::SendNamingLookupReply (const i2p::data::LeaseSet * leaseSet)
|
||||
{
|
||||
const i2p::data::IdentityEx& identity = leaseSet ? leaseSet->GetIdentity () : m_Session->localDestination->GetIdentity ();
|
||||
if (leaseSet)
|
||||
// we found LeaseSet for our address, store it to addressbook
|
||||
context.GetAddressBook ().InsertAddress (identity);
|
||||
SendNamingLookupReply (identity);
|
||||
}
|
||||
|
||||
void SAMSocket::SendNamingLookupReply (const i2p::data::IdentityEx& identity)
|
||||
{
|
||||
auto base64 = identity.ToBase64 ();
|
||||
#ifdef _MSC_VER
|
||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
|
||||
#else
|
||||
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
|
||||
#endif
|
||||
SendMessageReply (m_Buffer, l, false);
|
||||
}
|
||||
|
||||
void SAMSocket::ExtractParams (char * buf, size_t len, std::map<std::string, std::string>& params)
|
||||
{
|
||||
char * separator;
|
||||
do
|
||||
{
|
||||
separator = strchr (buf, ' ');
|
||||
if (separator) *separator = 0;
|
||||
char * value = strchr (buf, '=');
|
||||
if (value)
|
||||
{
|
||||
*value = 0;
|
||||
value++;
|
||||
params[buf] = value;
|
||||
}
|
||||
buf = separator + 1;
|
||||
}
|
||||
while (separator);
|
||||
}
|
||||
|
||||
void SAMSocket::Receive ()
|
||||
{
|
||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
||||
std::bind((m_SocketType == eSAMSocketTypeSession) ? &SAMSocket::HandleMessage : &SAMSocket::HandleReceived,
|
||||
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Stream)
|
||||
m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred);
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::I2PReceive ()
|
||||
{
|
||||
if (m_Stream)
|
||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
|
||||
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2),
|
||||
SAM_SOCKET_CONNECTION_MAX_IDLE);
|
||||
}
|
||||
|
||||
void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM stream read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
|
||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("SAM socket write error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
I2PReceive ();
|
||||
}
|
||||
|
||||
void SAMSocket::HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
LogPrint ("SAM incoming I2P connection for session ", m_ID);
|
||||
m_Stream = stream;
|
||||
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
|
||||
auto session = m_Owner.FindSession (m_ID);
|
||||
if (session)
|
||||
session->localDestination->StopAcceptingStreams ();
|
||||
if (!m_IsSilent)
|
||||
{
|
||||
// send remote peer address
|
||||
uint8_t ident[1024];
|
||||
size_t l = stream->GetRemoteIdentity ().ToBuffer (ident, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, m_Buffer, SAM_SOCKET_BUFFER_SIZE);
|
||||
m_Buffer[l1] = '\n';
|
||||
SendMessageReply (m_Buffer, l1 + 1, false);
|
||||
}
|
||||
I2PReceive ();
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& ident, const uint8_t * buf, size_t len)
|
||||
{
|
||||
auto base64 = ident.ToBase64 ();
|
||||
#ifdef _MSC_VER
|
||||
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
|
||||
#else
|
||||
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
|
||||
#endif
|
||||
if (len < SAM_SOCKET_BUFFER_SIZE - l)
|
||||
{
|
||||
memcpy (m_StreamBuffer + l, buf, len);
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l),
|
||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram size ", len," exceeds buffer");
|
||||
}
|
||||
|
||||
SAMBridge::SAMBridge (int port):
|
||||
m_IsRunning (false), m_Thread (nullptr),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
|
||||
m_DatagramEndpoint (boost::asio::ip::udp::v4 (), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
|
||||
{
|
||||
}
|
||||
|
||||
SAMBridge::~SAMBridge ()
|
||||
{
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void SAMBridge::Start ()
|
||||
{
|
||||
Accept ();
|
||||
ReceiveDatagram ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&SAMBridge::Run, this));
|
||||
}
|
||||
|
||||
void SAMBridge::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SAMBridge::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("SAM: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMBridge::Accept ()
|
||||
{
|
||||
auto newSocket = std::make_shared<SAMSocket> (*this);
|
||||
m_Acceptor.async_accept (newSocket->GetSocket (), std::bind (&SAMBridge::HandleAccept, this,
|
||||
std::placeholders::_1, newSocket));
|
||||
}
|
||||
|
||||
void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
LogPrint ("New SAM connection from ", socket->GetSocket ().remote_endpoint ());
|
||||
socket->ReceiveHandshake ();
|
||||
}
|
||||
else
|
||||
LogPrint ("SAM accept error: ", ecode.message ());
|
||||
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Accept ();
|
||||
}
|
||||
|
||||
SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
ClientDestination * localDestination = nullptr;
|
||||
if (destination != "")
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
keys.FromBase64 (destination);
|
||||
localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params);
|
||||
}
|
||||
else // transient
|
||||
{
|
||||
// extract signature type
|
||||
i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
|
||||
if (params)
|
||||
{
|
||||
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
|
||||
if (it != params->end ())
|
||||
// TODO: extract string values
|
||||
signatureType = boost::lexical_cast<int> (it->second);
|
||||
}
|
||||
localDestination = i2p::client::context.CreateNewLocalDestination (false, signatureType, params);
|
||||
}
|
||||
if (localDestination)
|
||||
{
|
||||
SAMSession session;
|
||||
session.localDestination = localDestination;
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession>(id, session));
|
||||
if (!ret.second)
|
||||
LogPrint ("Session ", id, " already exists");
|
||||
return &(ret.first->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SAMBridge::CloseSession (const std::string& id)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (id);
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
for (auto it1: it->second.sockets)
|
||||
it1->CloseStream ();
|
||||
it->second.sockets.clear ();
|
||||
i2p::client::context.DeleteLocalDestination (it->second.localDestination);
|
||||
m_Sessions.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
SAMSession * SAMBridge::FindSession (const std::string& id)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (id);
|
||||
if (it != m_Sessions.end ())
|
||||
return &it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SAMBridge::ReceiveDatagram ()
|
||||
{
|
||||
m_DatagramSocket.async_receive_from (
|
||||
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
|
||||
m_SenderEndpoint,
|
||||
std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
m_DatagramReceiveBuffer[bytes_transferred] = 0;
|
||||
char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n');
|
||||
*eol = 0; eol++;
|
||||
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
|
||||
LogPrint ("SAM datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
|
||||
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
|
||||
if (sessionID)
|
||||
{
|
||||
sessionID++;
|
||||
char * destination = strchr (sessionID, ' ');
|
||||
if (destination)
|
||||
{
|
||||
*destination = 0; destination++;
|
||||
auto session = FindSession (sessionID);
|
||||
if (session)
|
||||
{
|
||||
i2p::data::IdentityEx dest;
|
||||
dest.FromBase64 (destination);
|
||||
auto leaseSet = i2p::data::netdb.FindLeaseSet (dest.GetIdentHash ());
|
||||
if (leaseSet)
|
||||
session->localDestination->GetDatagramDestination ()->
|
||||
SendDatagramTo ((uint8_t *)eol, payloadLen, *leaseSet);
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM datagram destination not found");
|
||||
i2p::data::netdb.RequestDestination (dest.GetIdentHash (), true,
|
||||
session->localDestination->GetTunnelPool ());
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("Session ", sessionID, " not found");
|
||||
}
|
||||
else
|
||||
LogPrint ("Missing destination key");
|
||||
}
|
||||
else
|
||||
LogPrint ("Missing sessionID");
|
||||
ReceiveDatagram ();
|
||||
}
|
||||
else
|
||||
LogPrint ("SAM datagram receive error: ", ecode.message ());
|
||||
}
|
||||
}
|
||||
}
|
||||
283
SOCKS.cpp
283
SOCKS.cpp
@@ -1,283 +0,0 @@
|
||||
#include "SOCKS.h"
|
||||
#include "Identity.h"
|
||||
#include "NetDb.h"
|
||||
#include "Destination.h"
|
||||
#include "ClientContext.h"
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace proxy
|
||||
{
|
||||
const uint8_t socks_leaseset_timeout = 10;
|
||||
const uint8_t socks_timeout = 60;
|
||||
|
||||
void SOCKS4AHandler::AsyncSockRead()
|
||||
{
|
||||
LogPrint("--- socks4a async sock read");
|
||||
if(m_sock) {
|
||||
if (m_state == INITIAL) {
|
||||
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
|
||||
boost::bind(&SOCKS4AHandler::HandleSockRecv, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
} else {
|
||||
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
|
||||
boost::bind(&SOCKS4AHandler::HandleSockForward, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
} else {
|
||||
LogPrint("--- socks4a no socket for read");
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::AsyncStreamRead()
|
||||
{
|
||||
|
||||
LogPrint("--- socks4a async stream read");
|
||||
if (m_stream) {
|
||||
m_stream->AsyncReceive(
|
||||
boost::asio::buffer(m_stream_buff, socks_buffer_size),
|
||||
boost::bind(&SOCKS4AHandler::HandleStreamRecv, this,
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred), socks_timeout);
|
||||
} else {
|
||||
LogPrint("--- socks4a no stream for read");
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::Terminate() {
|
||||
CloseStream();
|
||||
CloseSock();
|
||||
delete this; // ew
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::SocksFailed()
|
||||
{
|
||||
LogPrint("--- socks4a failed");
|
||||
m_sock->send(boost::asio::buffer("\x00\x5b 12345"));
|
||||
Terminate();
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::CloseSock()
|
||||
{
|
||||
if (m_sock) {
|
||||
LogPrint("--- socks4a close sock");
|
||||
m_sock->close();
|
||||
delete m_sock;
|
||||
m_sock = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::CloseStream()
|
||||
{
|
||||
if (m_stream) {
|
||||
LogPrint("--- socks4a close stream");
|
||||
m_stream.reset ();
|
||||
}
|
||||
}
|
||||
|
||||
const size_t socks_hostname_size = 1024;
|
||||
const size_t socks_ident_size = 1024;
|
||||
const size_t destb32_len = 52;
|
||||
|
||||
void SOCKS4AHandler::HandleSockForward(const boost::system::error_code & ecode, std::size_t len)
|
||||
{
|
||||
if(ecode) {
|
||||
LogPrint("--- socks4a forward got error: ", ecode);
|
||||
Terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
LogPrint("--- socks4a sock forward: ", len);
|
||||
m_stream->Send(m_sock_buff, len);
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
|
||||
{
|
||||
LogPrint("--- socks4a sock recv: ", len);
|
||||
|
||||
if(ecode) {
|
||||
LogPrint(" --- sock recv got error: ", ecode);
|
||||
Terminate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_state == INITIAL) {
|
||||
|
||||
char hostbuff[socks_hostname_size];
|
||||
char identbuff[socks_ident_size];
|
||||
std::memset(hostbuff, 0, sizeof(hostbuff));
|
||||
std::memset(identbuff, 0, sizeof(hostbuff));
|
||||
std::string dest;
|
||||
// get port
|
||||
uint16_t port = 0;
|
||||
uint16_t idx1 = 0;
|
||||
uint16_t idx2 = 0;
|
||||
|
||||
LogPrint("--- socks4a state initial ", len);
|
||||
|
||||
// check valid request
|
||||
if( m_sock_buff[0] != 4 || m_sock_buff[1] != 1 || m_sock_buff[len-1] ) {
|
||||
LogPrint("--- socks4a rejected invalid");
|
||||
SocksFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
// get port
|
||||
port = m_sock_buff[3] | m_sock_buff[2] << 8;
|
||||
|
||||
// read ident
|
||||
do {
|
||||
LogPrint("--- socks4a ", (int) m_sock_buff[9+idx1]);
|
||||
identbuff[idx1] = m_sock_buff[8+idx1];
|
||||
} while( identbuff[idx1++] && idx1 < socks_ident_size );
|
||||
|
||||
LogPrint("--- socks4a ident ", identbuff);
|
||||
// read hostname
|
||||
do {
|
||||
hostbuff[idx2] = m_sock_buff[8+idx1+idx2];
|
||||
} while( hostbuff[idx2++] && idx2 < socks_hostname_size );
|
||||
|
||||
LogPrint("--- socks4a requested ", hostbuff, ":" , port);
|
||||
|
||||
dest = std::string(hostbuff);
|
||||
if(dest.find(".b32.i2p") == std::string::npos) {
|
||||
LogPrint("--- socks4a invalid hostname: ", dest);
|
||||
SocksFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( i2p::data::Base32ToByteStream(hostbuff, destb32_len, (uint8_t *) m_dest, 32) != 32 ) {
|
||||
LogPrint("--- sock4a invalid b32: ", dest);
|
||||
}
|
||||
|
||||
LogPrint("--- sock4a find lease set");
|
||||
m_ls = i2p::data::netdb.FindLeaseSet(m_dest);
|
||||
if (!m_ls || m_ls->HasNonExpiredLeases()) {
|
||||
i2p::data::netdb.RequestDestination (m_dest, true, i2p::client::context.GetSharedLocalDestination ()->GetTunnelPool ());
|
||||
m_ls_timer.expires_from_now(boost::posix_time::seconds(socks_leaseset_timeout));
|
||||
m_ls_timer.async_wait(boost::bind(&SOCKS4AHandler::LeaseSetTimeout, this, boost::asio::placeholders::error));
|
||||
} else {
|
||||
ConnectionSuccess();
|
||||
}
|
||||
} else {
|
||||
LogPrint("--- socks4a state?? ", m_state);
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::HandleStreamRecv(const boost::system::error_code & ecode, std::size_t len)
|
||||
{
|
||||
if(ecode) { LogPrint("--- socks4a stream recv error: ", ecode); m_state = END; }
|
||||
switch(m_state) {
|
||||
case INITIAL:
|
||||
case END:
|
||||
Terminate();
|
||||
return;
|
||||
case OKAY:
|
||||
LogPrint("--- socks4a stream recv ", len);
|
||||
boost::asio::async_write(*m_sock, boost::asio::buffer(m_stream_buff, len),
|
||||
boost::bind(&SOCKS4AHandler::StreamWrote, this,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::SockWrote(const boost::system::error_code & ecode)
|
||||
{
|
||||
LogPrint("--- socks4a sock wrote");
|
||||
if(ecode) { LogPrint("--- socks4a SockWrote error: ",ecode); }
|
||||
else { AsyncSockRead(); }
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::StreamWrote(const boost::system::error_code & ecode)
|
||||
{
|
||||
|
||||
LogPrint("--- socks4a stream wrote");
|
||||
if(ecode) { LogPrint("--- socks4a StreamWrote error: ",ecode); }
|
||||
else { AsyncStreamRead(); }
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::LeaseSetTimeout(const boost::system::error_code & ecode)
|
||||
{
|
||||
m_ls = i2p::data::netdb.FindLeaseSet(m_dest);
|
||||
if(m_ls) {
|
||||
ConnectionSuccess();
|
||||
} else {
|
||||
LogPrint("--- socks4a ls timeout");
|
||||
SocksFailed();
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::ConnectionSuccess()
|
||||
{
|
||||
LogPrint("--- socks4a connection success");
|
||||
boost::asio::async_write(*m_sock, boost::asio::buffer("\x00\x5a 12345"),
|
||||
boost::bind(&SOCKS4AHandler::SentConnectionSuccess, this,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void SOCKS4AHandler::SentConnectionSuccess(const boost::system::error_code & ecode)
|
||||
{
|
||||
LogPrint("--- socks4a making connection");
|
||||
m_stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream(*m_ls);
|
||||
m_state = OKAY;
|
||||
LogPrint("--- socks4a state is ", m_state);
|
||||
AsyncSockRead();
|
||||
AsyncStreamRead();
|
||||
}
|
||||
|
||||
void SOCKS4AServer::Run()
|
||||
{
|
||||
LogPrint("--- socks4a run");
|
||||
m_run = true;
|
||||
while(m_run) {
|
||||
try {
|
||||
m_ios.run();
|
||||
} catch (std::runtime_error & exc) {
|
||||
LogPrint("--- socks4a exception: ", exc.what());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKS4AServer::Accept()
|
||||
{
|
||||
m_new_sock = new boost::asio::ip::tcp::socket(m_ios);
|
||||
m_acceptor.async_accept(*m_new_sock,
|
||||
boost::bind(
|
||||
&SOCKS4AServer::HandleAccept, this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void SOCKS4AServer::Start()
|
||||
{
|
||||
m_run = true;
|
||||
m_thread = new std::thread(std::bind(&SOCKS4AServer::Run, this));
|
||||
m_acceptor.listen();
|
||||
Accept();
|
||||
}
|
||||
|
||||
void SOCKS4AServer::Stop()
|
||||
{
|
||||
m_acceptor.close();
|
||||
m_run = false;
|
||||
m_ios.stop();
|
||||
if (m_thread) {
|
||||
m_thread->join();
|
||||
delete m_thread;
|
||||
m_thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SOCKS4AServer::HandleAccept(const boost::system::error_code & ecode)
|
||||
{
|
||||
if (!ecode) {
|
||||
LogPrint("--- socks4a accepted");
|
||||
new SOCKS4AHandler(&m_ios, m_new_sock);
|
||||
Accept();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
99
SOCKS.h
99
SOCKS.h
@@ -1,99 +0,0 @@
|
||||
#ifndef SOCKS4A_H__
|
||||
#define SOCKS4A_H__
|
||||
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include "Identity.h"
|
||||
#include "Streaming.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace proxy
|
||||
{
|
||||
|
||||
const size_t socks_buffer_size = 8192;
|
||||
|
||||
class SOCKS4AHandler {
|
||||
|
||||
private:
|
||||
enum state {
|
||||
INITIAL,
|
||||
OKAY,
|
||||
END
|
||||
};
|
||||
|
||||
void GotClientRequest(boost::system::error_code & ecode, std::string & host, uint16_t port);
|
||||
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||
void HandleSockForward(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||
void HandleStreamRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
||||
void Terminate();
|
||||
void CloseSock();
|
||||
void CloseStream();
|
||||
void AsyncSockRead();
|
||||
void AsyncStreamRead();
|
||||
void SocksFailed();
|
||||
void LeaseSetTimeout(const boost::system::error_code & ecode);
|
||||
void StreamWrote(const boost::system::error_code & ecode);
|
||||
void SockWrote(const boost::system::error_code & ecode);
|
||||
void SentConnectionSuccess(const boost::system::error_code & ecode);
|
||||
void ConnectionSuccess();
|
||||
|
||||
uint8_t m_sock_buff[socks_buffer_size];
|
||||
uint8_t m_stream_buff[socks_buffer_size];
|
||||
|
||||
boost::asio::io_service * m_ios;
|
||||
boost::asio::ip::tcp::socket * m_sock;
|
||||
boost::asio::deadline_timer m_ls_timer;
|
||||
std::shared_ptr<i2p::stream::Stream> m_stream;
|
||||
i2p::data::LeaseSet * m_ls;
|
||||
i2p::data::IdentHash m_dest;
|
||||
state m_state;
|
||||
|
||||
|
||||
public:
|
||||
SOCKS4AHandler(boost::asio::io_service * ios, boost::asio::ip::tcp::socket * sock) :
|
||||
m_ios(ios), m_sock(sock), m_ls_timer(*ios),
|
||||
m_stream(nullptr), m_ls(nullptr), m_state(INITIAL) { AsyncSockRead(); }
|
||||
|
||||
~SOCKS4AHandler() { CloseSock(); CloseStream(); }
|
||||
bool isComplete() { return m_state == END; }
|
||||
};
|
||||
|
||||
class SOCKS4AServer {
|
||||
public:
|
||||
SOCKS4AServer(int port) : m_run(false),
|
||||
m_thread(nullptr),
|
||||
m_work(m_ios),
|
||||
m_acceptor(m_ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
|
||||
m_new_sock(nullptr) { }
|
||||
~SOCKS4AServer() { Stop(); }
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_ios; };
|
||||
|
||||
private:
|
||||
|
||||
void Run();
|
||||
void Accept();
|
||||
void HandleAccept(const boost::system::error_code& ecode);
|
||||
|
||||
bool m_run;
|
||||
std::thread * m_thread;
|
||||
boost::asio::io_service m_ios;
|
||||
boost::asio::io_service::work m_work;
|
||||
boost::asio::ip::tcp::acceptor m_acceptor;
|
||||
boost::asio::ip::tcp::socket * m_new_sock;
|
||||
|
||||
|
||||
};
|
||||
|
||||
typedef SOCKS4AServer SOCKSProxy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
386
SSU.cpp
386
SSU.cpp
@@ -1,386 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <boost/bind.hpp>
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
#include "SSU.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_Work (m_Service),
|
||||
m_WorkV6 (m_ServiceV6),m_Endpoint (boost::asio::ip::udp::v4 (), port),
|
||||
m_EndpointV6 (boost::asio::ip::udp::v6 (), port), m_Socket (m_Service, m_Endpoint),
|
||||
m_SocketV6 (m_ServiceV6), m_IntroducersUpdateTimer (m_Service)
|
||||
{
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
m_SocketV6.open (boost::asio::ip::udp::v6());
|
||||
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
m_SocketV6.bind (m_EndpointV6);
|
||||
}
|
||||
}
|
||||
|
||||
SSUServer::~SSUServer ()
|
||||
{
|
||||
}
|
||||
|
||||
void SSUServer::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
||||
m_Service.post (std::bind (&SSUServer::Receive, this));
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this));
|
||||
m_ServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
}
|
||||
if (i2p::context.IsUnreachable ())
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
}
|
||||
|
||||
void SSUServer::Stop ()
|
||||
{
|
||||
DeleteAllSessions ();
|
||||
m_IsRunning = false;
|
||||
m_Service.stop ();
|
||||
m_Socket.close ();
|
||||
m_ServiceV6.stop ();
|
||||
m_SocketV6.close ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
if (m_ThreadV6)
|
||||
{
|
||||
m_ThreadV6->join ();
|
||||
delete m_ThreadV6;
|
||||
m_ThreadV6 = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU server: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::RunV6 ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_ServiceV6.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU V6 server: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay)
|
||||
{
|
||||
m_Relays[tag] = relay;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindRelaySession (uint32_t tag)
|
||||
{
|
||||
auto it = m_Relays.find (tag);
|
||||
if (it != m_Relays.end ())
|
||||
return FindSession (it->second);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
|
||||
{
|
||||
if (to.protocol () == boost::asio::ip::udp::v4())
|
||||
m_Socket.send_to (boost::asio::buffer (buf, len), to);
|
||||
else
|
||||
m_SocketV6.send_to (boost::asio::buffer (buf, len), to);
|
||||
}
|
||||
|
||||
void SSUServer::Receive ()
|
||||
{
|
||||
m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, SSU_MTU_V4), m_SenderEndpoint,
|
||||
boost::bind (&SSUServer::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void SSUServer::ReceiveV6 ()
|
||||
{
|
||||
m_SocketV6.async_receive_from (boost::asio::buffer (m_ReceiveBufferV6, SSU_MTU_V6), m_SenderEndpointV6,
|
||||
boost::bind (&SSUServer::HandleReceivedFromV6, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
HandleReceivedBuffer (m_SenderEndpoint, m_ReceiveBuffer, bytes_transferred);
|
||||
Receive ();
|
||||
}
|
||||
else
|
||||
LogPrint ("SSU receive error: ", ecode.message ());
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
HandleReceivedBuffer (m_SenderEndpointV6, m_ReceiveBufferV6, bytes_transferred);
|
||||
ReceiveV6 ();
|
||||
}
|
||||
else
|
||||
LogPrint ("SSU V6 receive error: ", ecode.message ());
|
||||
}
|
||||
|
||||
void SSUServer::HandleReceivedBuffer (boost::asio::ip::udp::endpoint& from, uint8_t * buf, std::size_t bytes_transferred)
|
||||
{
|
||||
std::shared_ptr<SSUSession> session;
|
||||
auto it = m_Sessions.find (from);
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
if (!session)
|
||||
{
|
||||
session = std::make_shared<SSUSession> (*this, from);
|
||||
session->WaitForConnect ();
|
||||
m_Sessions[from] = session;
|
||||
LogPrint ("New SSU session from ", from.address ().to_string (), ":", from.port (), " created");
|
||||
}
|
||||
session->ProcessNextMessage (buf, bytes_transferred, from);
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const
|
||||
{
|
||||
if (!router) return nullptr;
|
||||
auto address = router->GetSSUAddress (true); // v4 only
|
||||
if (!address) return nullptr;
|
||||
auto session = FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
||||
if (session || !context.SupportsV6 ())
|
||||
return session;
|
||||
// try v6
|
||||
address = router->GetSSUV6Address ();
|
||||
if (!address) return nullptr;
|
||||
return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
|
||||
{
|
||||
auto it = m_Sessions.find (e);
|
||||
if (it != m_Sessions.end ())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
|
||||
{
|
||||
std::shared_ptr<SSUSession> session;
|
||||
if (router)
|
||||
{
|
||||
auto address = router->GetSSUAddress (!context.SupportsV6 ());
|
||||
if (address)
|
||||
{
|
||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
||||
auto it = m_Sessions.find (remoteEndpoint);
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
else
|
||||
{
|
||||
// otherwise create new session
|
||||
session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
||||
m_Sessions[remoteEndpoint] = session;
|
||||
|
||||
if (!router->UsesIntroducer ())
|
||||
{
|
||||
// connect directly
|
||||
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ",
|
||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
|
||||
session->Connect ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// connect through introducer
|
||||
int numIntroducers = address->introducers.size ();
|
||||
if (numIntroducers > 0)
|
||||
{
|
||||
std::shared_ptr<SSUSession> introducerSession;
|
||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
||||
// we might have a session to introducer already
|
||||
for (int i = 0; i < numIntroducers; i++)
|
||||
{
|
||||
introducer = &(address->introducers[i]);
|
||||
it = m_Sessions.find (boost::asio::ip::udp::endpoint (introducer->iHost, introducer->iPort));
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
introducerSession = it->second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (introducerSession) // session found
|
||||
LogPrint ("Session to introducer already exists");
|
||||
else // create new
|
||||
{
|
||||
LogPrint ("Creating new session to introducer");
|
||||
introducer = &(address->introducers[0]); // TODO:
|
||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
|
||||
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
|
||||
m_Sessions[introducerEndpoint] = introducerSession;
|
||||
}
|
||||
// introduce
|
||||
LogPrint ("Introduce new SSU session to [", router->GetIdentHashAbbreviation (),
|
||||
"] through introducer ", introducer->iHost, ":", introducer->iPort);
|
||||
session->WaitForIntroduction ();
|
||||
if (i2p::context.GetRouterInfo ().UsesIntroducer ()) // if we are unreachable
|
||||
Send (m_ReceiveBuffer, 0, remoteEndpoint); // send HolePunch
|
||||
introducerSession->Introduce (introducer->iTag, introducer->iKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Can't connect to unreachable router. No introducers presented");
|
||||
m_Sessions.erase (remoteEndpoint);
|
||||
session.reset ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address");
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->Close ();
|
||||
m_Sessions.erase (session->GetRemoteEndpoint ());
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::DeleteAllSessions ()
|
||||
{
|
||||
for (auto it: m_Sessions)
|
||||
it.second->Close ();
|
||||
m_Sessions.clear ();
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomSession (Filter filter)
|
||||
{
|
||||
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
|
||||
for (auto s :m_Sessions)
|
||||
if (filter (s.second)) filteredSessions.push_back (s.second);
|
||||
if (filteredSessions.size () > 0)
|
||||
{
|
||||
auto ind = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, filteredSessions.size ()-1);
|
||||
return filteredSessions[ind];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded)
|
||||
{
|
||||
return GetRandomSession (
|
||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetState () == eSessionStateEstablished &&
|
||||
session != excluded;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
std::set<SSUSession *> SSUServer::FindIntroducers (int maxNumIntroducers)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
std::set<SSUSession *> ret;
|
||||
for (int i = 0; i < maxNumIntroducers; i++)
|
||||
{
|
||||
auto session = GetRandomSession (
|
||||
[&ret, ts](std::shared_ptr<SSUSession> session)->bool
|
||||
{
|
||||
return session->GetRelayTag () && !ret.count (session.get ()) &&
|
||||
session->GetState () == eSessionStateEstablished &&
|
||||
ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION;
|
||||
}
|
||||
);
|
||||
if (session)
|
||||
{
|
||||
ret.insert (session.get ());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleIntroducersUpdateTimer ()
|
||||
{
|
||||
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
||||
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
// timeout expired
|
||||
std::list<boost::asio::ip::udp::endpoint> newList;
|
||||
size_t numIntroducers = 0;
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it :m_Introducers)
|
||||
{
|
||||
auto session = FindSession (it);
|
||||
if (session && ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION)
|
||||
{
|
||||
session->SendKeepAlive ();
|
||||
newList.push_back (it);
|
||||
numIntroducers++;
|
||||
}
|
||||
else
|
||||
i2p::context.RemoveIntroducer (it);
|
||||
}
|
||||
|
||||
if (numIntroducers < SSU_MAX_NUM_INTRODUCERS)
|
||||
{
|
||||
// create new
|
||||
auto introducers = FindIntroducers (SSU_MAX_NUM_INTRODUCERS);
|
||||
if (introducers.size () > 0)
|
||||
{
|
||||
for (auto it1: introducers)
|
||||
{
|
||||
auto router = it1->GetRemoteRouter ();
|
||||
if (router && i2p::context.AddIntroducer (*router, it1->GetRelayTag ()))
|
||||
{
|
||||
newList.push_back (it1->GetRemoteEndpoint ());
|
||||
if (newList.size () >= SSU_MAX_NUM_INTRODUCERS) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_Introducers = newList;
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
88
SSU.h
88
SSU.h
@@ -1,88 +0,0 @@
|
||||
#ifndef SSU_H__
|
||||
#define SSU_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
#include "aes.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "SSUSession.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
const int SSU_KEEP_ALIVE_INTERVAL = 30; // 30 seconds
|
||||
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
|
||||
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
|
||||
|
||||
class SSUServer
|
||||
{
|
||||
public:
|
||||
|
||||
SSUServer (int port);
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
std::shared_ptr<SSUSession> GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
|
||||
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
|
||||
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
|
||||
std::shared_ptr<SSUSession> GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded);
|
||||
void DeleteSession (std::shared_ptr<SSUSession> session);
|
||||
void DeleteAllSessions ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Socket.get_io_service(); };
|
||||
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
|
||||
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
|
||||
void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay);
|
||||
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void RunV6 ();
|
||||
void Receive ();
|
||||
void ReceiveV6 ();
|
||||
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleReceivedBuffer (boost::asio::ip::udp::endpoint& from, uint8_t * buf, std::size_t bytes_transferred);
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<SSUSession> GetRandomSession (Filter filter);
|
||||
|
||||
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
|
||||
void ScheduleIntroducersUpdateTimer ();
|
||||
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread, * m_ThreadV6;
|
||||
boost::asio::io_service m_Service, m_ServiceV6;
|
||||
boost::asio::io_service::work m_Work, m_WorkV6;
|
||||
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
|
||||
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
|
||||
boost::asio::ip::udp::endpoint m_SenderEndpoint, m_SenderEndpointV6;
|
||||
boost::asio::deadline_timer m_IntroducersUpdateTimer;
|
||||
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
|
||||
i2p::crypto::AESAlignedBuffer<2*SSU_MTU_V4> m_ReceiveBuffer;
|
||||
i2p::crypto::AESAlignedBuffer<2*SSU_MTU_V6> m_ReceiveBufferV6;
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions;
|
||||
std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer
|
||||
|
||||
public:
|
||||
// for HTTP only
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
430
SSUData.cpp
430
SSUData.cpp
@@ -1,430 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <boost/bind.hpp>
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "NetDb.h"
|
||||
#include "SSU.h"
|
||||
#include "SSUData.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
SSUData::SSUData (SSUSession& session):
|
||||
m_Session (session), m_ResendTimer (session.m_Server.GetService ())
|
||||
{
|
||||
m_MaxPacketSize = session.IsV6 () ? SSU_V6_MAX_PACKET_SIZE : SSU_V4_MAX_PACKET_SIZE;
|
||||
m_PacketSize = m_MaxPacketSize;
|
||||
auto remoteRouter = session.GetRemoteRouter ();
|
||||
if (remoteRouter)
|
||||
AdjustPacketSize (*remoteRouter);
|
||||
}
|
||||
|
||||
SSUData::~SSUData ()
|
||||
{
|
||||
for (auto it: m_IncomleteMessages)
|
||||
if (it.second)
|
||||
{
|
||||
DeleteI2NPMessage (it.second->msg);
|
||||
delete it.second;
|
||||
}
|
||||
for (auto it: m_SentMessages)
|
||||
delete it.second;
|
||||
}
|
||||
|
||||
void SSUData::AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter)
|
||||
{
|
||||
auto ssuAddress = remoteRouter.GetSSUAddress ();
|
||||
if (ssuAddress && ssuAddress->mtu)
|
||||
{
|
||||
if (m_Session.IsV6 ())
|
||||
m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
else
|
||||
m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
if (m_PacketSize > 0)
|
||||
{
|
||||
// make sure packet size multiple of 16
|
||||
m_PacketSize >>= 4;
|
||||
m_PacketSize <<= 4;
|
||||
if (m_PacketSize > m_MaxPacketSize) m_PacketSize = m_MaxPacketSize;
|
||||
LogPrint ("MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Unexpected MTU ", ssuAddress->mtu);
|
||||
m_PacketSize = m_MaxPacketSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::UpdatePacketSize (const i2p::data::IdentHash& remoteIdent)
|
||||
{
|
||||
auto routerInfo = i2p::data::netdb.FindRouter (remoteIdent);
|
||||
if (routerInfo)
|
||||
AdjustPacketSize (*routerInfo);
|
||||
}
|
||||
|
||||
void SSUData::ProcessSentMessageAck (uint32_t msgID)
|
||||
{
|
||||
auto it = m_SentMessages.find (msgID);
|
||||
if (it != m_SentMessages.end ())
|
||||
{
|
||||
delete it->second;
|
||||
m_SentMessages.erase (it);
|
||||
if (m_SentMessages.empty ())
|
||||
m_ResendTimer.cancel ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::ProcessAcks (uint8_t *& buf, uint8_t flag)
|
||||
{
|
||||
if (flag & DATA_FLAG_EXPLICIT_ACKS_INCLUDED)
|
||||
{
|
||||
// explicit ACKs
|
||||
uint8_t numAcks =*buf;
|
||||
buf++;
|
||||
for (int i = 0; i < numAcks; i++)
|
||||
ProcessSentMessageAck (be32toh (((uint32_t *)buf)[i]));
|
||||
buf += numAcks*4;
|
||||
}
|
||||
if (flag & DATA_FLAG_ACK_BITFIELDS_INCLUDED)
|
||||
{
|
||||
// explicit ACK bitfields
|
||||
uint8_t numBitfields =*buf;
|
||||
buf++;
|
||||
for (int i = 0; i < numBitfields; i++)
|
||||
{
|
||||
uint32_t msgID = be32toh (*(uint32_t *)buf);
|
||||
buf += 4; // msgID
|
||||
auto it = m_SentMessages.find (msgID);
|
||||
// process individual Ack bitfields
|
||||
bool isNonLast = false;
|
||||
int fragment = 0;
|
||||
do
|
||||
{
|
||||
uint8_t bitfield = *buf;
|
||||
isNonLast = bitfield & 0x80;
|
||||
bitfield &= 0x7F; // clear MSB
|
||||
if (bitfield && it != m_SentMessages.end ())
|
||||
{
|
||||
int numSentFragments = it->second->fragments.size ();
|
||||
// process bits
|
||||
uint8_t mask = 0x01;
|
||||
for (int j = 0; j < 7; j++)
|
||||
{
|
||||
if (bitfield & mask)
|
||||
{
|
||||
if (fragment < numSentFragments)
|
||||
{
|
||||
delete it->second->fragments[fragment];
|
||||
it->second->fragments[fragment] = nullptr;
|
||||
}
|
||||
}
|
||||
fragment++;
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
buf++;
|
||||
}
|
||||
while (isNonLast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::ProcessFragments (uint8_t * buf)
|
||||
{
|
||||
uint8_t numFragments = *buf; // number of fragments
|
||||
buf++;
|
||||
for (int i = 0; i < numFragments; i++)
|
||||
{
|
||||
uint32_t msgID = be32toh (*(uint32_t *)buf); // message ID
|
||||
buf += 4;
|
||||
uint8_t frag[4];
|
||||
frag[0] = 0;
|
||||
memcpy (frag + 1, buf, 3);
|
||||
buf += 3;
|
||||
uint32_t fragmentInfo = be32toh (*(uint32_t *)frag); // fragment info
|
||||
uint16_t fragmentSize = fragmentInfo & 0x1FFF; // bits 0 - 13
|
||||
bool isLast = fragmentInfo & 0x010000; // bit 16
|
||||
uint8_t fragmentNum = fragmentInfo >> 17; // bits 23 - 17
|
||||
LogPrint (eLogDebug, "SSU data fragment ", (int)fragmentNum, " of message ", msgID, " size=", (int)fragmentSize, isLast ? " last" : " non-last");
|
||||
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
|
||||
{
|
||||
LogPrint (eLogError, "Fragment size ", fragmentSize, "exceeds max SSU packet size");
|
||||
return;
|
||||
}
|
||||
|
||||
// find message with msgID
|
||||
I2NPMessage * msg = nullptr;
|
||||
IncompleteMessage * incompleteMessage = nullptr;
|
||||
auto it = m_IncomleteMessages.find (msgID);
|
||||
if (it != m_IncomleteMessages.end ())
|
||||
{
|
||||
// message exists
|
||||
incompleteMessage = it->second;
|
||||
msg = incompleteMessage->msg;
|
||||
}
|
||||
else
|
||||
{
|
||||
// create new message
|
||||
msg = NewI2NPMessage ();
|
||||
msg->len -= sizeof (I2NPHeaderShort);
|
||||
incompleteMessage = new IncompleteMessage (msg);
|
||||
m_IncomleteMessages[msgID] = incompleteMessage;
|
||||
}
|
||||
|
||||
// handle current fragment
|
||||
if (fragmentNum == incompleteMessage->nextFragmentNum)
|
||||
{
|
||||
// expected fragment
|
||||
memcpy (msg->buf + msg->len, buf, fragmentSize);
|
||||
msg->len += fragmentSize;
|
||||
incompleteMessage->nextFragmentNum++;
|
||||
if (!isLast && !incompleteMessage->savedFragments.empty ())
|
||||
{
|
||||
// try saved fragments
|
||||
for (auto it1 = incompleteMessage->savedFragments.begin (); it1 != incompleteMessage->savedFragments.end ();)
|
||||
{
|
||||
auto savedFragment = *it1;
|
||||
if (savedFragment->fragmentNum == incompleteMessage->nextFragmentNum)
|
||||
{
|
||||
memcpy (msg->buf + msg->len, savedFragment->buf, savedFragment->len);
|
||||
msg->len += savedFragment->len;
|
||||
isLast = savedFragment->isLast;
|
||||
incompleteMessage->nextFragmentNum++;
|
||||
incompleteMessage->savedFragments.erase (it1++);
|
||||
delete savedFragment;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (isLast)
|
||||
LogPrint (eLogDebug, "Message ", msgID, " complete");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fragmentNum < incompleteMessage->nextFragmentNum)
|
||||
// duplicate fragment
|
||||
LogPrint (eLogWarning, "Duplicate fragment ", (int)fragmentNum, " of message ", msgID, ". Ignored");
|
||||
else
|
||||
{
|
||||
// missing fragment
|
||||
LogPrint (eLogWarning, "Missing fragments from ", (int)incompleteMessage->nextFragmentNum, " to ", fragmentNum - 1, " of message ", msgID);
|
||||
auto savedFragment = new Fragment (fragmentNum, buf, fragmentSize, isLast);
|
||||
if (!incompleteMessage->savedFragments.insert (savedFragment).second)
|
||||
{
|
||||
LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
|
||||
delete savedFragment;
|
||||
}
|
||||
}
|
||||
isLast = false;
|
||||
}
|
||||
|
||||
if (isLast)
|
||||
{
|
||||
// delete incomplete message
|
||||
delete incompleteMessage;
|
||||
m_IncomleteMessages.erase (msgID);
|
||||
// process message
|
||||
SendMsgAck (msgID);
|
||||
msg->FromSSU (msgID);
|
||||
if (m_Session.GetState () == eSessionStateEstablished)
|
||||
{
|
||||
if (!m_ReceivedMessages.count (msgID))
|
||||
{
|
||||
if (m_ReceivedMessages.size () > 100) m_ReceivedMessages.clear ();
|
||||
m_ReceivedMessages.insert (msgID);
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU message ", msgID, " already received");
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we expect DeliveryStatus
|
||||
if (msg->GetHeader ()->typeID == eI2NPDeliveryStatus)
|
||||
{
|
||||
LogPrint ("SSU session established");
|
||||
m_Session.Established ();
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetHeader ()->typeID);
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
SendFragmentAck (msgID, fragmentNum);
|
||||
buf += fragmentSize;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUData::ProcessMessage (uint8_t * buf, size_t len)
|
||||
{
|
||||
//uint8_t * start = buf;
|
||||
uint8_t flag = *buf;
|
||||
buf++;
|
||||
LogPrint (eLogDebug, "Process SSU data flags=", (int)flag);
|
||||
// process acks if presented
|
||||
if (flag & (DATA_FLAG_ACK_BITFIELDS_INCLUDED | DATA_FLAG_EXPLICIT_ACKS_INCLUDED))
|
||||
ProcessAcks (buf, flag);
|
||||
// extended data if presented
|
||||
if (flag & DATA_FLAG_EXTENDED_DATA_INCLUDED)
|
||||
{
|
||||
uint8_t extendedDataSize = *buf;
|
||||
buf++; // size
|
||||
LogPrint (eLogDebug, "SSU extended data of ", extendedDataSize, " bytes presented");
|
||||
buf += extendedDataSize;
|
||||
}
|
||||
// process data
|
||||
ProcessFragments (buf);
|
||||
}
|
||||
|
||||
void SSUData::Send (i2p::I2NPMessage * msg)
|
||||
{
|
||||
uint32_t msgID = msg->ToSSU ();
|
||||
if (m_SentMessages.count (msgID) > 0)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU message ", msgID, " already sent");
|
||||
DeleteI2NPMessage (msg);
|
||||
return;
|
||||
}
|
||||
if (m_SentMessages.empty ()) // schedule resend at first message only
|
||||
ScheduleResend ();
|
||||
SentMessage * sentMessage = new SentMessage;
|
||||
m_SentMessages[msgID] = sentMessage;
|
||||
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
|
||||
sentMessage->numResends = 0;
|
||||
auto& fragments = sentMessage->fragments;
|
||||
msgID = htobe32 (msgID);
|
||||
size_t payloadSize = m_PacketSize - sizeof (SSUHeader) - 9; // 9 = flag + #frg(1) + messageID(4) + frag info (3)
|
||||
size_t len = msg->GetLength ();
|
||||
uint8_t * msgBuf = msg->GetSSUHeader ();
|
||||
|
||||
uint32_t fragmentNum = 0;
|
||||
while (len > 0)
|
||||
{
|
||||
Fragment * fragment = new Fragment;
|
||||
fragment->fragmentNum = fragmentNum;
|
||||
uint8_t * buf = fragment->buf;
|
||||
fragments.push_back (fragment);
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
|
||||
payload++;
|
||||
*payload = 1; // always 1 message fragment per message
|
||||
payload++;
|
||||
*(uint32_t *)payload = msgID;
|
||||
payload += 4;
|
||||
bool isLast = (len <= payloadSize);
|
||||
size_t size = isLast ? len : payloadSize;
|
||||
uint32_t fragmentInfo = (fragmentNum << 17);
|
||||
if (isLast)
|
||||
fragmentInfo |= 0x010000;
|
||||
|
||||
fragmentInfo |= size;
|
||||
fragmentInfo = htobe32 (fragmentInfo);
|
||||
memcpy (payload, (uint8_t *)(&fragmentInfo) + 1, 3);
|
||||
payload += 3;
|
||||
memcpy (payload, msgBuf, size);
|
||||
|
||||
size += payload - buf;
|
||||
if (size & 0x0F) // make sure 16 bytes boundary
|
||||
size = ((size >> 4) + 1) << 4; // (/16 + 1)*16
|
||||
fragment->len = size;
|
||||
|
||||
// encrypt message with session key
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size);
|
||||
m_Session.Send (buf, size);
|
||||
|
||||
if (!isLast)
|
||||
{
|
||||
len -= payloadSize;
|
||||
msgBuf += payloadSize;
|
||||
}
|
||||
else
|
||||
len = 0;
|
||||
fragmentNum++;
|
||||
}
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
|
||||
void SSUData::SendMsgAck (uint32_t msgID)
|
||||
{
|
||||
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
|
||||
payload++;
|
||||
*payload = 1; // number of ACKs
|
||||
payload++;
|
||||
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
||||
payload += 4;
|
||||
*payload = 0; // number of fragments
|
||||
|
||||
// encrypt message with session key
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
|
||||
m_Session.Send (buf, 48);
|
||||
}
|
||||
|
||||
void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum)
|
||||
{
|
||||
if (fragmentNum > 64)
|
||||
{
|
||||
LogPrint (eLogWarning, "Fragment number ", fragmentNum, " exceeds 64");
|
||||
return;
|
||||
}
|
||||
uint8_t buf[64 + 18];
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
|
||||
payload++;
|
||||
*payload = 1; // number of ACK bitfields
|
||||
payload++;
|
||||
// one ack
|
||||
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
||||
payload += 4;
|
||||
div_t d = div (fragmentNum, 7);
|
||||
memset (payload, 0x80, d.quot); // 0x80 means non-last
|
||||
payload += d.quot;
|
||||
*payload = 0x01 << d.rem; // set corresponding bit
|
||||
payload++;
|
||||
*payload = 0; // number of fragments
|
||||
|
||||
size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1)
|
||||
// encrypt message with session key
|
||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
|
||||
m_Session.Send (buf, len);
|
||||
}
|
||||
|
||||
void SSUData::ScheduleResend()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
|
||||
auto s = m_Session.shared_from_this();
|
||||
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
||||
{ s->m_Data.HandleResendTimer (ecode); });
|
||||
}
|
||||
|
||||
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it : m_SentMessages)
|
||||
{
|
||||
if (ts >= it.second->nextResendTime && it.second->numResends < MAX_NUM_RESENDS)
|
||||
{
|
||||
for (auto f: it.second->fragments)
|
||||
if (f) m_Session.Send (f->buf, f->len); // resend
|
||||
|
||||
it.second->numResends++;
|
||||
it.second->nextResendTime += it.second->numResends*RESEND_INTERVAL;
|
||||
}
|
||||
}
|
||||
ScheduleResend ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
114
SSUData.h
114
SSUData.h
@@ -1,114 +0,0 @@
|
||||
#ifndef SSU_DATA_H__
|
||||
#define SSU_DATA_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <boost/asio.hpp>
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
|
||||
const size_t SSU_MTU_V4 = 1484;
|
||||
const size_t SSU_MTU_V6 = 1472;
|
||||
const size_t IPV4_HEADER_SIZE = 20;
|
||||
const size_t IPV6_HEADER_SIZE = 40;
|
||||
const size_t UDP_HEADER_SIZE = 8;
|
||||
const size_t SSU_V4_MAX_PACKET_SIZE = SSU_MTU_V4 - IPV4_HEADER_SIZE - UDP_HEADER_SIZE; // 1456
|
||||
const size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1424
|
||||
const int RESEND_INTERVAL = 3; // in seconds
|
||||
const int MAX_NUM_RESENDS = 5;
|
||||
// data flags
|
||||
const uint8_t DATA_FLAG_EXTENDED_DATA_INCLUDED = 0x02;
|
||||
const uint8_t DATA_FLAG_WANT_REPLY = 0x04;
|
||||
const uint8_t DATA_FLAG_REQUEST_PREVIOUS_ACKS = 0x08;
|
||||
const uint8_t DATA_FLAG_EXPLICIT_CONGESTION_NOTIFICATION = 0x10;
|
||||
const uint8_t DATA_FLAG_ACK_BITFIELDS_INCLUDED = 0x40;
|
||||
const uint8_t DATA_FLAG_EXPLICIT_ACKS_INCLUDED = 0x80;
|
||||
|
||||
struct Fragment
|
||||
{
|
||||
int fragmentNum;
|
||||
size_t len;
|
||||
bool isLast;
|
||||
uint8_t buf[SSU_V4_MAX_PACKET_SIZE + 18]; // use biggest
|
||||
|
||||
Fragment () = default;
|
||||
Fragment (int n, const uint8_t * b, int l, bool last):
|
||||
fragmentNum (n), len (l), isLast (last) { memcpy (buf, b, len); };
|
||||
};
|
||||
|
||||
struct FragmentCmp
|
||||
{
|
||||
bool operator() (const Fragment * f1, const Fragment * f2) const
|
||||
{
|
||||
return f1->fragmentNum < f2->fragmentNum;
|
||||
};
|
||||
};
|
||||
|
||||
struct IncompleteMessage
|
||||
{
|
||||
I2NPMessage * msg;
|
||||
int nextFragmentNum;
|
||||
std::set<Fragment *, FragmentCmp> savedFragments;
|
||||
|
||||
IncompleteMessage (I2NPMessage * m): msg (m), nextFragmentNum (0) {};
|
||||
~IncompleteMessage () { for (auto it: savedFragments) { delete it; }; };
|
||||
};
|
||||
|
||||
struct SentMessage
|
||||
{
|
||||
std::vector<Fragment *> fragments;
|
||||
uint32_t nextResendTime; // in seconds
|
||||
int numResends;
|
||||
|
||||
~SentMessage () { for (auto it: fragments) { delete it; }; };
|
||||
};
|
||||
|
||||
class SSUSession;
|
||||
class SSUData
|
||||
{
|
||||
public:
|
||||
|
||||
SSUData (SSUSession& session);
|
||||
~SSUData ();
|
||||
|
||||
void ProcessMessage (uint8_t * buf, size_t len);
|
||||
void Send (i2p::I2NPMessage * msg);
|
||||
|
||||
void UpdatePacketSize (const i2p::data::IdentHash& remoteIdent);
|
||||
|
||||
private:
|
||||
|
||||
void SendMsgAck (uint32_t msgID);
|
||||
void SendFragmentAck (uint32_t msgID, int fragmentNum);
|
||||
void ProcessAcks (uint8_t *& buf, uint8_t flag);
|
||||
void ProcessFragments (uint8_t * buf);
|
||||
void ProcessSentMessageAck (uint32_t msgID);
|
||||
|
||||
void ScheduleResend ();
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
|
||||
void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter);
|
||||
|
||||
private:
|
||||
|
||||
SSUSession& m_Session;
|
||||
std::map<uint32_t, IncompleteMessage *> m_IncomleteMessages;
|
||||
std::map<uint32_t, SentMessage *> m_SentMessages;
|
||||
std::set<uint32_t> m_ReceivedMessages;
|
||||
boost::asio::deadline_timer m_ResendTimer;
|
||||
int m_MaxPacketSize, m_PacketSize;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
1018
SSUSession.cpp
1018
SSUSession.cpp
File diff suppressed because it is too large
Load Diff
417
Signature.h
417
Signature.h
@@ -1,417 +0,0 @@
|
||||
#ifndef SIGNATURE_H__
|
||||
#define SIGNATURE_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cryptopp/dsa.h>
|
||||
#include <cryptopp/rsa.h>
|
||||
#include <cryptopp/asn.h>
|
||||
#include <cryptopp/oids.h>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <cryptopp/eccrypto.h>
|
||||
#include "CryptoConst.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace crypto
|
||||
{
|
||||
class Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~Verifier () {};
|
||||
virtual bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const = 0;
|
||||
virtual size_t GetPublicKeyLen () const = 0;
|
||||
virtual size_t GetSignatureLen () const = 0;
|
||||
virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; };
|
||||
};
|
||||
|
||||
class Signer
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~Signer () {};
|
||||
virtual void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const = 0;
|
||||
};
|
||||
|
||||
const size_t DSA_PUBLIC_KEY_LENGTH = 128;
|
||||
const size_t DSA_SIGNATURE_LENGTH = 40;
|
||||
const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2;
|
||||
class DSAVerifier: public Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
DSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingKey, DSA_PUBLIC_KEY_LENGTH));
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
CryptoPP::DSA::Verifier verifier (m_PublicKey);
|
||||
return verifier.VerifyMessage (buf, len, signature, DSA_SIGNATURE_LENGTH);
|
||||
}
|
||||
|
||||
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; };
|
||||
size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; };
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::DSA::PublicKey m_PublicKey;
|
||||
};
|
||||
|
||||
class DSASigner: public Signer
|
||||
{
|
||||
public:
|
||||
|
||||
DSASigner (const uint8_t * signingPrivateKey)
|
||||
{
|
||||
m_PrivateKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH));
|
||||
}
|
||||
|
||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
CryptoPP::DSA::Signer signer (m_PrivateKey);
|
||||
signer.SignMessage (rnd, buf, len, signature);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::DSA::PrivateKey m_PrivateKey;
|
||||
};
|
||||
|
||||
inline void CreateDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CryptoPP::DSA::PrivateKey privateKey;
|
||||
CryptoPP::DSA::PublicKey publicKey;
|
||||
privateKey.Initialize (rnd, dsap, dsaq, dsag);
|
||||
privateKey.MakePublicKey (publicKey);
|
||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
||||
publicKey.GetPublicElement ().Encode (signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
||||
}
|
||||
|
||||
template<typename Hash, size_t keyLen>
|
||||
class ECDSAVerifier: public Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
template<typename Curve>
|
||||
ECDSAVerifier (Curve curve, const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey.Initialize (curve,
|
||||
CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, keyLen/2),
|
||||
CryptoPP::Integer (signingKey + keyLen/2, keyLen/2)));
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Verifier verifier (m_PublicKey);
|
||||
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length
|
||||
}
|
||||
|
||||
size_t GetPublicKeyLen () const { return keyLen; };
|
||||
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length
|
||||
|
||||
private:
|
||||
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey m_PublicKey;
|
||||
};
|
||||
|
||||
template<typename Hash>
|
||||
class ECDSASigner: public Signer
|
||||
{
|
||||
public:
|
||||
|
||||
template<typename Curve>
|
||||
ECDSASigner (Curve curve, const uint8_t * signingPrivateKey, size_t keyLen)
|
||||
{
|
||||
m_PrivateKey.Initialize (curve, CryptoPP::Integer (signingPrivateKey, keyLen/2)); // private key length
|
||||
}
|
||||
|
||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Signer signer (m_PrivateKey);
|
||||
signer.SignMessage (rnd, buf, len, signature);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey m_PrivateKey;
|
||||
};
|
||||
|
||||
template<typename Hash, typename Curve>
|
||||
inline void CreateECDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, Curve curve,
|
||||
size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey privateKey;
|
||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey publicKey;
|
||||
privateKey.Initialize (rnd, curve);
|
||||
privateKey.MakePublicKey (publicKey);
|
||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, keyLen/2);
|
||||
auto q = publicKey.GetPublicElement ();
|
||||
q.x.Encode (signingPublicKey, keyLen/2);
|
||||
q.y.Encode (signingPublicKey + keyLen/2, keyLen/2);
|
||||
}
|
||||
|
||||
// ECDSA_SHA256_P256
|
||||
const size_t ECDSAP256_KEY_LENGTH = 64;
|
||||
class ECDSAP256Verifier: public ECDSAVerifier<CryptoPP::SHA256, ECDSAP256_KEY_LENGTH>
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP256Verifier (const uint8_t * signingKey):
|
||||
ECDSAVerifier (CryptoPP::ASN1::secp256r1(), signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ECDSAP256Signer: public ECDSASigner<CryptoPP::SHA256>
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP256Signer (const uint8_t * signingPrivateKey):
|
||||
ECDSASigner (CryptoPP::ASN1::secp256r1(), signingPrivateKey, ECDSAP256_KEY_LENGTH)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CreateECDSARandomKeys<CryptoPP::SHA256> (rnd, CryptoPP::ASN1::secp256r1(), ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
||||
}
|
||||
|
||||
// ECDSA_SHA384_P384
|
||||
const size_t ECDSAP384_KEY_LENGTH = 96;
|
||||
class ECDSAP384Verifier: public ECDSAVerifier<CryptoPP::SHA384, ECDSAP384_KEY_LENGTH>
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP384Verifier (const uint8_t * signingKey):
|
||||
ECDSAVerifier (CryptoPP::ASN1::secp384r1(), signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ECDSAP384Signer: public ECDSASigner<CryptoPP::SHA384>
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP384Signer (const uint8_t * signingPrivateKey):
|
||||
ECDSASigner (CryptoPP::ASN1::secp384r1(), signingPrivateKey, ECDSAP384_KEY_LENGTH)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline void CreateECDSAP384RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CreateECDSARandomKeys<CryptoPP::SHA384> (rnd, CryptoPP::ASN1::secp384r1(), ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
||||
}
|
||||
|
||||
// ECDSA_SHA512_P521
|
||||
const size_t ECDSAP521_KEY_LENGTH = 132;
|
||||
class ECDSAP521Verifier: public ECDSAVerifier<CryptoPP::SHA512, ECDSAP521_KEY_LENGTH>
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP521Verifier (const uint8_t * signingKey):
|
||||
ECDSAVerifier (CryptoPP::ASN1::secp521r1(), signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ECDSAP521Signer: public ECDSASigner<CryptoPP::SHA512>
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP521Signer (const uint8_t * signingPrivateKey):
|
||||
ECDSASigner (CryptoPP::ASN1::secp521r1(), signingPrivateKey, ECDSAP521_KEY_LENGTH)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline void CreateECDSAP521RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CreateECDSARandomKeys<CryptoPP::SHA512> (rnd, CryptoPP::ASN1::secp521r1(), ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
||||
}
|
||||
|
||||
// RSA
|
||||
template<typename Hash, size_t keyLen>
|
||||
class RSAVerifier: public Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
RSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey.Initialize (CryptoPP::Integer (signingKey, keyLen), CryptoPP::Integer (rsae));
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Verifier verifier (m_PublicKey);
|
||||
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length
|
||||
}
|
||||
size_t GetPublicKeyLen () const { return keyLen; }
|
||||
size_t GetSignatureLen () const { return keyLen; }
|
||||
size_t GetPrivateKeyLen () const { return GetSignatureLen ()*2; };
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::RSA::PublicKey m_PublicKey;
|
||||
};
|
||||
|
||||
|
||||
template<typename Hash>
|
||||
class RSASigner: public Signer
|
||||
{
|
||||
public:
|
||||
|
||||
RSASigner (const uint8_t * signingPrivateKey, size_t keyLen)
|
||||
{
|
||||
m_PrivateKey.Initialize (CryptoPP::Integer (signingPrivateKey, keyLen/2),
|
||||
rsae,
|
||||
CryptoPP::Integer (signingPrivateKey + keyLen/2, keyLen/2));
|
||||
}
|
||||
|
||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Signer signer (m_PrivateKey);
|
||||
signer.SignMessage (rnd, buf, len, signature);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::RSA::PrivateKey m_PrivateKey;
|
||||
};
|
||||
|
||||
inline void CreateRSARandomKeys (CryptoPP::RandomNumberGenerator& rnd,
|
||||
size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CryptoPP::RSA::PrivateKey privateKey;
|
||||
privateKey.Initialize (rnd, publicKeyLen*8, rsae);
|
||||
privateKey.GetModulus ().Encode (signingPrivateKey, publicKeyLen);
|
||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey + publicKeyLen, publicKeyLen);
|
||||
privateKey.GetModulus ().Encode (signingPublicKey, publicKeyLen);
|
||||
}
|
||||
|
||||
|
||||
// RSA_SHA256_2048
|
||||
const size_t RSASHA2562048_KEY_LENGTH = 256;
|
||||
class RSASHA2562048Verifier: public RSAVerifier<CryptoPP::SHA256, RSASHA2562048_KEY_LENGTH>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA2562048Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class RSASHA2562048Signer: public RSASigner<CryptoPP::SHA256>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA2562048Signer (const uint8_t * signingPrivateKey):
|
||||
RSASigner (signingPrivateKey, RSASHA2562048_KEY_LENGTH*2)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// RSA_SHA384_3072
|
||||
const size_t RSASHA3843072_KEY_LENGTH = 384;
|
||||
class RSASHA3843072Verifier: public RSAVerifier<CryptoPP::SHA384, RSASHA3843072_KEY_LENGTH>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA3843072Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class RSASHA3843072Signer: public RSASigner<CryptoPP::SHA384>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA3843072Signer (const uint8_t * signingPrivateKey):
|
||||
RSASigner (signingPrivateKey, RSASHA3843072_KEY_LENGTH*2)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// RSA_SHA512_4096
|
||||
const size_t RSASHA5124096_KEY_LENGTH = 512;
|
||||
class RSASHA5124096Verifier: public RSAVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA5124096Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class RSASHA5124096Signer: public RSASigner<CryptoPP::SHA512>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA5124096Signer (const uint8_t * signingPrivateKey):
|
||||
RSASigner (signingPrivateKey, RSASHA5124096_KEY_LENGTH*2)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Raw verifiers
|
||||
class RawVerifier
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~RawVerifier () {};
|
||||
virtual void Update (const uint8_t * buf, size_t len) = 0;
|
||||
virtual bool Verify (const uint8_t * signature) = 0;
|
||||
};
|
||||
|
||||
template<typename Hash, size_t keyLen>
|
||||
class RSARawVerifier: public RawVerifier
|
||||
{
|
||||
public:
|
||||
|
||||
RSARawVerifier (const uint8_t * signingKey):
|
||||
n (signingKey, keyLen)
|
||||
{
|
||||
}
|
||||
|
||||
void Update (const uint8_t * buf, size_t len)
|
||||
{
|
||||
m_Hash.Update (buf, len);
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * signature)
|
||||
{
|
||||
// RSA encryption first
|
||||
CryptoPP::Integer enSig (a_exp_b_mod_c (CryptoPP::Integer (signature, keyLen),
|
||||
CryptoPP::Integer (i2p::crypto::rsae), n)); // s^e mod n
|
||||
uint8_t enSigBuf[keyLen];
|
||||
enSig.Encode (enSigBuf, keyLen);
|
||||
|
||||
uint8_t digest[Hash::DIGESTSIZE];
|
||||
m_Hash.Final (digest);
|
||||
if ((int)keyLen < Hash::DIGESTSIZE) return false; // can't verify digest longer than key
|
||||
// we assume digest is right aligned, at least for PKCS#1 v1.5 padding
|
||||
return !memcmp (enSigBuf + (keyLen - Hash::DIGESTSIZE), digest, Hash::DIGESTSIZE);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::Integer n; // RSA modulus
|
||||
Hash m_Hash;
|
||||
};
|
||||
|
||||
class RSASHA5124096RawVerifier: public RSARawVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH>
|
||||
{
|
||||
public:
|
||||
|
||||
RSASHA5124096RawVerifier (const uint8_t * signingKey): RSARawVerifier (signingKey)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
721
Streaming.cpp
721
Streaming.cpp
@@ -1,721 +0,0 @@
|
||||
#include <cryptopp/gzip.h>
|
||||
#include "Log.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Destination.h"
|
||||
#include "Streaming.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace stream
|
||||
{
|
||||
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local,
|
||||
const i2p::data::LeaseSet& remote, int port): m_Service (service), m_SendStreamID (0),
|
||||
m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1), m_IsOpen (false),
|
||||
m_IsReset (false), m_IsAckSendScheduled (false), m_LocalDestination (local),
|
||||
m_RemoteLeaseSet (&remote), m_RoutingSession (nullptr), m_CurrentOutboundTunnel (nullptr),
|
||||
m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
|
||||
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port)
|
||||
{
|
||||
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
||||
UpdateCurrentRemoteLease ();
|
||||
}
|
||||
|
||||
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local):
|
||||
m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1),
|
||||
m_IsOpen (false), m_IsReset (false), m_IsAckSendScheduled (false), m_LocalDestination (local),
|
||||
m_RemoteLeaseSet (nullptr), m_RoutingSession (nullptr), m_CurrentOutboundTunnel (nullptr),
|
||||
m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
|
||||
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0)
|
||||
{
|
||||
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
||||
}
|
||||
|
||||
Stream::~Stream ()
|
||||
{
|
||||
m_AckSendTimer.cancel ();
|
||||
while (!m_ReceiveQueue.empty ())
|
||||
{
|
||||
auto packet = m_ReceiveQueue.front ();
|
||||
m_ReceiveQueue.pop ();
|
||||
delete packet;
|
||||
}
|
||||
m_ReceiveTimer.cancel ();
|
||||
|
||||
for (auto it: m_SentPackets)
|
||||
delete it;
|
||||
m_SentPackets.clear ();
|
||||
m_ResendTimer.cancel ();
|
||||
|
||||
for (auto it: m_SavedPackets)
|
||||
delete it;
|
||||
m_SavedPackets.clear ();
|
||||
LogPrint (eLogDebug, "Stream deleted");
|
||||
}
|
||||
|
||||
void Stream::HandleNextPacket (Packet * packet)
|
||||
{
|
||||
m_NumReceivedBytes += packet->GetLength ();
|
||||
if (!m_SendStreamID)
|
||||
m_SendStreamID = packet->GetReceiveStreamID ();
|
||||
|
||||
if (!packet->IsNoAck ()) // ack received
|
||||
ProcessAck (packet);
|
||||
|
||||
int32_t receivedSeqn = packet->GetSeqn ();
|
||||
bool isSyn = packet->IsSYN ();
|
||||
if (!receivedSeqn && !isSyn)
|
||||
{
|
||||
// plain ack
|
||||
LogPrint (eLogDebug, "Plain ACK received");
|
||||
delete packet;
|
||||
return;
|
||||
}
|
||||
|
||||
LogPrint (eLogDebug, "Received seqn=", receivedSeqn);
|
||||
if (isSyn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
|
||||
{
|
||||
// we have received next in sequence message
|
||||
ProcessPacket (packet);
|
||||
|
||||
// we should also try stored messages if any
|
||||
for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();)
|
||||
{
|
||||
if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1))
|
||||
{
|
||||
Packet * savedPacket = *it;
|
||||
m_SavedPackets.erase (it++);
|
||||
|
||||
ProcessPacket (savedPacket);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// schedule ack for last message
|
||||
if (m_IsOpen)
|
||||
{
|
||||
if (!m_IsAckSendScheduled)
|
||||
{
|
||||
m_IsAckSendScheduled = true;
|
||||
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT));
|
||||
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
else if (isSyn)
|
||||
// we have to send SYN back to incoming connection
|
||||
Send (nullptr, 0); // also sets m_IsOpen
|
||||
}
|
||||
else
|
||||
{
|
||||
if (receivedSeqn <= m_LastReceivedSequenceNumber)
|
||||
{
|
||||
// we have received duplicate. Most likely our outbound tunnel is dead
|
||||
LogPrint (eLogWarning, "Duplicate message ", receivedSeqn, " received");
|
||||
m_CurrentOutboundTunnel = nullptr; // pick another outbound tunnel
|
||||
UpdateCurrentRemoteLease (); // pick another lease
|
||||
SendQuickAck (); // resend ack for previous message again
|
||||
delete packet; // packet dropped
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
|
||||
// save message and wait for missing message again
|
||||
SavePacket (packet);
|
||||
// send NACKs for missing messages ASAP
|
||||
if (m_IsAckSendScheduled)
|
||||
{
|
||||
m_IsAckSendScheduled = false;
|
||||
m_AckSendTimer.cancel ();
|
||||
}
|
||||
SendQuickAck ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::SavePacket (Packet * packet)
|
||||
{
|
||||
m_SavedPackets.insert (packet);
|
||||
}
|
||||
|
||||
void Stream::ProcessPacket (Packet * packet)
|
||||
{
|
||||
// process flags
|
||||
uint32_t receivedSeqn = packet->GetSeqn ();
|
||||
uint16_t flags = packet->GetFlags ();
|
||||
LogPrint (eLogDebug, "Process seqn=", receivedSeqn, ", flags=", flags);
|
||||
|
||||
const uint8_t * optionData = packet->GetOptionData ();
|
||||
if (flags & PACKET_FLAG_SYNCHRONIZE)
|
||||
LogPrint (eLogDebug, "Synchronize");
|
||||
|
||||
if (flags & PACKET_FLAG_DELAY_REQUESTED)
|
||||
{
|
||||
optionData += 2;
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_FROM_INCLUDED)
|
||||
{
|
||||
optionData += m_RemoteIdentity.FromBuffer (optionData, packet->GetOptionSize ());
|
||||
LogPrint (eLogInfo, "From identity ", m_RemoteIdentity.GetIdentHash ().ToBase64 ());
|
||||
if (!m_RemoteLeaseSet)
|
||||
LogPrint (eLogDebug, "Incoming stream from ", m_RemoteIdentity.GetIdentHash ().ToBase64 ());
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
|
||||
{
|
||||
uint16_t maxPacketSize = be16toh (*(uint16_t *)optionData);
|
||||
LogPrint (eLogDebug, "Max packet size ", maxPacketSize);
|
||||
optionData += 2;
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
|
||||
{
|
||||
LogPrint (eLogDebug, "Signature");
|
||||
uint8_t signature[256];
|
||||
auto signatureLen = m_RemoteIdentity.GetSignatureLen ();
|
||||
memcpy (signature, optionData, signatureLen);
|
||||
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
|
||||
if (!m_RemoteIdentity.Verify (packet->GetBuffer (), packet->GetLength (), signature))
|
||||
{
|
||||
LogPrint (eLogError, "Signature verification failed");
|
||||
Close ();
|
||||
flags |= PACKET_FLAG_CLOSE;
|
||||
}
|
||||
memcpy (const_cast<uint8_t *>(optionData), signature, signatureLen);
|
||||
optionData += signatureLen;
|
||||
}
|
||||
|
||||
packet->offset = packet->GetPayload () - packet->buf;
|
||||
if (packet->GetLength () > 0)
|
||||
{
|
||||
m_ReceiveQueue.push (packet);
|
||||
m_ReceiveTimer.cancel ();
|
||||
}
|
||||
else
|
||||
delete packet;
|
||||
|
||||
m_LastReceivedSequenceNumber = receivedSeqn;
|
||||
|
||||
if (flags & PACKET_FLAG_CLOSE)
|
||||
{
|
||||
LogPrint (eLogInfo, "Closed");
|
||||
Close ();
|
||||
m_IsOpen = false;
|
||||
m_IsReset = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::ProcessAck (Packet * packet)
|
||||
{
|
||||
uint32_t ackThrough = packet->GetAckThrough ();
|
||||
int nackCount = packet->GetNACKCount ();
|
||||
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
|
||||
{
|
||||
auto seqn = (*it)->GetSeqn ();
|
||||
if (seqn <= ackThrough)
|
||||
{
|
||||
if (nackCount > 0)
|
||||
{
|
||||
bool nacked = false;
|
||||
for (int i = 0; i < nackCount; i++)
|
||||
if (seqn == packet->GetNACK (i))
|
||||
{
|
||||
nacked = true;
|
||||
break;
|
||||
}
|
||||
if (nacked)
|
||||
{
|
||||
LogPrint (eLogDebug, "Packet ", seqn, " NACK");
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto sentPacket = *it;
|
||||
LogPrint (eLogDebug, "Packet ", seqn, " acknowledged");
|
||||
m_SentPackets.erase (it++);
|
||||
delete sentPacket;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (m_SentPackets.empty ())
|
||||
m_ResendTimer.cancel ();
|
||||
}
|
||||
|
||||
size_t Stream::Send (const uint8_t * buf, size_t len)
|
||||
{
|
||||
bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet
|
||||
std::vector<Packet *> packets;
|
||||
while (!m_IsOpen || len > 0)
|
||||
{
|
||||
Packet * p = new Packet ();
|
||||
uint8_t * packet = p->GetBuffer ();
|
||||
// TODO: implement setters
|
||||
size_t size = 0;
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
|
||||
size += 4; // sendStreamID
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID);
|
||||
size += 4; // receiveStreamID
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber++);
|
||||
size += 4; // sequenceNum
|
||||
if (isNoAck)
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_LastReceivedSequenceNumber);
|
||||
else
|
||||
*(uint32_t *)(packet + size) = 0;
|
||||
size += 4; // ack Through
|
||||
packet[size] = 0;
|
||||
size++; // NACK count
|
||||
size++; // resend delay
|
||||
if (!m_IsOpen)
|
||||
{
|
||||
// initial packet
|
||||
m_IsOpen = true; m_IsReset = false;
|
||||
uint16_t flags = PACKET_FLAG_SYNCHRONIZE | PACKET_FLAG_FROM_INCLUDED |
|
||||
PACKET_FLAG_SIGNATURE_INCLUDED | PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED;
|
||||
if (isNoAck) flags |= PACKET_FLAG_NO_ACK;
|
||||
*(uint16_t *)(packet + size) = htobe16 (flags);
|
||||
size += 2; // flags
|
||||
size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen ();
|
||||
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
|
||||
*(uint16_t *)(packet + size) = htobe16 (identityLen + signatureLen + 2); // identity + signature + packet size
|
||||
size += 2; // options size
|
||||
m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen);
|
||||
size += identityLen; // from
|
||||
*(uint16_t *)(packet + size) = htobe16 (STREAMING_MTU);
|
||||
size += 2; // max packet size
|
||||
uint8_t * signature = packet + size; // set it later
|
||||
memset (signature, 0, signatureLen); // zeroes for now
|
||||
size += signatureLen; // signature
|
||||
size_t sentLen = STREAMING_MTU - size;
|
||||
if (len < sentLen) sentLen = len;
|
||||
memcpy (packet + size, buf, sentLen);
|
||||
buf += sentLen;
|
||||
len -= sentLen;
|
||||
size += sentLen; // payload
|
||||
m_LocalDestination.GetOwner ().Sign (packet, size, signature);
|
||||
}
|
||||
else
|
||||
{
|
||||
// follow on packet
|
||||
*(uint16_t *)(packet + size) = 0;
|
||||
size += 2; // flags
|
||||
*(uint16_t *)(packet + size) = 0; // no options
|
||||
size += 2; // options size
|
||||
size_t sentLen = STREAMING_MTU - size;
|
||||
if (len < sentLen) sentLen = len;
|
||||
memcpy (packet + size, buf, sentLen);
|
||||
buf += sentLen;
|
||||
len -= sentLen;
|
||||
size += sentLen; // payload
|
||||
}
|
||||
p->len = size;
|
||||
packets.push_back (p);
|
||||
}
|
||||
if (packets.size () > 0)
|
||||
m_Service.post (std::bind (&Stream::PostPackets, shared_from_this (), packets));
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void Stream::SendQuickAck ()
|
||||
{
|
||||
int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber;
|
||||
if (!m_SavedPackets.empty ())
|
||||
{
|
||||
int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn ();
|
||||
if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn;
|
||||
}
|
||||
if (lastReceivedSeqn < 0)
|
||||
{
|
||||
LogPrint (eLogError, "No packets have been received yet");
|
||||
return;
|
||||
}
|
||||
|
||||
Packet p;
|
||||
uint8_t * packet = p.GetBuffer ();
|
||||
size_t size = 0;
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
|
||||
size += 4; // sendStreamID
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID);
|
||||
size += 4; // receiveStreamID
|
||||
*(uint32_t *)(packet + size) = 0; // this is plain Ack message
|
||||
size += 4; // sequenceNum
|
||||
*(uint32_t *)(packet + size) = htobe32 (lastReceivedSeqn);
|
||||
size += 4; // ack Through
|
||||
if (lastReceivedSeqn > m_LastReceivedSequenceNumber)
|
||||
{
|
||||
// fill NACKs
|
||||
uint8_t * nacks = packet + size + 1;
|
||||
uint8_t numNacks = 0;
|
||||
auto nextSeqn = m_LastReceivedSequenceNumber + 1;
|
||||
for (auto it: m_SavedPackets)
|
||||
{
|
||||
auto seqn = it->GetSeqn ();
|
||||
for (uint32_t i = nextSeqn; i < seqn; i++)
|
||||
{
|
||||
*(uint32_t *)nacks = htobe32 (i);
|
||||
nacks += 4;
|
||||
numNacks++;
|
||||
}
|
||||
nextSeqn = seqn + 1;
|
||||
}
|
||||
packet[size] = numNacks;
|
||||
size++; // NACK count
|
||||
size += numNacks*4; // NACKs
|
||||
}
|
||||
else
|
||||
{
|
||||
// No NACKs
|
||||
packet[size] = 0;
|
||||
size++; // NACK count
|
||||
}
|
||||
size++; // resend delay
|
||||
*(uint16_t *)(packet + size) = 0; // nof flags set
|
||||
size += 2; // flags
|
||||
*(uint16_t *)(packet + size) = 0; // no options
|
||||
size += 2; // options size
|
||||
p.len = size;
|
||||
|
||||
SendPackets (std::vector<Packet *> { &p });
|
||||
LogPrint ("Quick Ack sent");
|
||||
}
|
||||
|
||||
void Stream::Close ()
|
||||
{
|
||||
if (m_IsOpen)
|
||||
{
|
||||
m_IsOpen = false;
|
||||
Packet * p = new Packet ();
|
||||
uint8_t * packet = p->GetBuffer ();
|
||||
size_t size = 0;
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_SendStreamID);
|
||||
size += 4; // sendStreamID
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_RecvStreamID);
|
||||
size += 4; // receiveStreamID
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_SequenceNumber++);
|
||||
size += 4; // sequenceNum
|
||||
*(uint32_t *)(packet + size) = htobe32 (m_LastReceivedSequenceNumber);
|
||||
size += 4; // ack Through
|
||||
packet[size] = 0;
|
||||
size++; // NACK count
|
||||
size++; // resend delay
|
||||
*(uint16_t *)(packet + size) = htobe16 (PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
|
||||
size += 2; // flags
|
||||
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
|
||||
*(uint16_t *)(packet + size) = htobe16 (signatureLen); // signature only
|
||||
size += 2; // options size
|
||||
uint8_t * signature = packet + size;
|
||||
memset (packet + size, 0, signatureLen);
|
||||
size += signatureLen; // signature
|
||||
m_LocalDestination.GetOwner ().Sign (packet, size, signature);
|
||||
|
||||
p->len = size;
|
||||
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
|
||||
LogPrint ("FIN sent");
|
||||
m_ReceiveTimer.cancel ();
|
||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
||||
}
|
||||
}
|
||||
|
||||
size_t Stream::ConcatenatePackets (uint8_t * buf, size_t len)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while (pos < len && !m_ReceiveQueue.empty ())
|
||||
{
|
||||
Packet * packet = m_ReceiveQueue.front ();
|
||||
size_t l = std::min (packet->GetLength (), len - pos);
|
||||
memcpy (buf + pos, packet->GetBuffer (), l);
|
||||
pos += l;
|
||||
packet->offset += l;
|
||||
if (!packet->GetLength ())
|
||||
{
|
||||
m_ReceiveQueue.pop ();
|
||||
delete packet;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool Stream::SendPacket (Packet * packet)
|
||||
{
|
||||
if (packet)
|
||||
{
|
||||
if (m_IsAckSendScheduled)
|
||||
{
|
||||
m_IsAckSendScheduled = false;
|
||||
m_AckSendTimer.cancel ();
|
||||
}
|
||||
SendPackets (std::vector<Packet *> { packet });
|
||||
if (m_IsOpen)
|
||||
{
|
||||
bool isEmpty = m_SentPackets.empty ();
|
||||
m_SentPackets.insert (packet);
|
||||
if (isEmpty)
|
||||
ScheduleResend ();
|
||||
}
|
||||
else
|
||||
delete packet;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void Stream::PostPackets (const std::vector<Packet *> packets)
|
||||
{
|
||||
if (m_IsOpen)
|
||||
{
|
||||
if (packets.size () > 0)
|
||||
{
|
||||
m_IsAckSendScheduled = false;
|
||||
m_AckSendTimer.cancel ();
|
||||
}
|
||||
bool isEmpty = m_SentPackets.empty ();
|
||||
for (auto it: packets)
|
||||
m_SentPackets.insert (it);
|
||||
SendPackets (packets);
|
||||
if (isEmpty)
|
||||
ScheduleResend ();
|
||||
}
|
||||
else
|
||||
{
|
||||
// delete
|
||||
for (auto it: packets)
|
||||
delete it;
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::SendPackets (const std::vector<Packet *>& packets)
|
||||
{
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
UpdateCurrentRemoteLease ();
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
LogPrint ("Can't send packets. Missing remote LeaseSet");
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
|
||||
if (!m_CurrentOutboundTunnel)
|
||||
{
|
||||
LogPrint ("No outbound tunnels in the pool");
|
||||
return;
|
||||
}
|
||||
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (ts >= m_CurrentRemoteLease.endDate)
|
||||
UpdateCurrentRemoteLease ();
|
||||
if (ts < m_CurrentRemoteLease.endDate)
|
||||
{
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
for (auto it: packets)
|
||||
{
|
||||
auto msg = m_RoutingSession->WrapSingleMessage (CreateDataMessage (it->GetBuffer (), it->GetLength ()));
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeTunnel,
|
||||
m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID,
|
||||
msg
|
||||
});
|
||||
m_NumSentBytes += it->GetLength ();
|
||||
}
|
||||
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
|
||||
}
|
||||
else
|
||||
LogPrint ("All leases are expired");
|
||||
}
|
||||
|
||||
void Stream::ScheduleResend ()
|
||||
{
|
||||
m_ResendTimer.cancel ();
|
||||
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_TIMEOUT));
|
||||
m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Stream::HandleResendTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
std::vector<Packet *> packets;
|
||||
for (auto it : m_SentPackets)
|
||||
{
|
||||
it->numResendAttempts++;
|
||||
if (it->numResendAttempts <= MAX_NUM_RESEND_ATTEMPTS)
|
||||
packets.push_back (it);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Packet ", it->GetSeqn (), "was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts. Terminate");
|
||||
m_IsOpen = false;
|
||||
m_IsReset = true;
|
||||
m_ReceiveTimer.cancel ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (packets.size () > 0)
|
||||
{
|
||||
m_CurrentOutboundTunnel = nullptr; // pick another outbound tunnel
|
||||
UpdateCurrentRemoteLease (); // pick another lease
|
||||
SendPackets (packets);
|
||||
}
|
||||
ScheduleResend ();
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::HandleAckSendTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (m_IsAckSendScheduled)
|
||||
{
|
||||
if (m_IsOpen)
|
||||
SendQuickAck ();
|
||||
m_IsAckSendScheduled = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::UpdateCurrentRemoteLease ()
|
||||
{
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
m_RemoteLeaseSet = m_LocalDestination.GetOwner ().FindLeaseSet (m_RemoteIdentity.GetIdentHash ());
|
||||
if (!m_RemoteLeaseSet)
|
||||
LogPrint ("LeaseSet ", m_RemoteIdentity.GetIdentHash ().ToBase64 (), " not found");
|
||||
}
|
||||
if (m_RemoteLeaseSet)
|
||||
{
|
||||
if (!m_RoutingSession)
|
||||
m_RoutingSession = m_LocalDestination.GetOwner ().GetRoutingSession (*m_RemoteLeaseSet, 32);
|
||||
auto leases = m_RemoteLeaseSet->GetNonExpiredLeases ();
|
||||
if (!leases.empty ())
|
||||
{
|
||||
uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1);
|
||||
m_CurrentRemoteLease = leases[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
m_RemoteLeaseSet = m_LocalDestination.GetOwner ().FindLeaseSet (m_RemoteIdentity.GetIdentHash ()); // re-request expired
|
||||
m_CurrentRemoteLease.endDate = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_CurrentRemoteLease.endDate = 0;
|
||||
}
|
||||
|
||||
I2NPMessage * Stream::CreateDataMessage (const uint8_t * payload, size_t len)
|
||||
{
|
||||
I2NPMessage * msg = NewI2NPShortMessage ();
|
||||
CryptoPP::Gzip compressor;
|
||||
if (len <= i2p::stream::COMPRESSION_THRESHOLD_SIZE)
|
||||
compressor.SetDeflateLevel (CryptoPP::Gzip::MIN_DEFLATE_LEVEL);
|
||||
else
|
||||
compressor.SetDeflateLevel (CryptoPP::Gzip::DEFAULT_DEFLATE_LEVEL);
|
||||
compressor.Put (payload, len);
|
||||
compressor.MessageEnd();
|
||||
int size = compressor.MaxRetrievable ();
|
||||
uint8_t * buf = msg->GetPayload ();
|
||||
*(uint32_t *)buf = htobe32 (size); // length
|
||||
buf += 4;
|
||||
compressor.Get (buf, size);
|
||||
*(uint16_t *)(buf + 4) = 0; // source port
|
||||
*(uint16_t *)(buf + 6) = htobe16 (m_Port); // destination port
|
||||
buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol
|
||||
msg->len += size + 4;
|
||||
FillI2NPMessageHeader (msg, eI2NPData);
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
void StreamingDestination::Start ()
|
||||
{
|
||||
}
|
||||
|
||||
void StreamingDestination::Stop ()
|
||||
{
|
||||
ResetAcceptor ();
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams.clear ();
|
||||
}
|
||||
}
|
||||
|
||||
void StreamingDestination::HandleNextPacket (Packet * packet)
|
||||
{
|
||||
uint32_t sendStreamID = packet->GetSendStreamID ();
|
||||
if (sendStreamID)
|
||||
{
|
||||
auto it = m_Streams.find (sendStreamID);
|
||||
if (it != m_Streams.end ())
|
||||
it->second->HandleNextPacket (packet);
|
||||
else
|
||||
{
|
||||
LogPrint ("Unknown stream ", sendStreamID);
|
||||
delete packet;
|
||||
}
|
||||
}
|
||||
else // new incoming stream
|
||||
{
|
||||
auto incomingStream = CreateNewIncomingStream ();
|
||||
incomingStream->HandleNextPacket (packet);
|
||||
if (m_Acceptor != nullptr)
|
||||
m_Acceptor (incomingStream);
|
||||
else
|
||||
{
|
||||
LogPrint ("Acceptor for incoming stream is not set");
|
||||
DeleteStream (incomingStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Stream> StreamingDestination::CreateNewOutgoingStream (const i2p::data::LeaseSet& remote, int port)
|
||||
{
|
||||
auto s = std::make_shared<Stream> (m_Owner.GetService (), *this, remote, port);
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams[s->GetRecvStreamID ()] = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream ()
|
||||
{
|
||||
auto s = std::make_shared<Stream> (m_Owner.GetService (), *this);
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams[s->GetRecvStreamID ()] = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
void StreamingDestination::DeleteStream (std::shared_ptr<Stream> stream)
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
auto it = m_Streams.find (stream->GetRecvStreamID ());
|
||||
if (it != m_Streams.end ())
|
||||
m_Streams.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len)
|
||||
{
|
||||
// unzip it
|
||||
CryptoPP::Gunzip decompressor;
|
||||
decompressor.Put (buf, len);
|
||||
decompressor.MessageEnd();
|
||||
Packet * uncompressed = new Packet;
|
||||
uncompressed->offset = 0;
|
||||
uncompressed->len = decompressor.MaxRetrievable ();
|
||||
if (uncompressed->len <= MAX_PACKET_SIZE)
|
||||
{
|
||||
decompressor.Get (uncompressed->buf, uncompressed->len);
|
||||
HandleNextPacket (uncompressed);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Received packet size ", uncompressed->len, " exceeds max packet size. Skipped");
|
||||
delete uncompressed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
236
Streaming.h
236
Streaming.h
@@ -1,236 +0,0 @@
|
||||
#ifndef STREAMING_H__
|
||||
#define STREAMING_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <boost/asio.hpp>
|
||||
#include "I2PEndian.h"
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Garlic.h"
|
||||
#include "Tunnel.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
class ClientDestination;
|
||||
}
|
||||
namespace stream
|
||||
{
|
||||
const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001;
|
||||
const uint16_t PACKET_FLAG_CLOSE = 0x0002;
|
||||
const uint16_t PACKET_FLAG_RESET = 0x0004;
|
||||
const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008;
|
||||
const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010;
|
||||
const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020;
|
||||
const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040;
|
||||
const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080;
|
||||
const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100;
|
||||
const uint16_t PACKET_FLAG_ECHO = 0x0200;
|
||||
const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
|
||||
|
||||
const size_t STREAMING_MTU = 1730;
|
||||
const size_t MAX_PACKET_SIZE = 4096;
|
||||
const size_t COMPRESSION_THRESHOLD_SIZE = 66;
|
||||
const int RESEND_TIMEOUT = 10; // in seconds
|
||||
const int ACK_SEND_TIMEOUT = 200; // in milliseconds
|
||||
const int MAX_NUM_RESEND_ATTEMPTS = 5;
|
||||
|
||||
struct Packet
|
||||
{
|
||||
size_t len, offset;
|
||||
uint8_t buf[MAX_PACKET_SIZE];
|
||||
int numResendAttempts;
|
||||
|
||||
Packet (): len (0), offset (0), numResendAttempts (0) {};
|
||||
uint8_t * GetBuffer () { return buf + offset; };
|
||||
size_t GetLength () const { return len - offset; };
|
||||
|
||||
uint32_t GetSendStreamID () const { return be32toh (*(uint32_t *)buf); };
|
||||
uint32_t GetReceiveStreamID () const { return be32toh (*(uint32_t *)(buf + 4)); };
|
||||
uint32_t GetSeqn () const { return be32toh (*(uint32_t *)(buf + 8)); };
|
||||
uint32_t GetAckThrough () const { return be32toh (*(uint32_t *)(buf + 12)); };
|
||||
uint8_t GetNACKCount () const { return buf[16]; };
|
||||
uint32_t GetNACK (int i) const { return be32toh (((uint32_t *)(buf + 17))[i]); };
|
||||
const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags
|
||||
uint16_t GetFlags () const { return be16toh (*(uint16_t *)(GetOption () - 2)); };
|
||||
uint16_t GetOptionSize () const { return be16toh (*(uint16_t *)GetOption ()); };
|
||||
const uint8_t * GetOptionData () const { return GetOption () + 2; };
|
||||
const uint8_t * GetPayload () const { return GetOptionData () + GetOptionSize (); };
|
||||
|
||||
bool IsSYN () const { return GetFlags () & PACKET_FLAG_SYNCHRONIZE; };
|
||||
bool IsNoAck () const { return GetFlags () & PACKET_FLAG_NO_ACK; };
|
||||
};
|
||||
|
||||
struct PacketCmp
|
||||
{
|
||||
bool operator() (const Packet * p1, const Packet * p2) const
|
||||
{
|
||||
return p1->GetSeqn () < p2->GetSeqn ();
|
||||
};
|
||||
};
|
||||
|
||||
class StreamingDestination;
|
||||
class Stream: public std::enable_shared_from_this<Stream>
|
||||
{
|
||||
public:
|
||||
|
||||
Stream (boost::asio::io_service& service, StreamingDestination& local,
|
||||
const i2p::data::LeaseSet& remote, int port = 0); // outgoing
|
||||
Stream (boost::asio::io_service& service, StreamingDestination& local); // incoming
|
||||
|
||||
~Stream ();
|
||||
uint32_t GetSendStreamID () const { return m_SendStreamID; };
|
||||
uint32_t GetRecvStreamID () const { return m_RecvStreamID; };
|
||||
const i2p::data::LeaseSet * GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
|
||||
const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; };
|
||||
bool IsOpen () const { return m_IsOpen; };
|
||||
bool IsEstablished () const { return m_SendStreamID; };
|
||||
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
|
||||
|
||||
void HandleNextPacket (Packet * packet);
|
||||
size_t Send (const uint8_t * buf, size_t len);
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
|
||||
|
||||
void Close ();
|
||||
|
||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
size_t GetSendQueueSize () const { return m_SentPackets.size (); };
|
||||
size_t GetReceiveQueueSize () const { return m_ReceiveQueue.size (); };
|
||||
|
||||
private:
|
||||
|
||||
void SendQuickAck ();
|
||||
bool SendPacket (Packet * packet);
|
||||
void PostPackets (const std::vector<Packet *> packets);
|
||||
void SendPackets (const std::vector<Packet *>& packets);
|
||||
|
||||
void SavePacket (Packet * packet);
|
||||
void ProcessPacket (Packet * packet);
|
||||
void ProcessAck (Packet * packet);
|
||||
size_t ConcatenatePackets (uint8_t * buf, size_t len);
|
||||
|
||||
void UpdateCurrentRemoteLease ();
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler);
|
||||
|
||||
void ScheduleResend ();
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
void HandleAckSendTimer (const boost::system::error_code& ecode);
|
||||
|
||||
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service& m_Service;
|
||||
uint32_t m_SendStreamID, m_RecvStreamID, m_SequenceNumber;
|
||||
int32_t m_LastReceivedSequenceNumber;
|
||||
bool m_IsOpen, m_IsReset, m_IsAckSendScheduled;
|
||||
StreamingDestination& m_LocalDestination;
|
||||
i2p::data::IdentityEx m_RemoteIdentity;
|
||||
const i2p::data::LeaseSet * m_RemoteLeaseSet;
|
||||
i2p::garlic::GarlicRoutingSession * m_RoutingSession;
|
||||
i2p::data::Lease m_CurrentRemoteLease;
|
||||
i2p::tunnel::OutboundTunnel * m_CurrentOutboundTunnel;
|
||||
std::queue<Packet *> m_ReceiveQueue;
|
||||
std::set<Packet *, PacketCmp> m_SavedPackets;
|
||||
std::set<Packet *, PacketCmp> m_SentPackets;
|
||||
boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer;
|
||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
||||
uint16_t m_Port;
|
||||
};
|
||||
|
||||
class StreamingDestination
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::function<void (std::shared_ptr<Stream>)> Acceptor;
|
||||
|
||||
StreamingDestination (i2p::client::ClientDestination& owner): m_Owner (owner) {};
|
||||
~StreamingDestination () {};
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
std::shared_ptr<Stream> CreateNewOutgoingStream (const i2p::data::LeaseSet& remote, int port = 0);
|
||||
void DeleteStream (std::shared_ptr<Stream> stream);
|
||||
void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; };
|
||||
void ResetAcceptor () { m_Acceptor = nullptr; };
|
||||
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
|
||||
i2p::client::ClientDestination& GetOwner () { return m_Owner; };
|
||||
|
||||
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
void HandleNextPacket (Packet * packet);
|
||||
std::shared_ptr<Stream> CreateNewIncomingStream ();
|
||||
|
||||
private:
|
||||
|
||||
i2p::client::ClientDestination& m_Owner;
|
||||
std::mutex m_StreamsMutex;
|
||||
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams;
|
||||
Acceptor m_Acceptor;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
|
||||
};
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
|
||||
{
|
||||
if (!m_ReceiveQueue.empty ())
|
||||
{
|
||||
auto s = shared_from_this();
|
||||
m_Service.post ([=](void) { s->HandleReceiveTimer (
|
||||
boost::asio::error::make_error_code (boost::asio::error::operation_aborted),
|
||||
buffer, handler); });
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
|
||||
auto s = shared_from_this();
|
||||
m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
|
||||
{ s->HandleReceiveTimer (ecode, buffer, handler); });
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
|
||||
{
|
||||
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
|
||||
if (ecode == boost::asio::error::operation_aborted)
|
||||
{
|
||||
// timeout not expired
|
||||
if (m_IsOpen)
|
||||
// no error
|
||||
handler (boost::system::error_code (), received);
|
||||
else
|
||||
// socket closed
|
||||
handler (m_IsReset ? boost::asio::error::make_error_code (boost::asio::error::connection_reset) :
|
||||
boost::asio::error::make_error_code (boost::asio::error::operation_aborted), 0);
|
||||
}
|
||||
else
|
||||
// timeout expired
|
||||
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,85 +0,0 @@
|
||||
#include <string.h>
|
||||
#include "I2PEndian.h"
|
||||
#include "Log.h"
|
||||
#include "RouterContext.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Tunnel.h"
|
||||
#include "Transports.h"
|
||||
#include "TransitTunnel.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
TransitTunnel::TransitTunnel (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey):
|
||||
m_TunnelID (receiveTunnelID), m_NextTunnelID (nextTunnelID),
|
||||
m_NextIdent (nextIdent), m_NumTransmittedBytes (0)
|
||||
{
|
||||
m_Encryption.SetKeys (layerKey, ivKey);
|
||||
}
|
||||
|
||||
void TransitTunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg)
|
||||
{
|
||||
m_Encryption.Encrypt (tunnelMsg->GetPayload () + 4);
|
||||
}
|
||||
|
||||
void TransitTunnel::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
|
||||
{
|
||||
EncryptTunnelMsg (tunnelMsg);
|
||||
|
||||
LogPrint ("TransitTunnel: ",m_TunnelID,"->", m_NextTunnelID);
|
||||
m_NumTransmittedBytes += tunnelMsg->GetLength ();
|
||||
*(uint32_t *)(tunnelMsg->GetPayload ()) = htobe32 (m_NextTunnelID);
|
||||
FillI2NPMessageHeader (tunnelMsg, eI2NPTunnelData);
|
||||
|
||||
i2p::transport::transports.SendMessage (m_NextIdent, tunnelMsg);
|
||||
}
|
||||
|
||||
void TransitTunnel::SendTunnelDataMsg (i2p::I2NPMessage * msg)
|
||||
{
|
||||
LogPrint ("We are not a gateway for transit tunnel ", m_TunnelID);
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
|
||||
void TransitTunnelGateway::SendTunnelDataMsg (i2p::I2NPMessage * msg)
|
||||
{
|
||||
TunnelMessageBlock block;
|
||||
block.deliveryType = eDeliveryTypeLocal;
|
||||
block.data = msg;
|
||||
std::unique_lock<std::mutex> l(m_SendMutex);
|
||||
m_Gateway.SendTunnelDataMsg (block);
|
||||
}
|
||||
|
||||
void TransitTunnelEndpoint::HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg)
|
||||
{
|
||||
EncryptTunnelMsg (tunnelMsg);
|
||||
|
||||
LogPrint ("TransitTunnel endpoint for ", GetTunnelID ());
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (tunnelMsg);
|
||||
}
|
||||
|
||||
TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey,
|
||||
bool isGateway, bool isEndpoint)
|
||||
{
|
||||
if (isEndpoint)
|
||||
{
|
||||
LogPrint ("TransitTunnel endpoint: ", receiveTunnelID, " created");
|
||||
return new TransitTunnelEndpoint (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
}
|
||||
else if (isGateway)
|
||||
{
|
||||
LogPrint ("TransitTunnel gateway: ", receiveTunnelID, " created");
|
||||
return new TransitTunnelGateway (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
|
||||
return new TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
#ifndef TRANSIT_TUNNEL_H__
|
||||
#define TRANSIT_TUNNEL_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <mutex>
|
||||
#include "aes.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TunnelEndpoint.h"
|
||||
#include "TunnelGateway.h"
|
||||
#include "TunnelBase.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
class TransitTunnel: public TunnelBase // tunnel patricipant
|
||||
{
|
||||
public:
|
||||
|
||||
TransitTunnel (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey);
|
||||
|
||||
virtual void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
|
||||
virtual void SendTunnelDataMsg (i2p::I2NPMessage * msg);
|
||||
virtual size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
|
||||
|
||||
uint32_t GetTunnelID () const { return m_TunnelID; };
|
||||
|
||||
// implements TunnelBase
|
||||
void EncryptTunnelMsg (I2NPMessage * tunnelMsg);
|
||||
uint32_t GetNextTunnelID () const { return m_NextTunnelID; };
|
||||
const i2p::data::IdentHash& GetNextIdentHash () const { return m_NextIdent; };
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_TunnelID, m_NextTunnelID;
|
||||
i2p::data::IdentHash m_NextIdent;
|
||||
size_t m_NumTransmittedBytes;
|
||||
|
||||
i2p::crypto::TunnelEncryption m_Encryption;
|
||||
};
|
||||
|
||||
class TransitTunnelGateway: public TransitTunnel
|
||||
{
|
||||
public:
|
||||
|
||||
TransitTunnelGateway (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey):
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID,
|
||||
layerKey, ivKey), m_Gateway(this) {};
|
||||
|
||||
void SendTunnelDataMsg (i2p::I2NPMessage * msg);
|
||||
size_t GetNumTransmittedBytes () const { return m_Gateway.GetNumSentBytes (); };
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_SendMutex;
|
||||
TunnelGateway m_Gateway;
|
||||
};
|
||||
|
||||
class TransitTunnelEndpoint: public TransitTunnel
|
||||
{
|
||||
public:
|
||||
|
||||
TransitTunnelEndpoint (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey):
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
|
||||
m_Endpoint (false) {}; // transit endpoint is always outbound
|
||||
|
||||
void HandleTunnelDataMsg (i2p::I2NPMessage * tunnelMsg);
|
||||
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
|
||||
|
||||
private:
|
||||
|
||||
TunnelEndpoint m_Endpoint;
|
||||
};
|
||||
|
||||
TransitTunnel * CreateTransitTunnel (uint32_t receiveTunnelID,
|
||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
||||
const uint8_t * layerKey,const uint8_t * ivKey,
|
||||
bool isGateway, bool isEndpoint);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,76 +0,0 @@
|
||||
#ifndef TRANSPORT_SESSION_H__
|
||||
#define TRANSPORT_SESSION_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include "Identity.h"
|
||||
#include "RouterInfo.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
struct DHKeysPair // transient keys for transport sessions
|
||||
{
|
||||
uint8_t publicKey[256];
|
||||
uint8_t privateKey[256];
|
||||
};
|
||||
|
||||
class SignedData
|
||||
{
|
||||
public:
|
||||
|
||||
SignedData () {};
|
||||
void Insert (const uint8_t * buf, size_t len)
|
||||
{
|
||||
m_Stream.write ((char *)buf, len);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void Insert (T t)
|
||||
{
|
||||
m_Stream.write ((char *)&t, sizeof (T));
|
||||
}
|
||||
|
||||
bool Verify (const i2p::data::IdentityEx& ident, const uint8_t * signature) const
|
||||
{
|
||||
return ident.Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
||||
}
|
||||
|
||||
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
|
||||
{
|
||||
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::stringstream m_Stream;
|
||||
};
|
||||
|
||||
class TransportSession
|
||||
{
|
||||
public:
|
||||
|
||||
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
|
||||
m_RemoteRouter (in_RemoteRouter), m_DHKeysPair (nullptr)
|
||||
{
|
||||
if (m_RemoteRouter)
|
||||
m_RemoteIdentity = m_RemoteRouter->GetRouterIdentity ();
|
||||
}
|
||||
|
||||
virtual ~TransportSession () { delete m_DHKeysPair; };
|
||||
|
||||
std::shared_ptr<const i2p::data::RouterInfo> GetRemoteRouter () { return m_RemoteRouter; };
|
||||
const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; };
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<const i2p::data::RouterInfo> m_RemoteRouter;
|
||||
i2p::data::IdentityEx m_RemoteIdentity;
|
||||
DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
401
Transports.cpp
401
Transports.cpp
@@ -1,401 +0,0 @@
|
||||
#include <cryptopp/dh.h>
|
||||
#include <boost/bind.hpp>
|
||||
#include "Log.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "RouterContext.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "NetDb.h"
|
||||
#include "Transports.h"
|
||||
|
||||
using namespace i2p::data;
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
DHKeysPairSupplier::DHKeysPairSupplier (int size):
|
||||
m_QueueSize (size), m_IsRunning (false), m_Thread (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
DHKeysPairSupplier::~DHKeysPairSupplier ()
|
||||
{
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void DHKeysPairSupplier::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&DHKeysPairSupplier::Run, this));
|
||||
}
|
||||
|
||||
void DHKeysPairSupplier::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Acquired.notify_one ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DHKeysPairSupplier::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
int num;
|
||||
while ((num = m_QueueSize - m_Queue.size ()) > 0)
|
||||
CreateDHKeysPairs (num);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Acquired.wait (l); // wait for element gets aquired
|
||||
}
|
||||
}
|
||||
|
||||
void DHKeysPairSupplier::CreateDHKeysPairs (int num)
|
||||
{
|
||||
if (num > 0)
|
||||
{
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
i2p::transport::DHKeysPair * pair = new i2p::transport::DHKeysPair ();
|
||||
dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Queue.push (pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DHKeysPair * DHKeysPairSupplier::Acquire ()
|
||||
{
|
||||
if (!m_Queue.empty ())
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
auto pair = m_Queue.front ();
|
||||
m_Queue.pop ();
|
||||
m_Acquired.notify_one ();
|
||||
return pair;
|
||||
}
|
||||
else // queue is empty, create new
|
||||
{
|
||||
DHKeysPair * pair = new DHKeysPair ();
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(m_Rnd, pair->privateKey, pair->publicKey);
|
||||
return pair;
|
||||
}
|
||||
}
|
||||
|
||||
void DHKeysPairSupplier::Return (DHKeysPair * pair)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Queue.push (pair);
|
||||
}
|
||||
|
||||
Transports transports;
|
||||
|
||||
Transports::Transports ():
|
||||
m_Thread (nullptr), m_Work (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr),
|
||||
m_SSUServer (nullptr), m_DHKeysPairSupplier (5) // 5 pre-generated keys
|
||||
{
|
||||
}
|
||||
|
||||
Transports::~Transports ()
|
||||
{
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void Transports::Start ()
|
||||
{
|
||||
m_DHKeysPairSupplier.Start ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Transports::Run, this));
|
||||
// create acceptors
|
||||
auto addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (auto& address : addresses)
|
||||
{
|
||||
if (address.transportStyle == RouterInfo::eTransportNTCP && address.host.is_v4 ())
|
||||
{
|
||||
m_NTCPAcceptor = new boost::asio::ip::tcp::acceptor (m_Service,
|
||||
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), address.port));
|
||||
|
||||
LogPrint ("Start listening TCP port ", address.port);
|
||||
auto conn = std::make_shared<NTCPSession>(m_Service);
|
||||
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
|
||||
conn, boost::asio::placeholders::error));
|
||||
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
m_NTCPV6Acceptor = new boost::asio::ip::tcp::acceptor (m_Service);
|
||||
m_NTCPV6Acceptor->open (boost::asio::ip::tcp::v6());
|
||||
m_NTCPV6Acceptor->set_option (boost::asio::ip::v6_only (true));
|
||||
m_NTCPV6Acceptor->bind (boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address.port));
|
||||
m_NTCPV6Acceptor->listen ();
|
||||
|
||||
LogPrint ("Start listening V6 TCP port ", address.port);
|
||||
auto conn = std::make_shared<NTCPSession> (m_Service);
|
||||
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAcceptV6,
|
||||
this, conn, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
else if (address.transportStyle == RouterInfo::eTransportSSU && address.host.is_v4 ())
|
||||
{
|
||||
if (!m_SSUServer)
|
||||
{
|
||||
m_SSUServer = new SSUServer (address.port);
|
||||
LogPrint ("Start listening UDP port ", address.port);
|
||||
m_SSUServer->Start ();
|
||||
DetectExternalIP ();
|
||||
}
|
||||
else
|
||||
LogPrint ("SSU server already exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::Stop ()
|
||||
{
|
||||
if (m_SSUServer)
|
||||
{
|
||||
m_SSUServer->Stop ();
|
||||
delete m_SSUServer;
|
||||
m_SSUServer = nullptr;
|
||||
}
|
||||
m_NTCPSessions.clear ();
|
||||
|
||||
delete m_NTCPAcceptor;
|
||||
m_NTCPAcceptor = nullptr;
|
||||
delete m_NTCPV6Acceptor;
|
||||
m_NTCPV6Acceptor = nullptr;
|
||||
|
||||
m_DHKeysPairSupplier.Stop ();
|
||||
m_IsRunning = false;
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("Transports: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::AddNTCPSession (std::shared_ptr<NTCPSession> session)
|
||||
{
|
||||
if (session)
|
||||
m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session;
|
||||
}
|
||||
|
||||
void Transports::RemoveNTCPSession (std::shared_ptr<NTCPSession> session)
|
||||
{
|
||||
if (session)
|
||||
m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ());
|
||||
}
|
||||
|
||||
void Transports::HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ());
|
||||
conn->ServerLogin ();
|
||||
}
|
||||
|
||||
|
||||
if (error != boost::asio::error::operation_aborted)
|
||||
{
|
||||
conn = std::make_shared<NTCPSession> (m_Service);
|
||||
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
|
||||
conn, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ());
|
||||
conn->ServerLogin ();
|
||||
}
|
||||
|
||||
if (error != boost::asio::error::operation_aborted)
|
||||
{
|
||||
conn = std::make_shared<NTCPSession> (m_Service);
|
||||
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAcceptV6, this,
|
||||
conn, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn)
|
||||
{
|
||||
LogPrint ("Connecting to ", address ,":", port);
|
||||
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
|
||||
boost::bind (&Transports::HandleConnect, this, boost::asio::placeholders::error, conn));
|
||||
}
|
||||
|
||||
void Transports::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Connect error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ().GetIdentHash (), true);
|
||||
conn->Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Connected");
|
||||
if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6
|
||||
context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ());
|
||||
conn->ClientLogin ();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<NTCPSession> Transports::GetNextNTCPSession ()
|
||||
{
|
||||
for (auto session: m_NTCPSessions)
|
||||
if (session.second->IsEstablished ())
|
||||
return session.second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<NTCPSession> Transports::FindNTCPSession (const i2p::data::IdentHash& ident)
|
||||
{
|
||||
auto it = m_NTCPSessions.find (ident);
|
||||
if (it != m_NTCPSessions.end ())
|
||||
return it->second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Transports::SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
|
||||
{
|
||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
||||
// we send it to ourself
|
||||
i2p::HandleI2NPMessage (msg);
|
||||
else
|
||||
m_Service.post (boost::bind (&Transports::PostMessage, this, ident, msg));
|
||||
}
|
||||
|
||||
void Transports::PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
|
||||
{
|
||||
auto session = FindNTCPSession (ident);
|
||||
if (session)
|
||||
session->SendI2NPMessage (msg);
|
||||
else
|
||||
{
|
||||
auto r = netdb.FindRouter (ident);
|
||||
if (r)
|
||||
{
|
||||
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (r) : nullptr;
|
||||
if (ssuSession)
|
||||
ssuSession->SendI2NPMessage (msg);
|
||||
else
|
||||
{
|
||||
// existing session not found. create new
|
||||
// try NTCP first if message size < 16K
|
||||
auto address = r->GetNTCPAddress (!context.SupportsV6 ());
|
||||
if (address && !r->UsesIntroducer () && !r->IsUnreachable () && msg->GetLength () < NTCP_MAX_MESSAGE_SIZE)
|
||||
{
|
||||
auto s = std::make_shared<NTCPSession> (m_Service, r);
|
||||
AddNTCPSession (s);
|
||||
s->SendI2NPMessage (msg);
|
||||
Connect (address->host, address->port, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
// then SSU
|
||||
auto s = m_SSUServer ? m_SSUServer->GetSession (r) : nullptr;
|
||||
if (s)
|
||||
s->SendI2NPMessage (msg);
|
||||
else
|
||||
{
|
||||
LogPrint ("No NTCP and SSU addresses available");
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Router not found. Requested");
|
||||
i2p::data::netdb.RequestDestination (ident);
|
||||
auto resendTimer = new boost::asio::deadline_timer (m_Service);
|
||||
resendTimer->expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
|
||||
resendTimer->async_wait (boost::bind (&Transports::HandleResendTimer,
|
||||
this, boost::asio::placeholders::error, resendTimer, ident, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::HandleResendTimer (const boost::system::error_code& ecode,
|
||||
boost::asio::deadline_timer * timer, const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg)
|
||||
{
|
||||
auto r = netdb.FindRouter (ident);
|
||||
if (r)
|
||||
{
|
||||
LogPrint ("Router found. Sending message");
|
||||
PostMessage (ident, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Router not found. Failed to send message");
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
delete timer;
|
||||
}
|
||||
|
||||
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
||||
{
|
||||
if (!router) return;
|
||||
m_Service.post (boost::bind (&Transports::PostCloseSession, this, router));
|
||||
}
|
||||
|
||||
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
||||
{
|
||||
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
|
||||
if (ssuSession) // try SSU first
|
||||
{
|
||||
m_SSUServer->DeleteSession (ssuSession);
|
||||
LogPrint ("SSU session closed");
|
||||
}
|
||||
// TODO: delete NTCP
|
||||
}
|
||||
|
||||
void Transports::DetectExternalIP ()
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
||||
if (router && router->IsSSU () && m_SSUServer)
|
||||
m_SSUServer->GetSession (router, true); // peer test
|
||||
}
|
||||
}
|
||||
|
||||
DHKeysPair * Transports::GetNextDHKeysPair ()
|
||||
{
|
||||
return m_DHKeysPairSupplier.Acquire ();
|
||||
}
|
||||
|
||||
void Transports::ReuseDHKeysPair (DHKeysPair * pair)
|
||||
{
|
||||
m_DHKeysPairSupplier.Return (pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
115
Transports.h
115
Transports.h
@@ -1,115 +0,0 @@
|
||||
#ifndef TRANSPORTS_H__
|
||||
#define TRANSPORTS_H__
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <cryptopp/osrng.h>
|
||||
#include <boost/asio.hpp>
|
||||
#include "TransportSession.h"
|
||||
#include "NTCPSession.h"
|
||||
#include "SSU.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
class DHKeysPairSupplier
|
||||
{
|
||||
public:
|
||||
|
||||
DHKeysPairSupplier (int size);
|
||||
~DHKeysPairSupplier ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
DHKeysPair * Acquire ();
|
||||
void Return (DHKeysPair * pair);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void CreateDHKeysPairs (int num);
|
||||
|
||||
private:
|
||||
|
||||
const int m_QueueSize;
|
||||
std::queue<DHKeysPair *> m_Queue;
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
std::condition_variable m_Acquired;
|
||||
std::mutex m_AcquiredMutex;
|
||||
CryptoPP::AutoSeededRandomPool m_Rnd;
|
||||
};
|
||||
|
||||
class Transports
|
||||
{
|
||||
public:
|
||||
|
||||
Transports ();
|
||||
~Transports ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
i2p::transport::DHKeysPair * GetNextDHKeysPair ();
|
||||
void ReuseDHKeysPair (DHKeysPair * pair);
|
||||
|
||||
void AddNTCPSession (std::shared_ptr<NTCPSession> session);
|
||||
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
|
||||
|
||||
std::shared_ptr<NTCPSession> GetNextNTCPSession ();
|
||||
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
|
||||
|
||||
void SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
|
||||
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
|
||||
void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
|
||||
void HandleResendTimer (const boost::system::error_code& ecode, boost::asio::deadline_timer * timer,
|
||||
const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
|
||||
void PostMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
|
||||
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
||||
|
||||
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
|
||||
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn);
|
||||
|
||||
void DetectExternalIP ();
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
|
||||
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions;
|
||||
SSUServer * m_SSUServer;
|
||||
|
||||
DHKeysPairSupplier m_DHKeysPairSupplier;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
|
||||
const SSUServer * GetSSUServer () const { return m_SSUServer; };
|
||||
};
|
||||
|
||||
extern Transports transports;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
621
Tunnel.cpp
621
Tunnel.cpp
@@ -1,621 +0,0 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <cryptopp/sha.h>
|
||||
#include "RouterContext.h"
|
||||
#include "Log.h"
|
||||
#include "Timestamp.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Transports.h"
|
||||
#include "NetDb.h"
|
||||
#include "Tunnel.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
|
||||
Tunnel::Tunnel (TunnelConfig * config):
|
||||
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending)
|
||||
{
|
||||
}
|
||||
|
||||
Tunnel::~Tunnel ()
|
||||
{
|
||||
delete m_Config;
|
||||
}
|
||||
|
||||
void Tunnel::Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel)
|
||||
{
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
auto numHops = m_Config->GetNumHops ();
|
||||
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
|
||||
I2NPMessage * msg = NewI2NPMessage ();
|
||||
*msg->GetPayload () = numRecords;
|
||||
msg->len += numRecords*sizeof (I2NPBuildRequestRecordElGamalEncrypted) + 1;
|
||||
|
||||
// shuffle records
|
||||
std::vector<int> recordIndicies;
|
||||
for (int i = 0; i < numRecords; i++) recordIndicies.push_back(i);
|
||||
std::random_shuffle (recordIndicies.begin(), recordIndicies.end());
|
||||
|
||||
// create real records
|
||||
I2NPBuildRequestRecordElGamalEncrypted * records = (I2NPBuildRequestRecordElGamalEncrypted *)(msg->GetPayload () + 1);
|
||||
TunnelHopConfig * hop = m_Config->GetFirstHop ();
|
||||
int i = 0;
|
||||
while (hop)
|
||||
{
|
||||
int idx = recordIndicies[i];
|
||||
EncryptBuildRequestRecord (*hop->router,
|
||||
CreateBuildRequestRecord (hop->router->GetIdentHash (),
|
||||
hop->tunnelID,
|
||||
hop->nextRouter->GetIdentHash (),
|
||||
hop->nextTunnelID,
|
||||
hop->layerKey, hop->ivKey,
|
||||
hop->replyKey, hop->replyIV,
|
||||
hop->next ? rnd.GenerateWord32 () : replyMsgID, // we set replyMsgID for last hop only
|
||||
hop->isGateway, hop->isEndpoint),
|
||||
records[idx]);
|
||||
hop->recordIndex = idx;
|
||||
i++;
|
||||
hop = hop->next;
|
||||
}
|
||||
// fill up fake records with random data
|
||||
for (int i = numHops; i < numRecords; i++)
|
||||
{
|
||||
int idx = recordIndicies[i];
|
||||
rnd.GenerateBlock ((uint8_t *)(records + idx), sizeof (records[idx]));
|
||||
}
|
||||
|
||||
// decrypt real records
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
hop = m_Config->GetLastHop ()->prev;
|
||||
while (hop)
|
||||
{
|
||||
decryption.SetKey (hop->replyKey);
|
||||
// decrypt records after current hop
|
||||
TunnelHopConfig * hop1 = hop->next;
|
||||
while (hop1)
|
||||
{
|
||||
decryption.SetIV (hop->replyIV);
|
||||
decryption.Decrypt((uint8_t *)&records[hop1->recordIndex],
|
||||
sizeof (I2NPBuildRequestRecordElGamalEncrypted),
|
||||
(uint8_t *)&records[hop1->recordIndex]);
|
||||
hop1 = hop1->next;
|
||||
}
|
||||
hop = hop->prev;
|
||||
}
|
||||
FillI2NPMessageHeader (msg, eI2NPVariableTunnelBuild);
|
||||
|
||||
// send message
|
||||
if (outboundTunnel)
|
||||
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg);
|
||||
else
|
||||
i2p::transport::transports.SendMessage (GetNextIdentHash (), msg);
|
||||
}
|
||||
|
||||
bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len)
|
||||
{
|
||||
LogPrint ("TunnelBuildResponse ", (int)msg[0], " records.");
|
||||
|
||||
i2p::crypto::CBCDecryption decryption;
|
||||
TunnelHopConfig * hop = m_Config->GetLastHop ();
|
||||
while (hop)
|
||||
{
|
||||
decryption.SetKey (hop->replyKey);
|
||||
// decrypt records before and including current hop
|
||||
TunnelHopConfig * hop1 = hop;
|
||||
while (hop1)
|
||||
{
|
||||
auto idx = hop1->recordIndex;
|
||||
if (idx >= 0 && idx < msg[0])
|
||||
{
|
||||
uint8_t * record = msg + 1 + idx*sizeof (I2NPBuildResponseRecord);
|
||||
decryption.SetIV (hop->replyIV);
|
||||
decryption.Decrypt(record, sizeof (I2NPBuildResponseRecord), record);
|
||||
}
|
||||
else
|
||||
LogPrint ("Tunnel hop index ", idx, " is out of range");
|
||||
hop1 = hop1->prev;
|
||||
}
|
||||
hop = hop->prev;
|
||||
}
|
||||
|
||||
bool established = true;
|
||||
hop = m_Config->GetFirstHop ();
|
||||
while (hop)
|
||||
{
|
||||
I2NPBuildResponseRecord * record = (I2NPBuildResponseRecord *)(msg + 1 + hop->recordIndex*sizeof (I2NPBuildResponseRecord));
|
||||
LogPrint ("Ret code=", (int)record->ret);
|
||||
if (record->ret)
|
||||
// if any of participants declined the tunnel is not established
|
||||
established = false;
|
||||
hop = hop->next;
|
||||
}
|
||||
if (established)
|
||||
{
|
||||
// change reply keys to layer keys
|
||||
hop = m_Config->GetFirstHop ();
|
||||
while (hop)
|
||||
{
|
||||
hop->decryption.SetKeys (hop->layerKey, hop->ivKey);
|
||||
hop = hop->next;
|
||||
}
|
||||
}
|
||||
if (established) m_State = eTunnelStateEstablished;
|
||||
return established;
|
||||
}
|
||||
|
||||
void Tunnel::EncryptTunnelMsg (I2NPMessage * tunnelMsg)
|
||||
{
|
||||
uint8_t * payload = tunnelMsg->GetPayload () + 4;
|
||||
TunnelHopConfig * hop = m_Config->GetLastHop ();
|
||||
while (hop)
|
||||
{
|
||||
hop->decryption.Decrypt (payload);
|
||||
hop = hop->prev;
|
||||
}
|
||||
}
|
||||
|
||||
void InboundTunnel::HandleTunnelDataMsg (I2NPMessage * msg)
|
||||
{
|
||||
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
|
||||
msg->from = this;
|
||||
EncryptTunnelMsg (msg);
|
||||
m_Endpoint.HandleDecryptedTunnelDataMsg (msg);
|
||||
}
|
||||
|
||||
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg)
|
||||
{
|
||||
TunnelMessageBlock block;
|
||||
if (gwHash)
|
||||
{
|
||||
block.hash = gwHash;
|
||||
if (gwTunnel)
|
||||
{
|
||||
block.deliveryType = eDeliveryTypeTunnel;
|
||||
block.tunnelID = gwTunnel;
|
||||
}
|
||||
else
|
||||
block.deliveryType = eDeliveryTypeRouter;
|
||||
}
|
||||
else
|
||||
block.deliveryType = eDeliveryTypeLocal;
|
||||
block.data = msg;
|
||||
|
||||
std::unique_lock<std::mutex> l(m_SendMutex);
|
||||
m_Gateway.SendTunnelDataMsg (block);
|
||||
}
|
||||
|
||||
void OutboundTunnel::SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_SendMutex);
|
||||
for (auto& it : msgs)
|
||||
m_Gateway.PutTunnelDataMsg (it);
|
||||
m_Gateway.SendBuffer ();
|
||||
}
|
||||
|
||||
Tunnels tunnels;
|
||||
|
||||
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), m_ExploratoryPool (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Tunnels::~Tunnels ()
|
||||
{
|
||||
for (auto& it : m_OutboundTunnels)
|
||||
delete it;
|
||||
m_OutboundTunnels.clear ();
|
||||
|
||||
for (auto& it : m_InboundTunnels)
|
||||
delete it.second;
|
||||
m_InboundTunnels.clear ();
|
||||
|
||||
for (auto& it : m_TransitTunnels)
|
||||
delete it.second;
|
||||
m_TransitTunnels.clear ();
|
||||
|
||||
/*for (auto& it : m_PendingTunnels)
|
||||
delete it.second;
|
||||
m_PendingTunnels.clear ();*/
|
||||
|
||||
for (auto& it: m_Pools)
|
||||
delete it;
|
||||
m_Pools.clear ();
|
||||
}
|
||||
|
||||
InboundTunnel * Tunnels::GetInboundTunnel (uint32_t tunnelID)
|
||||
{
|
||||
auto it = m_InboundTunnels.find(tunnelID);
|
||||
if (it != m_InboundTunnels.end ())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TransitTunnel * Tunnels::GetTransitTunnel (uint32_t tunnelID)
|
||||
{
|
||||
auto it = m_TransitTunnels.find(tunnelID);
|
||||
if (it != m_TransitTunnels.end ())
|
||||
return it->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Tunnel * Tunnels::GetPendingTunnel (uint32_t replyMsgID)
|
||||
{
|
||||
auto it = m_PendingTunnels.find(replyMsgID);
|
||||
if (it != m_PendingTunnels.end () && it->second->GetState () == eTunnelStatePending)
|
||||
{
|
||||
it->second->SetState (eTunnelStateBuildReplyReceived);
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
InboundTunnel * Tunnels::GetNextInboundTunnel ()
|
||||
{
|
||||
InboundTunnel * tunnel = nullptr;
|
||||
size_t minReceived = 0;
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (auto it : m_InboundTunnels)
|
||||
{
|
||||
if (!it.second->IsEstablished ()) continue;
|
||||
if (!tunnel || it.second->GetNumReceivedBytes () < minReceived)
|
||||
{
|
||||
tunnel = it.second;
|
||||
minReceived = it.second->GetNumReceivedBytes ();
|
||||
}
|
||||
}
|
||||
return tunnel;
|
||||
}
|
||||
|
||||
OutboundTunnel * Tunnels::GetNextOutboundTunnel ()
|
||||
{
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0;
|
||||
OutboundTunnel * tunnel = nullptr;
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
for (auto it: m_OutboundTunnels)
|
||||
{
|
||||
if (it->IsEstablished ())
|
||||
{
|
||||
tunnel = it;
|
||||
i++;
|
||||
}
|
||||
if (i > ind && tunnel) break;
|
||||
}
|
||||
return tunnel;
|
||||
}
|
||||
|
||||
TunnelPool * Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops)
|
||||
{
|
||||
auto pool = new TunnelPool (localDestination, numInboundHops, numOutboundHops);
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
m_Pools.push_back (pool);
|
||||
return pool;
|
||||
}
|
||||
|
||||
void Tunnels::DeleteTunnelPool (TunnelPool * pool)
|
||||
{
|
||||
if (pool)
|
||||
{
|
||||
StopTunnelPool (pool);
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
m_Pools.remove (pool);
|
||||
}
|
||||
for (auto it: m_PendingTunnels)
|
||||
if (it.second->GetTunnelPool () == pool)
|
||||
it.second->SetTunnelPool (nullptr);
|
||||
delete pool;
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::StopTunnelPool (TunnelPool * pool)
|
||||
{
|
||||
if (pool)
|
||||
{
|
||||
pool->SetActive (false);
|
||||
pool->DetachTunnels ();
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
|
||||
m_TransitTunnels[tunnel->GetTunnelID ()] = tunnel;
|
||||
}
|
||||
|
||||
void Tunnels::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Tunnels::Run, this));
|
||||
}
|
||||
|
||||
void Tunnels::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Queue.WakeUp ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::Run ()
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready
|
||||
|
||||
uint64_t lastTs = 0;
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
I2NPMessage * msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
|
||||
while (msg)
|
||||
{
|
||||
uint32_t tunnelID = be32toh (*(uint32_t *)msg->GetPayload ());
|
||||
InboundTunnel * tunnel = GetInboundTunnel (tunnelID);
|
||||
if (tunnel)
|
||||
tunnel->HandleTunnelDataMsg (msg);
|
||||
else
|
||||
{
|
||||
TransitTunnel * transitTunnel = GetTransitTunnel (tunnelID);
|
||||
if (transitTunnel)
|
||||
transitTunnel->HandleTunnelDataMsg (msg);
|
||||
else
|
||||
{
|
||||
LogPrint ("Tunnel ", tunnelID, " not found");
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
msg = m_Queue.Get ();
|
||||
}
|
||||
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts - lastTs >= 15) // manage tunnels every 15 seconds
|
||||
{
|
||||
ManageTunnels ();
|
||||
lastTs = ts;
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint ("Tunnels: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageTunnels ()
|
||||
{
|
||||
ManagePendingTunnels ();
|
||||
ManageInboundTunnels ();
|
||||
ManageOutboundTunnels ();
|
||||
ManageTransitTunnels ();
|
||||
ManageTunnelPools ();
|
||||
}
|
||||
|
||||
void Tunnels::ManagePendingTunnels ()
|
||||
{
|
||||
// check pending tunnel. delete failed or timeout
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_PendingTunnels.begin (); it != m_PendingTunnels.end ();)
|
||||
{
|
||||
auto tunnel = it->second;
|
||||
switch (tunnel->GetState ())
|
||||
{
|
||||
case eTunnelStatePending:
|
||||
if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT)
|
||||
{
|
||||
LogPrint ("Pending tunnel build request ", it->first, " timeout. Deleted");
|
||||
delete tunnel;
|
||||
it = m_PendingTunnels.erase (it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
break;
|
||||
case eTunnelStateBuildFailed:
|
||||
LogPrint ("Pending tunnel build request ", it->first, " failed. Deleted");
|
||||
delete tunnel;
|
||||
it = m_PendingTunnels.erase (it);
|
||||
break;
|
||||
case eTunnelStateBuildReplyReceived:
|
||||
// intermidiate state, will be either established of build failed
|
||||
it++;
|
||||
break;
|
||||
default:
|
||||
it = m_PendingTunnels.erase (it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageOutboundTunnels ()
|
||||
{
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
{
|
||||
for (auto it = m_OutboundTunnels.begin (); it != m_OutboundTunnels.end ();)
|
||||
{
|
||||
auto tunnel = *it;
|
||||
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired");
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
auto pool = tunnel->GetTunnelPool ();
|
||||
if (pool)
|
||||
pool->TunnelExpired (tunnel);
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
it = m_OutboundTunnels.erase (it);
|
||||
}
|
||||
delete tunnel;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tunnel->IsEstablished () && ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
tunnel->SetState (eTunnelStateExpiring);
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_OutboundTunnels.size () < 5)
|
||||
{
|
||||
// trying to create one more oubound tunnel
|
||||
InboundTunnel * inboundTunnel = GetNextInboundTunnel ();
|
||||
if (!inboundTunnel) return;
|
||||
LogPrint ("Creating one hop outbound tunnel...");
|
||||
CreateTunnel<OutboundTunnel> (
|
||||
new TunnelConfig (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >
|
||||
{
|
||||
i2p::data::netdb.GetRandomRouter ()
|
||||
},
|
||||
inboundTunnel->GetTunnelConfig ()));
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageInboundTunnels ()
|
||||
{
|
||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
{
|
||||
for (auto it = m_InboundTunnels.begin (); it != m_InboundTunnels.end ();)
|
||||
{
|
||||
auto tunnel = it->second;
|
||||
if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
LogPrint ("Tunnel ", tunnel->GetTunnelID (), " expired");
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
auto pool = tunnel->GetTunnelPool ();
|
||||
if (pool)
|
||||
pool->TunnelExpired (tunnel);
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
it = m_InboundTunnels.erase (it);
|
||||
}
|
||||
delete tunnel;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tunnel->IsEstablished () && ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
tunnel->SetState (eTunnelStateExpiring);
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_InboundTunnels.empty ())
|
||||
{
|
||||
LogPrint ("Creating zero hops inbound tunnel...");
|
||||
CreateZeroHopsInboundTunnel ();
|
||||
if (!m_ExploratoryPool)
|
||||
m_ExploratoryPool = CreateTunnelPool (&i2p::context, 2, 2); // 2-hop exploratory
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5)
|
||||
{
|
||||
// trying to create one more inbound tunnel
|
||||
LogPrint ("Creating one hop inbound tunnel...");
|
||||
CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >
|
||||
{
|
||||
i2p::data::netdb.GetRandomRouter ()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageTransitTunnels ()
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_TransitTunnels.begin (); it != m_TransitTunnels.end ();)
|
||||
{
|
||||
if (ts > it->second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
LogPrint ("Transit tunnel ", it->second->GetTunnelID (), " expired");
|
||||
auto tmp = it->second;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
|
||||
it = m_TransitTunnels.erase (it);
|
||||
}
|
||||
delete tmp;
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::ManageTunnelPools ()
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
for (auto it: m_Pools)
|
||||
{
|
||||
TunnelPool * pool = it;
|
||||
if (pool->IsActive ())
|
||||
{
|
||||
pool->CreateTunnels ();
|
||||
pool->TestTunnels ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tunnels::PostTunnelData (I2NPMessage * msg)
|
||||
{
|
||||
if (msg) m_Queue.Put (msg);
|
||||
}
|
||||
|
||||
template<class TTunnel>
|
||||
TTunnel * Tunnels::CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel)
|
||||
{
|
||||
TTunnel * newTunnel = new TTunnel (config);
|
||||
uint32_t replyMsgID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
||||
m_PendingTunnels[replyMsgID] = newTunnel;
|
||||
newTunnel->Build (replyMsgID, outboundTunnel);
|
||||
return newTunnel;
|
||||
}
|
||||
|
||||
void Tunnels::AddOutboundTunnel (OutboundTunnel * newTunnel)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
m_OutboundTunnels.push_back (newTunnel);
|
||||
auto pool = newTunnel->GetTunnelPool ();
|
||||
if (pool && pool->IsActive ())
|
||||
pool->TunnelCreated (newTunnel);
|
||||
else
|
||||
newTunnel->SetTunnelPool (nullptr);
|
||||
}
|
||||
|
||||
void Tunnels::AddInboundTunnel (InboundTunnel * newTunnel)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
m_InboundTunnels[newTunnel->GetTunnelID ()] = newTunnel;
|
||||
auto pool = newTunnel->GetTunnelPool ();
|
||||
if (!pool)
|
||||
{
|
||||
// build symmetric outbound tunnel
|
||||
CreateTunnel<OutboundTunnel> (newTunnel->GetTunnelConfig ()->Invert (), GetNextOutboundTunnel ());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pool->IsActive ())
|
||||
pool->TunnelCreated (newTunnel);
|
||||
else
|
||||
newTunnel->SetTunnelPool (nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Tunnels::CreateZeroHopsInboundTunnel ()
|
||||
{
|
||||
CreateTunnel<InboundTunnel> (
|
||||
new TunnelConfig (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >
|
||||
{
|
||||
i2p::context.GetSharedRouterInfo ()
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
176
Tunnel.h
176
Tunnel.h
@@ -1,176 +0,0 @@
|
||||
#ifndef TUNNEL_H__
|
||||
#define TUNNEL_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include "Queue.h"
|
||||
#include "TunnelConfig.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "TransitTunnel.h"
|
||||
#include "TunnelEndpoint.h"
|
||||
#include "TunnelGateway.h"
|
||||
#include "TunnelBase.h"
|
||||
#include "I2NPProtocol.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
const int TUNNEL_EXPIRATION_TIMEOUT = 660; // 11 minutes
|
||||
const int TUNNEL_EXPIRATION_THRESHOLD = 60; // 1 minute
|
||||
const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds
|
||||
const int STANDARD_NUM_RECORDS = 5; // in VariableTunnelBuild message
|
||||
|
||||
enum TunnelState
|
||||
{
|
||||
eTunnelStatePending,
|
||||
eTunnelStateBuildReplyReceived,
|
||||
eTunnelStateBuildFailed,
|
||||
eTunnelStateEstablished,
|
||||
eTunnelStateTestFailed,
|
||||
eTunnelStateFailed,
|
||||
eTunnelStateExpiring
|
||||
};
|
||||
|
||||
class OutboundTunnel;
|
||||
class InboundTunnel;
|
||||
class Tunnel: public TunnelBase
|
||||
{
|
||||
public:
|
||||
|
||||
Tunnel (TunnelConfig * config);
|
||||
~Tunnel ();
|
||||
|
||||
void Build (uint32_t replyMsgID, OutboundTunnel * outboundTunnel = 0);
|
||||
|
||||
TunnelConfig * GetTunnelConfig () const { return m_Config; }
|
||||
TunnelState GetState () const { return m_State; };
|
||||
void SetState (TunnelState state) { m_State = state; };
|
||||
bool IsEstablished () const { return m_State == eTunnelStateEstablished; };
|
||||
bool IsFailed () const { return m_State == eTunnelStateFailed; };
|
||||
|
||||
TunnelPool * GetTunnelPool () const { return m_Pool; };
|
||||
void SetTunnelPool (TunnelPool * pool) { m_Pool = pool; };
|
||||
|
||||
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
|
||||
|
||||
// implements TunnelBase
|
||||
void EncryptTunnelMsg (I2NPMessage * tunnelMsg);
|
||||
uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; };
|
||||
const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); };
|
||||
|
||||
private:
|
||||
|
||||
TunnelConfig * m_Config;
|
||||
TunnelPool * m_Pool; // pool, tunnel belongs to, or null
|
||||
TunnelState m_State;
|
||||
};
|
||||
|
||||
class OutboundTunnel: public Tunnel
|
||||
{
|
||||
public:
|
||||
|
||||
OutboundTunnel (TunnelConfig * config): Tunnel (config), m_Gateway (this) {};
|
||||
|
||||
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, i2p::I2NPMessage * msg);
|
||||
void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
|
||||
std::shared_ptr<const i2p::data::RouterInfo> GetEndpointRouter () const
|
||||
{ return GetTunnelConfig ()->GetLastHop ()->router; };
|
||||
size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
|
||||
|
||||
// implements TunnelBase
|
||||
uint32_t GetTunnelID () const { return GetNextTunnelID (); };
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_SendMutex;
|
||||
TunnelGateway m_Gateway;
|
||||
};
|
||||
|
||||
class InboundTunnel: public Tunnel
|
||||
{
|
||||
public:
|
||||
|
||||
InboundTunnel (TunnelConfig * config): Tunnel (config), m_Endpoint (true) {};
|
||||
void HandleTunnelDataMsg (I2NPMessage * msg);
|
||||
size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
|
||||
|
||||
// implements TunnelBase
|
||||
uint32_t GetTunnelID () const { return GetTunnelConfig ()->GetLastHop ()->nextTunnelID; };
|
||||
private:
|
||||
|
||||
TunnelEndpoint m_Endpoint;
|
||||
};
|
||||
|
||||
|
||||
class Tunnels
|
||||
{
|
||||
public:
|
||||
|
||||
Tunnels ();
|
||||
~Tunnels ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
InboundTunnel * GetInboundTunnel (uint32_t tunnelID);
|
||||
Tunnel * GetPendingTunnel (uint32_t replyMsgID);
|
||||
InboundTunnel * GetNextInboundTunnel ();
|
||||
OutboundTunnel * GetNextOutboundTunnel ();
|
||||
TunnelPool * GetExploratoryPool () const { return m_ExploratoryPool; };
|
||||
TransitTunnel * GetTransitTunnel (uint32_t tunnelID);
|
||||
void AddTransitTunnel (TransitTunnel * tunnel);
|
||||
void AddOutboundTunnel (OutboundTunnel * newTunnel);
|
||||
void AddInboundTunnel (InboundTunnel * newTunnel);
|
||||
void PostTunnelData (I2NPMessage * msg);
|
||||
template<class TTunnel>
|
||||
TTunnel * CreateTunnel (TunnelConfig * config, OutboundTunnel * outboundTunnel = 0);
|
||||
TunnelPool * CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOuboundHops);
|
||||
void DeleteTunnelPool (TunnelPool * pool);
|
||||
void StopTunnelPool (TunnelPool * pool);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void ManageTunnels ();
|
||||
void ManageOutboundTunnels ();
|
||||
void ManageInboundTunnels ();
|
||||
void ManageTransitTunnels ();
|
||||
void ManagePendingTunnels ();
|
||||
void ManageTunnelPools ();
|
||||
|
||||
void CreateZeroHopsInboundTunnel ();
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
std::map<uint32_t, Tunnel *> m_PendingTunnels; // by replyMsgID
|
||||
std::mutex m_InboundTunnelsMutex;
|
||||
std::map<uint32_t, InboundTunnel *> m_InboundTunnels;
|
||||
std::mutex m_OutboundTunnelsMutex;
|
||||
std::list<OutboundTunnel *> m_OutboundTunnels;
|
||||
std::mutex m_TransitTunnelsMutex;
|
||||
std::map<uint32_t, TransitTunnel *> m_TransitTunnels;
|
||||
std::mutex m_PoolsMutex;
|
||||
std::list<TunnelPool *> m_Pools;
|
||||
TunnelPool * m_ExploratoryPool;
|
||||
i2p::util::Queue<I2NPMessage> m_Queue;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
|
||||
const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
|
||||
const decltype(m_TransitTunnels)& GetTransitTunnels () const { return m_TransitTunnels; };
|
||||
};
|
||||
|
||||
extern Tunnels tunnels;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
65
TunnelBase.h
65
TunnelBase.h
@@ -1,65 +0,0 @@
|
||||
#ifndef TUNNEL_BASE_H__
|
||||
#define TUNNEL_BASE_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "Timestamp.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Identity.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
const size_t TUNNEL_DATA_MSG_SIZE = 1028;
|
||||
const size_t TUNNEL_DATA_ENCRYPTED_SIZE = 1008;
|
||||
const size_t TUNNEL_DATA_MAX_PAYLOAD_SIZE = 1003;
|
||||
|
||||
enum TunnelDeliveryType
|
||||
{
|
||||
eDeliveryTypeLocal = 0,
|
||||
eDeliveryTypeTunnel = 1,
|
||||
eDeliveryTypeRouter = 2
|
||||
};
|
||||
struct TunnelMessageBlock
|
||||
{
|
||||
TunnelDeliveryType deliveryType;
|
||||
i2p::data::IdentHash hash;
|
||||
uint32_t tunnelID;
|
||||
I2NPMessage * data;
|
||||
};
|
||||
|
||||
class TunnelBase
|
||||
{
|
||||
public:
|
||||
|
||||
//WARNING!!! GetSecondsSinceEpoch() return uint64_t
|
||||
TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
|
||||
virtual ~TunnelBase () {};
|
||||
|
||||
virtual void EncryptTunnelMsg (I2NPMessage * tunnelMsg) = 0;
|
||||
virtual uint32_t GetNextTunnelID () const = 0;
|
||||
virtual const i2p::data::IdentHash& GetNextIdentHash () const = 0;
|
||||
virtual uint32_t GetTunnelID () const = 0; // as known at our side
|
||||
|
||||
uint32_t GetCreationTime () const { return m_CreationTime; };
|
||||
void SetCreationTime (uint32_t t) { m_CreationTime = t; };
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_CreationTime; // seconds since epoch
|
||||
};
|
||||
|
||||
struct TunnelCreationTimeCmp
|
||||
{
|
||||
bool operator() (const TunnelBase * t1, const TunnelBase * t2) const
|
||||
{
|
||||
if (t1->GetCreationTime () != t2->GetCreationTime ())
|
||||
return t1->GetCreationTime () > t2->GetCreationTime ();
|
||||
else
|
||||
return t1 < t2;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
223
TunnelConfig.h
223
TunnelConfig.h
@@ -1,223 +0,0 @@
|
||||
#ifndef TUNNEL_CONFIG_H__
|
||||
#define TUNNEL_CONFIG_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "aes.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
struct TunnelHopConfig
|
||||
{
|
||||
std::shared_ptr<const i2p::data::RouterInfo> router, nextRouter;
|
||||
uint32_t tunnelID, nextTunnelID;
|
||||
uint8_t layerKey[32];
|
||||
uint8_t ivKey[32];
|
||||
uint8_t replyKey[32];
|
||||
uint8_t replyIV[16];
|
||||
bool isGateway, isEndpoint;
|
||||
|
||||
TunnelHopConfig * next, * prev;
|
||||
i2p::crypto::TunnelDecryption decryption;
|
||||
int recordIndex; // record # in tunnel build message
|
||||
|
||||
TunnelHopConfig (std::shared_ptr<const i2p::data::RouterInfo> r)
|
||||
{
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
rnd.GenerateBlock (layerKey, 32);
|
||||
rnd.GenerateBlock (ivKey, 32);
|
||||
rnd.GenerateBlock (replyIV, 16);
|
||||
tunnelID = rnd.GenerateWord32 ();
|
||||
isGateway = true;
|
||||
isEndpoint = true;
|
||||
router = r;
|
||||
//nextRouter = nullptr;
|
||||
nextTunnelID = 0;
|
||||
|
||||
next = nullptr;
|
||||
prev = nullptr;
|
||||
}
|
||||
|
||||
void SetNextRouter (std::shared_ptr<const i2p::data::RouterInfo> r)
|
||||
{
|
||||
nextRouter = r;
|
||||
isEndpoint = false;
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
nextTunnelID = rnd.GenerateWord32 ();
|
||||
}
|
||||
|
||||
void SetReplyHop (const TunnelHopConfig * replyFirstHop)
|
||||
{
|
||||
nextRouter = replyFirstHop->router;
|
||||
nextTunnelID = replyFirstHop->tunnelID;
|
||||
isEndpoint = true;
|
||||
}
|
||||
|
||||
void SetNext (TunnelHopConfig * n)
|
||||
{
|
||||
next = n;
|
||||
if (next)
|
||||
{
|
||||
next->prev = this;
|
||||
next->isGateway = false;
|
||||
isEndpoint = false;
|
||||
nextRouter = next->router;
|
||||
nextTunnelID = next->tunnelID;
|
||||
}
|
||||
}
|
||||
|
||||
void SetPrev (TunnelHopConfig * p)
|
||||
{
|
||||
prev = p;
|
||||
if (prev)
|
||||
{
|
||||
prev->next = this;
|
||||
prev->isEndpoint = false;
|
||||
isGateway = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class TunnelConfig
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > peers,
|
||||
const TunnelConfig * replyTunnelConfig = nullptr) // replyTunnelConfig=nullptr means inbound
|
||||
{
|
||||
TunnelHopConfig * prev = nullptr;
|
||||
for (auto it: peers)
|
||||
{
|
||||
auto hop = new TunnelHopConfig (it);
|
||||
if (prev)
|
||||
prev->SetNext (hop);
|
||||
else
|
||||
m_FirstHop = hop;
|
||||
prev = hop;
|
||||
}
|
||||
m_LastHop = prev;
|
||||
|
||||
if (replyTunnelConfig) // outbound
|
||||
{
|
||||
m_FirstHop->isGateway = false;
|
||||
m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
|
||||
}
|
||||
else // inbound
|
||||
m_LastHop->SetNextRouter (i2p::context.GetSharedRouterInfo ());
|
||||
}
|
||||
|
||||
~TunnelConfig ()
|
||||
{
|
||||
TunnelHopConfig * hop = m_FirstHop;
|
||||
|
||||
while (hop)
|
||||
{
|
||||
auto tmp = hop;
|
||||
hop = hop->next;
|
||||
delete tmp;
|
||||
}
|
||||
}
|
||||
|
||||
TunnelHopConfig * GetFirstHop () const
|
||||
{
|
||||
return m_FirstHop;
|
||||
}
|
||||
|
||||
TunnelHopConfig * GetLastHop () const
|
||||
{
|
||||
return m_LastHop;
|
||||
}
|
||||
|
||||
int GetNumHops () const
|
||||
{
|
||||
int num = 0;
|
||||
TunnelHopConfig * hop = m_FirstHop;
|
||||
while (hop)
|
||||
{
|
||||
num++;
|
||||
hop = hop->next;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
void Print (std::stringstream& s) const
|
||||
{
|
||||
TunnelHopConfig * hop = m_FirstHop;
|
||||
if (!m_FirstHop->isGateway)
|
||||
s << "me";
|
||||
s << "-->" << m_FirstHop->tunnelID;
|
||||
while (hop)
|
||||
{
|
||||
s << ":" << hop->router->GetIdentHashAbbreviation () << "-->";
|
||||
if (!hop->isEndpoint)
|
||||
s << hop->nextTunnelID;
|
||||
else
|
||||
return;
|
||||
hop = hop->next;
|
||||
}
|
||||
// we didn't reach enpoint that mean we are last hop
|
||||
s << ":me";
|
||||
}
|
||||
|
||||
TunnelConfig * Invert () const
|
||||
{
|
||||
TunnelConfig * newConfig = new TunnelConfig ();
|
||||
TunnelHopConfig * hop = m_FirstHop, * nextNewHop = nullptr;
|
||||
while (hop)
|
||||
{
|
||||
TunnelHopConfig * newHop = new TunnelHopConfig (hop->router);
|
||||
if (nextNewHop)
|
||||
newHop->SetNext (nextNewHop);
|
||||
nextNewHop = newHop;
|
||||
newHop->isEndpoint = hop->isGateway;
|
||||
newHop->isGateway = hop->isEndpoint;
|
||||
|
||||
if (!hop->prev) // first hop
|
||||
{
|
||||
newConfig->m_LastHop = newHop;
|
||||
if (hop->isGateway) // inbound tunnel
|
||||
newHop->SetReplyHop (m_FirstHop); // use it as reply tunnel
|
||||
else
|
||||
newHop->SetNextRouter (i2p::context.GetSharedRouterInfo ());
|
||||
}
|
||||
if (!hop->next) newConfig->m_FirstHop = newHop; // last hop
|
||||
|
||||
hop = hop->next;
|
||||
}
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
TunnelConfig * Clone (const TunnelConfig * replyTunnelConfig = nullptr) const
|
||||
{
|
||||
std::vector<std::shared_ptr<const i2p::data::RouterInfo> > peers;
|
||||
TunnelHopConfig * hop = m_FirstHop;
|
||||
while (hop)
|
||||
{
|
||||
peers.push_back (hop->router);
|
||||
hop = hop->next;
|
||||
}
|
||||
return new TunnelConfig (peers, replyTunnelConfig);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
// this constructor can't be called from outside
|
||||
TunnelConfig (): m_FirstHop (nullptr), m_LastHop (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
TunnelHopConfig * m_FirstHop, * m_LastHop;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,270 +0,0 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <string.h>
|
||||
#include "Log.h"
|
||||
#include "NetDb.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Transports.h"
|
||||
#include "RouterContext.h"
|
||||
#include "TunnelEndpoint.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
TunnelEndpoint::~TunnelEndpoint ()
|
||||
{
|
||||
for (auto it: m_IncompleteMessages)
|
||||
i2p::DeleteI2NPMessage (it.second.data);
|
||||
for (auto it: m_OutOfSequenceFragments)
|
||||
i2p::DeleteI2NPMessage (it.second.data);
|
||||
}
|
||||
|
||||
void TunnelEndpoint::HandleDecryptedTunnelDataMsg (I2NPMessage * msg)
|
||||
{
|
||||
m_NumReceivedBytes += TUNNEL_DATA_MSG_SIZE;
|
||||
|
||||
uint8_t * decrypted = msg->GetPayload () + 20; // 4 + 16
|
||||
uint8_t * zero = (uint8_t *)memchr (decrypted + 4, 0, TUNNEL_DATA_ENCRYPTED_SIZE - 4); // witout 4-byte checksum
|
||||
if (zero)
|
||||
{
|
||||
LogPrint ("TunnelMessage: zero found at ", (int)(zero-decrypted));
|
||||
uint8_t * fragment = zero + 1;
|
||||
// verify checksum
|
||||
memcpy (msg->GetPayload () + TUNNEL_DATA_MSG_SIZE, msg->GetPayload () + 4, 16); // copy iv to the end
|
||||
uint8_t hash[32];
|
||||
CryptoPP::SHA256().CalculateDigest (hash, fragment, TUNNEL_DATA_MSG_SIZE -(fragment - msg->GetPayload ()) + 16); // payload + iv
|
||||
if (memcmp (hash, decrypted, 4))
|
||||
{
|
||||
LogPrint ("TunnelMessage: checksum verification failed");
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
return;
|
||||
}
|
||||
// process fragments
|
||||
while (fragment < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
|
||||
{
|
||||
uint8_t flag = fragment[0];
|
||||
fragment++;
|
||||
|
||||
bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
|
||||
uint32_t msgID = 0;
|
||||
int fragmentNum = 0;
|
||||
TunnelMessageBlockEx m;
|
||||
if (!isFollowOnFragment)
|
||||
{
|
||||
// first fragment
|
||||
|
||||
m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
|
||||
switch (m.deliveryType)
|
||||
{
|
||||
case eDeliveryTypeLocal: // 0
|
||||
LogPrint ("Delivery type local");
|
||||
break;
|
||||
case eDeliveryTypeTunnel: // 1
|
||||
LogPrint ("Delivery type tunnel");
|
||||
m.tunnelID = be32toh (*(uint32_t *)fragment);
|
||||
fragment += 4; // tunnelID
|
||||
m.hash = i2p::data::IdentHash (fragment);
|
||||
fragment += 32; // hash
|
||||
break;
|
||||
case eDeliveryTypeRouter: // 2
|
||||
LogPrint ("Delivery type router");
|
||||
m.hash = i2p::data::IdentHash (fragment);
|
||||
fragment += 32; // to hash
|
||||
break;
|
||||
default:
|
||||
;
|
||||
}
|
||||
|
||||
bool isFragmented = flag & 0x08;
|
||||
if (isFragmented)
|
||||
{
|
||||
// Message ID
|
||||
msgID = be32toh (*(uint32_t *)fragment);
|
||||
fragment += 4;
|
||||
LogPrint ("Fragmented message ", msgID);
|
||||
isLastFragment = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// follow on
|
||||
msgID = be32toh (*(uint32_t *)fragment); // MessageID
|
||||
fragment += 4;
|
||||
fragmentNum = (flag >> 1) & 0x3F; // 6 bits
|
||||
isLastFragment = flag & 0x01;
|
||||
LogPrint ("Follow on fragment ", fragmentNum, " of message ", msgID, isLastFragment ? " last" : " non-last");
|
||||
}
|
||||
|
||||
uint16_t size = be16toh (*(uint16_t *)fragment);
|
||||
fragment += 2;
|
||||
LogPrint ("Fragment size=", (int)size);
|
||||
|
||||
msg->offset = fragment - msg->buf;
|
||||
msg->len = msg->offset + size;
|
||||
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
|
||||
{
|
||||
// this is not last message. we have to copy it
|
||||
m.data = NewI2NPMessage ();
|
||||
m.data->offset += sizeof (TunnelGatewayHeader); // reserve room for TunnelGateway header
|
||||
m.data->len += sizeof (TunnelGatewayHeader);
|
||||
*(m.data) = *msg;
|
||||
}
|
||||
else
|
||||
m.data = msg;
|
||||
|
||||
if (!isFollowOnFragment && isLastFragment)
|
||||
HandleNextMessage (m);
|
||||
else
|
||||
{
|
||||
if (msgID) // msgID is presented, assume message is fragmented
|
||||
{
|
||||
if (!isFollowOnFragment) // create new incomlete message
|
||||
{
|
||||
m.nextFragmentNum = 1;
|
||||
auto& msg = m_IncompleteMessages[msgID];
|
||||
msg = m;
|
||||
HandleOutOfSequenceFragment (msgID, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
m.nextFragmentNum = fragmentNum;
|
||||
HandleFollowOnFragment (msgID, isLastFragment, m);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("Message is fragmented, but msgID is not presented");
|
||||
}
|
||||
|
||||
fragment += size;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("TunnelMessage: zero not found");
|
||||
i2p::DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m)
|
||||
{
|
||||
auto fragment = m.data->GetBuffer ();
|
||||
auto size = m.data->GetLength ();
|
||||
auto it = m_IncompleteMessages.find (msgID);
|
||||
if (it != m_IncompleteMessages.end())
|
||||
{
|
||||
auto& msg = it->second;
|
||||
if (m.nextFragmentNum == msg.nextFragmentNum)
|
||||
{
|
||||
if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if messega is not too long
|
||||
{
|
||||
memcpy (msg.data->buf + msg.data->len, fragment, size); // concatenate fragment
|
||||
msg.data->len += size;
|
||||
if (isLastFragment)
|
||||
{
|
||||
// message complete
|
||||
HandleNextMessage (msg);
|
||||
m_IncompleteMessages.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.nextFragmentNum++;
|
||||
HandleOutOfSequenceFragment (msgID, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped");
|
||||
i2p::DeleteI2NPMessage (msg.data);
|
||||
m_IncompleteMessages.erase (it);
|
||||
}
|
||||
i2p::DeleteI2NPMessage (m.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved");
|
||||
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("First fragment of message ", msgID, " not found. Saved");
|
||||
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, I2NPMessage * data)
|
||||
{
|
||||
auto it = m_OutOfSequenceFragments.find (msgID);
|
||||
if (it == m_OutOfSequenceFragments.end ())
|
||||
m_OutOfSequenceFragments.insert (std::pair<uint32_t, Fragment> (msgID, {fragmentNum, isLastFragment, data}));
|
||||
else
|
||||
i2p::DeleteI2NPMessage (data);
|
||||
}
|
||||
|
||||
void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
|
||||
{
|
||||
auto it = m_OutOfSequenceFragments.find (msgID);
|
||||
if (it != m_OutOfSequenceFragments.end ())
|
||||
{
|
||||
if (it->second.fragmentNum == msg.nextFragmentNum)
|
||||
{
|
||||
LogPrint ("Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
|
||||
auto size = it->second.data->GetLength ();
|
||||
memcpy (msg.data->buf + msg.data->len, it->second.data->GetBuffer (), size); // concatenate out-of-sync fragment
|
||||
msg.data->len += size;
|
||||
if (it->second.isLastFragment)
|
||||
{
|
||||
// message complete
|
||||
HandleNextMessage (msg);
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
}
|
||||
else
|
||||
msg.nextFragmentNum++;
|
||||
i2p::DeleteI2NPMessage (it->second.data);
|
||||
m_OutOfSequenceFragments.erase (it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
|
||||
{
|
||||
LogPrint ("TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetHeader()->typeID);
|
||||
switch (msg.deliveryType)
|
||||
{
|
||||
case eDeliveryTypeLocal:
|
||||
i2p::HandleI2NPMessage (msg.data);
|
||||
break;
|
||||
case eDeliveryTypeTunnel:
|
||||
i2p::transport::transports.SendMessage (msg.hash, i2p::CreateTunnelGatewayMsg (msg.tunnelID, msg.data));
|
||||
break;
|
||||
case eDeliveryTypeRouter:
|
||||
if (msg.hash == i2p::context.GetRouterInfo ().GetIdentHash ()) // check if message is sent to us
|
||||
i2p::HandleI2NPMessage (msg.data);
|
||||
else
|
||||
{
|
||||
// to somebody else
|
||||
if (!m_IsInbound) // outbound transit tunnel
|
||||
{
|
||||
if (msg.data->GetHeader()->typeID == eI2NPDatabaseStore ||
|
||||
msg.data->GetHeader()->typeID == eI2NPDatabaseSearchReply )
|
||||
{
|
||||
// catch RI or reply with new list of routers
|
||||
auto ds = NewI2NPMessage ();
|
||||
*ds = *(msg.data);
|
||||
i2p::data::netdb.PostI2NPMsg (ds);
|
||||
}
|
||||
i2p::transport::transports.SendMessage (msg.hash, msg.data);
|
||||
}
|
||||
else // we shouldn't send this message. possible leakage
|
||||
{
|
||||
LogPrint ("Message to another router arrived from an inbound tunnel. Dropped");
|
||||
i2p::DeleteI2NPMessage (msg.data);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LogPrint ("TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
354
TunnelPool.cpp
354
TunnelPool.cpp
@@ -1,354 +0,0 @@
|
||||
#include "I2PEndian.h"
|
||||
#include "CryptoConst.h"
|
||||
#include "Tunnel.h"
|
||||
#include "NetDb.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Garlic.h"
|
||||
#include "TunnelPool.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
TunnelPool::TunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numTunnels):
|
||||
m_LocalDestination (localDestination), m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
|
||||
m_NumTunnels (numTunnels), m_IsActive (true)
|
||||
{
|
||||
}
|
||||
|
||||
TunnelPool::~TunnelPool ()
|
||||
{
|
||||
DetachTunnels ();
|
||||
}
|
||||
|
||||
void TunnelPool::DetachTunnels ()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (auto it: m_InboundTunnels)
|
||||
it->SetTunnelPool (nullptr);
|
||||
m_InboundTunnels.clear ();
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
for (auto it: m_OutboundTunnels)
|
||||
it->SetTunnelPool (nullptr);
|
||||
m_OutboundTunnels.clear ();
|
||||
}
|
||||
m_Tests.clear ();
|
||||
}
|
||||
|
||||
void TunnelPool::TunnelCreated (InboundTunnel * createdTunnel)
|
||||
{
|
||||
if (!m_IsActive) return;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
m_InboundTunnels.insert (createdTunnel);
|
||||
}
|
||||
if (m_LocalDestination)
|
||||
m_LocalDestination->SetLeaseSetUpdated ();
|
||||
}
|
||||
|
||||
void TunnelPool::TunnelExpired (InboundTunnel * expiredTunnel)
|
||||
{
|
||||
if (expiredTunnel)
|
||||
{
|
||||
expiredTunnel->SetTunnelPool (nullptr);
|
||||
for (auto it: m_Tests)
|
||||
if (it.second.second == expiredTunnel) it.second.second = nullptr;
|
||||
RecreateInboundTunnel (expiredTunnel);
|
||||
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
m_InboundTunnels.erase (expiredTunnel);
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelPool::TunnelCreated (OutboundTunnel * createdTunnel)
|
||||
{
|
||||
if (!m_IsActive) return;
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
m_OutboundTunnels.insert (createdTunnel);
|
||||
}
|
||||
|
||||
void TunnelPool::TunnelExpired (OutboundTunnel * expiredTunnel)
|
||||
{
|
||||
if (expiredTunnel)
|
||||
{
|
||||
expiredTunnel->SetTunnelPool (nullptr);
|
||||
for (auto it: m_Tests)
|
||||
if (it.second.first == expiredTunnel) it.second.first = nullptr;
|
||||
RecreateOutboundTunnel (expiredTunnel);
|
||||
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
m_OutboundTunnels.erase (expiredTunnel);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<InboundTunnel *> TunnelPool::GetInboundTunnels (int num) const
|
||||
{
|
||||
std::vector<InboundTunnel *> v;
|
||||
int i = 0;
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (auto it : m_InboundTunnels)
|
||||
{
|
||||
if (i >= num) break;
|
||||
if (it->IsEstablished ())
|
||||
{
|
||||
v.push_back (it);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
OutboundTunnel * TunnelPool::GetNextOutboundTunnel (OutboundTunnel * suggested) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
return GetNextTunnel (m_OutboundTunnels, suggested);
|
||||
}
|
||||
|
||||
InboundTunnel * TunnelPool::GetNextInboundTunnel (InboundTunnel * suggested) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
return GetNextTunnel (m_InboundTunnels, suggested);
|
||||
}
|
||||
|
||||
template<class TTunnels>
|
||||
typename TTunnels::value_type TunnelPool::GetNextTunnel (TTunnels& tunnels,
|
||||
typename TTunnels::value_type suggested) const
|
||||
{
|
||||
if (tunnels.empty ()) return nullptr;
|
||||
if (suggested && tunnels.count (suggested) > 0 && suggested->IsEstablished ())
|
||||
return suggested;
|
||||
|
||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
uint32_t ind = rnd.GenerateWord32 (0, tunnels.size ()/2), i = 0;
|
||||
typename TTunnels::value_type tunnel = nullptr;
|
||||
for (auto it: tunnels)
|
||||
{
|
||||
if (it->IsEstablished ())
|
||||
{
|
||||
tunnel = it;
|
||||
i++;
|
||||
}
|
||||
if (i > ind && tunnel) break;
|
||||
}
|
||||
return tunnel;
|
||||
}
|
||||
|
||||
void TunnelPool::CreateTunnels ()
|
||||
{
|
||||
int num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (auto it : m_InboundTunnels)
|
||||
if (it->IsEstablished ()) num++;
|
||||
}
|
||||
for (int i = num; i < m_NumTunnels; i++)
|
||||
CreateInboundTunnel ();
|
||||
|
||||
num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
for (auto it : m_OutboundTunnels)
|
||||
if (it->IsEstablished ()) num++;
|
||||
}
|
||||
for (int i = num; i < m_NumTunnels; i++)
|
||||
CreateOutboundTunnel ();
|
||||
}
|
||||
|
||||
void TunnelPool::TestTunnels ()
|
||||
{
|
||||
auto& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
for (auto it: m_Tests)
|
||||
{
|
||||
LogPrint ("Tunnel test ", (int)it.first, " failed");
|
||||
// if test failed again with another tunnel we consider it failed
|
||||
if (it.second.first)
|
||||
{
|
||||
if (it.second.first->GetState () == eTunnelStateTestFailed)
|
||||
{
|
||||
it.second.first->SetState (eTunnelStateFailed);
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
m_OutboundTunnels.erase (it.second.first);
|
||||
}
|
||||
else
|
||||
it.second.first->SetState (eTunnelStateTestFailed);
|
||||
}
|
||||
if (it.second.second)
|
||||
{
|
||||
if (it.second.second->GetState () == eTunnelStateTestFailed)
|
||||
{
|
||||
it.second.second->SetState (eTunnelStateFailed);
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
m_InboundTunnels.erase (it.second.second);
|
||||
}
|
||||
if (m_LocalDestination)
|
||||
m_LocalDestination->SetLeaseSetUpdated ();
|
||||
}
|
||||
else
|
||||
it.second.second->SetState (eTunnelStateTestFailed);
|
||||
}
|
||||
}
|
||||
m_Tests.clear ();
|
||||
// new tests
|
||||
auto it1 = m_OutboundTunnels.begin ();
|
||||
auto it2 = m_InboundTunnels.begin ();
|
||||
while (it1 != m_OutboundTunnels.end () && it2 != m_InboundTunnels.end ())
|
||||
{
|
||||
bool failed = false;
|
||||
if ((*it1)->IsFailed ())
|
||||
{
|
||||
failed = true;
|
||||
it1++;
|
||||
}
|
||||
if ((*it2)->IsFailed ())
|
||||
{
|
||||
failed = true;
|
||||
it2++;
|
||||
}
|
||||
if (!failed)
|
||||
{
|
||||
uint32_t msgID = rnd.GenerateWord32 ();
|
||||
m_Tests[msgID] = std::make_pair (*it1, *it2);
|
||||
(*it1)->SendTunnelDataMsg ((*it2)->GetNextIdentHash (), (*it2)->GetNextTunnelID (),
|
||||
CreateDeliveryStatusMsg (msgID));
|
||||
it1++; it2++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelPool::ProcessGarlicMessage (I2NPMessage * msg)
|
||||
{
|
||||
if (m_LocalDestination)
|
||||
m_LocalDestination->ProcessGarlicMessage (msg);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Local destination doesn't exist. Dropped");
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelPool::ProcessDeliveryStatus (I2NPMessage * msg)
|
||||
{
|
||||
I2NPDeliveryStatusMsg * deliveryStatus = (I2NPDeliveryStatusMsg *)msg->GetPayload ();
|
||||
auto it = m_Tests.find (be32toh (deliveryStatus->msgID));
|
||||
if (it != m_Tests.end ())
|
||||
{
|
||||
// restore from test failed state if any
|
||||
if (it->second.first->GetState () == eTunnelStateTestFailed)
|
||||
it->second.first->SetState (eTunnelStateEstablished);
|
||||
if (it->second.second->GetState () == eTunnelStateTestFailed)
|
||||
it->second.second->SetState (eTunnelStateEstablished);
|
||||
LogPrint ("Tunnel test ", it->first, " successive. ", i2p::util::GetMillisecondsSinceEpoch () - be64toh (deliveryStatus->timestamp), " milliseconds");
|
||||
m_Tests.erase (it);
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_LocalDestination)
|
||||
m_LocalDestination->ProcessDeliveryStatusMessage (msg);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Local destination doesn't exist. Dropped");
|
||||
DeleteI2NPMessage (msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const i2p::data::RouterInfo> TunnelPool::SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const
|
||||
{
|
||||
bool isExploratory = (m_LocalDestination == &i2p::context); // TODO: implement it better
|
||||
auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop):
|
||||
i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop);
|
||||
|
||||
if (!hop)
|
||||
hop = i2p::data::netdb.GetRandomRouter ();
|
||||
return hop;
|
||||
}
|
||||
|
||||
void TunnelPool::CreateInboundTunnel ()
|
||||
{
|
||||
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
|
||||
if (!outboundTunnel)
|
||||
outboundTunnel = tunnels.GetNextOutboundTunnel ();
|
||||
LogPrint ("Creating destination inbound tunnel...");
|
||||
auto prevHop = i2p::context.GetSharedRouterInfo ();
|
||||
std::vector<std::shared_ptr<const i2p::data::RouterInfo> > hops;
|
||||
int numHops = m_NumInboundHops;
|
||||
if (outboundTunnel)
|
||||
{
|
||||
// last hop
|
||||
auto hop = outboundTunnel->GetTunnelConfig ()->GetFirstHop ()->router;
|
||||
if (hop->GetIdentHash () != i2p::context.GetIdentHash ()) // outbound shouldn't be zero-hop tunnel
|
||||
{
|
||||
prevHop = hop;
|
||||
hops.push_back (prevHop);
|
||||
numHops--;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < numHops; i++)
|
||||
{
|
||||
auto hop = SelectNextHop (prevHop);
|
||||
prevHop = hop;
|
||||
hops.push_back (hop);
|
||||
}
|
||||
std::reverse (hops.begin (), hops.end ());
|
||||
auto * tunnel = tunnels.CreateTunnel<InboundTunnel> (new TunnelConfig (hops), outboundTunnel);
|
||||
tunnel->SetTunnelPool (this);
|
||||
}
|
||||
|
||||
void TunnelPool::RecreateInboundTunnel (InboundTunnel * tunnel)
|
||||
{
|
||||
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
|
||||
if (!outboundTunnel)
|
||||
outboundTunnel = tunnels.GetNextOutboundTunnel ();
|
||||
LogPrint ("Re-creating destination inbound tunnel...");
|
||||
auto * newTunnel = tunnels.CreateTunnel<InboundTunnel> (tunnel->GetTunnelConfig ()->Clone (), outboundTunnel);
|
||||
newTunnel->SetTunnelPool (this);
|
||||
}
|
||||
|
||||
void TunnelPool::CreateOutboundTunnel ()
|
||||
{
|
||||
InboundTunnel * inboundTunnel = GetNextInboundTunnel ();
|
||||
if (!inboundTunnel)
|
||||
inboundTunnel = tunnels.GetNextInboundTunnel ();
|
||||
if (inboundTunnel)
|
||||
{
|
||||
LogPrint ("Creating destination outbound tunnel...");
|
||||
|
||||
auto prevHop = i2p::context.GetSharedRouterInfo ();
|
||||
std::vector<std::shared_ptr<const i2p::data::RouterInfo> > hops;
|
||||
for (int i = 0; i < m_NumOutboundHops; i++)
|
||||
{
|
||||
auto hop = SelectNextHop (prevHop);
|
||||
prevHop = hop;
|
||||
hops.push_back (hop);
|
||||
}
|
||||
|
||||
auto * tunnel = tunnels.CreateTunnel<OutboundTunnel> (
|
||||
new TunnelConfig (hops, inboundTunnel->GetTunnelConfig ()));
|
||||
tunnel->SetTunnelPool (this);
|
||||
}
|
||||
else
|
||||
LogPrint ("Can't create outbound tunnel. No inbound tunnels found");
|
||||
}
|
||||
|
||||
void TunnelPool::RecreateOutboundTunnel (OutboundTunnel * tunnel)
|
||||
{
|
||||
InboundTunnel * inboundTunnel = GetNextInboundTunnel ();
|
||||
if (!inboundTunnel)
|
||||
inboundTunnel = tunnels.GetNextInboundTunnel ();
|
||||
if (inboundTunnel)
|
||||
{
|
||||
LogPrint ("Re-creating destination outbound tunnel...");
|
||||
auto * newTunnel = tunnels.CreateTunnel<OutboundTunnel> (
|
||||
tunnel->GetTunnelConfig ()->Clone (inboundTunnel->GetTunnelConfig ()));
|
||||
newTunnel->SetTunnelPool (this);
|
||||
}
|
||||
else
|
||||
LogPrint ("Can't re-create outbound tunnel. No inbound tunnels found");
|
||||
}
|
||||
}
|
||||
}
|
||||
85
TunnelPool.h
85
TunnelPool.h
@@ -1,85 +0,0 @@
|
||||
#ifndef TUNNEL_POOL__
|
||||
#define TUNNEL_POOL__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <mutex>
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TunnelBase.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Garlic.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
class Tunnel;
|
||||
class InboundTunnel;
|
||||
class OutboundTunnel;
|
||||
|
||||
class TunnelPool // per local destination
|
||||
{
|
||||
public:
|
||||
|
||||
TunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numTunnels = 5);
|
||||
~TunnelPool ();
|
||||
|
||||
i2p::garlic::GarlicDestination * GetLocalDestination () const { return m_LocalDestination; };
|
||||
void SetLocalDestination (i2p::garlic::GarlicDestination * destination) { m_LocalDestination = destination; };
|
||||
|
||||
void CreateTunnels ();
|
||||
void TunnelCreated (InboundTunnel * createdTunnel);
|
||||
void TunnelExpired (InboundTunnel * expiredTunnel);
|
||||
void TunnelCreated (OutboundTunnel * createdTunnel);
|
||||
void TunnelExpired (OutboundTunnel * expiredTunnel);
|
||||
std::vector<InboundTunnel *> GetInboundTunnels (int num) const;
|
||||
OutboundTunnel * GetNextOutboundTunnel (OutboundTunnel * suggested = nullptr) const;
|
||||
InboundTunnel * GetNextInboundTunnel (InboundTunnel * suggested = nullptr) const;
|
||||
|
||||
void TestTunnels ();
|
||||
void ProcessGarlicMessage (I2NPMessage * msg);
|
||||
void ProcessDeliveryStatus (I2NPMessage * msg);
|
||||
|
||||
bool IsActive () const { return m_IsActive; };
|
||||
void SetActive (bool isActive) { m_IsActive = isActive; };
|
||||
void DetachTunnels ();
|
||||
|
||||
private:
|
||||
|
||||
void CreateInboundTunnel ();
|
||||
void CreateOutboundTunnel ();
|
||||
void RecreateInboundTunnel (InboundTunnel * tunnel);
|
||||
void RecreateOutboundTunnel (OutboundTunnel * tunnel);
|
||||
template<class TTunnels>
|
||||
typename TTunnels::value_type GetNextTunnel (TTunnels& tunnels,
|
||||
typename TTunnels::value_type suggested = nullptr) const;
|
||||
std::shared_ptr<const i2p::data::RouterInfo> SelectNextHop (std::shared_ptr<const i2p::data::RouterInfo> prevHop) const;
|
||||
|
||||
private:
|
||||
|
||||
i2p::garlic::GarlicDestination * m_LocalDestination;
|
||||
int m_NumInboundHops, m_NumOutboundHops, m_NumTunnels;
|
||||
mutable std::mutex m_InboundTunnelsMutex;
|
||||
std::set<InboundTunnel *, TunnelCreationTimeCmp> m_InboundTunnels; // recent tunnel appears first
|
||||
mutable std::mutex m_OutboundTunnelsMutex;
|
||||
std::set<OutboundTunnel *, TunnelCreationTimeCmp> m_OutboundTunnels;
|
||||
std::map<uint32_t, std::pair<OutboundTunnel *, InboundTunnel *> > m_Tests;
|
||||
bool m_IsActive;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
const decltype(m_OutboundTunnels)& GetOutboundTunnels () const { return m_OutboundTunnels; };
|
||||
const decltype(m_InboundTunnels)& GetInboundTunnels () const { return m_InboundTunnels; };
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
68
UPnP.cpp
68
UPnP.cpp
@@ -1,68 +0,0 @@
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include "Log.h"
|
||||
#include "UPnP.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
UPnP::UPnP (): m_Timer (m_Service),
|
||||
m_Endpoint (boost::asio::ip::udp::v4 (), UPNP_REPLY_PORT),
|
||||
m_MulticastEndpoint (boost::asio::ip::address::from_string (UPNP_GROUP), UPNP_PORT),
|
||||
m_Socket (m_Service, m_Endpoint.protocol ())
|
||||
{
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
m_Socket.set_option(boost::asio::ip::udp::socket::reuse_address(true));
|
||||
}
|
||||
|
||||
UPnP::~UPnP ()
|
||||
{
|
||||
}
|
||||
|
||||
void UPnP::Run ()
|
||||
{
|
||||
DiscoverRouter ();
|
||||
m_Service.run ();
|
||||
}
|
||||
|
||||
void UPnP::DiscoverRouter ()
|
||||
{
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(5)); // 5 seconds
|
||||
m_Timer.async_wait (boost::bind (&UPnP::HandleTimer, this, boost::asio::placeholders::error));
|
||||
|
||||
std::string address = UPNP_GROUP;
|
||||
address += ":" + boost::lexical_cast<std::string>(UPNP_PORT);
|
||||
std::string request = "M-SEARCH * HTTP/1.1\r\n"
|
||||
"HOST: " + address + "\r\n"
|
||||
"ST:" + UPNP_ROUTER + "\r\n"
|
||||
"MAN:\"ssdp:discover\"\r\n"
|
||||
"MX:3\r\n"
|
||||
"\r\n\r\n";
|
||||
m_Socket.send_to (boost::asio::buffer (request.c_str (), request.length ()), m_MulticastEndpoint);
|
||||
Receive ();
|
||||
}
|
||||
|
||||
void UPnP::Receive ()
|
||||
{
|
||||
m_Socket.async_receive_from (boost::asio::buffer (m_ReceiveBuffer, UPNP_MAX_PACKET_LEN), m_SenderEndpoint,
|
||||
boost::bind (&UPnP::HandleReceivedFrom, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void UPnP::HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred)
|
||||
{
|
||||
LogPrint ("UPnP: ", bytes_transferred, " received from ", m_SenderEndpoint.address ());
|
||||
std::string str (m_ReceiveBuffer, bytes_transferred);
|
||||
LogPrint (str);
|
||||
m_Timer.cancel ();
|
||||
}
|
||||
|
||||
void UPnP::HandleTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint ("UPnP: timeout expired");
|
||||
m_Service.stop ();
|
||||
}
|
||||
}
|
||||
}
|
||||
41
UPnP.h
41
UPnP.h
@@ -1,41 +0,0 @@
|
||||
#ifndef UPNP_H__
|
||||
#define UPNP_H__
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
const int UPNP_MAX_PACKET_LEN = 1500;
|
||||
const char UPNP_GROUP[] = "239.255.255.250";
|
||||
const int UPNP_PORT = 1900;
|
||||
const int UPNP_REPLY_PORT = 1901;
|
||||
const char UPNP_ROUTER[] = "urn:schemas-upnp-org:device:InternetGatewayDevice:1";
|
||||
|
||||
class UPnP
|
||||
{
|
||||
public:
|
||||
|
||||
UPnP ();
|
||||
~UPnP ();
|
||||
|
||||
void Run ();
|
||||
|
||||
|
||||
private:
|
||||
|
||||
void DiscoverRouter ();
|
||||
void Receive ();
|
||||
void HandleReceivedFrom (const boost::system::error_code& ecode, size_t bytes_transferred);
|
||||
void HandleTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
boost::asio::ip::udp::endpoint m_Endpoint, m_MulticastEndpoint, m_SenderEndpoint;
|
||||
boost::asio::ip::udp::socket m_Socket;
|
||||
char m_ReceiveBuffer[UPNP_MAX_PACKET_LEN];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
14
Win32/.gitignore
vendored
14
Win32/.gitignore
vendored
@@ -1,14 +0,0 @@
|
||||
*
|
||||
!*/
|
||||
|
||||
!*.h
|
||||
!*.cpp
|
||||
|
||||
!*.bat
|
||||
|
||||
!*.sln
|
||||
!*.vcproj
|
||||
!*.vcxproj
|
||||
!*.vcxproj.filters
|
||||
!*.iss
|
||||
!.gitignore
|
||||
111
Win32/DaemonWin32.cpp
Normal file
111
Win32/DaemonWin32.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#include <thread>
|
||||
#include <clocale>
|
||||
#include "Config.h"
|
||||
#include "Daemon.h"
|
||||
#include "util.h"
|
||||
#include "Log.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "Win32/Win32Service.h"
|
||||
#ifdef WIN32_APP
|
||||
#include "Win32/Win32App.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
bool DaemonWin32::init(int argc, char* argv[])
|
||||
{
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
SetConsoleOutputCP(1251);
|
||||
setlocale(LC_ALL, "Russian");
|
||||
|
||||
if (!Daemon_Singleton::init(argc, argv))
|
||||
return false;
|
||||
|
||||
std::string serviceControl; i2p::config::GetOption("svcctl", serviceControl);
|
||||
if (serviceControl == "install")
|
||||
{
|
||||
LogPrint(eLogInfo, "WinSVC: installing ", SERVICE_NAME, " as service");
|
||||
InstallService(
|
||||
SERVICE_NAME, // Name of service
|
||||
SERVICE_DISPLAY_NAME, // Name to display
|
||||
SERVICE_START_TYPE, // Service start type
|
||||
SERVICE_DEPENDENCIES, // Dependencies
|
||||
SERVICE_ACCOUNT, // Service running account
|
||||
SERVICE_PASSWORD // Password of the account
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else if (serviceControl == "remove")
|
||||
{
|
||||
LogPrint(eLogInfo, "WinSVC: uninstalling ", SERVICE_NAME, " service");
|
||||
UninstallService(SERVICE_NAME);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isDaemon)
|
||||
{
|
||||
LogPrint(eLogDebug, "Daemon: running as service");
|
||||
I2PService service(SERVICE_NAME);
|
||||
if (!I2PService::Run(service))
|
||||
{
|
||||
LogPrint(eLogError, "Daemon: Service failed to run w/err 0x%08lx\n", GetLastError());
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
LogPrint(eLogDebug, "Daemon: running as user");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DaemonWin32::start()
|
||||
{
|
||||
setlocale(LC_CTYPE, "");
|
||||
SetConsoleCP(1251);
|
||||
SetConsoleOutputCP(1251);
|
||||
setlocale(LC_ALL, "Russian");
|
||||
#ifdef WIN32_APP
|
||||
if (!i2p::win32::StartWin32App ()) return false;
|
||||
|
||||
// override log
|
||||
i2p::config::SetOption("log", std::string ("file"));
|
||||
#endif
|
||||
bool ret = Daemon_Singleton::start();
|
||||
if (ret && i2p::log::Logger().GetLogType() == eLogFile)
|
||||
{
|
||||
// TODO: find out where this garbage to console comes from
|
||||
SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE);
|
||||
SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE);
|
||||
}
|
||||
bool insomnia; i2p::config::GetOption("insomnia", insomnia);
|
||||
if (insomnia)
|
||||
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool DaemonWin32::stop()
|
||||
{
|
||||
#ifdef WIN32_APP
|
||||
i2p::win32::StopWin32App ();
|
||||
#endif
|
||||
return Daemon_Singleton::stop();
|
||||
}
|
||||
|
||||
void DaemonWin32::run ()
|
||||
{
|
||||
#ifdef WIN32_APP
|
||||
i2p::win32::RunWin32App ();
|
||||
#else
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //_WIN32
|
||||
@@ -1,282 +0,0 @@
|
||||
# NSIS Installer script. (Tested with NSIS 2.64 on Windows 7)
|
||||
# Author: Mikal Villa (Meeh)
|
||||
# Version: 1.1
|
||||
Name PurpleI2P
|
||||
|
||||
RequestExecutionLevel highest
|
||||
SetCompressor /SOLID lzma
|
||||
ShowInstDetails show
|
||||
|
||||
# General Symbol Definitions
|
||||
!define REGKEY "SOFTWARE\$(^Name)"
|
||||
!define VERSION 0.3.0.0
|
||||
!define COMPANY "The Privacy Solutions Project"
|
||||
!define URL "https://i2p.io"
|
||||
|
||||
# MUI Symbol Definitions
|
||||
!define MUI_ICON "ictoopie.ico"
|
||||
#!define MUI_WELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
|
||||
!define MUI_HEADERIMAGE
|
||||
!define MUI_HEADERIMAGE_RIGHT
|
||||
#!define MUI_HEADERIMAGE_BITMAP "../share/pixmaps/nsis-header.bmp"
|
||||
!define MUI_FINISHPAGE_NOAUTOCLOSE
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_ROOT HKLM
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_KEY ${REGKEY}
|
||||
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME StartMenuGroup
|
||||
!define MUI_STARTMENUPAGE_DEFAULTFOLDER PurpleI2P
|
||||
!define MUI_FINISHPAGE_RUN $INSTDIR\i2pd.exe
|
||||
!define MUI_FINISHPAGE_SHOWREADME $INSTDIR\Readme.txt
|
||||
|
||||
|
||||
!define MUI_UNICON "${NSISDIR}\Contrib\Graphics\Icons\modern-uninstall.ico"
|
||||
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "../share/pixmaps/nsis-wizard.bmp"
|
||||
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
|
||||
|
||||
# Included files
|
||||
!include Sections.nsh
|
||||
!include MUI2.nsh
|
||||
!include nsDialogs.nsh
|
||||
!include winmessages.nsh
|
||||
!include logiclib.nsh
|
||||
# Local included files
|
||||
!include nsi\helper_readme.nsh
|
||||
;!include nsi\servicelib.nsh
|
||||
|
||||
# Variables
|
||||
Var StartMenuGroup
|
||||
|
||||
# Installer pages
|
||||
# Execution flow of installer windows
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_README "../Readme.md"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
# Disabled for now. Use the bat
|
||||
;Page custom mode_selection # Meeh's hack for installing and starting service.
|
||||
!insertmacro MUI_PAGE_STARTMENU Application $StartMenuGroup
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
# Uninstall pages
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
# Installer languages
|
||||
!insertmacro MUI_LANGUAGE English
|
||||
|
||||
# Installer attributes
|
||||
OutFile PurpleI2P-0.3.0.0-win32-setup.exe
|
||||
InstallDir $PROGRAMFILES\PurpleI2P
|
||||
CRCCheck on
|
||||
XPStyle on
|
||||
BrandingText " "
|
||||
ShowInstDetails show
|
||||
VIProductVersion 0.3.0.0
|
||||
VIAddVersionKey ProductName PurpleI2P
|
||||
VIAddVersionKey ProductVersion "${VERSION}"
|
||||
VIAddVersionKey CompanyName "${COMPANY}"
|
||||
VIAddVersionKey CompanyWebsite "${URL}"
|
||||
VIAddVersionKey FileVersion "${VERSION}"
|
||||
VIAddVersionKey FileDescription ""
|
||||
VIAddVersionKey LegalCopyright ""
|
||||
InstallDirRegKey HKCU "${REGKEY}" Path
|
||||
ShowUninstDetails show
|
||||
|
||||
# Readme definitions
|
||||
|
||||
;--------------------------------
|
||||
;Languages
|
||||
;Set up install lang strings for 1st lang
|
||||
${ReadmeLanguage} "${LANG_ENGLISH}" \
|
||||
"Read Me" \
|
||||
"Please review the following important information." \
|
||||
"About $(^name):" \
|
||||
"$\n Click on scrollbar arrows or press Page Down to review the entire text."
|
||||
|
||||
;Add 2nd language
|
||||
!insertmacro MUI_LANGUAGE "Norwegian"
|
||||
|
||||
;set up install lang strings for second lang
|
||||
${ReadmeLanguage} "${LANG_NORWEGIAN}" \
|
||||
"Les meg!" \
|
||||
"Vennligst les informasjonen om hvordan du skal bruke PurpleI2P." \
|
||||
"Om $(^name):" \
|
||||
"$\n Klikk på scrollbaren til høyre for å se hele innholdet."
|
||||
|
||||
;--------------------------------
|
||||
|
||||
# Installer sections
|
||||
Section -Main SEC0000
|
||||
SetOutPath $INSTDIR
|
||||
SetOverwrite on
|
||||
File /oname=i2pd.exe Release\i2pd.exe
|
||||
File /oname=install_service.bat install_service.bat
|
||||
File /oname=uninstall_service.bat uninstall_service.bat
|
||||
File /oname=LICENSE.txt ..\LICENSE
|
||||
File /oname=Readme.txt ..\README.md
|
||||
SetOutPath $INSTDIR\src
|
||||
File /r /x *.nsi /x *.rc /x *.exe /x *.obj /x *.nsh /x *.sln /x *.vcxproj /x *.tlog /x *.log /x *.res /x *.pdb /x *.suo /x *.opensdf /x *.filters /x *.sdf /x *.iss /x *.aps /x .gitignore /x *.o ../\*.*
|
||||
SetOutPath $INSTDIR
|
||||
RMDir /r /REBOOTOK $INSTDIR\src\.git # Remove git directory
|
||||
RMDir /r /REBOOTOK $INSTDIR\src\Win32\Release # Removing release directory
|
||||
RMDir /r /REBOOTOK $INSTDIR\src\Win32\nsi
|
||||
WriteRegStr HKCU "${REGKEY}\Components" Main 1
|
||||
SectionEnd
|
||||
|
||||
Section -post SEC0001
|
||||
WriteRegStr HKCU "${REGKEY}" Path $INSTDIR
|
||||
SetOutPath $INSTDIR
|
||||
WriteUninstaller $INSTDIR\uninstall.exe
|
||||
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
|
||||
CreateDirectory $SMPROGRAMS\$StartMenuGroup
|
||||
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\PurpleI2P.lnk" $INSTDIR\i2pd.exe
|
||||
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Install PurpleI2P Service.lnk" $INSTDIR\install_service.bat
|
||||
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P Service.lnk" $INSTDIR\uninstall_service.bat
|
||||
CreateShortcut "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P.lnk" $INSTDIR\uninstall.exe
|
||||
!insertmacro MUI_STARTMENU_WRITE_END
|
||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayName "$(^Name)"
|
||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayVersion "${VERSION}"
|
||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" Publisher "${COMPANY}"
|
||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" URLInfoAbout "${URL}"
|
||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" DisplayIcon $INSTDIR\uninstall.exe
|
||||
WriteRegStr HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" UninstallString $INSTDIR\uninstall.exe
|
||||
WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoModify 1
|
||||
WriteRegDWORD HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)" NoRepair 1
|
||||
WriteRegStr HKCR "i2pd" "URL Protocol" ""
|
||||
WriteRegStr HKCR "i2pd" "" "URL:i2pd" # TODO: if a instance of own is found, relaunch with a proxyfied browser to open webage. (e.g i2pd://meeh.i2p)
|
||||
WriteRegStr HKCR "i2pd\DefaultIcon" "" $INSTDIR\i2pd.exe
|
||||
WriteRegStr HKCR "i2pd\shell\open\command" "" '"$INSTDIR\i2pd.exe" "%1"'
|
||||
SectionEnd
|
||||
|
||||
# Macro for selecting uninstaller sections
|
||||
!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID
|
||||
Push $R0
|
||||
ReadRegStr $R0 HKCU "${REGKEY}\Components" "${SECTION_NAME}"
|
||||
StrCmp $R0 1 0 next${UNSECTION_ID}
|
||||
!insertmacro SelectSection "${UNSECTION_ID}"
|
||||
GoTo done${UNSECTION_ID}
|
||||
next${UNSECTION_ID}:
|
||||
!insertmacro UnselectSection "${UNSECTION_ID}"
|
||||
done${UNSECTION_ID}:
|
||||
Pop $R0
|
||||
!macroend
|
||||
|
||||
|
||||
# Uninstaller sections
|
||||
Section /o -un.Main UNSEC0000
|
||||
Delete /REBOOTOK $INSTDIR\i2pd.exe
|
||||
Delete /REBOOTOK $INSTDIR\LICENSE.txt
|
||||
Delete /REBOOTOK $INSTDIR\Readme.txt
|
||||
Delete /REBOOTOK $INSTDIR\install_service.bat
|
||||
Delete /REBOOTOK $INSTDIR\uninstall_service.bat
|
||||
RMDir /r /REBOOTOK $INSTDIR\src
|
||||
DeleteRegValue HKCU "${REGKEY}\Components" Main
|
||||
SectionEnd
|
||||
|
||||
Section -un.post UNSEC0001
|
||||
DeleteRegKey HKCU "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\$(^Name)"
|
||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Uninstall PurpleI2P.lnk"
|
||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\PurpleI2P.lnk"
|
||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\Install PurpleI2P Service.lnk"
|
||||
Delete /REBOOTOK "$SMPROGRAMS\$StartMenuGroup\UnInstall PurpleI2P Service.lnk"
|
||||
Delete /REBOOTOK "$SMSTARTUP\PurpleI2P.lnk"
|
||||
Delete /REBOOTOK $INSTDIR\uninstall.exe
|
||||
Delete /REBOOTOK $INSTDIR\debug.log
|
||||
DeleteRegValue HKCU "${REGKEY}" StartMenuGroup
|
||||
DeleteRegValue HKCU "${REGKEY}" Path
|
||||
DeleteRegKey /IfEmpty HKCU "${REGKEY}\Components"
|
||||
DeleteRegKey /IfEmpty HKCU "${REGKEY}"
|
||||
DeleteRegKey HKCR "i2pd"
|
||||
RmDir /REBOOTOK $SMPROGRAMS\$StartMenuGroup
|
||||
RmDir /REBOOTOK $INSTDIR
|
||||
Push $R0
|
||||
StrCpy $R0 $StartMenuGroup 1
|
||||
StrCmp $R0 ">" no_smgroup
|
||||
no_smgroup:
|
||||
Pop $R0
|
||||
SectionEnd
|
||||
|
||||
; var hwndExecModeRadio
|
||||
; var hwndRunServiceNowRadio
|
||||
|
||||
; Function mode_selection
|
||||
; nsDialogs::Create 1018
|
||||
; Pop $0
|
||||
; ${NSD_CreateLabel} 0 10 75% 20u "How would you like PurpleI2P (i2pd) to run?"
|
||||
; Pop $0
|
||||
|
||||
; ${NSD_CreateRadioButton} 20 60 80% 25u "Service Mode"
|
||||
; Pop $hwndExecModeRadio
|
||||
; ${NSD_AddStyle} $hwndExecModeRadio ${WS_GROUP}
|
||||
|
||||
; ${NSD_CreateRadioButton} 20 90 80% 25u "Command line Mode"
|
||||
; Pop $0
|
||||
|
||||
; ${NSD_CreateButton} 20 150 -40 14u "Do it!"
|
||||
; Pop $0
|
||||
; ${NSD_OnClick} $0 perform_mode
|
||||
|
||||
; nsDialogs::Show
|
||||
; FunctionEnd
|
||||
|
||||
; Function start_now_selection
|
||||
; nsDialogs::Create 1018
|
||||
; Pop $0
|
||||
; ${NSD_CreateLabel} 0 10 75% 20u "Enable the service now?"
|
||||
; Pop $0
|
||||
|
||||
; ${NSD_CreateRadioButton} 20 60 80% 25u "Yes"
|
||||
; Pop $hwndRunServiceNowRadio
|
||||
; ${NSD_AddStyle} $hwndRunServiceNowRadio ${WS_GROUP}
|
||||
|
||||
; ${NSD_CreateRadioButton} 20 90 80% 25u "No"
|
||||
; Pop $0
|
||||
|
||||
; ${NSD_CreateButton} 20 150 -40 14u "Do it!"
|
||||
; Pop $0
|
||||
; ${NSD_OnClick} $0 perform_mode
|
||||
|
||||
; nsDialogs::Show
|
||||
; FunctionEnd
|
||||
|
||||
; Function perform_mode
|
||||
; ${NSD_GetState} $hwndExecModeRadio $0
|
||||
; ${If} $0 = ${BST_CHECKED}
|
||||
; Call service_mode
|
||||
; ${EndIF}
|
||||
; FunctionEnd
|
||||
|
||||
; Function start_now
|
||||
; ${NSD_GetState} $hwndRunServiceNowRadio $0
|
||||
; ${If} $0 = ${BST_CHECKED}
|
||||
; Call start_now_selection
|
||||
; ${EndIF}
|
||||
; FunctionEnd
|
||||
|
||||
; Function service_mode
|
||||
; Push "create"
|
||||
; Push "PurpleI2P Service"
|
||||
; Push "$INSTDIR\i2pd.exe;autostart=1;display=PurpleI2P"
|
||||
; Call Service
|
||||
; Pop $0 ; Actually more to write than !insertmacro, but much more fun :D
|
||||
; Push "start"
|
||||
; Push "PurpleI2P Service"
|
||||
; Call Service
|
||||
; Pop $0
|
||||
; Call start_now
|
||||
; !define MUI_FINISHPAGE_RUN_NOTCHECKED
|
||||
; !define MUI_FINISHPAGE_RUN_TEXT "No need to run now since we already installed and launched it as a Windows service!"
|
||||
; FunctionEnd
|
||||
|
||||
# Installer functions
|
||||
Function .onInit
|
||||
InitPluginsDir
|
||||
!insertmacro MUI_LANGDLL_DISPLAY
|
||||
FunctionEnd
|
||||
|
||||
# Uninstaller functions
|
||||
Function un.onInit
|
||||
ReadRegStr $INSTDIR HKCU "${REGKEY}" Path
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuGroup
|
||||
!insertmacro SELECT_UNSECTION Main ${UNSEC0000}
|
||||
!insertmacro MUI_UNGETLANGUAGE
|
||||
FunctionEnd
|
||||
@@ -1,84 +0,0 @@
|
||||
Building i2pd for Windows
|
||||
=========================
|
||||
|
||||
Requirements for building:
|
||||
|
||||
* Visual Studio 2013 (tested with VS2013 Update 1, Update 3, and Update 4 RC)
|
||||
* Boost (tested with 1.56 and 1.57)
|
||||
* Crypto++ (tested with 5.6.2)
|
||||
|
||||
|
||||
Building Boost (32-bit)
|
||||
-----------------------
|
||||
|
||||
Open a Visual Studio x86 command prompt and run the following:
|
||||
|
||||
cd C:\path\to\boost\sources
|
||||
bootstrap
|
||||
b2 toolset=msvc-12.0 --build-type=complete --libdir=C:\Boost\lib\Win32 install --with-filesystem --with-program_options --with-regex --with-date_time
|
||||
|
||||
|
||||
Building Boost (64-bit)
|
||||
-----------------------
|
||||
|
||||
Open a Visual Studio x64 command prompt and run the following:
|
||||
|
||||
cd C:\path\to\boost\sources
|
||||
bootstrap
|
||||
b2 toolset=msvc-12.0 --build-type=complete --libdir=C:\Boost\lib\x64 architecture=x86 address-model=64 install --with-filesystem --with-program_options --with-regex --with-date_time
|
||||
|
||||
After Boost is compiled, set the environment variable `BOOST` to the directory
|
||||
Boost was installed to. If you followed the instructions outlined here, you
|
||||
should set it to `C:\Boost`. Additionally, set the BOOSTVER variable to the
|
||||
version of Boost that you're using, but instead of a '.' use a '_'. For
|
||||
example, I have `BOOSTVER` set to `1_57`.
|
||||
|
||||
Building Crypto++
|
||||
-----------------
|
||||
|
||||
* Open the crypttest Solution in VS2013
|
||||
* Visual Studio will ask to update the Solution/Project. Allow it.
|
||||
* Build the `cryptopp` project, both the Debug and Release targets and for both
|
||||
Win32 and x64.
|
||||
* Create a folder called `cryptopp` in the crypto++ source directory, then copy
|
||||
the header files to this new directory.
|
||||
* Set the `CRYPTOPP` environment variable pointing to the Crypto++ source directory.
|
||||
|
||||
|
||||
Building i2pd
|
||||
-------------
|
||||
|
||||
## Prep work ##
|
||||
|
||||
I strongly advise setting up your own `INCLUDES` and `LIBS` instead of relying
|
||||
on the settings in the i2pd project file. By using your own settings, if the
|
||||
i2pd devs change the paths in the project file, your builds will still work.
|
||||
|
||||
To do this, create or edit the file
|
||||
`%localappdata%\Microsoft\MSBuild\v4.0\Microsoft.Cpp.Win32.user`.
|
||||
|
||||
For comparison, my file is reproduced below:
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ImportGroup Label="PropertySheets">
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup>
|
||||
<LibraryPath>$(CRYPTOPP)\$(Platform)\Output\$(Configuration);$(BOOST)\lib\$(Platform);$(LibraryPath)</LibraryPath>
|
||||
<IncludePath>$(CRYPTOPP);$(BOOST)\include\boost-$(BOOSTVER);$(IncludePath)</IncludePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup />
|
||||
<ItemGroup />
|
||||
</Project>
|
||||
|
||||
|
||||
If you want to build x64 binaries as well, you'll want to edit or create the
|
||||
file `%localappdata%\Microsoft\MSBuild\v4.0\Microsoft.Cpp.x64.user`. If you
|
||||
followed the steps outlined earlier you can copy (or link) the win32 file to
|
||||
the x64 one.
|
||||
|
||||
## Anti-Climatic End ##
|
||||
|
||||
After following the above instructions, you'll be able to build Debug Win32,
|
||||
Debug x64, Release Win32, and Release x64 i2pd binaries.
|
||||
Binary file not shown.
38
Win32/Resource.rc2
Normal file
38
Win32/Resource.rc2
Normal file
@@ -0,0 +1,38 @@
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#error this file is not editable by Microsoft Visual C++
|
||||
#endif //APSTUDIO_INVOKED
|
||||
|
||||
#include "../libi2pd/version.h"
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION I2PD_VERSION_MAJOR,I2PD_VERSION_MINOR,I2PD_VERSION_MICRO,I2PD_VERSION_PATCH
|
||||
PRODUCTVERSION I2P_VERSION_MAJOR,I2P_VERSION_MINOR,I2P_VERSION_MICRO,I2P_VERSION_PATCH
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x40004L
|
||||
FILETYPE 0x1L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Purple I2P"
|
||||
VALUE "FileDescription", "C++ I2P daemon"
|
||||
VALUE "FileVersion", I2PD_VERSION
|
||||
VALUE "InternalName", CODENAME
|
||||
VALUE "LegalCopyright", "Copyright (C) 2013-2017, The PurpleI2P Project"
|
||||
VALUE "OriginalFilename", "i2pd"
|
||||
VALUE "ProductName", "Purple I2P"
|
||||
VALUE "ProductVersion", I2P_VERSION
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
||||
394
Win32/Win32App.cpp
Normal file
394
Win32/Win32App.cpp
Normal file
@@ -0,0 +1,394 @@
|
||||
#include <string.h>
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
#include "ClientContext.h"
|
||||
#include "Config.h"
|
||||
#include "NetDb.hpp"
|
||||
#include "RouterContext.h"
|
||||
#include "Transports.h"
|
||||
#include "Tunnel.h"
|
||||
#include "version.h"
|
||||
#include "resource.h"
|
||||
#include "Daemon.h"
|
||||
#include "Win32App.h"
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#define ID_ABOUT 2000
|
||||
#define ID_EXIT 2001
|
||||
#define ID_CONSOLE 2002
|
||||
#define ID_APP 2003
|
||||
#define ID_GRACEFUL_SHUTDOWN 2004
|
||||
#define ID_STOP_GRACEFUL_SHUTDOWN 2005
|
||||
#define ID_RELOAD 2006
|
||||
|
||||
#define ID_TRAY_ICON 2050
|
||||
#define WM_TRAYICON (WM_USER + 1)
|
||||
|
||||
#define IDT_GRACEFUL_SHUTDOWN_TIMER 2100
|
||||
#define FRAME_UPDATE_TIMER 2101
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace win32
|
||||
{
|
||||
static void ShowPopupMenu (HWND hWnd, POINT *curpos, int wDefaultItem)
|
||||
{
|
||||
HMENU hPopup = CreatePopupMenu();
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_CONSOLE, "Open &console");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_APP, "Show app");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_ABOUT, "&About...");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_RELOAD, "&Reload configs");
|
||||
if (!i2p::util::DaemonWin32::Instance ().isGraceful)
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_GRACEFUL_SHUTDOWN, "&Graceful shutdown");
|
||||
else
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_STOP_GRACEFUL_SHUTDOWN, "&Stop graceful shutdown");
|
||||
InsertMenu (hPopup, -1, MF_BYPOSITION | MF_STRING, ID_EXIT, "E&xit");
|
||||
SetMenuDefaultItem (hPopup, ID_CONSOLE, FALSE);
|
||||
SendMessage (hWnd, WM_INITMENUPOPUP, (WPARAM)hPopup, 0);
|
||||
|
||||
POINT p;
|
||||
if (!curpos)
|
||||
{
|
||||
GetCursorPos (&p);
|
||||
curpos = &p;
|
||||
}
|
||||
|
||||
WORD cmd = TrackPopupMenu (hPopup, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD | TPM_NONOTIFY, curpos->x, curpos->y, 0, hWnd, NULL);
|
||||
SendMessage (hWnd, WM_COMMAND, cmd, 0);
|
||||
|
||||
DestroyMenu(hPopup);
|
||||
}
|
||||
|
||||
static void AddTrayIcon (HWND hWnd)
|
||||
{
|
||||
NOTIFYICONDATA nid;
|
||||
memset(&nid, 0, sizeof(nid));
|
||||
nid.cbSize = sizeof(nid);
|
||||
nid.hWnd = hWnd;
|
||||
nid.uID = ID_TRAY_ICON;
|
||||
nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP | NIF_INFO;
|
||||
nid.uCallbackMessage = WM_TRAYICON;
|
||||
nid.hIcon = LoadIcon (GetModuleHandle(NULL), MAKEINTRESOURCE (MAINICON));
|
||||
strcpy (nid.szTip, "i2pd");
|
||||
strcpy (nid.szInfo, "i2pd is starting");
|
||||
Shell_NotifyIcon(NIM_ADD, &nid );
|
||||
}
|
||||
|
||||
static void RemoveTrayIcon (HWND hWnd)
|
||||
{
|
||||
NOTIFYICONDATA nid;
|
||||
nid.hWnd = hWnd;
|
||||
nid.uID = ID_TRAY_ICON;
|
||||
Shell_NotifyIcon (NIM_DELETE, &nid);
|
||||
}
|
||||
|
||||
static void ShowUptime (std::stringstream& s, int seconds)
|
||||
{
|
||||
int num;
|
||||
|
||||
if ((num = seconds / 86400) > 0) {
|
||||
s << num << " days, ";
|
||||
seconds -= num * 86400;
|
||||
}
|
||||
if ((num = seconds / 3600) > 0) {
|
||||
s << num << " hours, ";
|
||||
seconds -= num * 3600;
|
||||
}
|
||||
if ((num = seconds / 60) > 0) {
|
||||
s << num << " min, ";
|
||||
seconds -= num * 60;
|
||||
}
|
||||
s << seconds << " seconds\n";
|
||||
}
|
||||
|
||||
template <typename size> static void ShowTransfered (std::stringstream& s, size transfer)
|
||||
{
|
||||
auto bytes = transfer & 0x03ff;
|
||||
transfer >>= 10;
|
||||
auto kbytes = transfer & 0x03ff;
|
||||
transfer >>= 10;
|
||||
auto mbytes = transfer & 0x03ff;
|
||||
transfer >>= 10;
|
||||
auto gbytes = transfer & 0x03ff;
|
||||
|
||||
if (gbytes)
|
||||
s << gbytes << " GB, ";
|
||||
if (mbytes)
|
||||
s << mbytes << " MB, ";
|
||||
if (kbytes)
|
||||
s << kbytes << " KB, ";
|
||||
s << bytes << " Bytes\n";
|
||||
}
|
||||
|
||||
static void PrintMainWindowText (std::stringstream& s)
|
||||
{
|
||||
s << "\n";
|
||||
s << "Status: ";
|
||||
switch (i2p::context.GetStatus())
|
||||
{
|
||||
case eRouterStatusOK: s << "OK"; break;
|
||||
case eRouterStatusTesting: s << "Testing"; break;
|
||||
case eRouterStatusFirewalled: s << "Firewalled"; break;
|
||||
case eRouterStatusError:
|
||||
{
|
||||
switch (i2p::context.GetError())
|
||||
{
|
||||
case eRouterErrorClockSkew: s << "Clock skew"; break;
|
||||
default: s << "Error";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: s << "Unknown";
|
||||
}
|
||||
s << "; ";
|
||||
s << "Success Rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate() << "%\n";
|
||||
s << "Uptime: "; ShowUptime(s, i2p::context.GetUptime ());
|
||||
s << "\n";
|
||||
s << "Inbound: " << i2p::transport::transports.GetInBandwidth() / 1024 << " KiB/s; ";
|
||||
s << "Outbound: " << i2p::transport::transports.GetOutBandwidth() / 1024 << " KiB/s\n";
|
||||
s << "Received: "; ShowTransfered (s, i2p::transport::transports.GetTotalReceivedBytes());
|
||||
s << "Sent: "; ShowTransfered (s, i2p::transport::transports.GetTotalSentBytes());
|
||||
s << "\n";
|
||||
s << "Routers: " << i2p::data::netdb.GetNumRouters () << "; ";
|
||||
s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << "; ";
|
||||
s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "\n";
|
||||
s << "Tunnels: ";
|
||||
s << "In: " << i2p::tunnel::tunnels.CountInboundTunnels() << "; ";
|
||||
s << "Out: " << i2p::tunnel::tunnels.CountOutboundTunnels() << "; ";
|
||||
s << "Transit: " << i2p::tunnel::tunnels.CountTransitTunnels() << "\n";
|
||||
s << "\n";
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (uMsg)
|
||||
{
|
||||
case WM_CREATE:
|
||||
{
|
||||
AddTrayIcon (hWnd);
|
||||
break;
|
||||
}
|
||||
case WM_CLOSE:
|
||||
{
|
||||
RemoveTrayIcon (hWnd);
|
||||
KillTimer (hWnd, FRAME_UPDATE_TIMER);
|
||||
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
||||
PostQuitMessage (0);
|
||||
break;
|
||||
}
|
||||
case WM_COMMAND:
|
||||
{
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case ID_ABOUT:
|
||||
{
|
||||
std::stringstream text;
|
||||
text << "Version: " << I2PD_VERSION << " " << CODENAME;
|
||||
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
|
||||
return 0;
|
||||
}
|
||||
case ID_EXIT:
|
||||
{
|
||||
PostMessage (hWnd, WM_CLOSE, 0, 0);
|
||||
return 0;
|
||||
}
|
||||
case ID_GRACEFUL_SHUTDOWN:
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
SetTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER, 10*60*1000, nullptr); // 10 minutes
|
||||
i2p::util::DaemonWin32::Instance ().isGraceful = true;
|
||||
return 0;
|
||||
}
|
||||
case ID_STOP_GRACEFUL_SHUTDOWN:
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (true);
|
||||
KillTimer (hWnd, IDT_GRACEFUL_SHUTDOWN_TIMER);
|
||||
i2p::util::DaemonWin32::Instance ().isGraceful = false;
|
||||
return 0;
|
||||
}
|
||||
case ID_RELOAD:
|
||||
{
|
||||
i2p::client::context.ReloadConfig();
|
||||
std::stringstream text;
|
||||
text << "I2Pd reloading configs...";
|
||||
MessageBox( hWnd, TEXT(text.str ().c_str ()), TEXT("i2pd"), MB_ICONINFORMATION | MB_OK );
|
||||
return 0;
|
||||
}
|
||||
case ID_CONSOLE:
|
||||
{
|
||||
char buf[30];
|
||||
std::string httpAddr; i2p::config::GetOption("http.address", httpAddr);
|
||||
uint16_t httpPort; i2p::config::GetOption("http.port", httpPort);
|
||||
snprintf(buf, 30, "http://%s:%d", httpAddr.c_str(), httpPort);
|
||||
ShellExecute(NULL, "open", buf, NULL, NULL, SW_SHOWNORMAL);
|
||||
return 0;
|
||||
}
|
||||
case ID_APP:
|
||||
{
|
||||
ShowWindow(hWnd, SW_SHOW);
|
||||
SetTimer(hWnd, FRAME_UPDATE_TIMER, 3000, NULL);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_SYSCOMMAND:
|
||||
{
|
||||
switch (wParam)
|
||||
{
|
||||
case SC_MINIMIZE:
|
||||
{
|
||||
ShowWindow(hWnd, SW_HIDE);
|
||||
KillTimer (hWnd, FRAME_UPDATE_TIMER);
|
||||
return 0;
|
||||
}
|
||||
case SC_CLOSE:
|
||||
{
|
||||
std::string close; i2p::config::GetOption("close", close);
|
||||
if (0 == close.compare("ask"))
|
||||
switch(::MessageBox(hWnd, "Would you like to minimize instead of exiting?"
|
||||
" You can add 'close' configuration option. Valid values are: ask, minimize, exit.",
|
||||
"Minimize instead of exiting?", MB_ICONQUESTION | MB_YESNOCANCEL | MB_DEFBUTTON1))
|
||||
{
|
||||
case IDYES: close = "minimize"; break;
|
||||
case IDNO: close = "exit"; break;
|
||||
default: return 0;
|
||||
}
|
||||
if (0 == close.compare("minimize"))
|
||||
{
|
||||
ShowWindow(hWnd, SW_HIDE);
|
||||
KillTimer (hWnd, FRAME_UPDATE_TIMER);
|
||||
return 0;
|
||||
}
|
||||
if (0 != close.compare("exit"))
|
||||
{
|
||||
::MessageBox(hWnd, close.c_str(), "Unknown close action in config", MB_OK | MB_ICONWARNING);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case WM_TRAYICON:
|
||||
{
|
||||
switch (lParam)
|
||||
{
|
||||
case WM_LBUTTONUP:
|
||||
case WM_RBUTTONUP:
|
||||
{
|
||||
SetForegroundWindow (hWnd);
|
||||
ShowPopupMenu(hWnd, NULL, -1);
|
||||
PostMessage (hWnd, WM_APP + 1, 0, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_TIMER:
|
||||
{
|
||||
if (wParam == IDT_GRACEFUL_SHUTDOWN_TIMER)
|
||||
{
|
||||
PostMessage (hWnd, WM_CLOSE, 0, 0); // exit
|
||||
return 0;
|
||||
}
|
||||
if (wParam == FRAME_UPDATE_TIMER)
|
||||
{
|
||||
InvalidateRect(hWnd, NULL, TRUE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_PAINT:
|
||||
{
|
||||
HDC hDC;
|
||||
PAINTSTRUCT ps;
|
||||
RECT rp;
|
||||
HFONT hFont;
|
||||
std::stringstream s; PrintMainWindowText (s);
|
||||
hDC = BeginPaint (hWnd, &ps);
|
||||
GetClientRect(hWnd, &rp);
|
||||
SetTextColor(hDC, 0x00D43B69);
|
||||
hFont = CreateFont(18,0,0,0,0,0,0,0,DEFAULT_CHARSET,0,0,0,0,TEXT("Times New Roman"));
|
||||
SelectObject(hDC,hFont);
|
||||
DrawText(hDC, TEXT(s.str().c_str()), s.str().length(), &rp, DT_CENTER|DT_VCENTER);
|
||||
DeleteObject(hFont);
|
||||
EndPaint(hWnd, &ps);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return DefWindowProc( hWnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
||||
bool StartWin32App ()
|
||||
{
|
||||
if (FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd")))
|
||||
{
|
||||
MessageBox(NULL, TEXT("I2Pd is running already"), TEXT("Warning"), MB_OK);
|
||||
return false;
|
||||
}
|
||||
// register main window
|
||||
auto hInst = GetModuleHandle(NULL);
|
||||
WNDCLASSEX wclx;
|
||||
memset (&wclx, 0, sizeof(wclx));
|
||||
wclx.cbSize = sizeof(wclx);
|
||||
wclx.style = 0;
|
||||
wclx.lpfnWndProc = WndProc;
|
||||
//wclx.cbClsExtra = 0;
|
||||
//wclx.cbWndExtra = 0;
|
||||
wclx.hInstance = hInst;
|
||||
wclx.hIcon = LoadIcon (hInst, MAKEINTRESOURCE(MAINICON));
|
||||
wclx.hCursor = LoadCursor (NULL, IDC_ARROW);
|
||||
//wclx.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
|
||||
wclx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||
wclx.lpszMenuName = NULL;
|
||||
wclx.lpszClassName = I2PD_WIN32_CLASSNAME;
|
||||
RegisterClassEx (&wclx);
|
||||
// create new window
|
||||
if (!CreateWindow(I2PD_WIN32_CLASSNAME, TEXT("i2pd"), WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, 100, 100, 350, 210, NULL, NULL, hInst, NULL))
|
||||
{
|
||||
MessageBox(NULL, "Failed to create main window", TEXT("Warning!"), MB_ICONERROR | MB_OK | MB_TOPMOST);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int RunWin32App ()
|
||||
{
|
||||
MSG msg;
|
||||
while (GetMessage (&msg, NULL, 0, 0 ))
|
||||
{
|
||||
TranslateMessage (&msg);
|
||||
DispatchMessage (&msg);
|
||||
}
|
||||
return msg.wParam;
|
||||
}
|
||||
|
||||
void StopWin32App ()
|
||||
{
|
||||
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
|
||||
if (hWnd)
|
||||
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_EXIT, 0), 0);
|
||||
UnregisterClass (I2PD_WIN32_CLASSNAME, GetModuleHandle(NULL));
|
||||
}
|
||||
|
||||
bool GracefulShutdown ()
|
||||
{
|
||||
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
|
||||
if (hWnd)
|
||||
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_GRACEFUL_SHUTDOWN, 0), 0);
|
||||
return hWnd;
|
||||
}
|
||||
|
||||
bool StopGracefulShutdown ()
|
||||
{
|
||||
HWND hWnd = FindWindow (I2PD_WIN32_CLASSNAME, TEXT("i2pd"));
|
||||
if (hWnd)
|
||||
PostMessage (hWnd, WM_COMMAND, MAKEWPARAM(ID_STOP_GRACEFUL_SHUTDOWN, 0), 0);
|
||||
return hWnd;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
17
Win32/Win32App.h
Normal file
17
Win32/Win32App.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef WIN32APP_H__
|
||||
#define WIN32APP_H__
|
||||
|
||||
#define I2PD_WIN32_CLASSNAME "i2pd main window"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace win32
|
||||
{
|
||||
bool StartWin32App ();
|
||||
void StopWin32App ();
|
||||
int RunWin32App ();
|
||||
bool GracefulShutdown ();
|
||||
bool StopGracefulShutdown ();
|
||||
}
|
||||
}
|
||||
#endif // WIN32APP_H__
|
||||
@@ -7,15 +7,14 @@
|
||||
#include <strsafe.h>
|
||||
#include <windows.h>
|
||||
|
||||
#include "../Daemon.h"
|
||||
#include "../Log.h"
|
||||
#include "Daemon.h"
|
||||
#include "Log.h"
|
||||
|
||||
I2PService *I2PService::s_service = NULL;
|
||||
|
||||
BOOL I2PService::isService()
|
||||
{
|
||||
BOOL bIsService = FALSE;
|
||||
|
||||
HWINSTA hWinStation = GetProcessWindowStation();
|
||||
if (hWinStation != NULL)
|
||||
{
|
||||
@@ -31,28 +30,23 @@ BOOL I2PService::isService()
|
||||
BOOL I2PService::Run(I2PService &service)
|
||||
{
|
||||
s_service = &service;
|
||||
|
||||
SERVICE_TABLE_ENTRY serviceTable[] =
|
||||
{
|
||||
{ service.m_name, ServiceMain },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
return StartServiceCtrlDispatcher(serviceTable);
|
||||
}
|
||||
|
||||
|
||||
void WINAPI I2PService::ServiceMain(DWORD dwArgc, PSTR *pszArgv)
|
||||
{
|
||||
assert(s_service != NULL);
|
||||
|
||||
s_service->m_statusHandle = RegisterServiceCtrlHandler(
|
||||
s_service->m_name, ServiceCtrlHandler);
|
||||
if (s_service->m_statusHandle == NULL)
|
||||
{
|
||||
throw GetLastError();
|
||||
}
|
||||
|
||||
s_service->Start(dwArgc, pszArgv);
|
||||
}
|
||||
|
||||
@@ -61,27 +55,23 @@ void WINAPI I2PService::ServiceCtrlHandler(DWORD dwCtrl)
|
||||
{
|
||||
switch (dwCtrl)
|
||||
{
|
||||
case SERVICE_CONTROL_STOP: s_service->Stop(); break;
|
||||
case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;
|
||||
case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;
|
||||
case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;
|
||||
case SERVICE_CONTROL_INTERROGATE: break;
|
||||
default: break;
|
||||
case SERVICE_CONTROL_STOP: s_service->Stop(); break;
|
||||
case SERVICE_CONTROL_PAUSE: s_service->Pause(); break;
|
||||
case SERVICE_CONTROL_CONTINUE: s_service->Continue(); break;
|
||||
case SERVICE_CONTROL_SHUTDOWN: s_service->Shutdown(); break;
|
||||
case SERVICE_CONTROL_INTERROGATE: break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
I2PService::I2PService(PSTR pszServiceName,
|
||||
BOOL fCanStop,
|
||||
BOOL fCanShutdown,
|
||||
BOOL fCanPauseContinue)
|
||||
{
|
||||
m_name = (pszServiceName == NULL) ? "" : pszServiceName;
|
||||
|
||||
m_name = (pszServiceName == NULL) ? (PSTR)"" : pszServiceName;
|
||||
m_statusHandle = NULL;
|
||||
|
||||
m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
|
||||
m_status.dwCurrentState = SERVICE_START_PENDING;
|
||||
|
||||
DWORD dwControlsAccepted = 0;
|
||||
@@ -91,16 +81,14 @@ I2PService::I2PService(PSTR pszServiceName,
|
||||
dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN;
|
||||
if (fCanPauseContinue)
|
||||
dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE;
|
||||
m_status.dwControlsAccepted = dwControlsAccepted;
|
||||
|
||||
m_status.dwControlsAccepted = dwControlsAccepted;
|
||||
m_status.dwWin32ExitCode = NO_ERROR;
|
||||
m_status.dwServiceSpecificExitCode = 0;
|
||||
m_status.dwCheckPoint = 0;
|
||||
m_status.dwWaitHint = 0;
|
||||
|
||||
m_fStopping = FALSE;
|
||||
|
||||
// Create a manual-reset event that is not signaled at first to indicate
|
||||
// Create a manual-reset event that is not signaled at first to indicate
|
||||
// the stopped signal of the service.
|
||||
m_hStoppedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
if (m_hStoppedEvent == NULL)
|
||||
@@ -109,7 +97,6 @@ I2PService::I2PService(PSTR pszServiceName,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
I2PService::~I2PService(void)
|
||||
{
|
||||
if (m_hStoppedEvent)
|
||||
@@ -119,93 +106,73 @@ I2PService::~I2PService(void)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void I2PService::Start(DWORD dwArgc, PSTR *pszArgv)
|
||||
{
|
||||
try
|
||||
{
|
||||
SetServiceStatus(SERVICE_START_PENDING);
|
||||
|
||||
OnStart(dwArgc, pszArgv);
|
||||
|
||||
SetServiceStatus(SERVICE_RUNNING);
|
||||
}
|
||||
catch (DWORD dwError)
|
||||
{
|
||||
LogPrint("Win32Service Start", dwError);
|
||||
|
||||
LogPrint(eLogError, "Win32Service Start", dwError);
|
||||
SetServiceStatus(SERVICE_STOPPED, dwError);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogPrint("Win32Service failed to start.", EVENTLOG_ERROR_TYPE);
|
||||
|
||||
LogPrint(eLogError, "Win32Service failed to start.", EVENTLOG_ERROR_TYPE);
|
||||
SetServiceStatus(SERVICE_STOPPED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void I2PService::OnStart(DWORD dwArgc, PSTR *pszArgv)
|
||||
{
|
||||
LogPrint("Win32Service in OnStart",
|
||||
EVENTLOG_INFORMATION_TYPE);
|
||||
|
||||
LogPrint(eLogInfo, "Win32Service in OnStart", EVENTLOG_INFORMATION_TYPE);
|
||||
Daemon.start();
|
||||
|
||||
//i2p::util::config::OptionParser(dwArgc, pszArgv);
|
||||
//i2p::util::filesystem::ReadConfigFile(i2p::util::config::mapArgs, i2p::util::config::mapMultiArgs);
|
||||
//i2p::context.OverrideNTCPAddress(i2p::util::config::GetCharArg("-host", "127.0.0.1"),
|
||||
// i2p::util::config::GetArg("-port", 17070));
|
||||
|
||||
_worker = new std::thread(std::bind(&I2PService::WorkerThread, this));
|
||||
}
|
||||
|
||||
|
||||
void I2PService::WorkerThread()
|
||||
{
|
||||
while (!m_fStopping)
|
||||
{
|
||||
::Sleep(1000); // Simulate some lengthy operations.
|
||||
::Sleep(1000); // Simulate some lengthy operations.
|
||||
}
|
||||
|
||||
// Signal the stopped event.
|
||||
SetEvent(m_hStoppedEvent);
|
||||
}
|
||||
|
||||
|
||||
void I2PService::Stop()
|
||||
{
|
||||
DWORD dwOriginalState = m_status.dwCurrentState;
|
||||
try
|
||||
{
|
||||
SetServiceStatus(SERVICE_STOP_PENDING);
|
||||
|
||||
OnStop();
|
||||
|
||||
SetServiceStatus(SERVICE_STOPPED);
|
||||
}
|
||||
catch (DWORD dwError)
|
||||
{
|
||||
LogPrint("Win32Service Stop", dwError);
|
||||
|
||||
LogPrint(eLogInfo, "Win32Service Stop", dwError);
|
||||
SetServiceStatus(dwOriginalState);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogPrint("Win32Service failed to stop.", EVENTLOG_ERROR_TYPE);
|
||||
|
||||
LogPrint(eLogError, "Win32Service failed to stop.", EVENTLOG_ERROR_TYPE);
|
||||
SetServiceStatus(dwOriginalState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void I2PService::OnStop()
|
||||
{
|
||||
// Log a service stop message to the Application log.
|
||||
LogPrint("Win32Service in OnStop", EVENTLOG_INFORMATION_TYPE);
|
||||
|
||||
LogPrint(eLogInfo, "Win32Service in OnStop", EVENTLOG_INFORMATION_TYPE);
|
||||
Daemon.stop();
|
||||
|
||||
m_fStopping = TRUE;
|
||||
if (WaitForSingleObject(m_hStoppedEvent, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
@@ -215,102 +182,83 @@ void I2PService::OnStop()
|
||||
delete _worker;
|
||||
}
|
||||
|
||||
|
||||
void I2PService::Pause()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetServiceStatus(SERVICE_PAUSE_PENDING);
|
||||
|
||||
OnPause();
|
||||
|
||||
SetServiceStatus(SERVICE_PAUSED);
|
||||
}
|
||||
catch (DWORD dwError)
|
||||
{
|
||||
LogPrint("Win32Service Pause", dwError);
|
||||
|
||||
LogPrint(eLogError, "Win32Service Pause", dwError);
|
||||
SetServiceStatus(SERVICE_RUNNING);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogPrint("Win32Service failed to pause.", EVENTLOG_ERROR_TYPE);
|
||||
|
||||
LogPrint(eLogError, "Win32Service failed to pause.", EVENTLOG_ERROR_TYPE);
|
||||
SetServiceStatus(SERVICE_RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void I2PService::OnPause()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void I2PService::Continue()
|
||||
{
|
||||
try
|
||||
{
|
||||
SetServiceStatus(SERVICE_CONTINUE_PENDING);
|
||||
|
||||
OnContinue();
|
||||
|
||||
SetServiceStatus(SERVICE_RUNNING);
|
||||
}
|
||||
catch (DWORD dwError)
|
||||
{
|
||||
LogPrint("Win32Service Continue", dwError);
|
||||
|
||||
LogPrint(eLogError, "Win32Service Continue", dwError);
|
||||
SetServiceStatus(SERVICE_PAUSED);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogPrint("Win32Service failed to resume.", EVENTLOG_ERROR_TYPE);
|
||||
|
||||
LogPrint(eLogError, "Win32Service failed to resume.", EVENTLOG_ERROR_TYPE);
|
||||
SetServiceStatus(SERVICE_PAUSED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void I2PService::OnContinue()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void I2PService::Shutdown()
|
||||
{
|
||||
try
|
||||
{
|
||||
OnShutdown();
|
||||
|
||||
SetServiceStatus(SERVICE_STOPPED);
|
||||
}
|
||||
catch (DWORD dwError)
|
||||
{
|
||||
LogPrint("Win32Service Shutdown", dwError);
|
||||
LogPrint(eLogError, "Win32Service Shutdown", dwError);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogPrint("Win32Service failed to shut down.", EVENTLOG_ERROR_TYPE);
|
||||
LogPrint(eLogError, "Win32Service failed to shut down.", EVENTLOG_ERROR_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void I2PService::OnShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void I2PService::SetServiceStatus(DWORD dwCurrentState,
|
||||
DWORD dwWin32ExitCode,
|
||||
DWORD dwWaitHint)
|
||||
{
|
||||
static DWORD dwCheckPoint = 1;
|
||||
|
||||
|
||||
m_status.dwCurrentState = dwCurrentState;
|
||||
m_status.dwWin32ExitCode = dwWin32ExitCode;
|
||||
m_status.dwWaitHint = dwWaitHint;
|
||||
|
||||
m_status.dwCheckPoint =
|
||||
((dwCurrentState == SERVICE_RUNNING) ||
|
||||
(dwCurrentState == SERVICE_STOPPED)) ?
|
||||
@@ -335,12 +283,7 @@ void FreeHandles(SC_HANDLE schSCManager, SC_HANDLE schService)
|
||||
}
|
||||
}
|
||||
|
||||
void InstallService(PSTR pszServiceName,
|
||||
PSTR pszDisplayName,
|
||||
DWORD dwStartType,
|
||||
PSTR pszDependencies,
|
||||
PSTR pszAccount,
|
||||
PSTR pszPassword)
|
||||
void InstallService(PCSTR pszServiceName, PCSTR pszDisplayName, DWORD dwStartType, PCSTR pszDependencies, PCSTR pszAccount, PCSTR pszPassword)
|
||||
{
|
||||
printf("Try to install Win32Service (%s).\n", pszServiceName);
|
||||
|
||||
@@ -354,10 +297,10 @@ void InstallService(PSTR pszServiceName,
|
||||
FreeHandles(schSCManager, schService);
|
||||
return;
|
||||
}
|
||||
strncat(szPath, " --daemon", MAX_PATH);
|
||||
|
||||
// Open the local default service control manager database
|
||||
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT |
|
||||
SC_MANAGER_CREATE_SERVICE);
|
||||
schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE);
|
||||
if (schSCManager == NULL)
|
||||
{
|
||||
printf("OpenSCManager failed w/err 0x%08lx\n", GetLastError());
|
||||
@@ -381,6 +324,7 @@ void InstallService(PSTR pszServiceName,
|
||||
pszAccount, // Service running account
|
||||
pszPassword // Password of the account
|
||||
);
|
||||
|
||||
if (schService == NULL)
|
||||
{
|
||||
printf("CreateService failed w/err 0x%08lx\n", GetLastError());
|
||||
@@ -394,7 +338,7 @@ void InstallService(PSTR pszServiceName,
|
||||
FreeHandles(schSCManager, schService);
|
||||
}
|
||||
|
||||
void UninstallService(PSTR pszServiceName)
|
||||
void UninstallService(PCSTR pszServiceName)
|
||||
{
|
||||
printf("Try to uninstall Win32Service (%s).\n", pszServiceName);
|
||||
|
||||
@@ -412,8 +356,7 @@ void UninstallService(PSTR pszServiceName)
|
||||
}
|
||||
|
||||
// Open the service with delete, stop, and query status permissions
|
||||
schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP |
|
||||
SERVICE_QUERY_STATUS | DELETE);
|
||||
schService = OpenService(schSCManager, pszServiceName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE);
|
||||
if (schService == NULL)
|
||||
{
|
||||
printf("OpenService failed w/err 0x%08lx\n", GetLastError());
|
||||
|
||||
@@ -2,16 +2,14 @@
|
||||
#define WIN_32_SERVICE_H__
|
||||
|
||||
#include <thread>
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
// Internal name of the service
|
||||
#define SERVICE_NAME "i2pService"
|
||||
#define SERVICE_NAME "i2pdService"
|
||||
|
||||
// Displayed name of the service
|
||||
#define SERVICE_DISPLAY_NAME "i2p router service"
|
||||
#define SERVICE_DISPLAY_NAME "i2pd router service"
|
||||
|
||||
// Service start options.
|
||||
#define SERVICE_START_TYPE SERVICE_DEMAND_START
|
||||
@@ -26,7 +24,6 @@
|
||||
#define SERVICE_PASSWORD NULL
|
||||
#endif
|
||||
|
||||
|
||||
class I2PService
|
||||
{
|
||||
public:
|
||||
@@ -73,13 +70,15 @@ private:
|
||||
std::thread* _worker;
|
||||
};
|
||||
|
||||
void InstallService(PSTR pszServiceName,
|
||||
PSTR pszDisplayName,
|
||||
void InstallService(
|
||||
PCSTR pszServiceName,
|
||||
PCSTR pszDisplayName,
|
||||
DWORD dwStartType,
|
||||
PSTR pszDependencies,
|
||||
PSTR pszAccount,
|
||||
PSTR pszPassword);
|
||||
PCSTR pszDependencies,
|
||||
PCSTR pszAccount,
|
||||
PCSTR pszPassword
|
||||
);
|
||||
|
||||
void UninstallService(PSTR pszServiceName);
|
||||
void UninstallService(PCSTR pszServiceName);
|
||||
|
||||
#endif // WIN_32_SERVICE_H__
|
||||
@@ -1,30 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2013
|
||||
VisualStudioVersion = 12.0.30723.0
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "i2pd", "i2pd.vcxproj", "{930568EC-31C9-406A-AD1C-9636DF5D8FAA}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|Win32.Deploy.0 = Debug|Win32
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Debug|x64.Build.0 = Debug|x64
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|Win32.Build.0 = Release|Win32
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|Win32.Deploy.0 = Release|Win32
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|x64.ActiveCfg = Release|x64
|
||||
{930568EC-31C9-406A-AD1C-9636DF5D8FAA}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,283 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\AddressBook.cpp" />
|
||||
<ClCompile Include="..\aes.cpp" />
|
||||
<ClCompile Include="..\base64.cpp" />
|
||||
<ClCompile Include="..\BOB.cpp" />
|
||||
<ClCompile Include="..\CryptoConst.cpp" />
|
||||
<ClCompile Include="..\Daemon.cpp" />
|
||||
<ClCompile Include="..\DaemonWin32.cpp" />
|
||||
<ClCompile Include="..\Garlic.cpp" />
|
||||
<ClCompile Include="..\HTTPProxy.cpp" />
|
||||
<ClCompile Include="..\HTTPServer.cpp" />
|
||||
<ClCompile Include="..\I2NPProtocol.cpp" />
|
||||
<ClCompile Include="..\i2p.cpp" />
|
||||
<ClCompile Include="..\I2PEndian.cpp" />
|
||||
<ClCompile Include="..\Identity.cpp" />
|
||||
<ClCompile Include="..\LeaseSet.cpp" />
|
||||
<ClCompile Include="..\Log.cpp" />
|
||||
<ClCompile Include="..\NetDb.cpp" />
|
||||
<ClCompile Include="..\NTCPSession.cpp" />
|
||||
<ClCompile Include="..\Reseed.cpp" />
|
||||
<ClCompile Include="..\RouterContext.cpp" />
|
||||
<ClCompile Include="..\RouterInfo.cpp" />
|
||||
<ClCompile Include="..\SAM.cpp" />
|
||||
<ClCompile Include="..\SSU.cpp" />
|
||||
<ClCompile Include="..\SSUData.cpp" />
|
||||
<ClCompile Include="..\SSUSession.cpp" />
|
||||
<ClCompile Include="..\Streaming.cpp" />
|
||||
<ClCompile Include="..\Datagram.cpp" />
|
||||
<ClCompile Include="..\Destination.cpp" />
|
||||
<ClCompile Include="..\TransitTunnel.cpp" />
|
||||
<ClCompile Include="..\Transports.cpp" />
|
||||
<ClCompile Include="..\Tunnel.cpp" />
|
||||
<ClCompile Include="..\TunnelEndpoint.cpp" />
|
||||
<ClCompile Include="..\TunnelGateway.cpp" />
|
||||
<ClCompile Include="..\TunnelPool.cpp" />
|
||||
<ClCompile Include="..\UPnP.cpp" />
|
||||
<ClCompile Include="..\util.cpp" />
|
||||
<ClCompile Include="..\SOCKS.cpp" />
|
||||
<ClCompile Include="..\I2PTunnel.cpp" />
|
||||
<ClCompile Include="..\ClientContext.cpp" />
|
||||
<ClCompile Include="Win32Service.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\AddressBook.h" />
|
||||
<ClInclude Include="..\base64.h" />
|
||||
<ClInclude Include="..\BOB.h" />
|
||||
<ClInclude Include="..\CryptoConst.h" />
|
||||
<ClInclude Include="..\Daemon.h" />
|
||||
<ClInclude Include="..\ElGamal.h" />
|
||||
<ClInclude Include="..\Garlic.h" />
|
||||
<ClInclude Include="..\HTTPProxy.h" />
|
||||
<ClInclude Include="..\HTTPServer.h" />
|
||||
<ClInclude Include="..\I2NPProtocol.h" />
|
||||
<ClInclude Include="..\I2PEndian.h" />
|
||||
<ClInclude Include="..\Identity.h" />
|
||||
<ClInclude Include="..\LeaseSet.h" />
|
||||
<ClInclude Include="..\LittleBigEndian.h" />
|
||||
<ClInclude Include="..\Log.h" />
|
||||
<ClInclude Include="..\NetDb.h" />
|
||||
<ClInclude Include="..\NTCPSession.h" />
|
||||
<ClInclude Include="..\Queue.h" />
|
||||
<ClInclude Include="..\Reseed.h" />
|
||||
<ClInclude Include="..\RouterContext.h" />
|
||||
<ClInclude Include="..\RouterInfo.h" />
|
||||
<ClInclude Include="..\SAM.h" />
|
||||
<ClInclude Include="..\SSU.h" />
|
||||
<ClInclude Include="..\SSUData.h" />
|
||||
<ClInclude Include="..\SSUSession.h" />
|
||||
<ClInclude Include="..\Streaming.h" />
|
||||
<ClInclude Include="..\Datagram.h" />
|
||||
<ClInclude Include="..\Destination.h" />
|
||||
<ClInclude Include="..\Timestamp.h" />
|
||||
<ClInclude Include="..\TransitTunnel.h" />
|
||||
<ClInclude Include="..\Transports.h" />
|
||||
<ClInclude Include="..\Tunnel.h" />
|
||||
<ClInclude Include="..\TunnelBase.h" />
|
||||
<ClInclude Include="..\TunnelConfig.h" />
|
||||
<ClInclude Include="..\TunnelEndpoint.h" />
|
||||
<ClInclude Include="..\TunnelGateway.h" />
|
||||
<ClInclude Include="..\TunnelPool.h" />
|
||||
<ClInclude Include="..\UPnP.h" />
|
||||
<ClInclude Include="..\util.h" />
|
||||
<ClInclude Include="..\SOCKS.h" />
|
||||
<ClInclude Include="..\I2PTunnel.h" />
|
||||
<ClInclude Include="..\version.h" />
|
||||
<ClInclude Include="..\Signature.h" />
|
||||
<ClInclude Include="..\ClientContext.h" />
|
||||
<ClInclude Include="..\TransportSession.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="Win32Service.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Resource.rc" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{930568EC-31C9-406A-AD1C-9636DF5D8FAA}</ProjectGuid>
|
||||
<RootNamespace>i2pd</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120_xp</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v120_xp</PlatformToolset>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v120_xp</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v120_xp</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>NotSet</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<IncludePath>./..;$(IncludePath);$(BOOST);$(CRYPTOPP);C:\build-lib\cryptopp;C:\build-lib\boost_1_57_0\</IncludePath>
|
||||
<LibraryPath>$(BOOST)\stage\lib;C:\build-lib\cryptopp;C:\build-lib\boost_1_57_0\stage\lib;$(CRYPTOPP)\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath)</LibraryPath>
|
||||
<SourcePath>./..;$(VC_SourcePath);</SourcePath>
|
||||
<TargetName>$(ProjectName)_d</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<IncludePath>./..;$(IncludePath);$(BOOST);$(CRYPTOPP)</IncludePath>
|
||||
<LibraryPath>$(BOOST)\stage\lib;$(CRYPTOPP)\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath)</LibraryPath>
|
||||
<SourcePath>./..;$(VC_SourcePath);</SourcePath>
|
||||
<TargetName>$(ProjectName)_d</TargetName>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<IncludePath>./..;$(IncludePath);$(BOOST);C:\build-lib\boost_1_57_0\;C:\build-lib</IncludePath>
|
||||
<LibraryPath>C:\build-lib\boost_1_57_0\stage\lib;C:\build-lib\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath)</LibraryPath>
|
||||
<SourcePath>./..;$(VC_SourcePath);</SourcePath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<IncludePath>./..;$(IncludePath);$(BOOST);$(CRYPTOPP)</IncludePath>
|
||||
<LibraryPath>$(BOOST)\stage\lib;$(CRYPTOPP)\cryptopp\$(Platform)\Output\$(Configuration);$(LibraryPath)</LibraryPath>
|
||||
<SourcePath>./..;$(VC_SourcePath);</SourcePath>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_MBCS;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
|
||||
<Version>0.2</Version>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_MBCS;_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
|
||||
<Version>0.2</Version>
|
||||
<SubSystem>Console</SubSystem>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level2</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>false</OptimizeReferences>
|
||||
<AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
|
||||
<Version>
|
||||
</Version>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<MinimumRequiredVersion>5.01</MinimumRequiredVersion>
|
||||
<LinkErrorReporting>NoErrorReport</LinkErrorReporting>
|
||||
</Link>
|
||||
<Manifest>
|
||||
<AssemblyIdentity>
|
||||
</AssemblyIdentity>
|
||||
<ComponentFileName>
|
||||
</ComponentFileName>
|
||||
</Manifest>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PreprocessorDefinitions>_WIN32_WINNT=0x0502;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>false</OptimizeReferences>
|
||||
<AdditionalDependencies>cryptlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
|
||||
<Version>
|
||||
</Version>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<MinimumRequiredVersion>5.02</MinimumRequiredVersion>
|
||||
<LinkErrorReporting>NoErrorReport</LinkErrorReporting>
|
||||
</Link>
|
||||
<Manifest>
|
||||
<AssemblyIdentity>
|
||||
</AssemblyIdentity>
|
||||
<ComponentFileName>
|
||||
</ComponentFileName>
|
||||
</Manifest>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
@@ -1,284 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Win32">
|
||||
<UniqueIdentifier>{a880a08c-16b8-4243-82ea-6bfc63bb7dab}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\Identity.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\LeaseSet.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Log.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NetDb.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\NTCPSession.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\RouterContext.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\RouterInfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Streaming.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\TransitTunnel.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Transports.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Tunnel.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\TunnelEndpoint.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\TunnelGateway.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\base64.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Garlic.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\HTTPServer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\I2NPProtocol.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\i2p.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\I2PEndian.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\SOCKS.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\SSU.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\util.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Reseed.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\UPnP.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\HTTPProxy.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\TunnelPool.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\AddressBook.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Win32Service.cpp">
|
||||
<Filter>Win32</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Daemon.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\DaemonWin32.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\SSUData.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\CryptoConst.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\aes.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\I2PTunnel.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\SAM.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\SSUSession.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Datagram.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\Destination.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\ClientContext.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\Identity.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\LeaseSet.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\LittleBigEndian.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Log.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NetDb.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NTCPSession.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Queue.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\RouterContext.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\RouterInfo.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Streaming.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Timestamp.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\TransitTunnel.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Transports.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Tunnel.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\TunnelBase.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\TunnelConfig.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\TunnelEndpoint.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\TunnelGateway.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\base64.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\CryptoConst.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\ElGamal.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Garlic.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\HTTPServer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\I2NPProtocol.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\I2PEndian.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\SOCKS.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\SSU.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\util.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Reseed.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\UPnP.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\HTTPProxy.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\TunnelPool.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\AddressBook.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Win32Service.h">
|
||||
<Filter>Win32</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Daemon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\SSUData.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\I2PTunnel.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\version.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Signature.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\SAM.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\SSUSession.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Datagram.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\Destination.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\ClientContext.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\TransportSession.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Resource.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 97 KiB |
@@ -1,149 +0,0 @@
|
||||
|
||||
#define I2Pd_AppName "i2pd"
|
||||
#define I2Pd_ver "0.2"
|
||||
|
||||
[Setup]
|
||||
AppName={#I2Pd_AppName}
|
||||
AppVersion={#I2Pd_ver}
|
||||
DefaultDirName={pf}\I2Pd
|
||||
DefaultGroupName=I2Pd
|
||||
UninstallDisplayIcon={app}\I2Pd.exe
|
||||
Compression=lzma2
|
||||
SolidCompression=yes
|
||||
OutputDir=.
|
||||
LicenseFile=.\..\LICENSE
|
||||
OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver}
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
|
||||
|
||||
[Files]
|
||||
Source: "x64\Release\i2pd.exe"; DestDir: "{app}"; DestName: "i2pd.exe"; Check: Is64BitInstallMode
|
||||
Source: "Release\i2pd.exe"; DestDir: "{app}"; Check: not Is64BitInstallMode
|
||||
Source: "..\README.md"; DestDir: "{app}"; DestName: "Readme.txt"; AfterInstall: ConvertLineEndings
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\I2Pd"; Filename: "{app}\i2pd.exe"
|
||||
Name: "{group}\Readme"; Filename: "{app}\Readme.txt"
|
||||
|
||||
|
||||
[Registry]
|
||||
Root: HKCU; Subkey: "Environment"; ValueName: "Path"; ValueType: "string"; ValueData: "{app};{olddata}"; Check: NotOnPathAlready(); Flags: preservestringtype;
|
||||
|
||||
[Code]
|
||||
|
||||
var
|
||||
DefaultTop,
|
||||
DefaultLeft,
|
||||
DefaultHeight,
|
||||
DefaultBackTop,
|
||||
DefaultNextTop,
|
||||
DefaultCancelTop,
|
||||
DefaultBevelTop,
|
||||
DefaultOuterHeight: Integer;
|
||||
|
||||
const
|
||||
LicenseHeight = 400;
|
||||
LF = #10;
|
||||
CR = #13;
|
||||
CRLF = CR + LF;
|
||||
|
||||
procedure ConvertLineEndings();
|
||||
var
|
||||
FilePath : String;
|
||||
FileContents : String;
|
||||
begin
|
||||
FilePath := ExpandConstant(CurrentFileName)
|
||||
LoadStringFromFile(FilePath, FileContents);
|
||||
StringChangeEx(FileContents, LF, CRLF, False);
|
||||
SaveStringToFile(FilePath, FileContents, False);
|
||||
end;
|
||||
|
||||
procedure InitializeWizard();
|
||||
begin
|
||||
DefaultTop := WizardForm.Top;
|
||||
DefaultLeft := WizardForm.Left;
|
||||
DefaultHeight := WizardForm.Height;
|
||||
DefaultBackTop := WizardForm.BackButton.Top;
|
||||
DefaultNextTop := WizardForm.NextButton.Top;
|
||||
DefaultCancelTop := WizardForm.CancelButton.Top;
|
||||
DefaultBevelTop := WizardForm.Bevel.Top;
|
||||
DefaultOuterHeight := WizardForm.OuterNotebook.Height;
|
||||
|
||||
WizardForm.InnerPage.Height := WizardForm.InnerPage.Height + (LicenseHeight - DefaultHeight);
|
||||
WizardForm.InnerNotebook.Height := WizardForm.InnerNotebook.Height + (LicenseHeight - DefaultHeight);
|
||||
WizardForm.LicensePage.Height := WizardForm.LicensePage.Height + (LicenseHeight - DefaultHeight);
|
||||
WizardForm.LicenseMemo.Height := WizardForm.LicenseMemo.Height + (LicenseHeight - DefaultHeight);
|
||||
WizardForm.LicenseNotAcceptedRadio.Top := WizardForm.LicenseNotAcceptedRadio.Top + (LicenseHeight - DefaultHeight);
|
||||
WizardForm.LicenseAcceptedRadio.Top := WizardForm.LicenseAcceptedRadio.Top + (LicenseHeight - DefaultHeight);
|
||||
|
||||
end;
|
||||
|
||||
procedure CurPageChanged(CurPageID: Integer);
|
||||
begin
|
||||
if CurPageID = wpLicense then
|
||||
begin
|
||||
WizardForm.Top := DefaultTop - (LicenseHeight - DefaultHeight) div 2;
|
||||
WizardForm.Height := LicenseHeight;
|
||||
WizardForm.OuterNotebook.Height := WizardForm.OuterNotebook.Height + (LicenseHeight - DefaultHeight);
|
||||
WizardForm.CancelButton.Top := DefaultCancelTop + (LicenseHeight - DefaultHeight);
|
||||
WizardForm.NextButton.Top := DefaultNextTop + (LicenseHeight - DefaultHeight);
|
||||
WizardForm.BackButton.Top := DefaultBackTop + (LicenseHeight - DefaultHeight);
|
||||
WizardForm.Bevel.Top := DefaultBevelTop + (LicenseHeight - DefaultHeight);
|
||||
end
|
||||
else
|
||||
begin
|
||||
WizardForm.Top := DefaultTop;
|
||||
WizardForm.Left := DefaultLeft;
|
||||
WizardForm.Height := DefaultHeight;
|
||||
WizardForm.OuterNotebook.Height := DefaultOuterHeight;
|
||||
WizardForm.CancelButton.Top := DefaultCancelTop;
|
||||
WizardForm.NextButton.Top := DefaultNextTop;
|
||||
WizardForm.BackButton.Top := DefaultBackTop;
|
||||
WizardForm.Bevel.Top := DefaultBevelTop;
|
||||
end;
|
||||
end;
|
||||
|
||||
function NotOnPathAlready(): Boolean;
|
||||
var
|
||||
BinDir, Path: String;
|
||||
begin
|
||||
Log('Checking if i2pd dir is already in the %PATH%');
|
||||
if RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'Path', Path) then
|
||||
begin // Successfully read the value
|
||||
Log('HKCUEnvironmentPATH = ' + Path);
|
||||
BinDir := ExpandConstant('{app}');
|
||||
Log('Looking for i2pd dir in %PATH%: ' + BinDir + ' in ' + Path);
|
||||
if Pos(LowerCase(BinDir), Lowercase(Path)) = 0 then
|
||||
begin
|
||||
Log('Did not find i2pd dir in %PATH% so I will add it');
|
||||
Result := True;
|
||||
end
|
||||
else
|
||||
begin
|
||||
Log('Found i2pd dir in %PATH% so will not add it again');
|
||||
Result := False;
|
||||
end
|
||||
end
|
||||
else // The key probably doesn't exist
|
||||
begin
|
||||
Log('Could not access HKCUEnvironmentPATH so I assume that it is OK to add it');
|
||||
Result := True;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
||||
procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
|
||||
var
|
||||
BinDir, Path: String;
|
||||
begin
|
||||
if (CurUninstallStep = usPostUninstall)
|
||||
and (RegQueryStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path)) then
|
||||
begin
|
||||
BinDir := ExpandConstant('{app}');
|
||||
if Pos(LowerCase(BinDir) + ';', Lowercase(Path)) <> 0 then
|
||||
begin
|
||||
StringChange(Path, BinDir + ';', '');
|
||||
RegWriteStringValue(HKEY_CURRENT_USER, 'Environment', 'PATH', Path);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
@@ -1 +0,0 @@
|
||||
i2pd --service=install
|
||||
41
Win32/installer.iss
Normal file
41
Win32/installer.iss
Normal file
@@ -0,0 +1,41 @@
|
||||
#define I2Pd_AppName "i2pd"
|
||||
#define I2Pd_ver "2.18.0"
|
||||
#define I2Pd_Publisher "PurpleI2P"
|
||||
|
||||
[Setup]
|
||||
AppName={#I2Pd_AppName}
|
||||
AppVersion={#I2Pd_ver}
|
||||
AppPublisher={#I2Pd_Publisher}
|
||||
DefaultDirName={pf}\I2Pd
|
||||
DefaultGroupName=I2Pd
|
||||
UninstallDisplayIcon={app}\I2Pd.exe
|
||||
OutputDir=.
|
||||
LicenseFile=../LICENSE
|
||||
OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver}
|
||||
SetupIconFile=mask.ico
|
||||
InternalCompressLevel=ultra64
|
||||
Compression=lzma/ultra64
|
||||
SolidCompression=true
|
||||
ArchitecturesInstallIn64BitMode=x64
|
||||
AppVerName={#I2Pd_AppName}
|
||||
ExtraDiskSpaceRequired=15
|
||||
AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2}
|
||||
AppPublisherURL=http://i2pd.website/
|
||||
AppSupportURL=https://github.com/PurpleI2P/i2pd/issues
|
||||
AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases
|
||||
|
||||
[Files]
|
||||
Source: ..\i2pd_x86.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: not IsWin64
|
||||
Source: ..\i2pd_x64.exe; DestDir: {app}; DestName: i2pd.exe; Flags: ignoreversion; Check: IsWin64
|
||||
Source: ..\README.md; DestDir: {app}; DestName: Readme.txt; Flags: onlyifdoesntexist
|
||||
Source: ..\contrib\i2pd.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist
|
||||
Source: ..\contrib\subscriptions.txt; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist
|
||||
Source: ..\contrib\tunnels.conf; DestDir: {userappdata}\i2pd; Flags: onlyifdoesntexist
|
||||
Source: ..\contrib\certificates\*; DestDir: {userappdata}\i2pd\certificates; Flags: onlyifdoesntexist recursesubdirs createallsubdirs
|
||||
|
||||
[Icons]
|
||||
Name: {group}\I2Pd; Filename: {app}\i2pd.exe
|
||||
Name: {group}\Readme; Filename: {app}\Readme.txt
|
||||
|
||||
[UninstallDelete]
|
||||
Type: filesandordirs; Name: {app}
|
||||
BIN
Win32/mask.bmp
Normal file
BIN
Win32/mask.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
Win32/mask.ico
Normal file
BIN
Win32/mask.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 153 KiB |
@@ -1,57 +0,0 @@
|
||||
!verbose push
|
||||
!verbose 3
|
||||
|
||||
!ifndef _MUI_EXTRAPAGES_NSH
|
||||
!define _MUI_EXTRAPAGES_NSH
|
||||
|
||||
!ifmacrondef MUI_EXTRAPAGE_README & MUI_PAGE_README & MUI_UNPAGE_README & ReadmeLangStrings
|
||||
|
||||
!macro MUI_EXTRAPAGE_README UN ReadmeFile
|
||||
!verbose push
|
||||
!verbose 3
|
||||
!define MUI_PAGE_HEADER_TEXT "$(${UN}ReadmeHeader)"
|
||||
!define MUI_PAGE_HEADER_SUBTEXT "$(${UN}ReadmeSubHeader)"
|
||||
!define MUI_LICENSEPAGE_TEXT_TOP "$(${UN}ReadmeTextTop)"
|
||||
!define MUI_LICENSEPAGE_TEXT_BOTTOM "$(${UN}ReadmeTextBottom)"
|
||||
!define MUI_LICENSEPAGE_BUTTON "$(^NextBtn)"
|
||||
!insertmacro MUI_${UN}PAGE_LICENSE "${ReadmeFile}"
|
||||
!verbose pop
|
||||
!macroend
|
||||
|
||||
!define ReadmeRun "!insertmacro MUI_EXTRAPAGE_README"
|
||||
|
||||
|
||||
!macro MUI_PAGE_README ReadmeFile
|
||||
!verbose push
|
||||
!verbose 3
|
||||
${ReadmeRun} "" "${ReadmeFile}"
|
||||
!verbose pop
|
||||
!macroend
|
||||
|
||||
|
||||
!macro MUI_UNPAGE_README ReadmeFile
|
||||
!verbose push
|
||||
!verbose 3
|
||||
${ReadmeRun} "UN" "${ReadmeFile}"
|
||||
!verbose pop
|
||||
!macroend
|
||||
|
||||
|
||||
!macro ReadmeLangStrings UN MUI_LANG ReadmeHeader ReadmeSubHeader ReadmeTextTop ReadmeTextBottom
|
||||
!verbose push
|
||||
!verbose 3
|
||||
LangString ${UN}ReadmeHeader ${MUI_LANG} "${ReadmeHeader}"
|
||||
LangString ${UN}ReadmeSubHeader ${MUI_LANG} "${ReadmeSubHeader}"
|
||||
LangString ${UN}ReadmeTextTop ${MUI_LANG} "${ReadmeTextTop}"
|
||||
LangString ${UN}ReadmeTextBottom ${MUI_LANG} "${ReadmeTextBottom}"
|
||||
!verbose pop
|
||||
!macroend
|
||||
|
||||
!define ReadmeLanguage `!insertmacro ReadmeLangStrings ""`
|
||||
|
||||
!define Un.ReadmeLanguage `!insertmacro ReadmeLangStrings "UN"`
|
||||
|
||||
!endif
|
||||
!endif
|
||||
|
||||
!verbose pop
|
||||
@@ -1,419 +0,0 @@
|
||||
; NSIS SERVICE LIBRARY - servicelib.nsh
|
||||
; Version 1.8.1 - Jun 21th, 2013
|
||||
; Questions/Comments - dselkirk@hotmail.com
|
||||
;
|
||||
; Description:
|
||||
; Provides an interface to window services
|
||||
;
|
||||
; Inputs:
|
||||
; action - systemlib action ie. create, delete, start, stop, pause,
|
||||
; continue, installed, running, status
|
||||
; name - name of service to manipulate
|
||||
; param - action parameters; usage: var1=value1;var2=value2;...etc.
|
||||
; (don't forget to add a ';' after the last value!)
|
||||
;
|
||||
; Actions:
|
||||
; create - creates a new windows service
|
||||
; Parameters:
|
||||
; path - path to service executable
|
||||
; autostart - automatically start with system ie. 1|0
|
||||
; interact - interact with the desktop ie. 1|0
|
||||
; depend - service dependencies
|
||||
; user - user that runs the service
|
||||
; password - password of the above user
|
||||
; display - display name in service's console
|
||||
; description - Description of service
|
||||
; starttype - start type (supersedes autostart)
|
||||
; servicetype - service type (supersedes interact)
|
||||
;
|
||||
; delete - deletes a windows service
|
||||
; start - start a stopped windows service
|
||||
; stop - stops a running windows service
|
||||
; pause - pauses a running windows service
|
||||
; continue - continues a paused windows service
|
||||
; installed - is the provided service installed
|
||||
; Parameters:
|
||||
; action - if true then invokes the specified action
|
||||
; running - is the provided service running
|
||||
; Parameters:
|
||||
; action - if true then invokes the specified action
|
||||
; status - check the status of the provided service
|
||||
;
|
||||
; Usage:
|
||||
; Method 1:
|
||||
; Push "action"
|
||||
; Push "name"
|
||||
; Push "param"
|
||||
; Call Service
|
||||
; Pop $0 ;response
|
||||
;
|
||||
; Method 2:
|
||||
; !insertmacro SERVICE "action" "name" "param"
|
||||
;
|
||||
; History:
|
||||
; 1.0 - 09/15/2003 - Initial release
|
||||
; 1.1 - 09/16/2003 - Changed &l to i, thx brainsucker
|
||||
; 1.2 - 02/29/2004 - Fixed documentation.
|
||||
; 1.3 - 01/05/2006 - Fixed interactive flag and pop order (Kichik)
|
||||
; 1.4 - 12/07/2006 - Added display and depend, fixed datatypes (Vitoco)
|
||||
; 1.5 - 06/25/2008 - Added description of service.(DeSafe.com/liuqixing#gmail.com)
|
||||
; 1.5.1 - 06/12/2009 - Added use of __UNINSTALL__
|
||||
; 1.6 - 08/02/2010 - Fixed description implementation (Anders)
|
||||
; 1.7 - 04/11/2010 - Added get running service process id (Nico)
|
||||
; 1.8 - 24/03/2011 - Added starttype and servicetype (Sergius)
|
||||
; 1.8.1 - 21/06/2013 - Added dynamic ASCII & Unicode support (Zinthose)
|
||||
|
||||
!ifndef SERVICELIB
|
||||
!define SERVICELIB
|
||||
|
||||
!define SC_MANAGER_ALL_ACCESS 0x3F
|
||||
!define SC_STATUS_PROCESS_INFO 0x0
|
||||
!define SERVICE_ALL_ACCESS 0xF01FF
|
||||
|
||||
!define SERVICE_CONTROL_STOP 1
|
||||
!define SERVICE_CONTROL_PAUSE 2
|
||||
!define SERVICE_CONTROL_CONTINUE 3
|
||||
|
||||
!define SERVICE_STOPPED 0x1
|
||||
!define SERVICE_START_PENDING 0x2
|
||||
!define SERVICE_STOP_PENDING 0x3
|
||||
!define SERVICE_RUNNING 0x4
|
||||
!define SERVICE_CONTINUE_PENDING 0x5
|
||||
!define SERVICE_PAUSE_PENDING 0x6
|
||||
!define SERVICE_PAUSED 0x7
|
||||
|
||||
!define SERVICE_KERNEL_DRIVER 0x00000001
|
||||
!define SERVICE_FILE_SYSTEM_DRIVER 0x00000002
|
||||
!define SERVICE_WIN32_OWN_PROCESS 0x00000010
|
||||
!define SERVICE_WIN32_SHARE_PROCESS 0x00000020
|
||||
!define SERVICE_INTERACTIVE_PROCESS 0x00000100
|
||||
|
||||
|
||||
!define SERVICE_BOOT_START 0x00000000
|
||||
!define SERVICE_SYSTEM_START 0x00000001
|
||||
!define SERVICE_AUTO_START 0x00000002
|
||||
!define SERVICE_DEMAND_START 0x00000003
|
||||
!define SERVICE_DISABLED 0x00000004
|
||||
|
||||
## Added by Zinthose for Native Unicode Support
|
||||
!ifdef NSIS_UNICODE
|
||||
!define APITAG "W"
|
||||
!else
|
||||
!define APITAG "A"
|
||||
!endif
|
||||
|
||||
!macro SERVICE ACTION NAME PARAM
|
||||
Push '${ACTION}'
|
||||
Push '${NAME}'
|
||||
Push '${PARAM}'
|
||||
!ifdef __UNINSTALL__
|
||||
Call un.Service
|
||||
!else
|
||||
Call Service
|
||||
!endif
|
||||
!macroend
|
||||
|
||||
!macro FUNC_GETPARAM
|
||||
Push $0
|
||||
Push $1
|
||||
Push $2
|
||||
Push $3
|
||||
Push $4
|
||||
Push $5
|
||||
Push $6
|
||||
Push $7
|
||||
Exch 8
|
||||
Pop $1 ;name
|
||||
Exch 8
|
||||
Pop $2 ;source
|
||||
StrCpy $0 ""
|
||||
StrLen $7 $2
|
||||
StrCpy $3 0
|
||||
lbl_loop:
|
||||
IntCmp $3 $7 0 0 lbl_done
|
||||
StrLen $4 "$1="
|
||||
StrCpy $5 $2 $4 $3
|
||||
StrCmp $5 "$1=" 0 lbl_next
|
||||
IntOp $5 $3 + $4
|
||||
StrCpy $3 $5
|
||||
lbl_loop2:
|
||||
IntCmp $3 $7 0 0 lbl_done
|
||||
StrCpy $6 $2 1 $3
|
||||
StrCmp $6 ";" 0 lbl_next2
|
||||
IntOp $6 $3 - $5
|
||||
StrCpy $0 $2 $6 $5
|
||||
Goto lbl_done
|
||||
lbl_next2:
|
||||
IntOp $3 $3 + 1
|
||||
Goto lbl_loop2
|
||||
lbl_next:
|
||||
IntOp $3 $3 + 1
|
||||
Goto lbl_loop
|
||||
lbl_done:
|
||||
Pop $5
|
||||
Pop $4
|
||||
Pop $3
|
||||
Pop $2
|
||||
Pop $1
|
||||
Exch 2
|
||||
Pop $6
|
||||
Pop $7
|
||||
Exch $0
|
||||
!macroend
|
||||
|
||||
!macro CALL_GETPARAM VAR NAME DEFAULT LABEL
|
||||
Push $1
|
||||
Push ${NAME}
|
||||
Call ${UN}GETPARAM
|
||||
Pop $6
|
||||
StrCpy ${VAR} "${DEFAULT}"
|
||||
StrCmp $6 "" "${LABEL}" 0
|
||||
StrCpy ${VAR} $6
|
||||
!macroend
|
||||
|
||||
!macro FUNC_SERVICE UN
|
||||
Push $0
|
||||
Push $1
|
||||
Push $2
|
||||
Push $3
|
||||
Push $4
|
||||
Push $5
|
||||
Push $6
|
||||
Push $7
|
||||
Exch 8
|
||||
Pop $1 ;param
|
||||
Exch 8
|
||||
Pop $2 ;name
|
||||
Exch 8
|
||||
Pop $3 ;action
|
||||
;$0 return
|
||||
;$4 OpenSCManager
|
||||
;$5 OpenService
|
||||
|
||||
StrCpy $0 "false"
|
||||
System::Call 'advapi32::OpenSCManager${APITAG}(n, n, i ${SC_MANAGER_ALL_ACCESS}) i.r4'
|
||||
IntCmp $4 0 lbl_done
|
||||
StrCmp $3 "create" lbl_create
|
||||
System::Call 'advapi32::OpenService${APITAG}(i r4, t r2, i ${SERVICE_ALL_ACCESS}) i.r5'
|
||||
IntCmp $5 0 lbl_done
|
||||
|
||||
lbl_select:
|
||||
StrCmp $3 "delete" lbl_delete
|
||||
StrCmp $3 "start" lbl_start
|
||||
StrCmp $3 "stop" lbl_stop
|
||||
StrCmp $3 "pause" lbl_pause
|
||||
StrCmp $3 "continue" lbl_continue
|
||||
StrCmp $3 "installed" lbl_installed
|
||||
StrCmp $3 "running" lbl_running
|
||||
StrCmp $3 "status" lbl_status
|
||||
StrCmp $3 "processid" lbl_processid
|
||||
Goto lbl_done
|
||||
|
||||
; create service
|
||||
lbl_create:
|
||||
Push $R1 ;depend
|
||||
Push $R2 ;user
|
||||
Push $R3 ;password
|
||||
Push $R4 ;servicetype/interact
|
||||
Push $R5 ;starttype/autostart
|
||||
Push $R6 ;path
|
||||
Push $R7 ;display
|
||||
Push $R8 ;description
|
||||
|
||||
!insertmacro CALL_GETPARAM $R1 "depend" "n" "lbl_depend"
|
||||
StrCpy $R1 't "$R1"'
|
||||
lbl_depend:
|
||||
StrCmp $R1 "n" 0 lbl_machine ;old name of depend param
|
||||
!insertmacro CALL_GETPARAM $R1 "machine" "n" "lbl_machine"
|
||||
StrCpy $R1 't "$R1"'
|
||||
lbl_machine:
|
||||
|
||||
!insertmacro CALL_GETPARAM $R2 "user" "n" "lbl_user"
|
||||
StrCpy $R2 't "$R2"'
|
||||
lbl_user:
|
||||
|
||||
!insertmacro CALL_GETPARAM $R3 "password" "n" "lbl_password"
|
||||
StrCpy $R3 't "$R3"'
|
||||
lbl_password:
|
||||
|
||||
!insertmacro CALL_GETPARAM $R4 "interact" "${SERVICE_WIN32_OWN_PROCESS}" "lbl_interact"
|
||||
StrCpy $6 ${SERVICE_WIN32_OWN_PROCESS}
|
||||
IntCmp $R4 0 +2
|
||||
IntOp $6 $6 | ${SERVICE_INTERACTIVE_PROCESS}
|
||||
StrCpy $R4 $6
|
||||
lbl_interact:
|
||||
|
||||
!insertmacro CALL_GETPARAM $R4 "servicetype" "$R4" "lbl_servicetype"
|
||||
lbl_servicetype:
|
||||
|
||||
!insertmacro CALL_GETPARAM $R5 "autostart" "${SERVICE_DEMAND_START}" "lbl_autostart"
|
||||
StrCpy $6 ${SERVICE_DEMAND_START}
|
||||
IntCmp $R5 0 +2
|
||||
StrCpy $6 ${SERVICE_AUTO_START}
|
||||
StrCpy $R5 $6
|
||||
lbl_autostart:
|
||||
|
||||
!insertmacro CALL_GETPARAM $R5 "starttype" "$R5" "lbl_starttype"
|
||||
lbl_starttype:
|
||||
|
||||
!insertmacro CALL_GETPARAM $R6 "path" "n" "lbl_path"
|
||||
lbl_path:
|
||||
|
||||
!insertmacro CALL_GETPARAM $R7 "display" "$2" "lbl_display"
|
||||
lbl_display:
|
||||
|
||||
!insertmacro CALL_GETPARAM $R8 "description" "$2" "lbl_description"
|
||||
lbl_description:
|
||||
|
||||
System::Call 'advapi32::CreateService${APITAG}(i r4, t r2, t R7, i ${SERVICE_ALL_ACCESS}, \
|
||||
i R4, i R5, i 0, t R6, n, n, $R1, $R2, $R3) i.r6'
|
||||
|
||||
; write description of service (SERVICE_CONFIG_DESCRIPTION)
|
||||
System::Call 'advapi32::ChangeServiceConfig2${APITAG}(ir6,i1,*t "$R8")i.R7'
|
||||
strcmp $R7 "error" 0 lbl_descriptioncomplete
|
||||
WriteRegStr HKLM "SYSTEM\CurrentControlSet\Services\$2" "Description" $R8
|
||||
lbl_descriptioncomplete:
|
||||
|
||||
Pop $R8
|
||||
Pop $R7
|
||||
Pop $R6
|
||||
Pop $R5
|
||||
Pop $R4
|
||||
Pop $R3
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
StrCmp $6 0 lbl_done lbl_good
|
||||
|
||||
; delete service
|
||||
lbl_delete:
|
||||
System::Call 'advapi32::DeleteService(i r5) i.r6'
|
||||
StrCmp $6 0 lbl_done lbl_good
|
||||
|
||||
; start service
|
||||
lbl_start:
|
||||
System::Call 'advapi32::StartService${APITAG}(i r5, i 0, i 0) i.r6'
|
||||
StrCmp $6 0 lbl_done lbl_good
|
||||
|
||||
; stop service
|
||||
lbl_stop:
|
||||
Push $R1
|
||||
System::Call '*(i,i,i,i,i,i,i) i.R1'
|
||||
System::Call 'advapi32::ControlService(i r5, i ${SERVICE_CONTROL_STOP}, i $R1) i'
|
||||
System::Free $R1
|
||||
Pop $R1
|
||||
StrCmp $6 0 lbl_done lbl_good
|
||||
|
||||
; pause service
|
||||
lbl_pause:
|
||||
Push $R1
|
||||
System::Call '*(i,i,i,i,i,i,i) i.R1'
|
||||
System::Call 'advapi32::ControlService(i r5, i ${SERVICE_CONTROL_PAUSE}, i $R1) i'
|
||||
System::Free $R1
|
||||
Pop $R1
|
||||
StrCmp $6 0 lbl_done lbl_good
|
||||
|
||||
; continue service
|
||||
lbl_continue:
|
||||
Push $R1
|
||||
System::Call '*(i,i,i,i,i,i,i) i.R1'
|
||||
System::Call 'advapi32::ControlService(i r5, i ${SERVICE_CONTROL_CONTINUE}, i $R1) i'
|
||||
System::Free $R1
|
||||
Pop $R1
|
||||
StrCmp $6 0 lbl_done lbl_good
|
||||
|
||||
; is installed
|
||||
lbl_installed:
|
||||
!insertmacro CALL_GETPARAM $7 "action" "" "lbl_good"
|
||||
StrCpy $3 $7
|
||||
Goto lbl_select
|
||||
|
||||
; is service running
|
||||
lbl_running:
|
||||
Push $R1
|
||||
System::Call '*(i,i,i,i,i,i,i) i.R1'
|
||||
System::Call 'advapi32::QueryServiceStatus(i r5, i $R1) i'
|
||||
System::Call '*$R1(i, i.r6)'
|
||||
System::Free $R1
|
||||
Pop $R1
|
||||
IntFmt $6 "0x%X" $6
|
||||
StrCmp $6 ${SERVICE_RUNNING} 0 lbl_done
|
||||
!insertmacro CALL_GETPARAM $7 "action" "" "lbl_good"
|
||||
StrCpy $3 $7
|
||||
Goto lbl_select
|
||||
|
||||
lbl_status:
|
||||
Push $R1
|
||||
System::Call '*(i,i,i,i,i,i,i) i.R1'
|
||||
System::Call 'advapi32::QueryServiceStatus(i r5, i $R1) i'
|
||||
System::Call '*$R1(i, i .r6)'
|
||||
System::Free $R1
|
||||
Pop $R1
|
||||
IntFmt $6 "0x%X" $6
|
||||
StrCpy $0 "running"
|
||||
IntCmp $6 ${SERVICE_RUNNING} lbl_done
|
||||
StrCpy $0 "stopped"
|
||||
IntCmp $6 ${SERVICE_STOPPED} lbl_done
|
||||
StrCpy $0 "start_pending"
|
||||
IntCmp $6 ${SERVICE_START_PENDING} lbl_done
|
||||
StrCpy $0 "stop_pending"
|
||||
IntCmp $6 ${SERVICE_STOP_PENDING} lbl_done
|
||||
StrCpy $0 "running"
|
||||
IntCmp $6 ${SERVICE_RUNNING} lbl_done
|
||||
StrCpy $0 "continue_pending"
|
||||
IntCmp $6 ${SERVICE_CONTINUE_PENDING} lbl_done
|
||||
StrCpy $0 "pause_pending"
|
||||
IntCmp $6 ${SERVICE_PAUSE_PENDING} lbl_done
|
||||
StrCpy $0 "paused"
|
||||
IntCmp $6 ${SERVICE_PAUSED} lbl_done
|
||||
StrCpy $0 "unknown"
|
||||
Goto lbl_done
|
||||
|
||||
lbl_processid:
|
||||
Push $R1
|
||||
Push $R2
|
||||
System::Call '*(i,i,i,i,i,i,i,i,i) i.R1'
|
||||
System::Call '*(i 0) i.R2'
|
||||
System::Call "advapi32::QueryServiceStatusEx(i r5, i ${SC_STATUS_PROCESS_INFO}, i $R1, i 36, i $R2) i"
|
||||
System::Call "*$R1(i,i,i,i,i,i,i, i .r0)"
|
||||
System::Free $R2
|
||||
System::Free $R1
|
||||
Pop $R2
|
||||
Pop $R1
|
||||
Goto lbl_done
|
||||
|
||||
lbl_good:
|
||||
StrCpy $0 "true"
|
||||
lbl_done:
|
||||
IntCmp $5 0 +2
|
||||
System::Call 'advapi32::CloseServiceHandle(i r5) n'
|
||||
IntCmp $4 0 +2
|
||||
System::Call 'advapi32::CloseServiceHandle(i r4) n'
|
||||
Pop $4
|
||||
Pop $3
|
||||
Pop $2
|
||||
Pop $1
|
||||
Exch 3
|
||||
Pop $5
|
||||
Pop $7
|
||||
Pop $6
|
||||
Exch $0
|
||||
!macroend
|
||||
|
||||
Function Service
|
||||
!insertmacro FUNC_SERVICE ""
|
||||
FunctionEnd
|
||||
|
||||
Function un.Service
|
||||
!insertmacro FUNC_SERVICE "un."
|
||||
FunctionEnd
|
||||
|
||||
Function GetParam
|
||||
!insertmacro FUNC_GETPARAM
|
||||
FunctionEnd
|
||||
|
||||
Function un.GetParam
|
||||
!insertmacro FUNC_GETPARAM
|
||||
FunctionEnd
|
||||
|
||||
!undef APITAG
|
||||
!endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user