mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
3877 Commits
v0.3.0-1st
...
2.19.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fc16e76af1 | ||
|
|
0dff636dbe | ||
|
|
00df3f8d4e | ||
|
|
34c45f2694 | ||
|
|
a188de2e5c | ||
|
|
27fbf67352 | ||
|
|
b226e22d2f | ||
|
|
5bc157eb19 | ||
|
|
f4122abbad | ||
|
|
f0b32e3f54 | ||
|
|
fe00999b2c | ||
|
|
39eed0f6fb | ||
|
|
510d29b381 | ||
|
|
0aa618b938 | ||
|
|
5884852612 | ||
|
|
5b29592174 | ||
|
|
96411cc93e | ||
|
|
7d862d8eba | ||
|
|
dd392941d0 | ||
|
|
3cec5235c9 | ||
|
|
b5682012d3 | ||
|
|
4351a2736c | ||
|
|
9c7cadb191 | ||
|
|
4d9143734f | ||
|
|
3cec923294 | ||
|
|
58c92b8405 | ||
|
|
985b618932 | ||
|
|
a027a42c46 | ||
|
|
bdc7acffbe | ||
|
|
6bd73cdea2 | ||
|
|
59954c1d7c | ||
|
|
a59cdcc9e0 | ||
|
|
e1bfa786fc | ||
|
|
d5214099c5 | ||
|
|
e05110ff44 | ||
|
|
706b976a28 | ||
|
|
2bd7a92d20 | ||
|
|
6b37a41e00 | ||
|
|
5447259e1a | ||
|
|
ee0ae0b74b | ||
|
|
966256ac32 | ||
|
|
6b9061515f | ||
|
|
df60e78766 | ||
|
|
bf1e1ad457 | ||
|
|
7fa5b06359 | ||
|
|
3b46e9f351 | ||
|
|
046a80cfe4 | ||
|
|
a8278fc78b | ||
|
|
7f3127ac89 | ||
|
|
7cdb021a1f | ||
|
|
74c0b729c2 | ||
|
|
5cb81f8532 | ||
|
|
4f23d7b7df | ||
|
|
a70d0edf2e | ||
|
|
8c9eaccc11 | ||
|
|
86c1984982 | ||
|
|
cd0f75106a | ||
|
|
b5291b5151 | ||
|
|
46283dc0ea | ||
|
|
56e76ec59f | ||
|
|
4cedaa9e80 | ||
|
|
516f140bef | ||
|
|
5d86c1c9a6 | ||
|
|
d289aa71eb | ||
|
|
ed2818eaa2 | ||
|
|
f8fe124428 | ||
|
|
5ec11c53e9 | ||
|
|
42d118d9a2 | ||
|
|
d8b4765f23 | ||
|
|
be69280d0d | ||
|
|
53a1a097a6 | ||
|
|
a22e9a2ca7 | ||
|
|
db03595473 | ||
|
|
8fadac0fdc | ||
|
|
a63bc1cdca | ||
|
|
6265d452e9 | ||
|
|
b095399770 | ||
|
|
db8a546b8f | ||
|
|
6e95318cba | ||
|
|
08a8ab9892 | ||
|
|
c7b796ff31 | ||
|
|
ad23ccb219 | ||
|
|
be7a84fdf3 | ||
|
|
2fbbbf298b | ||
|
|
0df68872ab | ||
|
|
0ced38cdcb | ||
|
|
b046c45a9e | ||
|
|
2ce1ab1634 | ||
|
|
7225231814 | ||
|
|
11dca2b352 | ||
|
|
97127e86dc | ||
|
|
cb81195959 | ||
|
|
adaff9f354 | ||
|
|
66de7ad049 | ||
|
|
1e1e4da144 | ||
|
|
623433099b | ||
|
|
73b3fbc2da | ||
|
|
5f525d0e43 | ||
|
|
60463fdafa | ||
|
|
b7a67b4b03 | ||
|
|
4643c92d33 | ||
|
|
396cba7339 | ||
|
|
a2b3ee53e0 | ||
|
|
2c67d2055c | ||
|
|
c8de7aa23c | ||
|
|
fa154cc4d6 | ||
|
|
d9b8731ddc | ||
|
|
6cebc1a2a2 | ||
|
|
faac35cd1e | ||
|
|
6916147dda | ||
|
|
e2da16e9c3 | ||
|
|
0c661e7373 | ||
|
|
413f8e8462 | ||
|
|
eefbbd4efe | ||
|
|
83932a6f02 | ||
|
|
c175dc30f8 | ||
|
|
17aa91803a | ||
|
|
48099a367e | ||
|
|
a9b64893d8 | ||
|
|
387e030d83 | ||
|
|
855cc9ed83 | ||
|
|
82534eef12 | ||
|
|
ff4e254618 | ||
|
|
571a13f0a7 | ||
|
|
2cb6283d00 | ||
|
|
f4056e57bb | ||
|
|
e80da3cbeb | ||
|
|
c0436297c2 | ||
|
|
0d05b4f095 | ||
|
|
f06c8710be | ||
|
|
f11266972e | ||
|
|
479edaf80d | ||
|
|
ff5c26adf2 | ||
|
|
5361e11395 | ||
|
|
b041bcdc65 | ||
|
|
b7c350202d | ||
|
|
b1a6c5ddf7 | ||
|
|
ac943b5712 | ||
|
|
ce8d701ecb | ||
|
|
182ffe4495 | ||
|
|
c13983d395 | ||
|
|
066f8863fd | ||
|
|
e58aaa3f32 | ||
|
|
ca1fa11cb1 | ||
|
|
64ed485cdf | ||
|
|
b0781668e2 | ||
|
|
f9fc744949 | ||
|
|
2661db23f6 | ||
|
|
7d78f60d29 | ||
|
|
1d934bd543 | ||
|
|
190435acd9 | ||
|
|
656236cb4d | ||
|
|
6d15be9a32 | ||
|
|
18d3c81018 | ||
|
|
12292afdec | ||
|
|
aef0f4d7b8 | ||
|
|
21545ab7da | ||
|
|
5a2b795440 | ||
|
|
1303dd478c | ||
|
|
7b4fc19fca | ||
|
|
008a064764 | ||
|
|
82a4630061 | ||
|
|
0f77b4810d | ||
|
|
2f7cfddfc4 | ||
|
|
84608c16b3 | ||
|
|
157411dcc6 | ||
|
|
59672d23cc | ||
|
|
ce30f89c60 | ||
|
|
ce9c9411b1 | ||
|
|
cf0d5b616d | ||
|
|
29e861d1e6 | ||
|
|
c7accd4a5c | ||
|
|
b469080cd7 | ||
|
|
547a0057e6 | ||
|
|
b980ca4a9e | ||
|
|
098b2e968e | ||
|
|
cd59ca8376 | ||
|
|
f2e6fad104 | ||
|
|
8d7fde0287 | ||
|
|
91fdb038d9 | ||
|
|
a0188765c5 | ||
|
|
b970a005de | ||
|
|
b64878f4fa | ||
|
|
c8936c79bf | ||
|
|
f876cc9079 | ||
|
|
a5cc2f3b5d | ||
|
|
9c93d6f931 | ||
|
|
a077d7671f | ||
|
|
6485ebe9a7 | ||
|
|
ecb6bb220a | ||
|
|
e3dc400d74 | ||
|
|
3bb4151074 | ||
|
|
1de4c2e8c6 | ||
|
|
fbcc4f28e7 | ||
|
|
30fb0f5a94 | ||
|
|
b02464990b | ||
|
|
4988a32d33 | ||
|
|
b3e5874631 | ||
|
|
f5349dcef9 | ||
|
|
486a4cfdd6 | ||
|
|
2277dcb069 | ||
|
|
a618a01b1e | ||
|
|
7e60069968 | ||
|
|
91e45d9a4a | ||
|
|
dea6fbf285 | ||
|
|
48cc0f4289 | ||
|
|
cdc5fce583 | ||
|
|
b41a17d548 | ||
|
|
606cbaa519 | ||
|
|
aaf8f527ef | ||
|
|
b7596b7f70 | ||
|
|
0309b574e8 | ||
|
|
ca057177c7 | ||
|
|
5d9bf18267 | ||
|
|
f1b8742782 | ||
|
|
7786c97330 | ||
|
|
f2a14047eb | ||
|
|
124a9cb030 | ||
|
|
3ec000d0f8 | ||
|
|
aac1141ca6 | ||
|
|
33cb96126a | ||
|
|
441db9ad7f | ||
|
|
5225e1d7d1 | ||
|
|
de849b3f6a | ||
|
|
fb4387c41f | ||
|
|
a9061a8f58 | ||
|
|
0c099dc52b | ||
|
|
713e92c28f | ||
|
|
d111025012 | ||
|
|
5f2e6b1262 | ||
|
|
b6d838731f | ||
|
|
56db8b40b2 | ||
|
|
f488c97a09 | ||
|
|
31df49a884 | ||
|
|
e5fdced4ac | ||
|
|
71546367cf | ||
|
|
857817dae8 | ||
|
|
ae3fca15c7 | ||
|
|
6bb7382dbd | ||
|
|
badb837b46 | ||
|
|
74f5b70a5d | ||
|
|
ac495da5fe | ||
|
|
56f6e57118 | ||
|
|
33735b343d | ||
|
|
1b56d66fc8 | ||
|
|
0994211a48 | ||
|
|
62d9a47c3d | ||
|
|
e77037c2b8 | ||
|
|
030a6ebb71 | ||
|
|
5a657cff89 | ||
|
|
f3488be7af | ||
|
|
4af0caa506 | ||
|
|
0728991821 | ||
|
|
21c35f770b | ||
|
|
f039af6eda | ||
|
|
eb3f703b46 | ||
|
|
b88b82a85c | ||
|
|
1d0791dbf5 | ||
|
|
87f2eefd35 | ||
|
|
b8a2c9f955 | ||
|
|
319d748639 | ||
|
|
4f84d687e4 | ||
|
|
fbb9991128 | ||
|
|
62bac24246 | ||
|
|
4aa8461bea | ||
|
|
ce57a130fc | ||
|
|
80567312ed | ||
|
|
180730f9cf | ||
|
|
fca2693488 | ||
|
|
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 | ||
|
|
df304fb38b | ||
|
|
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 | ||
|
|
68b1afa2df | ||
|
|
34c98e03c1 | ||
|
|
41e40bbc0d | ||
|
|
80149342f2 | ||
|
|
1967dee50c | ||
|
|
ab80def94b | ||
|
|
254bf313a2 | ||
|
|
938d5d901a | ||
|
|
7b00d828b2 | ||
|
|
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 | ||
|
|
25ccfef4b0 | ||
|
|
ec8e3574f9 | ||
|
|
7da694825d | ||
|
|
9606883b78 | ||
|
|
54cffb583f | ||
|
|
fe4d640504 | ||
|
|
1a69770e15 | ||
|
|
75b67b7ea7 | ||
|
|
e3f077ee9a | ||
|
|
f357a5864c | ||
|
|
6e32c389b1 | ||
|
|
219abaa7e1 | ||
|
|
82d5bf2f8a | ||
|
|
1ffe7955a3 | ||
|
|
ef73353a0a | ||
|
|
52a0b9ca03 | ||
|
|
0a9eca9f94 | ||
|
|
b399d45d66 | ||
|
|
d6fe4556fb | ||
|
|
6ad0313dbe | ||
|
|
ca2566e778 | ||
|
|
2b093a0e3f | ||
|
|
0e212f29d0 | ||
|
|
cd3a7040b0 | ||
|
|
cf8e229098 | ||
|
|
05a62af99b | ||
|
|
56c404e6c1 | ||
|
|
5ee4969322 | ||
|
|
8a43e070d8 | ||
|
|
aa0cb6b2c4 | ||
|
|
d6ec412422 | ||
|
|
4e94bc9efc | ||
|
|
bc0f0e96a3 | ||
|
|
9552edf82d | ||
|
|
8c218bd5df | ||
|
|
01bb492faf | ||
|
|
afd0a43725 | ||
|
|
366b160727 | ||
|
|
8b61aedc3f | ||
|
|
23e49e730f | ||
|
|
0e935a3511 | ||
|
|
56a95fa9b5 | ||
|
|
6ac846f95d | ||
|
|
9ce56eb95f | ||
|
|
3643d2f1da | ||
|
|
96851ab2fd | ||
|
|
7e52f8af5e | ||
|
|
ca3817abe3 | ||
|
|
aaf9a70153 | ||
|
|
0ee7f02f51 | ||
|
|
18d9325800 | ||
|
|
bec638914a | ||
|
|
67c4c237a9 | ||
|
|
6c98c80268 | ||
|
|
342c87e15b | ||
|
|
5966113268 | ||
|
|
a6ff3df591 | ||
|
|
9438f388ad | ||
|
|
0b3ee77717 | ||
|
|
7ccb7f05bf | ||
|
|
5b53665f4f | ||
|
|
b66aa7408e | ||
|
|
5dbee6b300 | ||
|
|
a0893eabfa | ||
|
|
c8e34052a7 | ||
|
|
a2d69a8b66 | ||
|
|
fd7fca1d54 | ||
|
|
47589125e0 | ||
|
|
02256be720 | ||
|
|
b9b224fdc3 | ||
|
|
cb06f8e0bb | ||
|
|
bb05bcf39f | ||
|
|
b7d1b74ffa | ||
|
|
d1cca92459 | ||
|
|
1d497cf8d6 | ||
|
|
efa771310d | ||
|
|
8f12881162 | ||
|
|
85624e4f4b | ||
|
|
5bbe661392 | ||
|
|
5e31d6b2bd | ||
|
|
06621a2198 | ||
|
|
4d77dad9cc | ||
|
|
e89938e9df | ||
|
|
1568128356 | ||
|
|
d13329471e | ||
|
|
9dbd5a583a | ||
|
|
07ad7fea9e | ||
|
|
fbff749838 | ||
|
|
797c8750d8 | ||
|
|
9164ac8a3e | ||
|
|
8788e1b2fd | ||
|
|
9a9d6e8e00 | ||
|
|
047a371050 | ||
|
|
392075bf60 | ||
|
|
07d5e8c756 | ||
|
|
e84d4e5f42 | ||
|
|
4c8d85870b | ||
|
|
519330015f | ||
|
|
08762870b4 | ||
|
|
517a7ba3ab | ||
|
|
3d0349191d | ||
|
|
76478ceaa2 | ||
|
|
9328bd1caf | ||
|
|
b2a6b6657f | ||
|
|
0214ad69a6 | ||
|
|
1c0c530769 | ||
|
|
75666e3e39 | ||
|
|
60e4e52373 | ||
|
|
30a1f9d447 | ||
|
|
cb293b93d2 | ||
|
|
3ab65bbe0d | ||
|
|
e7f05cc462 | ||
|
|
cfcec8229c | ||
|
|
4e54fbec08 | ||
|
|
38ee813e41 | ||
|
|
9acf80e563 | ||
|
|
9539fb8cb0 | ||
|
|
bd13406f42 | ||
|
|
fbec753dcd | ||
|
|
e6c92a535d | ||
|
|
32a767dc91 | ||
|
|
3ca560b895 | ||
|
|
5e83d950f5 | ||
|
|
95027930f2 | ||
|
|
3ff1158384 | ||
|
|
404f21ea76 | ||
|
|
49173da84d | ||
|
|
d794dfb7e5 | ||
|
|
8350f16b20 | ||
|
|
e1c25fedb0 | ||
|
|
3e3cfa3d68 | ||
|
|
092b445e36 | ||
|
|
b9806ac86b | ||
|
|
a8c08563f1 | ||
|
|
eb6b04d6c2 | ||
|
|
68c321609d | ||
|
|
fae01f61d2 | ||
|
|
aca87b5fd1 | ||
|
|
77687a70a2 | ||
|
|
357a9a6a56 | ||
|
|
f7d90648e3 | ||
|
|
6153d799bc | ||
|
|
f15508aff5 | ||
|
|
0ccb66476e | ||
|
|
6c13ad78a5 | ||
|
|
8fde36a4b0 | ||
|
|
de14f8dcd7 | ||
|
|
1e81652a62 | ||
|
|
f7ce86e0c4 | ||
|
|
9eb5982ea3 | ||
|
|
4778c8bdfb | ||
|
|
9574163aeb | ||
|
|
199ff0c210 | ||
|
|
e0635548e9 | ||
|
|
95524c8db3 | ||
|
|
1a0957b571 | ||
|
|
97656e7349 | ||
|
|
0262a8b057 | ||
|
|
4bd8b44ab2 | ||
|
|
4dc33a6f45 | ||
|
|
335de27095 | ||
|
|
fa461b1254 | ||
|
|
81c63b0c30 | ||
|
|
dcefe7d221 | ||
|
|
ed3aaefe96 | ||
|
|
6042aefd17 | ||
|
|
1c3f70056a | ||
|
|
d8b9968aed | ||
|
|
8a357ac46c | ||
|
|
5187701af1 |
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
|
||||
133
AddressBook.cpp
133
AddressBook.cpp
@@ -1,133 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "base64.h"
|
||||
#include "util.h"
|
||||
#include "Identity.h"
|
||||
#include "Log.h"
|
||||
#include "AddressBook.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
|
||||
AddressBook::AddressBook (): m_IsLoaded (false), m_IsDowloading (false)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
m_Addresses[address] = ident.GetIdentHash ();
|
||||
LogPrint (address,"->",ident.GetIdentHash ().ToBase32 (), ".b32.i2p added");
|
||||
}
|
||||
|
||||
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 ()
|
||||
{
|
||||
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;
|
||||
}
|
||||
int numAddresses = 0;
|
||||
|
||||
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 ();
|
||||
numAddresses++;
|
||||
}
|
||||
}
|
||||
LogPrint (numAddresses, " addresses loaded");
|
||||
m_IsLoaded = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +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 AddressBook
|
||||
{
|
||||
public:
|
||||
|
||||
AddressBook ();
|
||||
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
|
||||
const i2p::data::IdentHash * FindAddress (const std::string& address);
|
||||
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
|
||||
|
||||
private:
|
||||
|
||||
void LoadHosts ();
|
||||
void LoadHostsFromI2P ();
|
||||
|
||||
std::map<std::string, i2p::data::IdentHash> m_Addresses;
|
||||
bool m_IsLoaded, m_IsDowloading;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
274
ChangeLog
Normal file
274
ChangeLog
Normal file
@@ -0,0 +1,274 @@
|
||||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.19.0] - 2018-06-26
|
||||
### Added
|
||||
- ECIES support for RouterInfo
|
||||
- HTTP outproxy authorization
|
||||
- AVX/AESNI runtime detection
|
||||
- Initial implementation of NTCP2
|
||||
- I2CP session reconfigure
|
||||
- I2CP method ClientServicesInfo
|
||||
- Datagrams to websocks
|
||||
### Changed
|
||||
- RouterInfo uses EdDSA signature by default
|
||||
- Remove stream bans
|
||||
- Android build system changed to gradle
|
||||
- Multiple changes in QT GUI
|
||||
- Dockerfile
|
||||
### Fixed
|
||||
- zero tunnelID issue
|
||||
- tunnels reload
|
||||
- headers in webconsole
|
||||
- XSS in webconsole from SAM session name
|
||||
- build for gcc 8
|
||||
- cmake build scripts
|
||||
- systemd service files
|
||||
- some netbsd issues
|
||||
|
||||
## [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,199 +0,0 @@
|
||||
#include "util.h"
|
||||
#include "Log.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)
|
||||
{
|
||||
}
|
||||
|
||||
ClientContext::~ClientContext ()
|
||||
{
|
||||
delete m_HttpProxy;
|
||||
delete m_SocksProxy;
|
||||
delete m_IrcTunnel;
|
||||
delete m_ServerTunnel;
|
||||
delete m_SamBridge;
|
||||
}
|
||||
|
||||
void ClientContext::Start ()
|
||||
{
|
||||
if (!m_SharedLocalDestination)
|
||||
{
|
||||
m_SharedLocalDestination = new ClientDestination (false, i2p::data::SIGNING_KEY_TYPE_DSA_SHA1); // 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 = i2p::client::context.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 = i2p::client::context.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");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
for (auto it: m_Destinations)
|
||||
{
|
||||
it.second->Stop ();
|
||||
delete it.second;
|
||||
}
|
||||
m_Destinations.clear ();
|
||||
m_SharedLocalDestination = 0; // deleted through m_Destination
|
||||
}
|
||||
|
||||
void ClientContext::LoadLocalDestinations ()
|
||||
{
|
||||
int numDestinations = 0;
|
||||
boost::filesystem::path p (i2p::util::filesystem::GetDataDir());
|
||||
boost::filesystem::directory_iterator end;
|
||||
for (boost::filesystem::directory_iterator it (p); it != end; ++it)
|
||||
{
|
||||
if (boost::filesystem::is_regular_file (*it) && it->path ().extension () == ".dat")
|
||||
{
|
||||
auto fullPath =
|
||||
#if BOOST_VERSION > 10500
|
||||
it->path().string();
|
||||
#else
|
||||
it->path();
|
||||
#endif
|
||||
auto localDestination = new ClientDestination (fullPath, true);
|
||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
||||
numDestinations++;
|
||||
}
|
||||
}
|
||||
if (numDestinations > 0)
|
||||
LogPrint (numDestinations, " local destinations loaded");
|
||||
}
|
||||
|
||||
ClientDestination * ClientContext::LoadLocalDestination (const std::string& filename, bool isPublic)
|
||||
{
|
||||
auto localDestination = new ClientDestination (i2p::util::filesystem::GetFullPath (filename), 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)
|
||||
{
|
||||
auto localDestination = new ClientDestination (isPublic, sigType);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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 "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 = true, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1); // transient
|
||||
ClientDestination * CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true);
|
||||
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:
|
||||
|
||||
void LoadLocalDestinations ();
|
||||
|
||||
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;
|
||||
|
||||
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,35 +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
|
||||
}
|
||||
}
|
||||
|
||||
#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);
|
||||
|
||||
auto service = m_Owner.GetService ();
|
||||
if (service)
|
||||
service->post (boost::bind (&DatagramDestination::SendMsg, this,
|
||||
CreateDataMessage (buf, len + headerLen), remote));
|
||||
else
|
||||
LogPrint (eLogWarning, "Failed to send datagram. Destination is not running");
|
||||
}
|
||||
|
||||
void DatagramDestination::SendMsg (I2NPMessage * msg, const i2p::data::LeaseSet& remote)
|
||||
{
|
||||
auto leases = remote.GetNonExpiredLeases ();
|
||||
if (!leases.empty ())
|
||||
{
|
||||
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
|
||||
});
|
||||
m_Owner.SendTunnelDataMsgs (msgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Failed to send datagram. All leases expired");
|
||||
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
|
||||
|
||||
310
Destination.cpp
310
Destination.cpp
@@ -1,310 +0,0 @@
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <cryptopp/dh.h>
|
||||
#include "Log.h"
|
||||
#include "util.h"
|
||||
#include "NetDb.h"
|
||||
#include "Destination.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
ClientDestination::ClientDestination (bool isPublic, i2p::data::SigningKeyType sigType):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr),
|
||||
m_CurrentOutboundTunnel (nullptr), m_LeaseSet (nullptr), m_IsPublic (isPublic),
|
||||
m_DatagramDestination (nullptr)
|
||||
{
|
||||
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this, 3); // 3-hops tunnel
|
||||
if (m_IsPublic)
|
||||
LogPrint ("Local address ", GetIdentHash ().ToBase32 (), ".b32.i2p created");
|
||||
m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
|
||||
}
|
||||
|
||||
ClientDestination::ClientDestination (const std::string& fullPath, bool isPublic):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr),
|
||||
m_CurrentOutboundTunnel (nullptr), m_LeaseSet (nullptr), m_IsPublic (isPublic),
|
||||
m_DatagramDestination (nullptr)
|
||||
{
|
||||
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);
|
||||
m_Keys.FromBuffer (buf, len);
|
||||
delete[] buf;
|
||||
LogPrint ("Local address ", GetIdentHash ().ToBase32 (), ".b32.i2p loaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Can't open file ", fullPath, " Creating new one");
|
||||
m_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 = m_Keys.GetFullLen ();
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
len = m_Keys.ToBuffer (buf, len);
|
||||
f.write ((char *)buf, len);
|
||||
delete[] buf;
|
||||
|
||||
LogPrint ("New private keys file ", fullPath, " for ", m_Keys.GetPublic ().GetIdentHash ().ToBase32 (), ".b32.i2p created");
|
||||
}
|
||||
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this, 3); // 3-hops tunnel
|
||||
m_StreamingDestination = new i2p::stream::StreamingDestination (*this); // TODO:
|
||||
}
|
||||
|
||||
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Service (nullptr), m_Work (nullptr),
|
||||
m_Keys (keys), m_CurrentOutboundTunnel (nullptr), m_LeaseSet (nullptr), m_IsPublic (isPublic),
|
||||
m_DatagramDestination (nullptr)
|
||||
{
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (*this, 3); // 3-hops tunnel
|
||||
if (m_IsPublic)
|
||||
LogPrint ("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);
|
||||
delete m_LeaseSet;
|
||||
delete m_Work;
|
||||
delete m_Service;
|
||||
delete m_StreamingDestination;
|
||||
delete m_DatagramDestination;
|
||||
}
|
||||
|
||||
void ClientDestination::Run ()
|
||||
{
|
||||
if (m_Service)
|
||||
m_Service->run ();
|
||||
}
|
||||
|
||||
void ClientDestination::Start ()
|
||||
{
|
||||
m_Service = new boost::asio::io_service;
|
||||
m_Work = new boost::asio::io_service::work (*m_Service);
|
||||
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)
|
||||
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
|
||||
m_IsRunning = false;
|
||||
if (m_Service)
|
||||
m_Service->stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
delete m_Work; m_Work = nullptr;
|
||||
delete m_Service; m_Service = nullptr;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::SendTunnelDataMsgs (const std::vector<i2p::tunnel::TunnelMessageBlock>& msgs)
|
||||
{
|
||||
m_CurrentOutboundTunnel = m_Pool->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
|
||||
if (m_CurrentOutboundTunnel)
|
||||
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
|
||||
else
|
||||
{
|
||||
LogPrint ("No outbound tunnels in the pool");
|
||||
for (auto it: msgs)
|
||||
DeleteI2NPMessage (it.data);
|
||||
}
|
||||
}
|
||||
|
||||
void ClientDestination::ProcessGarlicMessage (I2NPMessage * msg)
|
||||
{
|
||||
m_Service->post (boost::bind (&ClientDestination::HandleGarlicMessage, this, msg));
|
||||
}
|
||||
|
||||
void ClientDestination::ProcessDeliveryStatusMessage (I2NPMessage * msg)
|
||||
{
|
||||
m_Service->post (boost::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 ("Remote LeaseSet");
|
||||
auto it = m_RemoteLeaseSets.find (msg->key);
|
||||
if (it != m_RemoteLeaseSets.end ())
|
||||
{
|
||||
it->second->Update (buf + offset, len - offset);
|
||||
LogPrint ("Remote LeaseSet updated");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("New remote LeaseSet added");
|
||||
m_RemoteLeaseSets[msg->key] = new i2p::data::LeaseSet (buf + offset, len - offset);
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint ("Unexpected client's DatabaseStore type ", msg->type, ". Dropped");
|
||||
}
|
||||
|
||||
void ClientDestination::SetLeaseSetUpdated ()
|
||||
{
|
||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
||||
UpdateLeaseSet ();
|
||||
if (m_IsPublic)
|
||||
i2p::data::netdb.PublishLeaseSet (m_LeaseSet, m_Pool);
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
Destination.h
102
Destination.h
@@ -1,102 +0,0 @@
|
||||
#ifndef DESTINATION_H__
|
||||
#define DESTINATION_H__
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#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;
|
||||
|
||||
class ClientDestination: public i2p::garlic::GarlicDestination
|
||||
{
|
||||
public:
|
||||
|
||||
ClientDestination (bool isPublic, i2p::data::SigningKeyType sigType);
|
||||
ClientDestination (const std::string& fullPath, bool isPublic);
|
||||
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic);
|
||||
~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 (); };
|
||||
|
||||
void ResetCurrentOutboundTunnel () { m_CurrentOutboundTunnel = nullptr; };
|
||||
const i2p::data::LeaseSet * FindLeaseSet (const i2p::data::IdentHash& ident);
|
||||
void SendTunnelDataMsgs (const std::vector<i2p::tunnel::TunnelMessageBlock>& msgs);
|
||||
|
||||
// streaming
|
||||
i2p::stream::StreamingDestination * GetStreamingDestination () const { return m_StreamingDestination; };
|
||||
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
|
||||
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 HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
|
||||
|
||||
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::tunnel::OutboundTunnel * m_CurrentOutboundTunnel;
|
||||
i2p::data::LeaseSet * m_LeaseSet;
|
||||
bool m_IsPublic;
|
||||
|
||||
i2p::stream::StreamingDestination * m_StreamingDestination;
|
||||
i2p::datagram::DatagramDestination * m_DatagramDestination;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
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
|
||||
550
Garlic.cpp
550
Garlic.cpp
@@ -1,550 +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 ();
|
||||
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->AddSessionKey (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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (tagCount > 0)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (int i = 0; i < tagCount; i++)
|
||||
m_Tags[SessionTag(buf + i*32, ts)] = decryption;
|
||||
}
|
||||
buf += tagCount*32;
|
||||
uint32_t payloadSize = be32toh (*(uint32_t *)buf);
|
||||
if (payloadSize > len)
|
||||
{
|
||||
LogPrint ("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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
147
Garlic.h
147
Garlic.h
@@ -1,147 +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
|
||||
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
|
||||
|
||||
|
||||
996
HTTPServer.cpp
996
HTTPServer.cpp
@@ -1,996 +0,0 @@
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "base64.h"
|
||||
#include "Log.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TransitTunnel.h"
|
||||
#include "Transports.h"
|
||||
#include "NetDb.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Streaming.h"
|
||||
#include "Destination.h"
|
||||
#include "RouterContext.h"
|
||||
#include "ClientContext.h"
|
||||
#include "HTTPServer.h"
|
||||
|
||||
// For image and info
|
||||
#include "version.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
const std::string HTTPConnection::itoopieImage =
|
||||
"<img alt=\"ICToopie Icon\" src=\"data:image/png;base64,"
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAABmJLR0QAAAAAAAD5Q7t/AAAACXBIWXM"
|
||||
"AAA3XAAAN1wFCKJt4AAAAB3RJTUUH3ggRChYFXVBoSgAAIABJREFUeNrtnXl8VOX1/9/PvXcmewiQBB"
|
||||
"J2CKsKihQXkCJuiD8VKyptXejXaikWbe1C1dqi0lpr7UvrgihV64ZCXaqCUBEUQVBAAZUl7EtYEkLIP"
|
||||
"pmZ+zy/P+6dySx3JgESkpAcX/MiznLvc8/5POc55zznnAfaqFWTaIXPnAt0AzoBqYAB1AAVwAFgF3Ck"
|
||||
"DQCnBvUHxgEXAMOBLsfw22+BT4ElwGKgrE1ftAwaBswEygFlv+QJvALX2AH8BchrY3HzpF8A+xtA4PU"
|
||||
"BwxZgUhvLmwf9AfA1suBjgaEK+GWbDdA0dAswC0iwhVEvSk5A9smFThmIjFSUroPHC0cr0HYexNxTiH"
|
||||
"aMfBFAiT2e99sA0PiUBXwMnFEfwZ/ZB3n1eTDmTDh3IMKdgoYZoi8CXBCABhioA/uRn3+H+OgreGcFq"
|
||||
"vAoWj15udQ2Oj1tAGgcmgS8WJfgczsif3sd3D4OkZyCZnqPQUWEkKaBlgDbd2PO+gDx5H/B462TZwq4"
|
||||
"zPYc2gDQgPQmcH084Z/eE/nkHYjRw9H8VQ17c02A5ka99j/kb59DHDgSl3cC+BswrQ0AJ04GsB4YFEv"
|
||||
"47VJQr/8eNW4kuv8kKF8jEfXSfOSUf6JVe+PydhEwtg0Ax0/Jtv+dHesLU65EPn0Xmt/XJM+ibn0M8+"
|
||||
"XF6HH4+xVwdhsAjp0Sgb1AB6dxCoH67B+oEaeh+80mVE8GLP0a8+LfI6R05KcA1gFntQHg2GgX0N3pg"
|
||||
"87tkd/NRktPbj7jr/SghkxG7j7k6DEI23O5uLkxWWumwl8WS/i9OmPueQ3RnIQPkJKI2PUq+jkDgs5l"
|
||||
"pGdwEfDPNgDUTQ9hbd5EUfds5PZ/owvRPIHr98Oqp9EvHBITBFOBa9qWgNg0FFjrZO1npKIOvgm61my"
|
||||
"1Vq1d4IbhP0euzo9pE3TAih62ASCCioH2TrNn72vQuUPzF34QBDoqdyLywBHHMa+zwd62BITQX+yZEU"
|
||||
"X/uR+V04TCN9ygFOaafNTbyzHnLsNc9g3S60ca7hjLgYlY9yxajNjFWcBNbRqgltKBUidmTRiFnPcnd"
|
||||
"L9DwEUI0JNgz17k5xuRBYfRvX7I6YD8/mBEr+5o/uoTEHwC6vn3UE+9h9qwwxmAw/oh/3or4qJhaE5j"
|
||||
"fGcF5vUzHH/rtV3dNgBgxfdvcfL1a+YjhIgep6Ej/zYX+eg8tMOlzs/RLQv52M/gujHo/pr6D0bXYG0"
|
||||
"+5iX3II5W1I9Hlw/HnP8Qhimjtce432N+uDoKBAJ4AJje2gHQDjjqNPtn34265ZJwxmkarMnHvOi3iA"
|
||||
"pP/cY/5izkx4/UL2CkaTBvGf6Jfw6L7gXus/aCCy4YcujQoZL8/HzdXrKC4x7UHfXdbLTI+1TXINPHO"
|
||||
"/JbNLUMmoMNMN1J+DkdkLdeGc4cXYO3l+M/ZypaiPAFsHvMmDFFl1122ZoxY8Zsyc7OLgxl7JKv0YZM"
|
||||
"RhquugezJh8zQvjmpEmT9hUWFuYrpc5etmyZsWXLliylVOLs2bPXCyFKA/fauAcxfjr+SLsgORHtjz+"
|
||||
"OuYl1F62c/Dhk3My5F7/vQ1Toa8XjmIHPhRAK2L1w4cIDSimPiqCCgoJdI0aM2EtIptAtl+BTH4VfM/"
|
||||
"SlPkalJ9feIyEhQa5fv36Nik/Fffv2LbHHIwH5v4ejx24uQkLttUNe+1uz8K/CIZUrIxVTLUWGMXAhM"
|
||||
"tFdK/y8vLzNSimzDuGo++67b37oPdY8HS2cwOuZqWECqtm0adNaVT86AhQEftuvK361NAIAC1G/uc4R"
|
||||
"AAo4s7UuAT9xUv+/uQ5l1tSqcE3A/f9GeWwru127dnu2bt3auz7jnzFjxriJEyeuEkIIgDufRjm5boY"
|
||||
"bZn4QHIuYPn367gEDBtTXV2+/atWqI4GlIH8f2uYdhFkCUsG06x1/q2jCBNOmNgKVEwDK/otKctcK10"
|
||||
"hEuS5G+U3LaNq5c2dhz549s4/hPj4hxFEgE6BoHmSkhj+7pmHqlwXvWaaUcmFtR9ebMjMzNxcXF/cHm"
|
||||
"DEJNe2GcIAabjhnCuaXW6KAexCrYKVVaQDH2TW8PzItNXxcK9cjbeGTnZ295xiFD+CaMmWKPwD4uZ9G"
|
||||
"g+7bnbX3vP766w8fq/ABpk2bFrTqV26ytorDjB0v3Oi8H5hje0OtCgCOrJh4ocWoUFqxsXac11xzzXG"
|
||||
"Nefz48cGrLvsWZUSkcBwuq00RHTNmzHFlGFx55ZU5gb93HUQ6cffakTG17oWtDQDnO6n/K8+JUs1s3x"
|
||||
"9cT8WgQYNkHdfdiVUVFEaDBw/2Bf7eVgCROTyGXntfl8t1XBmFOTk5e4O+vxflJOrcXLTUxKjdQgWc0"
|
||||
"9oAcKZT5C+vdzjbBODzhwfqnC722Wef7cnMzNwthOglhEjMzMxct2HDhj1BARtG8CpHK6OF0yWz9u/8"
|
||||
"/PxOAEoppJSlU6ZM2dipU6cCIcSXEyZM2KaUKncaQ3l5eXrQHkhHd/T8vTDydEctcEZrA0CPyDfOykP"
|
||||
"hD2eOlJCdEXxPff7551FFmgsWLDg4atSorsXFxd3t2WQUFxcPGTJkSJeFCxceBti2bVtwoyk1CREpnD"
|
||||
"7dEQGj9IknnvABFBcXl+u6rs+cOXNQYWFhLvC9t956K0/TtIMQvee/fPny4FUHdEcqf/RDmyYM6VN/m"
|
||||
"+hUBUCa05uDutuhkgjdOLRvSFRvyZLIHcODV1xxRaxqHu3yyy/XgKqXXnopKI7enR3EZyLGnGnBwuPx"
|
||||
"dP/666935+Xl7QNSIpYqJYToO3Xq1PWRN3vooYeqA98dOwzNdFislILeOTENwVYDAEeXp1uWNUOi7IJ"
|
||||
"za4VbVFTUafXq1RtCZr+POFnDQIfbb7/962effbZdQDgjT7eyd8IsdB9MqQ09q6FDh3rKysoGOvquSq"
|
||||
"mnnnoqzGpftGjRVxs3buwf+MrE0bFd7JwOxLJjcloLABz3/TukoTktmwkuxPgRwVmohg8fHtQg+/btK"
|
||||
"60r1vD888+PCHXrbr7YWTjXjkHLzggKp59SKl5BUW9gD8CKFSu2jh07tm8AYPdMRCkVGwDtU2Omkbca"
|
||||
"ACThLGhHhvtNeGZqqLEoemVnZx+srKwsGjhwYHo9A04A/L9zUZkZzs/t98D8GfUPjuXn538+ZsyYb0e"
|
||||
"OHNkXq9sInTKQf/kpuowDHU3EvEdGawGA476cz4zN/OwMtNl3WxaCUkoVFRV1Sk1NTZg5c+aeY4k8vv"
|
||||
"w7hN8f+wvD+qH9YzL1iQPI/v37T1y6dOnpAYClJKK+eQ7N74v/Q1PGXAJcrQUAjiyqjJO9oxTcOg7jr"
|
||||
"7eGCSdtzpw5I6ln7eeqf0JaUvwZ7jfhVxMwnrmTuuINQa8By1CVB96AjLS6NUhI0CkKG60FAJVOb+4p"
|
||||
"wtTjjMjvg2k3YCx6GJmUEK3eY1G3LGT+i6hhfev3vH4f/OwK9J2voEYPiS+UIX2Q707HXDsLPSkBrT7"
|
||||
"rx/7imOOoONmCMJoIAMWOAChEF5qThx0+Q8eciV71PuqRNzGffg+xtyiaoalJyAuHwE8vR1w1yioaPZ"
|
||||
"YScSmhayba0sfQjpYhF3yJ2rwXUVqJmdkO47QeyEuGItLSrHzF+qacCQFbC1Ax3NZDJ1sQTbUbmGxrg"
|
||||
"TCZdEzHPPweRn0TOYUAPQHwYe4uRPj8kJwAudmAjoYv2t07YYYJazk67hnngot+g1yyzjE9zDjZy0BT"
|
||||
"bgc7bgXXLEBqIqab1OLJSIbkSzCrvVFayw+4W4sNAFbxZxR9/DWnNB04gHQQPlhl5LQmAKx3evO9ldY"
|
||||
"O4KlK76+KaYqsbG0AWO20BL35CWiJp6bwDRe8sTTmUvxxawOAIytKKtBWf4N5KgLA40EuXR+T5/NbGw"
|
||||
"A+j/XB0/+1agBONZr5flxtqFobAMBqohRF//4IzedvGoY0mvpPRP15Tkz1/3JTjaupAfCvWK7oA68it"
|
||||
"VOol/m8j5HFZTHd7tlNNa7mwOJYcT9VMx+haS2/pb2RiOr8A9ShEsdnWYjVXbRVagCAR2IAUdz+BKbR"
|
||||
"wkNCQsATc5ExhC+AGU06vmbAowSs3rqOa/6GWaiB3WmxJmGlB5lxTUxeb8U61ILWrAFqgEdjgfHSe1C"
|
||||
"Gq2UK30hAjbsvpvAF8KumHmNzmVnTsGLhUXTwCNqND+NvaSDQNXj4VczPN8bUspuABU0+zmbEs93EaK"
|
||||
"H2zU60HlmYZ+WhqRbiHK74DnnTIzEnmMCqjDrU1ONsbhb2GuLkxy97DHX+ac0fBNv2Yw68NW73D59t+"
|
||||
"zQ5NTfjamw8UI76NWLtVqRoxo7hzoP4T7utztYvbqyDrZp+qWpm/KvCSrUeH+sLsz9EDO+PHNANTTYj"
|
||||
"TaAJWL8D84zb0eKlhIfQ97CaSnzVBoBwWgecS5zj2V5fitAE8sJhCGk2/TJmuOHVxcjL7zvm84ausgG"
|
||||
"/rs0GAObOhQ8+QLz8Msp2D+Pa/qMGIz/8M8JtNGETSRfqhzMw3/jkuCeTAO4B/tpmBAJCMFIpXsc63r"
|
||||
"VOJa8J1CvTUD+67OScFhI665evx3/FH9DKqsL4qM7nbDqSIQ9QqK3hm/rwWQBPY5192GoB4BaCuUpxN"
|
||||
"cexNTq0L2r5P8DVyNrAcMGuA6jJT6AWrQnn37WMlT/kKg2UkCh0NHR01vKt+ojP1CrW1XXO0HvA1a0R"
|
||||
"AFcC79ZzPMECzsgPrj4P+e4DDX+CSKAl7RfrMR94BSK7fmbTUT3Ar0QmGULGwK6Ojh+/eoV31XyWiDj"
|
||||
"PtpwY7fJPVQC8BfxACOKWYuaQLccx2ncOZ/o6kam2sUu7h0dTvCFFRmf0Qm6Y7dxXONCvxzTrl9ZtGJ"
|
||||
"anvnkr5pyl8NwCKyoZ7beOkrfzQ91H/fLPNTQKOCin8VdR41wgJbDyA88/1QEwGPiEOgoiu5Erf8r1n"
|
||||
"rMY5K+mJmy8bzI/4W0WBlOp774W+eht4YWZhhtmvYf8cDVKSkSfXNSg7ojeOaiMVLT0ZJQmrPMAj1bC"
|
||||
"7kPIrQVoq7cgF64BUzovKSkkq3uYrAaSp/uPI4Otkmp1O/fidwaOAOZhHZN3SgLgfuDBgBp3KrZIJkl"
|
||||
"N4UbPBXzP54kQfIDms9T9Mm8HI2oFc1DZIZW/moCH30D+4aWGe84cstRVXMJYRmlefCd0rU1sM6fzRL"
|
||||
"xw8R3AM41q05xkwacDn2L1BwqKPEL4YjyXem7mB14fPmIJX0Own0NB5o0dhszNQg+tzFWg/vDSiQ+6P"
|
||||
"e3UBQzjIkbQk66ahxpOVPgAQxio96OXmc9OJxAo2zN4HauZdosHwDXA20RUBIXO/q50lvcztaoD7ZSv"
|
||||
"DgYnkKDW8m1w/HeOR0SWZb++JLwGbzTnmns5oO2hAB9+R2AlkyS70ln0opsaSB8xmAGiI+21GrwoFB5"
|
||||
"qGowhXnxcw2XiEZ6N9RUFPAXc2JIB4Lbdm8siLfcQ4Ysfc7XnOsZ5a/Ai6+EF7qZAL6E0cCKHuvz88A"
|
||||
"JNw4B5n9UCII8e8lf8n2EiMdCRSFVOpfTiFQJBAm6VTpoukbqJiR8TZY+jIYUeSd9jcF3L049bMgBGA"
|
||||
"EvsiJ5ygncG6eoh7q7sRKaswVtvS/o9/ucOXHPCBSj8EZE4F+r9lbWz/xauFQFB2tpFuHHp7pBgYxXV"
|
||||
"nGwy0EV72vlLKNXrMJg3NMb9tUYE1hu2T+uKYeKIUWqY/wUeqcimo1THEPvREHzE58HrTr4SEen7L15"
|
||||
"VO/s7k6UGM6BZppVJJNl0rCuMvKElaYAJwNxYwZoA/VbdVnkeQ81o/1nV6Zx8wJKg8NOTURcNR4SWlB"
|
||||
"s6vLAo1Pi4tFHV+ImQAlzxxfBhS/IC/g3cHE/wncmSM/h1VRop6niEn0Sieo/FQd//l9egTE+EJtNRc"
|
||||
"2oLz9TFjBD+ZlptJoA4QSQBvNqY929ItTizLuFfxAjfs8yoSCNF1RWW0NAQCAo4qCXgVoHzIrexWy/m"
|
||||
"aFBl3j0hOkPovyHG32jORaKaLOCVSALVeKQ7Rum/hkYhxfH6Ec1pCRqgHzA5nvCvZaz3x4yvqcErnFW"
|
||||
"hItA9TUPjOV5P/IgVLstZEGoU3/MNYZD5DouCxt+lZyPbpYX7/oYBL1rHs+gAlzASWWe/p8aY2YJt7J"
|
||||
"YzeFJU4RG96Sb/zr1a5GzX0JTtzcRS/6olAOD78f1AF5OY4KmiWsRaCQPCr6BK/IoHU8qoDNn0UXzKl"
|
||||
"65P+TLMoPzNhGjfH5D/XWmpiySS1Bn016rxnHQAHKRI3sujwefdwV7xPkvkWEaFCXtP7CODBPBcY4+z"
|
||||
"oZaA5+NFq3T0uDo4FOJT+VOo8IO92CLzANuloi45L9pgeGtZ7VoymnOaxPhLJIFHmBX1/qesUu4Ip2g"
|
||||
"jW+PN8HdbCgAgTkJnNR7xBesNZ+FLBAINwYv8J6EKjwgLFMW42S+uQpkR5wYaBrywqPYnFzAM1QRFxl"
|
||||
"vZJQs4GMWLQooJPftaR+drNsYa4OsnY6wNCYAvgHtjgeBv4tmk6Li+InASvBu3WslaV9jMV+ERw9DWM"
|
||||
"VOvRkQaf6YfteDL4DOp0+jXJMbfmhhueyQYXRis5CvRVOq/MQJBD2PFrsMPfRDgVT5xFw+mxArzSqRI"
|
||||
"I1XhgCClrGtI25Yb0A3ZKSt67M8tqLX2hjMkZry/MUlHZyf7HD9zYYQ9/Vd8J2NMGA/WplmLA4C1jMP"
|
||||
"fIx9MAUcpE1P5U6qJiSL02RVevNzFT6rDIgKiFkChdONF0Y0ZjUR44t3ae57DmcJsAt9fR6OcCkfg+U"
|
||||
"JOw9DR+JgVsS7zwskab2OFR39rxwQEhG/3HqZETOa+1AqqRKTW60GuvIfJ1YrwXUKlwq8xfkT0rFm3G"
|
||||
"XPL3tr3z2+CAzgkUr3CO3IHex0/r6Raq8KjAEykWs6aWNb/yy0dAACvAGdBtBleQZW4nftSN7FN1yNS"
|
||||
"6Rdbvn/Y+h+6lAC8+jGyqgYZ6B1gGPDQa7UXGckw5cI4qeq/iCPyRu7mbRaJeJ7HS8yTblx8yCexwp5"
|
||||
"+2546aZHIBiUFbGCwGMIGFfSKrAcaDCgNEbrdKy5hpHcyP/J48XMXD6QWUiycMoSc3ptwAfLBW6wzhT"
|
||||
"In1D7L37mHbuSeTACom7hbefE5tX+NMnrGcaFawRpKKXca4zzghhYLgOD6Hf32UwLuUIE0sJDvJuKmM"
|
||||
"1nmLgr0+gg/8v9Tk5CV1bWnjbzPbGIHnRo+4vcOi8w5vB+qTcsmZVDR1UXKp5Uc+ayKHKxDMlQ95HEX"
|
||||
"8M8WuQTMJe52zi90xA9DPw58twYvuynQNa3W4g8FqF1rJ2JpglDhA5RSftKcfxcGK1gbVhiyrS/mUzl"
|
||||
"0mZZJxv960rtyIPLGduyq54Q7cjKXrgYFwAgeZ26Mh7yXnoYf9YaAoQJEQPjBYI/t5gUEnKzhfzKHzS"
|
||||
"t7oeZ2Y98vO7K/h5viyMJLJx37AUuUOEn5rjp6WDh3eBKHurnoEBiTX4GElOe70PPlLmyvBwgOt0gAf"
|
||||
"AK8wi/FDaDmhrw/i1xm00esQ8kXEDxiFUL2Ddh0gRkf+i8gHu7EnkkZDDg9Ee3yVLo+lE3u9jwyN+Wx"
|
||||
"9/I0CoK/dxjLG7wvKqk6KVogAmji0lQSvA539iuY0I4+d3TgmzpAcLBFAmA01llw07GS2QOa4Gfs51v"
|
||||
"2iwXsls+QIbrSTaym1zYXYriyNUGE8EFAoog+W7BaQVcX3d7uRtdNeRR1dVEYg5ni1/xZSRq/lYSIsK"
|
||||
"U6GbHz2kwFT+YwECiLc8k9LQ4AS4EPQNwMarptC1xvT843gMeplgB3YfIj9sov0LTpZH/lFlo7oCBU+"
|
||||
"EKgBKhfH8SbJJz3cf0WELJ29aP9be2d1eoRSsXPuFcVU6Ias9XgTvbJiLHFTe8yFUaqFiNQ0FJtgPsB"
|
||||
"RY9gHlhoOcvEoFrOEjdRpv5Cd93Axz5d4+IJsqJHD/KASiHANgeEUlCp6DpsJ4UaURGjIFVJ3E/m0Gd"
|
||||
"GNt85gaCMCjGFP/Im800dXWkNpPAEgkQS1Lfkq9/zSJgDtNWLHg9ufiitkPSOiaeTTKIhZr+HjqKAYv"
|
||||
"XTGN+5kgzxfxxVW+ijJZPAdo6I6jFKZp93iKLDaLNmcbEQLITa+kBbKwig9I4O+G/MgGGJVBjCPnNYw"
|
||||
"EEfe5ZXoS2qQH+9FFUl4x68qC5mBOczlNPoRwJuzY9JfcPFOjoJuNjJPrmElfyPzwKuZlixaprGgbKB"
|
||||
"5FZE6C6XgKMmBefuIHGXz/ngTKz0r5tbFAAA3gHtGpCRLuB0+/U4XfTVpMvz2MFWMrTNJJs3vbJTlJa"
|
||||
"h3XGHJQEhKFSKzIALGOYOKstWsOko1rk6qdQ2WjrmtT6T9rIX3UQvutGJTNWJTC2NFBJJUAKBDz8VVI"
|
||||
"rDlMj9HBJb2ckGtigPNYHQZTndkPTAoJCj5NMl4Nnel8XWGdlk+hUFm2vouaSSqldL8a6uJjcOz4WtP"
|
||||
"OfRUmgW8G8QHzJAADzChVHfeYw8A+AfZGiv0V+MI1sD+N3vLH1805AgQ2YLgRTWul/7r9VLuKlfgWqm"
|
||||
"EvpRwpWUcCc1/ALFFBQ/Zq/9eeT3Q1/1ucdJpxNKCfsZMJfB2uVsMDeBWMnSsIe4mk5iMO3Mn5OijaC"
|
||||
"repAj2gIKzUsvRf/7v5A/vxS9x3pLA2ga+UohlLKqdYMbQfFiqvG0mosictERwC4U0LGelxAYlNIZHT"
|
||||
"DRqKELKXTFSy7J+ElAEd7WsiNdSeMA5XQ+Xo1kz6eTTie0BCwgV4xjv3qZwdzMhmBk7zqgEz3FU+xSk"
|
||||
"8gWP6VQ/RGrRChAd16A/s/PLOHfMQV95rPcISVPaAIlVVDgIiLCHP85UijhdLycQRIppAeXdwMvGyhm"
|
||||
"KZmouKAXdOMw15KGP6SPX31ySqup4UU7sh0+VlHP8adgdUlrORpgHPvVJ8BoOwNGBE3Z03Czhz/QWXx"
|
||||
"qFWKJj6nNzX7sJsQXr1hsnTYNo8SDlJJUzT40Mij8qzmAi1QOotjHUUpIohQFpNm3KyWLJLpSzun4aU"
|
||||
"+P4MwMTRb14mYAOfSljH/hxU/HGI8kGUcy3uNo4phEAj+nmq8o5BAmAkEqCWThZxUGVTH7IAis+r+qF"
|
||||
"qcBAjQfxBUhCJ8IooLBKoES8RZ7w5B/xyC0nhmoHpeiCtpBUhJi8mSUYTBL+cVtZuhEuRZBp5CRavYr"
|
||||
"dE5Jju2oRZMynicZ6eCvp1PCJDpwoodNaiGawwCeZDvK0fUTWI2yf9dUdtwJO8ZzgSsi1NsboJLYpv0"
|
||||
"nQvgPno22dyOqqBi1Efjr47D4BWsM0i8GmPG0pLIF7QO89svHsZ+zqZPO2BgRxA54G6SEQIYsG5Y6i3"
|
||||
"XE/RtNKfwGAYBTD5Nr6KLNo0q+ZP//tN7wu3SE2o4amoc6+n2YPh2uGop+9W0BnqlBUbPDy+5Geeq+5"
|
||||
"JLqcH5xSj3X+2PncCz137WpPbkGzwi6jjOEQZW6DvgJML0DHDyI0HOgSqCOjIO1WxFTf4Lr7AtRN90W"
|
||||
"nMOZUVngnkaK4fqAc0iI0AKCdNo3+L0q2E3shpcjTzkAzOMbBTkqGM0YiOjTGfHwFtTi3jBnPaJfGVp"
|
||||
"7N77Jd1rzzdDEwGCMNSzWGzNiduLUz8Ho6tgIVSRVIaDSHTeKup5SALBAsLE2GrgC9ccdlqAPZSB67E"
|
||||
"XMWYt5ur3lcUMvhKlUXiD6F7bqF1HdaPs4brIhYonJaoQOEV5Sgi5gF6yMuHA6+5QDQPDJIh6tfwGs2"
|
||||
"YGcPhqu3w6fPoo41AuhFJmOFziA0WjtrCXQJWLvwN0oRYQq5C+N9ChLt+8pC4C1ayE3t/b/P95sPfz0"
|
||||
"T+BWgbjvPUR5KZLo42Ks0Gg57fFQ0iiDU4BOedh7+2PGB04k0lITtDUUGon4IxzZLqcsAAD2xyh+XeN"
|
||||
"DLP8MuXYtAEVhnnqot7++Eas7wqOCimWUNnjLjEi7xkVCRFQw7ZQGQCxav8FeC28HYEuYpx66ibKaZF"
|
||||
"z17B51rCGw0ohedKV0Ib+Bc/IOBw1LgUGNXa4sGjoY1+IAEGIkQWgihAjODs1eDJJZFzeF6vhIx0MZq"
|
||||
"VE6YSGJeBvIGHRhssIOBen4cJFIDUaEBiht3QB4KfjXUlsEwlacHpKosVVzCnoDLwV7KMHauCECfCm8"
|
||||
"SPkJc0YDlnGASjIAQXYwLhCph3a0bgDU0pwwdahIJBMdDRNFEkspaDBlqQFrHXoXdgFSUZhk8zrF6Mf"
|
||||
"ZD1YDNnOIr+kKKFLxkYKLcnwOu5Gr2wBg0b+i1PFhBN0QgORbulLaQD1ziznM7qDraYbxIweNZHwcoS"
|
||||
"MfUnbMRqEBrGIbi+kEKNz46GTnJRwOb5Nr0xdtAKh1/cJBUI2BH0V7u5Z8Dj70E8ycEVQx116HXUhyQ"
|
||||
"7Zt/HiQQC4GBpJtdGQ1+49B81TxNkWsIc/WYT664wI0SvDhj2oV9kJTM725nRmUjXWapgpzC/uisxMT"
|
||||
"PwbZ7OaH9Dgu5awo5jUSKSMZ8NMHHZBstwHREUmGHXoyMdll8+cHFNOZrLjTaC+FfEA6pp0QkoGfLFx"
|
||||
"IwIdkDypiwgmgE1DYlAxvbsfGVWIdFnVWGHtr8JGDzlEklbSngqP0JbHeO3cGUEARr5OMh2QAeqAF/y"
|
||||
"ulxj7ixyTN5omGhgs/lRhsQqMPB0iinQMHJYso5nOysGoC/HRB0Q6XvYUt7YBzpPDvp5G7gLZEDRAAZ"
|
||||
"U0UwzrjRaFxyF6VsyjiCjTS6Ri2/05YGOko24EVlFFK96Bm6YYXt531I4B9gMcWVx4ayr63AA7hpxwd"
|
||||
"8HIhRxlMeyRuNLx8w2E+IR1JKtauv4+sEDXvR7Eb6SD8X2CdBUAbAJzpOmqLjWupD4rDVFMa3GARJLC"
|
||||
"fXAyS8JBCd2oopgwfJeiU0t6e/9Z33fjJBfQQ004g2YZJID0uG5O0kM814ACSimCF8mEySeEwEiuDAF"
|
||||
"z46IwgwW4CJIBKajgQteYLrJPS/9ZcGN2MT+HlQ6wzBmopGS9dSKAUH4WIei5hVgQuE500jChNcRBJO"
|
||||
"aEF6X76YKAIL1IvwUsxRths1jDJQpJur/UBQB3G5Kij/yBsO6eouTDZaMYAqHJ4x025zfAUFEe/Nz35"
|
||||
"AAABiUlEQVTwUoHAjJppVk5vMpJ0dNwkhC0TGlCJj8OANyIeoDA4iEnnkJZe1sEGbtojqcCHHz8JGCT"
|
||||
"jQqIH+13VYHIAiT8uX4cAi9s0QHxKBKqDccGIM4VIwkMSbhLwY+BGpxrwIzAwcKHZwgv9XQ1evAiq0C"
|
||||
"hH2QEZFZMvafjojIGsg0cC6+yXIkyqo1LCnWgHcc5Fbn0AOA34zjEqeEM9x69C/lVYuwuh28surGNr6"
|
||||
"pOfH6kffWQCabijMv1N/FQgKMVPTdQOX11jfgbrRLBWTgMdATia+pVSncyyMB8JmCQiSUQFtdOJXfMn"
|
||||
"bRrAmcqD1vWpTQLoBexqykE0t3N0noCoLdpTlRQnsSFkS9AABlbCtqL1kKDVJ4TU0sWtzAISWAdptmk"
|
||||
"Am9phNX9QTcwD1cg8K8HqBLYO+FEbAMIpF3gc+AGNv1G1GPgSqzYgkKeTBmTar2ygg22TGHZgqgBYb/"
|
||||
"+mHGvzKrRS0R/yqsZq++6BRshpPMUDQcfzHFrIsqZHhWqasAtHc6b/D3cbSAuGcmWdAAAAAElFTkSuQmCC\" />";
|
||||
|
||||
const std::string HTTPConnection::itoopieFavicon =
|
||||
"data:image/vnd.microsoft.icon;base64,"
|
||||
"AAABAAEAQEAAAAEAIAAoQgAAFgAAACgAAABAAAAAgAAAAAEAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAABsAAAAgAAAAIAAAACAAAAAgAAAAIAAAACAAAAAgQAAAIMAAACEAAAAg"
|
||||
"wAAAIAAAACCAAAASQAAACUAAAAmAAAAOwAAAFIAAABgAAAAVAAAACcAAAAVAAAADQAAAAcAAAADAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACjAAAA/wAAAP4AAAD+AAAA/gAAAP4AAAD+A"
|
||||
"AAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/"
|
||||
"AAAA/wAAAP8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAP8AAACbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZAAAAP8AA"
|
||||
"AD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4A"
|
||||
"AAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/wAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAKQAAAAAAAAAAAAAAAADbAAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP8AAAD/AAAA/gAA"
|
||||
"AP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAIA/gAAAPwAAAD/AAAA/gAAAP4AAAD+AAAA/gA"
|
||||
"AAP4AAAD+AAAA/wAAAFkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApAAAAAAAAAAAAHAAAAP8AAAD+AAAA/gAAAPwACwD9ABEA"
|
||||
"/QASAPgAFgD4ABUA+AATAP0ADQD8AAIA/AAAAP8AAAD+AAAA/gAAAP8ADQD7ACMA/QAlAP8AJwD/ACI"
|
||||
"A/QATAPwAAAD8AAAA/gAAAP4AAAD+AAAA/gAAAKIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe"
|
||||
"AAAA/gAAAPwAJAD+AAwA+wAAAP8AAAD/AAAA/wAEAPwAEgD8ACQA/gAmAP8AHAD8AAEA/gAAAP4ACwD"
|
||||
"8ACUA/wAkAP8AFwD8AAkA/AAQAP0AIwD+ACQA/wANAPsAAAD+AAAA/gAAAP8AAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAEAAABNAAAA/wAAAP8AGAD8AAAA/QAYAPMARQDyAFEA9wBOAPIAPAD0ABEA8QAAAP8"
|
||||
"AGgD7ACMA/wAVAP0AAAD9ACMA/QAkAP8ADwD9AAgA+ABBAPUALADyAAAA/AAUAPwAJgD/ABgA/AAAAP"
|
||||
"0AAAD/AAAAdgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAP4ADQD8AAgA/wAqAPAAcAD/AGoA/wB"
|
||||
"qAP8AagD/AGoA/wBvAP8ATwDvAAAA/QAbAP0AEwD8AAYA/QAkAP8AGAD8AAsA9wBuAPgAagD/AGsA/w"
|
||||
"BjAPkADAD3AAQA/QAiAP4AGgD9AAAA/gAAAP8AAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZgAAAP8AAAD+ABY"
|
||||
"A+wAQAPQAcQD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBTAPEAAAD/AAcA/AAMAPwAIQD9AA"
|
||||
"AA+gBoAPgAagD/AGoA/wBqAP8AagD/AHAA/gAuAO0AAAD/AB8A/QAPAPwAAAD+AAAA+AAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAANIAAAD+AAwA+QAAAP0AZQD1AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8Abg"
|
||||
"D/ABcA8QAAAP4ACAD9AAAA+QBeAPkAagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AbQD/ADkA7wAAAP8AJ"
|
||||
"AD9AAAA/gAAAP8AAAAfAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAALi8AAAAAAAAAAAAAAAD/AAAA/QAAAP8AKwD1AGsA/wBqAP8AagD/AGoA/wBqAP"
|
||||
"8AagD/AGoA/wBqAP8AagD/AGoA/wA9APUAAAD/AAAA/wBKAPMAagD/AGoA/wBqAP8AagD/AGoA/wBqA"
|
||||
"P8AagD/AGoA/wBxAP8ADgD1ABMA/AAHAPsAAAD/AAAARQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzPAIAAAAgAAAA/wAAAPwAAAD/AGQA9A"
|
||||
"BqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8ASQD1AAAA/wASAPMAcAD/AGoA/"
|
||||
"wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AawD/ACsA+QAOAP4ADQD8AAAA/wAAAEUAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7gAAAAAAAADgwAQAYLwAA//8AAA"
|
||||
"ICIwAAAP8AAAD+AAYA8wBwAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/A"
|
||||
"EIA9QAAAP8AMADvAGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGsA/wApAP0AEQD8"
|
||||
"AAIA/QAAAP8AAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAVgEAAA"
|
||||
"AAACJ5AQA2ygAAND8BAHV+AgAAAAAAAAH/AAAA/gAcAPQAbAD/AGoA/wBqAP8AagD/AGoA/wBqAP8Aa"
|
||||
"gD/AGoA/wBqAP8AagD/AGsA/wApAPYAAAD+AB4A+gBsAP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8A"
|
||||
"agD/AGoA/wBrAP8ALADxAAAA/AAAAP4AAAD/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAABYAhQAfAE8ABgBWAQAAAAAAIoABAAAAAAA5UgEAAAAAAAAA+AAAAP4AJQD2AGsA/wBqA"
|
||||
"P8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBsAPgAAAD6AAAA/gANAPAAbgD/AGoA/wBq"
|
||||
"AP8AagD/AGoA/wBqAP8AagD/AGoA/wBqAP8AagD/ADsA+QAAAP8AAAD+AAAA4AAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtQAAAAAAAABYABgAZgEGAIMBAAAAAAA+fgEAAAAAAAAAA"
|
||||
"AACA8wAAAH+ACkA9wBgAPgARQDwADoA9gA5APYASgD0AHAA9wBrAP8AagD/AGoA/wBxAP0AFQD0AAAA"
|
||||
"/gAAAP4AAAD5AHMA/QBqAP8AagD/AGoA/wBqAP8AagD/AGoA/wBvAP0ATgDxAEwA9AA1AOkAAAD/AAA"
|
||||
"A/gAAAP8AAAB3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAvAAzAKcBAADXAAAAAAAcAFYBF"
|
||||
"gB4ASAAZwEAXT8BADpdAgAAAAAAAQG+AAEB/gAAAf8AAAD/AAAA/wAAAP8AAAD/AAAA/wAAAPsAJAD1"
|
||||
"AEQA9ABEAPMACQD3AAAA/wAAAP4AAAD+AAAA/wAiAPMASAD4AE8A9gBuAPgAbQD/AGoA/wBvAP8AFQD"
|
||||
"yAAAA/wAAAP8AAAD/AAAA/gAAAP4AAAD+AAAA/wAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAABgCuAQAAAAAAAAAAAAAAACEAkQIWAJMBGQBXAQCWlAIAAAAAAAAA7gABAf4AAQD+AAAA/wAAAP8A"
|
||||
"AAD0AAAA/gAAAP8AAAD/AAAA/gAAAP8AAAD/AAAA/gAAAP4IAAn9JwAs/TsARP5CAEz+PQBG/ioAMP4"
|
||||
"EAAX6ABkA9gBAAPUAUwDrAAAA/wADEfUAFXztAA5U9wAAAPcAAAD/AAAA/gAAAP4AAACYAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAABcAugAkALIAAAAAAAAAAAAJABQAHwCaAR8AhwMNKUwCAAMDRgAB"
|
||||
"Af8AAAD+AAAA+QAbn/MAK/zwACz//wAs//cAIMDkAAAA+QAAAP4AAAD+CAAK/V0Aav6TAKn+nQC0/5c"
|
||||
"Arf+VAKv/lACq/5QAq/+WAK3/nwC3/4IAlv5FAE/+AAAA/wAAAP8AHKnzACnx/wAq9v8ALP/5AAo+9A"
|
||||
"AAAP4AAAD+AAAA7QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACtAQAAAAAAAKoACABt"
|
||||
"AQwAYwAAAAAAAQAERQAAAP8AAAD+AAAA/wAfuPEAKfL/ACnz/wAq+v8AIsrwAAEH9gAAAP4AAAD+JAA"
|
||||
"p/ZoAsf+TAKj/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/lACq/0sAVv4AAAD/AC"
|
||||
"PQ8gAp8/8AKfP/ACny/wAYke4AAAD/AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAgAAAAK0AAAAAAGsA/wAAAAAAAAACSgAAAf8AAQH+AAAA/gAAAP8AGZHnACfm+AAdqvIACjz"
|
||||
"tAAAA/wAAAP4AAAD+CgAL/ZwAtP+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAK"
|
||||
"n/kwCp/5IAqP9OAFn+AAAA/wAetPMAKfP/ACny/wAq+PcAAQfyAAAA/gAAAP4AAAD/AAAAEgAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKkAAAAAAAUAFwAAAAAAAAABeQAAAP8AAAH+AAAA/wMBBf0"
|
||||
"AAAH+AAAA/wAAAP8AAAD/AAEB/gAAAP4AAAD9AAAB/k4AWv6SAKj/kwCp/5MAqf+TAKn/kwCp/5MAqf"
|
||||
"+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/ZwB2/gAAAP4ACC32ACHC9AAZk/AAAQX3AAAA/gAAA"
|
||||
"P4AAAD+AAAA/wAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABUAYQEAAAAAAAAAwAA"
|
||||
"AAP8BAAH+EwAV/YkAnfJ6AIzzBQAH/QAAAP4AAAD+AAEB/gARF/0Aa5P9AMb//gACAv4oAC7+lwCu/5"
|
||||
"MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5oAsv8YABv9AAAA/"
|
||||
"gAAAP8AAAD/AAAA/ksAVv0SABX9AAAA/gAAAP8AAABmAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAEAAAATAAAA6AAAAP0AAAD+IgAo+5oAsfCSAKnxlQCr8RYAGvwAAQH+AAAA/gAAAP4AQFf9AL"
|
||||
"r//wC3/P8AXn79AAAA/o8ApP6TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/k"
|
||||
"wCp/5MAqf+TAKn/mgCx/0QATv4AAAD+AAAA/gAAAP2gALj/WQBn/gAAAP4AAAD+AAAAtwAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOAAAA9gAAAP0AAAD+HwAk+5wAs/CSAKnxkwCp8VEAXfcAAA"
|
||||
"D+AAAB/gAAAP4AAQH+AAAA/QDD//8At/v/ALDz/gAAAP5aAGf+kwCo/5MAqf+TAKn/kwCp/5MAqf+TA"
|
||||
"Kn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf9sAHz+AAAA/gAAAP4AAAD9nwC3/5YArf8A"
|
||||
"AAD+AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA2AAAAP0AAAD+AAAA/pgAr/"
|
||||
"CTAKnxkwCp8ZIAqPGHAJvyAAAA/wAAAP4AAAD+AAEB/gAAAP4Ae6X9ALb7/wC+//8AJjX9KgAw/ZcAr"
|
||||
"f+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/mQCv/wAA"
|
||||
"AP4AAAD+AAAA/Z8Atv+YAK//HwAk/QAAAP4AAAD/AAAAZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHA"
|
||||
"AAAP8AAAD+AAAA/gAAAP+RAKbxkgCo8ZMAqfGTAKnxkgCo8XYAiPQEAAX+AAAA/yUAK/sBAAH+ACg3/"
|
||||
"QC+//8Atvr/AG6W/QAAAP6YAK//kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp"
|
||||
"/5MAqf+TAKn/kwCp/5MAqf9oAHf+AAAA/kAASf6UAKv/kwCp/0cAUv4AAAD+AAAA/gAAAMkAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAACRAAAA/wAAAP4AAAD+BQAG/nsAjfOUAKrxkwCp8ZMAqfGTAKjxn"
|
||||
"QC08JcArvCYAK/xSQBU9wAAAP4Aqen+ALf7/wC3+v4AAAD+WwBo/pMAqf+TAKn/kwCp/5MAqf+TAKn/"
|
||||
"kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5wAs/6ZAK//kwCp/5MAqf9bAGj"
|
||||
"+AAAA/gAAAP4AAAD/AAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE0AAAD7AAAA/QAAAP4AA"
|
||||
"AD/TABY+J0AtPCTAKnxkwCp8ZMAqfGTAKnxkwCp8Y4ApPEAAAD/AFRx/QC4/P8Avf//AC0+/RQAGP2c"
|
||||
"ALP/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+"
|
||||
"TAKn/kwCp/5MAqf+TAKn/YQBv/wAAAP4AAAD+AAAA/wAAAG0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAABAAAAAAAAALgAAAD/AAAA/gABAP4bAB78jwCk8pMAqfGTAKnxkwCp8ZMAqfGXAK3xKwAy+wAC"
|
||||
"BP0Axv//ALb6/wBvlf0AAAD+dwCJ/p8Atv+dALT+lgCt/p4Atv6eALb/lwCu/5MAqf+TAKn/kwCp/5M"
|
||||
"Aqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCo/1IAXv0AAAD+AAAA/gAAAP4AAACRAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATgAAAP8AAAD9AAAB/gAAAP5ZAGf2ngC1"
|
||||
"8JMAqfGTAKnxkwCp8WAAb/UAAAD+AJLH/gC3+/8As/X+AAAA/gsADP0AAAD9AAAA/gAAAP4AAAD+AQA"
|
||||
"C/SMAKP1NAFj+gQCU/pwAs/+TAKn/kwCp/5MAqf+TAKn/kwCp/5MAqf+TAKn/kwCp/5sAsv8cACH9AA"
|
||||
"AA/gAAAP4AAAD/AAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO"
|
||||
"AAAA1QAAAP8AAAD+AAAA/hMAFfxzAIT0nQC08JMAqfF5AIrzAAAA/gA6T/0Au///ALn//wBkh/4AERj"
|
||||
"9ACs7/QBFX/0AT2z9AElk/gAxQ/0AAgT9AAAA/gAAAP4UABf9bwCA/pcArv+TAKn/kwCp/5MAqf+TAK"
|
||||
"n/kwCp/5MAqv9rAHr+AAAA/gAAAP4AAAD/AAAAhgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB+AAAA/wAAAP4AAAD+AAAA/hQAF/xJAFP4GwAf/AAAAP4"
|
||||
"ABgn9AMP//wC3+/8Auf7/AML//wC8//8Auf7/ALj9/wC5/v8AvP//AMb//wCj4P0AWnj9AAAA/QAAAP"
|
||||
"42AD/9mgCx/5MAqP+TAKn/lACq/54Atv9YAGX+AAAA/gAAAP4AAAD/AAAArAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAh4AAADVAAAA/gA"
|
||||
"AAP4AAAD+AAAA/gAAAP4AEhr9AJ3V/gC3+v8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/w"
|
||||
"C3+/8At/v/ALf8/wDB//8AYYT9AAAA/ggACv1cAGn+bAB9/UEAS/4CAAL+AAAA/gAAAP4AAAD/AAAAl"
|
||||
"gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAABAAEBFAAAAP8AAAD+AAAA/gAAAP4AUGz9AML//wC2+/8At/v/ALf7/wC3+/8At/v/AL"
|
||||
"f7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf8/wCq5v4AERj+AAEA/gAAAP4AAAD+A"
|
||||
"AAA/gAAAP4AAAD/AAAAdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAOgAAAD+AAAA/gAAAP4Aeqf9ALz//wC3+/8At/"
|
||||
"v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At"
|
||||
"/r/ALz//gAcJvwAAQH+AAAA/gAAAP4AAAD/AAAAfwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI0AAAD/AAAA/gAAAP"
|
||||
"4Ac579ALn+/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+"
|
||||
"/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Avf/+ABcg/QAAAf4AAAD+AAAA/wAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAABIAAAD/AAAA/gAAAP4AP1X9AL7//wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/"
|
||||
"wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wCy8v4AAAD+AAAA"
|
||||
"/gAAAP8AAACKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAACoAAAA/QAAAP4ABAb9AL7//wC3+/8At/v/ALf7/wC3+/8At/v/A"
|
||||
"Lf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/"
|
||||
"ALf7/wC3+/8Atvr/AHmm/QAAAP4AAAD+AAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAA/wAAAP4AAAD+AHWh/QC2+v8At"
|
||||
"/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8A"
|
||||
"t/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wDD//8AGSP9AAAA/gAAAP8AAABuAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbQAAA"
|
||||
"P8AAAD+AAUH/QDE//8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3"
|
||||
"+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Atvv/AH+s/gA"
|
||||
"AAP4AAAD+AAAA2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAALMAAAD+AAAA/gBZd/0At/z/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7"
|
||||
"/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf"
|
||||
"7/wC3+/8At/v/ALf7/wDF//8AAAD9AAAA/gAAAP8AAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwAAAA/gAAAP4Al9D9ALf7/wC3+/8At/v/"
|
||||
"ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v"
|
||||
"/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Auf7/AERe/QAAAP4AAAD/AAAAUQAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAP4A"
|
||||
"AAD9AMb//gC3+/8At/v/ALf7/wC3+/8Auf7/AMb//gC2+f4AwP/+AMT//wC4/f8At/v/ALf7/wC3+/8"
|
||||
"At/v/ALf7/wC3+/8At/v/ALf7/wC8//8Avf//ALn+/wC3+/8At/v/ALf7/wC3+/8At/v/ALb7/wBvlf"
|
||||
"4AAAD+AAAA/gAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAACgAAAP8AAAD+ABgg/QDA//8At/v/ALf7/wC2+v8AuPz+AEVe/QAAAP0AAAD+AAAA/gA"
|
||||
"EBv0AU2/9ALz//wC3+/8At/v/ALf7/wC3+/8Atvr/AL7//wBmi/0ALj/9ACQx/QBJZP0Ai739AMD//w"
|
||||
"C3+/8At/v/ALf7/wC3+/8Aksj+AAAA/gAAAP4AAACZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcAAAD/AAAA/gAtPf4AvP//ALf7/wC3+/8As/T+AAo"
|
||||
"O/QAAAP0SEhLtAAAA/gAAAP4AAAD+AAAA/gAWH/0Av///ALf7/wC3+/8At/v/ALHx/gANEv0AAAD+AA"
|
||||
"AA/gAAAP4AAAD/AAAA/wAuP/0Avv//ALf7/wC3+/8At/v/AKjm/QAAAP4AAAD+AAAApwAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATAAAA/wAAAP4AL0H"
|
||||
"+ALz//wC3+/8AvP//ADJE/QAAAPfc3Nz1TExM6wAAAP4AAAD+AAAA/gAAAP8AAAD+AGeN/gC3+/8At/"
|
||||
"v/AML//wAeKf0AAAH/AAAA/gAAAP4AAAD+cXFx3YSEhOoAAAD9AEVe/QC6//8At/v/ALf7/wCs7P8AA"
|
||||
"AD+AAAA/gAAAKEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAP8AAAD+ACAt/QC+//8At/v/ALP1/gAAAP95eXns/////2FhYeoBAQH/AAAA/gEBAf"
|
||||
"9eXl7lDQ0N9gAgLP0Av///ALf7/wCNwf4AAAD/NDQ03AAAAP8AAAD+AAAA/6Ojo+b/////eXl55AAAA"
|
||||
"P8AtPf+ALf7/wC3+/8AoNv9AAAA/gAAAP4AAACHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/AAAA/gAAAP0Ax///ALf7/wCIu/0AAAD/ysrK6/"
|
||||
"/////8/Pz6Hx8f6wAAAP8kJCTm/f39/WVlZe4ABAX8AMT//wC3/P8AV3X9ERER7/////9MTEzrAAAA6"
|
||||
"ElJSeb///////////X19e8AAAD/AHuo/QC3+/8At/v/AIW2/gAAAP4AAAD/AAAAZAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0wAAAP4AAAD+AK"
|
||||
"fl/gC3+/8AjcH+AAAA/83Nze/////////////////////w//////////9gYGDsAA8V/QDC//8Auv//A"
|
||||
"EJa/UFBQeb////////////////////////////////////vAAAA/wBvlfwAt/v/ALf7/wBdff0AAAD+"
|
||||
"AAAA/wAAAC0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAI4AAAD+AAAA/gBnjP0At/v/ALz//gAAAP+CgoLx/////////////////////////////"
|
||||
"///CAkJ8QBCXP4Auf7/ALj8/wBScf0DAwPw////////////////////////////////tra26AAAAP8A"
|
||||
"hrf+ALf7/wDA//8AHCf9AAAA/gAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3AAAA/wAAAP4AEBb9AMT//wC6//8AQVn+AAAA9+Pj4"
|
||||
"/L/////////////////////cnJy1gAAAP8AlMr9ALf7/wC3+/8AhLT+AAAA/5mZmeL/////////////"
|
||||
"/////////////R0dHesAAAD9AL///gC3+/8ApuX+AAAA/gAAAP4AAAC1AAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPMAAAD+AAAA/"
|
||||
"gCGtv0Atvr/ALn+/wARF/0AAAD3cHBw7bGxseqioqLuOjo67QAAAP8ARFz9ALz//wC3+/8At/v/AML/"
|
||||
"/wAbJf0AAAD/jo6O5v////b//////f397zw8PPEAAAD/AGCC/QC4/P8Auv//AEJb/QAAAP4AAAD/AAA"
|
||||
"AUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAABpAAAA/wAAAP4AERf9AMP//wC3+/8Auv/+AEVf/AAAAP8AAAD/AAAA/wAAAP0AWnn9"
|
||||
"AMH//wC3+/8At/v/ALf7/wC3+/8AtPf+ABsm/QAAAP8AAADwDg4O+QAAAPwAAAD+AD1T/QDB//8At/v"
|
||||
"/AKLd/gAAAP4AAAD+AAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPIAAAD+AAAA/gBfgP0Auf7/ALf7/wC5/v8A"
|
||||
"wv//AJnS/gCWzv4Awf//ALj9/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wDC//8Ahbb+AFBt/QA3TP0"
|
||||
"ATGn9AI/D/gC+//8At/v/AMD//wATGv0AAAD+AAAA/wAAAE4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAA9AAAA/wAA"
|
||||
"AP4AAAD+AJPJ/QC2+v8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC"
|
||||
"3+/8At/v/ALf7/wC4/f8Au///ALj9/wC3+/8At/v/AML//wA0SP0AAAD+AAAA/QAAAMMAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAJYAAAD/AAAA/gAAAP4Andj9ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf"
|
||||
"7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/AML//wA+Vf0AAAD+AA"
|
||||
"AA/gAAAPYAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwwAAAP4AAAD+AAAA/gCGt/0AvP/"
|
||||
"/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/8Atv"
|
||||
"r/ALr//gAoNv0AAAD+AAAA/gAAAP8AAAAeAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAC9AAAA/wAAAP4AAAD+AEFY/QC6/f4At/z/ALf7/wC3+/8At/v/ALf7/wC3+/8At/v/ALf7/wC3+/"
|
||||
"8At/v/ALf7/wC2+v8Aw///AICv/gAEBv0AAAH+AAAA/QAAAP8AAAAiAAAAAQAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIMAAAD/AAAA/gAAAP4AAAD9AFl3/QCt7/4AwP//ALj9/w"
|
||||
"C2+/8At/v/ALf7/wC3+/8At/v/ALz//wDB//4AeKP+ABki/QAAAP4AAAD+AAAA/wAAAOEAAAASAAAAA"
|
||||
"QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALAAAAOUAAAD/AA"
|
||||
"AA/gAAAP4AAAD+ABcg/QBQbv0AbJD9AH2s/gCBsP0Ac5v+AFt6/QAtPv0AAAD9AAAA/gAAAP4AAAD+A"
|
||||
"AAA/wAAAIQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAWgAAAOIAAAD/AAAA/gAAAP4AAAD+AAAA/gAAAP4AAAD+AAAA/gAAAP4AA"
|
||||
"AD+AAAA/gAAAP8AAAD/AAAAmQAAAAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAIgAAADRAAAA/wAAA"
|
||||
"P8AAAD/AAAA/wAAAP8AAAD/AAAA5QAAAJoAAABWAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAIAAAACQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///gAf///////4AAAAD/////wAAAAP////"
|
||||
"/AAAAB/////+AAAAH/////4AAAA//////gAAAD/////8AAAAH/////wAAAAP////+AAAAA/////4AAA"
|
||||
"AD/////gAAAAP////+AAAAA/////4AAAAD/////gAAAAP////+AAAAA/////4AAAAB/////gAAAAD//"
|
||||
"//+AAAAAP////wAAAAA////+AAAAAD////wAAAAAP///8AAAAAA////gAAAAAB///8AAAAAAH///gAA"
|
||||
"AAAAf//+AAAAAAA///4AAAAAAD///4AAAAAAP///wAAAAAAf///wAAAAAD////gAAAAAP////gAAAAB"
|
||||
"/////AAAAAP////+AAAAD/////wAAAAf////+AAAAB/////4AAAAD/////AAAAAP////8AAAAA/////"
|
||||
"wAAAAB////+AAAAAH////4AAAAAf////gAAAAA////+AAAAAD////4AAAAAP////gAAAAA////+AAAA"
|
||||
"AD////4AAAAAf////gAAAAB////+AAAAAH////8AAAAAf////wAAAAD/////gAAAAP////+AAAAB///"
|
||||
"//8AAAAH/////wAAAA//////gAAAH//////AAAA//////+AAAH//////+AAA///////+AAP///////+"
|
||||
"AH//////////////8=";
|
||||
|
||||
const char HTTP_COMMAND_TUNNELS[] = "tunnels";
|
||||
const char HTTP_COMMAND_TRANSIT_TUNNELS[] = "transit_tunnels";
|
||||
const char HTTP_COMMAND_TRANSPORTS[] = "transports";
|
||||
const char HTTP_COMMAND_START_ACCEPTING_TUNNELS[] = "start_accepting_tunnels";
|
||||
const char HTTP_COMMAND_STOP_ACCEPTING_TUNNELS[] = "stop_accepting_tunnels";
|
||||
const char HTTP_COMMAND_LOCAL_DESTINATIONS[] = "local_destinations";
|
||||
const char HTTP_COMMAND_LOCAL_DESTINATION[] = "local_destination";
|
||||
const char HTTP_PARAM_BASE32_ADDRESS[] = "b32";
|
||||
|
||||
namespace misc_strings
|
||||
{
|
||||
|
||||
const char name_value_separator[] = { ':', ' ' };
|
||||
const char crlf[] = { '\r', '\n' };
|
||||
|
||||
} // namespace misc_strings
|
||||
|
||||
std::vector<boost::asio::const_buffer> HTTPConnection::reply::to_buffers(int status)
|
||||
{
|
||||
std::vector<boost::asio::const_buffer> buffers;
|
||||
if (headers.size () > 0)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case 105: buffers.push_back(boost::asio::buffer("HTTP/1.0 105 Name Not Resolved\r\n")); break;
|
||||
case 200: buffers.push_back(boost::asio::buffer("HTTP/1.0 200 OK\r\n")); break;
|
||||
case 400: buffers.push_back(boost::asio::buffer("HTTP/1.0 400 Bad Request\r\n")); break;
|
||||
case 404: buffers.push_back(boost::asio::buffer("HTTP/1.0 404 Not Found\r\n")); break;
|
||||
case 408: buffers.push_back(boost::asio::buffer("HTTP/1.0 408 Request Timeout\r\n")); break;
|
||||
case 500: buffers.push_back(boost::asio::buffer("HTTP/1.0 500 Internal Server Error\r\n")); break;
|
||||
case 502: buffers.push_back(boost::asio::buffer("HTTP/1.0 502 Bad Gateway\r\n")); break;
|
||||
case 503: buffers.push_back(boost::asio::buffer("HTTP/1.0 503 Not Implemented\r\n")); break;
|
||||
case 504: buffers.push_back(boost::asio::buffer("HTTP/1.0 504 Gateway Timeout\r\n")); break;
|
||||
default:
|
||||
buffers.push_back(boost::asio::buffer("HTTP/1.0 200 OK\r\n"));
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < headers.size(); ++i)
|
||||
{
|
||||
header& h = headers[i];
|
||||
buffers.push_back(boost::asio::buffer(h.name));
|
||||
buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
|
||||
buffers.push_back(boost::asio::buffer(h.value));
|
||||
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
|
||||
}
|
||||
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
|
||||
}
|
||||
buffers.push_back(boost::asio::buffer(content));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
void HTTPConnection::Terminate ()
|
||||
{
|
||||
if (m_Stream)
|
||||
{
|
||||
m_Stream->Close ();
|
||||
i2p::stream::DeleteStream (m_Stream);
|
||||
m_Stream = nullptr;
|
||||
}
|
||||
m_Socket->close ();
|
||||
//delete this;
|
||||
}
|
||||
|
||||
void HTTPConnection::Receive ()
|
||||
{
|
||||
m_Socket->async_read_some (boost::asio::buffer (m_Buffer, HTTP_CONNECTION_BUFFER_SIZE),
|
||||
boost::bind(&HTTPConnection::HandleReceive, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void HTTPConnection::HandleReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
if (!m_Stream) // new request
|
||||
{
|
||||
m_Buffer[bytes_transferred] = 0;
|
||||
m_BufferLen = bytes_transferred;
|
||||
RunRequest();
|
||||
}
|
||||
else // follow-on
|
||||
m_Stream->Send ((uint8_t *)m_Buffer, bytes_transferred);
|
||||
Receive ();
|
||||
}
|
||||
else if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
|
||||
void HTTPConnection::RunRequest ()
|
||||
{
|
||||
auto address = ExtractAddress ();
|
||||
if (address.length () > 1 && address[1] != '?') // not just '/' or '/?'
|
||||
{
|
||||
std::string uri ("/"), b32;
|
||||
size_t pos = address.find ('/', 1);
|
||||
if (pos == std::string::npos)
|
||||
b32 = address.substr (1); // excluding leading '/' to end of line
|
||||
else
|
||||
{
|
||||
b32 = address.substr (1, pos - 1); // excluding leading '/' to next '/'
|
||||
uri = address.substr (pos); // rest of line
|
||||
}
|
||||
|
||||
HandleDestinationRequest (b32, uri);
|
||||
}
|
||||
else
|
||||
HandleRequest (address);
|
||||
}
|
||||
|
||||
std::string HTTPConnection::ExtractAddress ()
|
||||
{
|
||||
char * get = strstr (m_Buffer, "GET");
|
||||
if (get)
|
||||
{
|
||||
char * http = strstr (get, "HTTP");
|
||||
if (http)
|
||||
return std::string (get + 4, http - get - 5);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void HTTPConnection::ExtractParams (const std::string& str, std::map<std::string, std::string>& params)
|
||||
{
|
||||
if (str[0] != '&') return;
|
||||
size_t pos = 1, end;
|
||||
do
|
||||
{
|
||||
end = str.find ('&', pos);
|
||||
std::string param = str.substr (pos, end - pos);
|
||||
LogPrint (param);
|
||||
size_t e = param.find ('=');
|
||||
if (e != std::string::npos)
|
||||
params[param.substr(0, e)] = param.substr(e+1);
|
||||
pos = end + 1;
|
||||
}
|
||||
while (end != std::string::npos);
|
||||
}
|
||||
|
||||
void HTTPConnection::HandleWriteReply (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
|
||||
void HTTPConnection::HandleWrite (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode || (m_Stream && !m_Stream->IsOpen ()))
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else // data keeps coming
|
||||
AsyncStreamReceive ();
|
||||
}
|
||||
|
||||
void HTTPConnection::HandleRequest (const std::string& address)
|
||||
{
|
||||
std::stringstream s;
|
||||
// Html5 head start
|
||||
s << "<!DOCTYPE html>\n<html lang=\"en\">"; // TODO: Add support for locale.
|
||||
s << "<head><meta charset=\"utf-8\" />"; // TODO: Find something to parse html/template system. This is horrible.
|
||||
s << "<link rel='shortcut icon' href='";
|
||||
s << itoopieFavicon;
|
||||
s << "' /><title>Purple I2P " << VERSION " Webconsole</title></head>";
|
||||
// Head end
|
||||
if (address.length () > 1)
|
||||
HandleCommand (address.substr (2), s);
|
||||
else
|
||||
FillContent (s);
|
||||
s << "</html>";
|
||||
SendReply (s.str ());
|
||||
}
|
||||
|
||||
void HTTPConnection::FillContent (std::stringstream& s)
|
||||
{
|
||||
s << "<h2>Welcome to the Webconsole!</h2><br><br>";
|
||||
s << "<b>Data path:</b> " << i2p::util::filesystem::GetDataDir().string() << "<br>" << "<br>";
|
||||
s << "<b>Our external address:</b>" << "<br>";
|
||||
for (auto& address : i2p::context.GetRouterInfo().GetAddresses())
|
||||
{
|
||||
switch (address.transportStyle)
|
||||
{
|
||||
case i2p::data::RouterInfo::eTransportNTCP:
|
||||
if (address.host.is_v6 ())
|
||||
s << "NTCP6 ";
|
||||
else
|
||||
s << "NTCP ";
|
||||
break;
|
||||
case i2p::data::RouterInfo::eTransportSSU:
|
||||
if (address.host.is_v6 ())
|
||||
s << "SSU6 ";
|
||||
else
|
||||
s << "SSU ";
|
||||
break;
|
||||
default:
|
||||
s << "Unknown ";
|
||||
}
|
||||
s << address.host.to_string() << ":" << address.port << "<br>";
|
||||
}
|
||||
s << "<br><b>Routers:</b> <i>" << i2p::data::netdb.GetNumRouters () << "</i> ";
|
||||
s << "<b>Floodfills:</b> <i>" << i2p::data::netdb.GetNumFloodfills () << "</i> ";
|
||||
s << "<b>LeaseSets:</b> <i>" << i2p::data::netdb.GetNumLeaseSets () << "</i><br>";
|
||||
|
||||
s << "<br><b><a href=/?" << HTTP_COMMAND_LOCAL_DESTINATIONS << ">Local destinations</a></b>";
|
||||
s << "<br><b><a href=/?" << HTTP_COMMAND_TUNNELS << ">Tunnels</a></b>";
|
||||
s << "<br><b><a href=/?" << HTTP_COMMAND_TRANSIT_TUNNELS << ">Transit tunnels</a></b>";
|
||||
s << "<br><b><a href=/?" << HTTP_COMMAND_TRANSPORTS << ">Transports</a></b><br>";
|
||||
|
||||
if (i2p::context.AcceptsTunnels ())
|
||||
s << "<br><b><a href=/?" << HTTP_COMMAND_STOP_ACCEPTING_TUNNELS << ">Stop accepting tunnels</a></b><br>";
|
||||
else
|
||||
s << "<br><b><a href=/?" << HTTP_COMMAND_START_ACCEPTING_TUNNELS << ">Start accepting tunnels</a></b><br>";
|
||||
|
||||
s << "<p><a href=\"zmw2cyw2vj7f6obx3msmdvdepdhnw2ctc4okza2zjxlukkdfckhq.b32.i2p\">Flibusta</a></p>";
|
||||
}
|
||||
|
||||
void HTTPConnection::HandleCommand (const std::string& command, std::stringstream& s)
|
||||
{
|
||||
size_t paramsPos = command.find('&');
|
||||
std::string cmd = command.substr (0, paramsPos);
|
||||
if (cmd == HTTP_COMMAND_TRANSPORTS)
|
||||
ShowTransports (s);
|
||||
else if (cmd == HTTP_COMMAND_TUNNELS)
|
||||
ShowTunnels (s);
|
||||
else if (cmd == HTTP_COMMAND_TRANSIT_TUNNELS)
|
||||
ShowTransitTunnels (s);
|
||||
else if (cmd == HTTP_COMMAND_START_ACCEPTING_TUNNELS)
|
||||
StartAcceptingTunnels (s);
|
||||
else if (cmd == HTTP_COMMAND_STOP_ACCEPTING_TUNNELS)
|
||||
StopAcceptingTunnels (s);
|
||||
else if (cmd == HTTP_COMMAND_LOCAL_DESTINATIONS)
|
||||
ShowLocalDestinations (s);
|
||||
else if (cmd == HTTP_COMMAND_LOCAL_DESTINATION)
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (command.substr (paramsPos), params);
|
||||
auto b32 = params[HTTP_PARAM_BASE32_ADDRESS];
|
||||
ShowLocalDestination (b32, s);
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::ShowTransports (std::stringstream& s)
|
||||
{
|
||||
s << "NTCP<br>";
|
||||
for (auto it: i2p::transport::transports.GetNTCPSessions ())
|
||||
{
|
||||
if (it.second && it.second->IsEstablished ())
|
||||
{
|
||||
// incoming connection doesn't have remote RI
|
||||
bool outgoing = it.second->GetRemoteRouter ();
|
||||
if (outgoing) s << "-->";
|
||||
s << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase64 ().substr (0, 4) << ": "
|
||||
<< it.second->GetSocket ().remote_endpoint().address ().to_string ();
|
||||
if (!outgoing) s << "-->";
|
||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
s << "<br>";
|
||||
}
|
||||
s << std::endl;
|
||||
}
|
||||
auto ssuServer = i2p::transport::transports.GetSSUServer ();
|
||||
if (ssuServer)
|
||||
{
|
||||
s << "<br>SSU<br>";
|
||||
for (auto it: ssuServer->GetSessions ())
|
||||
{
|
||||
// incoming connections don't have remote router
|
||||
bool outgoing = it.second->GetRemoteRouter ();
|
||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
||||
if (outgoing) s << "-->";
|
||||
s << endpoint.address ().to_string () << ":" << endpoint.port ();
|
||||
if (!outgoing) s << "-->";
|
||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
s << "<br>";
|
||||
s << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::ShowTunnels (std::stringstream& s)
|
||||
{
|
||||
for (auto it: i2p::tunnel::tunnels.GetOutboundTunnels ())
|
||||
{
|
||||
it->GetTunnelConfig ()->Print (s);
|
||||
if (it->GetTunnelPool () && !it->GetTunnelPool ()->IsExploratory ())
|
||||
s << " " << "Pool";
|
||||
auto state = it->GetState ();
|
||||
if (state == i2p::tunnel::eTunnelStateFailed)
|
||||
s << " " << "Failed";
|
||||
else if (state == i2p::tunnel::eTunnelStateExpiring)
|
||||
s << " " << "Exp";
|
||||
s << " " << (int)it->GetNumSentBytes () << "<br>";
|
||||
s << std::endl;
|
||||
}
|
||||
|
||||
for (auto it: i2p::tunnel::tunnels.GetInboundTunnels ())
|
||||
{
|
||||
it.second->GetTunnelConfig ()->Print (s);
|
||||
if (it.second->GetTunnelPool () && !it.second->GetTunnelPool ()->IsExploratory ())
|
||||
s << " " << "Pool";
|
||||
auto state = it.second->GetState ();
|
||||
if (state == i2p::tunnel::eTunnelStateFailed)
|
||||
s << " " << "Failed";
|
||||
else if (state == i2p::tunnel::eTunnelStateExpiring)
|
||||
s << " " << "Exp";
|
||||
s << " " << (int)it.second->GetNumReceivedBytes () << "<br>";
|
||||
s << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::ShowTransitTunnels (std::stringstream& s)
|
||||
{
|
||||
for (auto it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
||||
{
|
||||
if (dynamic_cast<i2p::tunnel::TransitTunnelGateway *>(it.second))
|
||||
s << it.second->GetTunnelID () << "-->";
|
||||
else if (dynamic_cast<i2p::tunnel::TransitTunnelEndpoint *>(it.second))
|
||||
s << "-->" << it.second->GetTunnelID ();
|
||||
else
|
||||
s << "-->" << it.second->GetTunnelID () << "-->";
|
||||
s << " " << it.second->GetNumTransmittedBytes () << "<br>";
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::ShowLocalDestinations (std::stringstream& s)
|
||||
{
|
||||
for (auto& it: i2p::client::context.GetDestinations ())
|
||||
{
|
||||
std::string b32 = it.first.ToBase32 ();
|
||||
s << "<a href=/?" << HTTP_COMMAND_LOCAL_DESTINATION;
|
||||
s << "&" << HTTP_PARAM_BASE32_ADDRESS << "=" << b32 << ">";
|
||||
s << b32 << ".b32.i2p</a><br>" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::ShowLocalDestination (const std::string& b32, std::stringstream& s)
|
||||
{
|
||||
i2p::data::IdentHash ident;
|
||||
i2p::data::Base32ToByteStream (b32.c_str (), b32.length (), ident, 32);
|
||||
auto dest = i2p::client::context.FindLocalDestination (ident);
|
||||
if (dest)
|
||||
{
|
||||
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>";
|
||||
auto pool = dest->GetTunnelPool ();
|
||||
if (pool)
|
||||
{
|
||||
s << "<b>Tunnels:</b><br>";
|
||||
for (auto it: pool->GetOutboundTunnels ())
|
||||
{
|
||||
it->GetTunnelConfig ()->Print (s);
|
||||
s << "<br>" << std::endl;
|
||||
}
|
||||
for (auto it: pool->GetInboundTunnels ())
|
||||
{
|
||||
it->GetTunnelConfig ()->Print (s);
|
||||
s << "<br>" << std::endl;
|
||||
}
|
||||
}
|
||||
s << "<br><b>Streams:</b><br>";
|
||||
for (auto it: dest->GetStreamingDestination ()->GetStreams ())
|
||||
{
|
||||
s << it.first << "->" << it.second->GetRemoteIdentity ().GetIdentHash ().ToBase32 () << ".b32.i2p ";
|
||||
s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
|
||||
s << "<br>"<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::StartAcceptingTunnels (std::stringstream& s)
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (true);
|
||||
s << "Accepting tunnels started" << std::endl;
|
||||
}
|
||||
|
||||
void HTTPConnection::StopAcceptingTunnels (std::stringstream& s)
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
s << "Accepting tunnels stopped" << std::endl;
|
||||
}
|
||||
|
||||
void HTTPConnection::HandleDestinationRequest (const std::string& address, const std::string& uri)
|
||||
{
|
||||
std::string request = "GET " + uri + " HTTP/1.1\r\nHost:" + address + "\r\n";
|
||||
LogPrint("HTTP Client Request: ", request);
|
||||
SendToAddress (address, 80, request.c_str (), request.size ());
|
||||
}
|
||||
|
||||
void HTTPConnection::SendToAddress (const std::string& address, int port, const char * buf, size_t len)
|
||||
{
|
||||
i2p::data::IdentHash destination;
|
||||
if (!i2p::client::context.GetAddressBook ().GetIdentHash (address, destination))
|
||||
{
|
||||
LogPrint ("Unknown address ", address);
|
||||
SendReply ("<html>" + itoopieImage + "<br>Unknown address " + address + "</html>", 404);
|
||||
return;
|
||||
}
|
||||
|
||||
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination);
|
||||
if (leaseSet && leaseSet->HasNonExpiredLeases ())
|
||||
SendToDestination (leaseSet, port, buf, len);
|
||||
else
|
||||
{
|
||||
i2p::data::netdb.RequestDestination (destination, true, i2p::client::context.GetSharedLocalDestination ()->GetTunnelPool ());
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(HTTP_DESTINATION_REQUEST_TIMEOUT));
|
||||
m_Timer.async_wait (boost::bind (&HTTPConnection::HandleDestinationRequestTimeout,
|
||||
this, boost::asio::placeholders::error, destination, port, buf, len));
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
|
||||
i2p::data::IdentHash destination, int port, const char * buf, size_t len)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (destination);
|
||||
if (leaseSet && leaseSet->HasNonExpiredLeases ())
|
||||
SendToDestination (leaseSet, port, buf, len);
|
||||
else
|
||||
// still no LeaseSet
|
||||
SendReply (leaseSet ? "<html>" + itoopieImage + "<br>Leases expired</html>" : "<html>" + itoopieImage + "LeaseSet not found</html>", 504);
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::SendToDestination (const i2p::data::LeaseSet * remote, int port, const char * buf, size_t len)
|
||||
{
|
||||
if (!m_Stream)
|
||||
m_Stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (*remote, port);
|
||||
if (m_Stream)
|
||||
{
|
||||
m_Stream->Send ((uint8_t *)buf, len);
|
||||
AsyncStreamReceive ();
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::AsyncStreamReceive ()
|
||||
{
|
||||
if (m_Stream)
|
||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, 8192),
|
||||
boost::bind (&HTTPConnection::HandleStreamReceive, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
|
||||
45); // 45 seconds timeout
|
||||
}
|
||||
|
||||
void HTTPConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
|
||||
boost::bind (&HTTPConnection::HandleWrite, this, boost::asio::placeholders::error));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ecode == boost::asio::error::timed_out)
|
||||
SendReply ("<html>" + itoopieImage + "<br>Not responding</html>", 504);
|
||||
else if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPConnection::SendReply (const std::string& content, int status)
|
||||
{
|
||||
m_Reply.content = content;
|
||||
m_Reply.headers.resize(2);
|
||||
m_Reply.headers[0].name = "Content-Length";
|
||||
m_Reply.headers[0].value = boost::lexical_cast<std::string>(m_Reply.content.size());
|
||||
m_Reply.headers[1].name = "Content-Type";
|
||||
m_Reply.headers[1].value = "text/html";
|
||||
|
||||
boost::asio::async_write (*m_Socket, m_Reply.to_buffers(status),
|
||||
boost::bind (&HTTPConnection::HandleWriteReply, this,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
HTTPServer::HTTPServer (int port):
|
||||
m_Thread (nullptr), m_Work (m_Service),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
|
||||
m_NewSocket (nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
HTTPServer::~HTTPServer ()
|
||||
{
|
||||
Stop ();
|
||||
}
|
||||
|
||||
void HTTPServer::Start ()
|
||||
{
|
||||
m_Thread = new std::thread (std::bind (&HTTPServer::Run, this));
|
||||
m_Acceptor.listen ();
|
||||
Accept ();
|
||||
}
|
||||
|
||||
void HTTPServer::Stop ()
|
||||
{
|
||||
m_Acceptor.close();
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::Run ()
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
|
||||
void HTTPServer::Accept ()
|
||||
{
|
||||
m_NewSocket = new boost::asio::ip::tcp::socket (m_Service);
|
||||
m_Acceptor.async_accept (*m_NewSocket, boost::bind (&HTTPServer::HandleAccept, this,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void HTTPServer::HandleAccept(const boost::system::error_code& ecode)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
CreateConnection(m_NewSocket); // new HTTPConnection(m_NewSocket);
|
||||
Accept ();
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::CreateConnection(boost::asio::ip::tcp::socket * m_NewSocket)
|
||||
{
|
||||
new HTTPConnection (m_NewSocket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
135
HTTPServer.h
135
HTTPServer.h
@@ -1,135 +0,0 @@
|
||||
#ifndef HTTP_SERVER_H__
|
||||
#define HTTP_SERVER_H__
|
||||
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#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;
|
||||
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
|
||||
|
||||
|
||||
589
I2NPProtocol.cpp
589
I2NPProtocol.cpp
@@ -1,589 +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->GetGarlicDestination ().AddSessionKey (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 && msg->from->GetTunnelPool ())
|
||||
msg->from->GetTunnelPool ()->GetGarlicDestination ().ProcessGarlicMessage (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
208
I2NPProtocol.h
208
I2NPProtocol.h
@@ -1,208 +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; };
|
||||
|
||||
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__
|
||||
|
||||
284
I2PTunnel.cpp
284
I2PTunnel.cpp
@@ -1,284 +0,0 @@
|
||||
#include <boost/bind.hpp>
|
||||
#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_Stream = m_Owner->GetLocalDestination ()->CreateStream (*leaseSet);
|
||||
m_Stream->Send (m_Buffer, 0); // connect
|
||||
StreamReceive ();
|
||||
Receive ();
|
||||
}
|
||||
|
||||
I2PTunnelConnection::I2PTunnelConnection (I2PTunnel * owner, i2p::stream::Stream * stream,
|
||||
boost::asio::ip::tcp::socket * socket, const boost::asio::ip::tcp::endpoint& target):
|
||||
m_Socket (socket), m_Stream (stream), m_Owner (owner)
|
||||
{
|
||||
if (m_Socket)
|
||||
m_Socket->async_connect (target, boost::bind (&I2PTunnelConnection::HandleConnect,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
I2PTunnelConnection::~I2PTunnelConnection ()
|
||||
{
|
||||
delete m_Socket;
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Terminate ()
|
||||
{
|
||||
if (m_Stream)
|
||||
{
|
||||
m_Stream->Close ();
|
||||
i2p::stream::DeleteStream (m_Stream);
|
||||
m_Stream = nullptr;
|
||||
}
|
||||
m_Socket->close ();
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveConnection (this);
|
||||
//delete this;
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Receive ()
|
||||
{
|
||||
m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
||||
boost::bind(&I2PTunnelConnection::HandleReceived, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
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),
|
||||
boost::bind (&I2PTunnelConnection::HandleStreamReceive, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
|
||||
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),
|
||||
boost::bind (&I2PTunnelConnection::HandleWrite, this, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("I2PTunnel connect error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
if (m_Stream) m_Stream->Close ();
|
||||
i2p::stream::DeleteStream (m_Stream);
|
||||
m_Stream = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("I2PTunnel connected");
|
||||
StreamReceive ();
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnel::AddConnection (I2PTunnelConnection * conn)
|
||||
{
|
||||
m_Connections.insert (conn);
|
||||
}
|
||||
|
||||
void I2PTunnel::RemoveConnection (I2PTunnelConnection * conn)
|
||||
{
|
||||
m_Connections.erase (conn);
|
||||
}
|
||||
|
||||
void I2PTunnel::ClearConnections ()
|
||||
{
|
||||
for (auto it: m_Connections)
|
||||
delete it;
|
||||
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, boost::bind (&I2PClientTunnel::HandleAccept, this,
|
||||
boost::asio::placeholders::error, 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 (boost::bind (&I2PClientTunnel::HandleDestinationRequestTimer,
|
||||
this, boost::asio::placeholders::error, 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 = new I2PTunnelConnection (this, socket, m_RemoteLeaseSet);
|
||||
AddConnection (connection);
|
||||
}
|
||||
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 (i2p::stream::Stream * stream)
|
||||
{
|
||||
if (stream)
|
||||
new I2PTunnelConnection (this, stream, new boost::asio::ip::tcp::socket (GetService ()), m_Endpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
123
I2PTunnel.h
123
I2PTunnel.h
@@ -1,123 +0,0 @@
|
||||
#ifndef I2PTUNNEL_H__
|
||||
#define I2PTUNNEL_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#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:
|
||||
|
||||
I2PTunnelConnection (I2PTunnel * owner, boost::asio::ip::tcp::socket * socket,
|
||||
const i2p::data::LeaseSet * leaseSet);
|
||||
I2PTunnelConnection (I2PTunnel * owner, i2p::stream::Stream * stream, boost::asio::ip::tcp::socket * socket,
|
||||
const boost::asio::ip::tcp::endpoint& target);
|
||||
~I2PTunnelConnection ();
|
||||
|
||||
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;
|
||||
i2p::stream::Stream * m_Stream;
|
||||
I2PTunnel * m_Owner;
|
||||
};
|
||||
|
||||
class I2PTunnel
|
||||
{
|
||||
public:
|
||||
|
||||
I2PTunnel (boost::asio::io_service& service, ClientDestination * localDestination):
|
||||
m_Service (service), m_LocalDestination (localDestination) {};
|
||||
virtual ~I2PTunnel () { ClearConnections (); };
|
||||
|
||||
void AddConnection (I2PTunnelConnection * conn);
|
||||
void RemoveConnection (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<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 (i2p::stream::Stream * stream);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::ip::tcp::endpoint m_Endpoint;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
327
Identity.cpp
327
Identity.cpp
@@ -1,327 +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_ECDSA_SHA256_P256)
|
||||
{
|
||||
memcpy (m_StandardIdentity.signingKey + 64, signingKey, 64);
|
||||
m_StandardIdentity.certificate.type = CERTIFICATE_TYPE_KEY;
|
||||
m_ExtendedLen = 4; // 4 bytes extra
|
||||
m_StandardIdentity.certificate.length = htobe16 (4);
|
||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
||||
*(uint16_t *)m_ExtendedBuffer = htobe16 (SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
||||
*(uint16_t *)(m_ExtendedBuffer + 2) = htobe16 (CRYPTO_KEY_TYPE_ELGAMAL);
|
||||
uint8_t buf[DEFAULT_IDENTITY_SIZE + 4];
|
||||
ToBuffer (buf, DEFAULT_IDENTITY_SIZE + 4);
|
||||
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
|
||||
}
|
||||
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)
|
||||
{
|
||||
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
|
||||
|
||||
delete[] m_ExtendedBuffer;
|
||||
if (m_StandardIdentity.certificate.length)
|
||||
{
|
||||
m_ExtendedLen = be16toh (m_StandardIdentity.certificate.length);
|
||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
||||
memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
|
||||
}
|
||||
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[512];
|
||||
auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 512);
|
||||
return FromBuffer (buf, len);
|
||||
}
|
||||
|
||||
size_t IdentityEx::GetSigningPublicKeyLen () const
|
||||
{
|
||||
if (!m_Verifier) CreateVerifier ();
|
||||
if (m_Verifier)
|
||||
return m_Verifier->GetPublicKeyLen ();
|
||||
return 128;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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:
|
||||
m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + 64);
|
||||
break;
|
||||
default:
|
||||
LogPrint ("Signing key type ", (int)keyType, " is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
|
||||
{
|
||||
m_Public = Identity (keys);
|
||||
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
|
||||
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, 20); // 20 - DSA
|
||||
delete m_Signer;
|
||||
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, 128); // 128
|
||||
delete m_Signer;
|
||||
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.GetSignatureLen ()/2; // 20 for DSA
|
||||
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
delete m_Signer;
|
||||
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.GetSignatureLen ()/2; // 20 for DSA
|
||||
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
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 ()
|
||||
{
|
||||
if (m_Public.GetSigningKeyType () == SIGNING_KEY_TYPE_ECDSA_SHA256_P256)
|
||||
m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey);
|
||||
else
|
||||
m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey);
|
||||
}
|
||||
|
||||
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type)
|
||||
{
|
||||
if (type == SIGNING_KEY_TYPE_ECDSA_SHA256_P256)
|
||||
{
|
||||
PrivateKeys keys;
|
||||
auto& rnd = i2p::context.GetRandomNumberGenerator ();
|
||||
// encryption
|
||||
uint8_t publicKey[256];
|
||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
||||
dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey);
|
||||
// signature
|
||||
uint8_t signingPublicKey[64];
|
||||
i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
||||
keys.m_Public = IdentityEx (publicKey, signingPublicKey, SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
243
Identity.h
243
Identity.h
@@ -1,243 +0,0 @@
|
||||
#ifndef IDENTITY_H__
|
||||
#define IDENTITY_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include "base64.h"
|
||||
#include "ElGamal.h"
|
||||
#include "Signature.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
template<int sz>
|
||||
class Tag
|
||||
{
|
||||
public:
|
||||
|
||||
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); };
|
||||
Tag (const Tag<sz>& ) = default;
|
||||
#ifndef _WIN32 // FIXME!!! msvs 2013 can't compile it
|
||||
Tag (Tag<sz>&& ) = default;
|
||||
#endif
|
||||
Tag () = default;
|
||||
|
||||
Tag<sz>& operator= (const Tag<sz>& ) = default;
|
||||
#ifndef _WIN32
|
||||
Tag<sz>& operator= (Tag<sz>&& ) = default;
|
||||
#endif
|
||||
|
||||
uint8_t * operator()() { return m_Buf; };
|
||||
const uint8_t * operator()() const { return m_Buf; };
|
||||
|
||||
operator uint8_t * () { return m_Buf; };
|
||||
operator const uint8_t * () const { return m_Buf; };
|
||||
|
||||
const uint64_t * GetLL () const { return ll; };
|
||||
|
||||
bool operator== (const Tag<sz>& other) const { return !memcmp (m_Buf, other.m_Buf, sz); };
|
||||
bool operator< (const Tag<sz>& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; };
|
||||
|
||||
std::string ToBase64 () const
|
||||
{
|
||||
char str[sz*2];
|
||||
int l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
|
||||
str[l] = 0;
|
||||
return std::string (str);
|
||||
}
|
||||
|
||||
std::string ToBase32 () const
|
||||
{
|
||||
char str[sz*2];
|
||||
int l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
|
||||
str[l] = 0;
|
||||
return std::string (str);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
union // 8 bytes alignment
|
||||
{
|
||||
uint8_t m_Buf[sz];
|
||||
uint64_t ll[sz/8];
|
||||
};
|
||||
};
|
||||
typedef Tag<32> IdentHash;
|
||||
|
||||
#pragma pack(1)
|
||||
struct Keys
|
||||
{
|
||||
uint8_t privateKey[256];
|
||||
uint8_t signingPrivateKey[20];
|
||||
uint8_t publicKey[256];
|
||||
uint8_t signingKey[128];
|
||||
};
|
||||
|
||||
const uint8_t CERTIFICATE_TYPE_NULL = 0;
|
||||
const uint8_t CERTIFICATE_TYPE_HASHCASH = 1;
|
||||
const uint8_t CERTIFICATE_TYPE_HIDDEN = 2;
|
||||
const uint8_t CERTIFICATE_TYPE_SIGNED = 3;
|
||||
const uint8_t CERTIFICATE_TYPE_MULTIPLE = 4;
|
||||
const uint8_t CERTIFICATE_TYPE_KEY = 5;
|
||||
|
||||
struct Identity
|
||||
{
|
||||
uint8_t publicKey[256];
|
||||
uint8_t signingKey[128];
|
||||
struct
|
||||
{
|
||||
uint8_t type;
|
||||
uint16_t length;
|
||||
} certificate;
|
||||
|
||||
Identity () = default;
|
||||
Identity (const Keys& keys) { *this = keys; };
|
||||
Identity& operator=(const Keys& keys);
|
||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
IdentHash Hash () const;
|
||||
};
|
||||
#pragma pack()
|
||||
Keys CreateRandomKeys ();
|
||||
|
||||
const size_t DEFAULT_IDENTITY_SIZE = sizeof (Identity); // 387 bytes
|
||||
|
||||
const uint16_t CRYPTO_KEY_TYPE_ELGAMAL = 0;
|
||||
const uint16_t SIGNING_KEY_TYPE_DSA_SHA1 = 0;
|
||||
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA256_P256 = 1;
|
||||
typedef uint16_t SigningKeyType;
|
||||
|
||||
class IdentityEx
|
||||
{
|
||||
public:
|
||||
|
||||
IdentityEx ();
|
||||
IdentityEx (const uint8_t * publicKey, const uint8_t * signingKey,
|
||||
SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
|
||||
IdentityEx (const uint8_t * buf, size_t len);
|
||||
IdentityEx (const IdentityEx& other);
|
||||
~IdentityEx ();
|
||||
IdentityEx& operator=(const IdentityEx& other);
|
||||
IdentityEx& operator=(const Identity& standard);
|
||||
|
||||
size_t FromBase64(const std::string& s);
|
||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
||||
const Identity& GetStandardIdentity () const { return m_StandardIdentity; };
|
||||
const IdentHash& GetIdentHash () const { return m_IdentHash; };
|
||||
size_t GetFullLen () const { return m_ExtendedLen + DEFAULT_IDENTITY_SIZE; };
|
||||
size_t GetSigningPublicKeyLen () const;
|
||||
size_t GetSignatureLen () const;
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
|
||||
SigningKeyType GetSigningKeyType () const;
|
||||
|
||||
private:
|
||||
|
||||
void CreateVerifier () const;
|
||||
|
||||
private:
|
||||
|
||||
Identity m_StandardIdentity;
|
||||
IdentHash m_IdentHash;
|
||||
mutable i2p::crypto::Verifier * m_Verifier;
|
||||
size_t m_ExtendedLen;
|
||||
uint8_t * m_ExtendedBuffer;
|
||||
};
|
||||
|
||||
class PrivateKeys // for eepsites
|
||||
{
|
||||
public:
|
||||
|
||||
PrivateKeys (): m_Signer (nullptr) {};
|
||||
PrivateKeys (const PrivateKeys& other): m_Signer (nullptr) { *this = other; };
|
||||
PrivateKeys (const Keys& keys): m_Signer (nullptr) { *this = keys; };
|
||||
PrivateKeys& operator=(const Keys& keys);
|
||||
PrivateKeys& operator=(const PrivateKeys& other);
|
||||
~PrivateKeys () { delete m_Signer; };
|
||||
|
||||
const IdentityEx& GetPublic () const { return m_Public; };
|
||||
const uint8_t * GetPrivateKey () const { return m_PrivateKey; };
|
||||
const uint8_t * GetSigningPrivateKey () const { return m_SigningPrivateKey; };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
|
||||
|
||||
size_t GetFullLen () const { return m_Public.GetFullLen () + 256 + m_Public.GetSignatureLen ()/2; };
|
||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
||||
|
||||
static PrivateKeys CreateRandomKeys (SigningKeyType type = SIGNING_KEY_TYPE_DSA_SHA1);
|
||||
|
||||
private:
|
||||
|
||||
void CreateSigner ();
|
||||
|
||||
private:
|
||||
|
||||
IdentityEx m_Public;
|
||||
uint8_t m_PrivateKey[256];
|
||||
uint8_t m_SigningPrivateKey[128]; // assume private key doesn't exceed 128 bytes
|
||||
i2p::crypto::Signer * m_Signer;
|
||||
};
|
||||
|
||||
// kademlia
|
||||
struct XORMetric
|
||||
{
|
||||
union
|
||||
{
|
||||
uint8_t metric[32];
|
||||
uint64_t metric_ll[4];
|
||||
};
|
||||
|
||||
void SetMin () { memset (metric, 0, 32); };
|
||||
void SetMax () { memset (metric, 0xFF, 32); };
|
||||
bool operator< (const XORMetric& other) const { return memcmp (metric, other.metric, 32) < 0; };
|
||||
};
|
||||
|
||||
IdentHash CreateRoutingKey (const IdentHash& ident);
|
||||
XORMetric operator^(const IdentHash& key1, const IdentHash& key2);
|
||||
|
||||
// destination for delivery instuctions
|
||||
class RoutingDestination
|
||||
{
|
||||
public:
|
||||
|
||||
RoutingDestination (): m_ElGamalEncryption (nullptr) {};
|
||||
virtual ~RoutingDestination () { delete m_ElGamalEncryption; };
|
||||
|
||||
virtual const IdentHash& GetIdentHash () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
|
||||
virtual bool IsDestination () const = 0; // for garlic
|
||||
|
||||
i2p::crypto::ElGamalEncryption * GetElGamalEncryption () const
|
||||
{
|
||||
if (!m_ElGamalEncryption)
|
||||
m_ElGamalEncryption = new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ());
|
||||
return m_ElGamalEncryption;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
mutable i2p::crypto::ElGamalEncryption * m_ElGamalEncryption; // use lazy initialization
|
||||
};
|
||||
|
||||
class LocalDestination
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~LocalDestination() {};
|
||||
virtual const PrivateKeys& GetPrivateKeys () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPrivateKey () const = 0;
|
||||
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
|
||||
|
||||
const IdentityEx& GetIdentity () const { return GetPrivateKeys ().GetPublic (); };
|
||||
const IdentHash& GetIdentHash () const { return GetIdentity ().GetIdentHash (); };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
GetPrivateKeys ().Sign (buf, len, signature);
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
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.
|
||||
|
||||
122
LeaseSet.cpp
122
LeaseSet.cpp
@@ -1,122 +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 ();
|
||||
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 = 2048;
|
||||
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
|
||||
38
Log.cpp
38
Log.cpp
@@ -1,38 +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_LogFile)
|
||||
m_LogFile->flush();
|
||||
}
|
||||
|
||||
void Log::SetLogFile (const std::string& fullFilePath)
|
||||
{
|
||||
if (m_LogFile) delete m_LogFile;
|
||||
m_LogFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
|
||||
if (m_LogFile->is_open ())
|
||||
LogPrint("Logging to file ", fullFilePath, " enabled.");
|
||||
else
|
||||
{
|
||||
delete m_LogFile;
|
||||
m_LogFile = nullptr;
|
||||
}
|
||||
}
|
||||
106
Log.h
106
Log.h
@@ -1,106 +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_LogFile (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); };
|
||||
~Log () { delete m_LogFile; };
|
||||
|
||||
void SetLogFile (const std::string& fullFilePath);
|
||||
std::ofstream * GetLogFile () const { return m_LogFile; };
|
||||
|
||||
private:
|
||||
|
||||
void Flush ();
|
||||
|
||||
private:
|
||||
|
||||
std::ofstream * m_LogFile;
|
||||
};
|
||||
|
||||
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 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->GetLogFile ()) ? new LogMsg (*g_Log->GetLogFile (), 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
|
||||
132
Makefile
132
Makefile
@@ -1,29 +1,127 @@
|
||||
UNAME := $(shell uname -s)
|
||||
SYS := $(shell $(CXX) -dumpmachine)
|
||||
SHLIB := libi2pd.so
|
||||
ARLIB := libi2pd.a
|
||||
SHLIB_CLIENT := libi2pdclient.so
|
||||
ARLIB_CLIENT := libi2pdclient.a
|
||||
I2PD := i2pd
|
||||
GREP := grep
|
||||
DEPS := obj/make.dep
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
include Makefile.osx
|
||||
else ifeq ($(UNAME), FreeBSD)
|
||||
include Makefile.bsd
|
||||
else
|
||||
include Makefile.linux
|
||||
LIB_SRC_DIR := libi2pd
|
||||
LIB_CLIENT_SRC_DIR := libi2pd_client
|
||||
DAEMON_SRC_DIR := daemon
|
||||
|
||||
include filelist.mk
|
||||
|
||||
USE_AESNI := yes
|
||||
USE_AVX := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
|
||||
ifeq ($(WEBSOCKETS),1)
|
||||
NEEDED_CXXFLAGS += -DWITH_EVENTS
|
||||
endif
|
||||
|
||||
all: obj i2p
|
||||
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 linux, $(SYS))$(findstring gnu, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.linux
|
||||
else ifneq (, $(findstring freebsd, $(SYS))$(findstring openbsd, $(SYS)))
|
||||
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||
include Makefile.bsd
|
||||
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
|
||||
|
||||
i2p: $(OBJECTS:obj/%=obj/%)
|
||||
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS) $(LIBS)
|
||||
ifeq ($(USE_MESHNET),yes)
|
||||
NEEDED_CXXFLAGS += -DMESHNET
|
||||
endif
|
||||
|
||||
.SUFFIXES:
|
||||
.SUFFIXES: .c .cc .C .cpp .o
|
||||
NEEDED_CXXFLAGS += -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR)
|
||||
|
||||
obj/%.o : %.cpp
|
||||
$(CXX) -o $@ $< -c $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS)
|
||||
all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD)
|
||||
|
||||
obj:
|
||||
mkdir -p obj
|
||||
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))
|
||||
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 i2p
|
||||
$(RM) -r obj
|
||||
$(RM) -r docs/generated
|
||||
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||
|
||||
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
|
||||
|
||||
16
Makefile.bsd
16
Makefile.bsd
@@ -1,8 +1,12 @@
|
||||
CXX = g++
|
||||
CXXFLAGS = -O2
|
||||
NEEDED_CXXFLAGS = -std=c++11
|
||||
include filelist.mk
|
||||
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
|
||||
LIBS =
|
||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
|
||||
48
Makefile.homebrew
Normal file
48
Makefile.homebrew
Normal file
@@ -0,0 +1,48 @@
|
||||
# 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
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
CXXFLAGS += -maes -DAESNI
|
||||
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}/
|
||||
@@ -1,43 +1,77 @@
|
||||
CXXFLAGS = -g -Wall
|
||||
CXXVER := $(shell $(CXX) -dumpversion)
|
||||
# set defaults instead redefine
|
||||
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
|
||||
INCFLAGS ?=
|
||||
|
||||
FGREP = fgrep
|
||||
IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(FGREP) -c "64")
|
||||
USE_AESNI := yes
|
||||
ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # >= 4.10
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
## 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 $(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
|
||||
NEEDED_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
|
||||
NEEDED_CXXFLAGS += -std=c++0x
|
||||
else ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||
NEEDED_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-8]"),1) # gcc 7 ubuntu or gcc 8 arch
|
||||
NEEDED_CXXFLAGS += -std=c++11
|
||||
else # not supported
|
||||
$(error Compiler too old)
|
||||
endif
|
||||
|
||||
LIBDIR := /usr/lib
|
||||
NEEDED_CXXFLAGS += -fPIC
|
||||
|
||||
include filelist.mk
|
||||
INCFLAGS =
|
||||
ifeq ($(STATIC),yes)
|
||||
LDLIBS += $(LIBDIR)/libcryptopp.a $(LIBDIR)/libboost_system.a
|
||||
LDLIBS += $(LIBDIR)/libboost_date_time.a $(LIBDIR)/libboost_filesystem.a
|
||||
LDLIBS += $(LIBDIR)/libboost_regex.a $(LIBDIR)/libboost_program_options.a
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
|
||||
USE_AESNI := no
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
# 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
|
||||
LIBS =
|
||||
|
||||
# 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
|
||||
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
ifeq ($(IS_64),1)
|
||||
#check if AES-NI is supported by CPU
|
||||
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
|
||||
CPU_FLAGS = -maes -DAESNI
|
||||
endif
|
||||
ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0)
|
||||
machine := $(shell uname -m)
|
||||
ifeq ($(machine), aarch64)
|
||||
CXXFLAGS += -DARM64AES
|
||||
else
|
||||
CPU_FLAGS += -maes -DAESNI
|
||||
endif
|
||||
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 $@
|
||||
47
Makefile.osx
47
Makefile.osx
@@ -1,18 +1,37 @@
|
||||
CXX = clang++
|
||||
CXXFLAGS = -g -Wall -std=c++11 -lstdc++ -I/usr/local/include
|
||||
include filelist.mk
|
||||
INCFLAGS = -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
|
||||
LIBS =
|
||||
|
||||
# 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
|
||||
CXXFLAGS += -maes -DAESNI
|
||||
|
||||
# Apple Mac OSX
|
||||
UNAME_S := $(shell uname -s)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
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
|
||||
|
||||
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}/
|
||||
|
||||
640
NTCPSession.cpp
640
NTCPSession.cpp
@@ -1,640 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "I2PEndian.h"
|
||||
#include <boost/bind.hpp>
|
||||
#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, 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 ("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 ("First 32 bytes of shared key is all zeros. Ignored");
|
||||
return;
|
||||
}
|
||||
}
|
||||
memcpy (aesKey, nonZero, 32);
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::Terminate ()
|
||||
{
|
||||
m_IsEstablished = false;
|
||||
m_Socket.close ();
|
||||
transports.RemoveNTCPSession (this);
|
||||
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 ("NTCP session ", numDelayed, " not sent");
|
||||
// TODO: notify tunnels
|
||||
|
||||
delete this;
|
||||
LogPrint ("NTCP session terminated");
|
||||
}
|
||||
|
||||
void NTCPSession::Connected ()
|
||||
{
|
||||
LogPrint ("NTCP session 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 (),
|
||||
boost::bind(&NTCPSession::HandlePhase1Sent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void NTCPSession::ServerLogin ()
|
||||
{
|
||||
// receive Phase1
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
|
||||
boost::bind(&NTCPSession::HandlePhase1Received, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Couldn't send Phase 1 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Phase 1 sent: ", bytes_transferred);
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (),
|
||||
boost::bind(&NTCPSession::HandlePhase2Received, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Phase 1 read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("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 ("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 (),
|
||||
boost::bind(&NTCPSession::HandlePhase2Sent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, tsB));
|
||||
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Couldn't send Phase 2 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Phase 2 sent: ", bytes_transferred);
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase3, sizeof (NTCPPhase3)), boost::asio::transfer_all (),
|
||||
boost::bind(&NTCPSession::HandlePhase3Received, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, 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 ("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 ("Incorrect hash");
|
||||
transports.ReuseDHKeysPair (m_DHKeysPair);
|
||||
m_DHKeysPair = nullptr;
|
||||
Terminate ();
|
||||
return ;
|
||||
}
|
||||
SendPhase3 ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::SendPhase3 ()
|
||||
{
|
||||
m_Establisher->phase3.size = htons (i2p::data::DEFAULT_IDENTITY_SIZE);
|
||||
memcpy (&m_Establisher->phase3.ident, &i2p::context.GetIdentity ().GetStandardIdentity (), i2p::data::DEFAULT_IDENTITY_SIZE); // TODO:
|
||||
uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ());
|
||||
m_Establisher->phase3.timestamp = tsA;
|
||||
|
||||
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 (i2p::context.GetPrivateKeys (), m_Establisher->phase3.signature);
|
||||
|
||||
m_Encryption.Encrypt((uint8_t *)&m_Establisher->phase3, sizeof(NTCPPhase3), (uint8_t *)&m_Establisher->phase3);
|
||||
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase3, sizeof (NTCPPhase3)), boost::asio::transfer_all (),
|
||||
boost::bind(&NTCPSession::HandlePhase3Sent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, tsA));
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Couldn't send Phase 3 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Phase 3 sent: ", bytes_transferred);
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase4, sizeof (NTCPPhase4)), boost::asio::transfer_all (),
|
||||
boost::bind(&NTCPSession::HandlePhase4Received, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, tsA));
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Phase 3 read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Phase 3 received: ", bytes_transferred);
|
||||
m_Decryption.Decrypt ((uint8_t *)&m_Establisher->phase3, sizeof(NTCPPhase3), (uint8_t *)&m_Establisher->phase3);
|
||||
m_RemoteIdentity = m_Establisher->phase3.ident;
|
||||
|
||||
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 (m_Establisher->phase3.timestamp); // tsA
|
||||
s.Insert (tsB); // tsB
|
||||
if (!s.Verify (m_RemoteIdentity, m_Establisher->phase3.signature))
|
||||
{
|
||||
LogPrint ("signature verification failed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
|
||||
SendPhase4 (tsB);
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::SendPhase4 (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 (m_Establisher->phase3.timestamp); // tsA
|
||||
s.Insert (tsB); // tsB
|
||||
s.Sign (i2p::context.GetPrivateKeys (), m_Establisher->phase4.signature);
|
||||
m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase4, sizeof(NTCPPhase4), (uint8_t *)&m_Establisher->phase4);
|
||||
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase4, sizeof (NTCPPhase4)), boost::asio::transfer_all (),
|
||||
boost::bind(&NTCPSession::HandlePhase4Sent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Couldn't send Phase 4 message: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Phase 4 sent: ", bytes_transferred);
|
||||
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 ("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 ("Phase 4 received: ", bytes_transferred);
|
||||
m_Decryption.Decrypt((uint8_t *)&m_Establisher->phase4, sizeof(NTCPPhase4), (uint8_t *)&m_Establisher->phase4);
|
||||
|
||||
// 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_Establisher->phase4.signature))
|
||||
{
|
||||
LogPrint ("signature verification failed");
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
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),
|
||||
boost::bind(&NTCPSession::HandleReceived, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("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 ("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 ("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 (),
|
||||
boost::bind(&NTCPSession::HandleSent, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, 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 ("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 (boost::bind (&NTCPSession::HandleTerminationTimer,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NTCPClient::NTCPClient (boost::asio::io_service& service, const boost::asio::ip::address& address,
|
||||
int port, const i2p::data::RouterInfo& in_RouterInfo):
|
||||
NTCPSession (service, &in_RouterInfo), m_Endpoint (address, port)
|
||||
{
|
||||
Connect ();
|
||||
}
|
||||
|
||||
void NTCPClient::Connect ()
|
||||
{
|
||||
LogPrint ("Connecting to ", m_Endpoint.address ().to_string (),":", m_Endpoint.port ());
|
||||
GetSocket ().async_connect (m_Endpoint, boost::bind (&NTCPClient::HandleConnect,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void NTCPClient::HandleConnect (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint ("Connect error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true);
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Connected");
|
||||
if (GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6
|
||||
context.UpdateNTCPV6Address (GetSocket ().local_endpoint ().address ());
|
||||
ClientLogin ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPServerConnection::Connected ()
|
||||
{
|
||||
LogPrint ("NTCP server session connected");
|
||||
transports.AddNTCPSession (this);
|
||||
NTCPSession::Connected ();
|
||||
}
|
||||
}
|
||||
}
|
||||
175
NTCPSession.h
175
NTCPSession.h
@@ -1,175 +0,0 @@
|
||||
#ifndef NTCP_SESSION_H__
|
||||
#define NTCP_SESSION_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <list>
|
||||
#include <boost/asio.hpp>
|
||||
#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;
|
||||
};
|
||||
|
||||
struct NTCPPhase3
|
||||
{
|
||||
uint16_t size;
|
||||
i2p::data::Identity ident;
|
||||
uint32_t timestamp;
|
||||
uint8_t padding[15];
|
||||
uint8_t signature[40];
|
||||
};
|
||||
|
||||
|
||||
struct NTCPPhase4
|
||||
{
|
||||
uint8_t signature[40];
|
||||
uint8_t padding[8];
|
||||
};
|
||||
|
||||
#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
|
||||
|
||||
class NTCPSession: public TransportSession
|
||||
{
|
||||
public:
|
||||
|
||||
NTCPSession (boost::asio::io_service& service, const i2p::data::RouterInfo * in_RemoteRouter = nullptr);
|
||||
~NTCPSession ();
|
||||
|
||||
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 Terminate ();
|
||||
virtual 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 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 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;
|
||||
NTCPPhase3 phase3;
|
||||
NTCPPhase4 phase4;
|
||||
} * m_Establisher;
|
||||
|
||||
uint8_t m_ReceiveBuffer[NTCP_BUFFER_SIZE + 16], m_TimeSyncBuffer[16];
|
||||
int m_ReceiveBufferOffset;
|
||||
|
||||
i2p::I2NPMessage * m_NextMessage;
|
||||
std::list<i2p::I2NPMessage *> m_DelayedMessages;
|
||||
size_t m_NextMessageOffset;
|
||||
|
||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
||||
};
|
||||
|
||||
class NTCPClient: public NTCPSession
|
||||
{
|
||||
public:
|
||||
|
||||
NTCPClient (boost::asio::io_service& service, const boost::asio::ip::address& address, int port, const i2p::data::RouterInfo& in_RouterInfo);
|
||||
|
||||
private:
|
||||
|
||||
void Connect ();
|
||||
void HandleConnect (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
boost::asio::ip::tcp::endpoint m_Endpoint;
|
||||
};
|
||||
|
||||
class NTCPServerConnection: public NTCPSession
|
||||
{
|
||||
public:
|
||||
|
||||
NTCPServerConnection (boost::asio::io_service& service):
|
||||
NTCPSession (service) {};
|
||||
|
||||
protected:
|
||||
|
||||
virtual void Connected ();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
898
NetDb.cpp
898
NetDb.cpp
@@ -1,898 +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 (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)
|
||||
msg = m_Pool->GetGarlicDestination ().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_ReseedRetries (0), 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);
|
||||
while (m_RouterInfos.size () < 100 && m_ReseedRetries < 10)
|
||||
{
|
||||
Reseeder reseeder;
|
||||
reseeder.reseedNow();
|
||||
m_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 IdentHash& ident, const uint8_t * buf, int len)
|
||||
{
|
||||
DeleteRequestedDestination (ident);
|
||||
auto it = m_RouterInfos.find(ident);
|
||||
if (it != m_RouterInfos.end ())
|
||||
{
|
||||
auto ts = it->second->GetTimestamp ();
|
||||
it->second->Update (buf, len);
|
||||
if (it->second->GetTimestamp () > ts)
|
||||
LogPrint ("RouterInfo updated");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("New RouterInfo added");
|
||||
auto r = std::make_shared<RouterInfo> (buf, len);
|
||||
m_RouterInfos[r->GetIdentHash ()] = r;
|
||||
if (r->IsFloodfill ())
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
m_Floodfills.push_back (r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RouterInfo * NetDb::FindRouter (const IdentHash& ident) const
|
||||
{
|
||||
auto it = m_RouterInfos.find (ident);
|
||||
if (it != m_RouterInfos.end ())
|
||||
return it->second.get ();
|
||||
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 ())
|
||||
{
|
||||
if (boost::filesystem::exists (GetFilePath (fullDirectory, it.second.get ())))
|
||||
{
|
||||
boost::filesystem::remove (GetFilePath (fullDirectory, it.second.get ()));
|
||||
deletedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count > 0)
|
||||
LogPrint (count," new/updated routers saved");
|
||||
if (deletedCount > 0)
|
||||
LogPrint (deletedCount," routers deleted");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
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)) // request floodfill only once
|
||||
{
|
||||
floodfills.insert (floodfill);
|
||||
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 (const RouterInfo * compatibleWith) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router.get () != compatibleWith &&
|
||||
router->IsCompatible (*compatibleWith);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetHighBandwidthRandomRouter (const RouterInfo * compatibleWith) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[compatibleWith](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router.get () != 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;
|
||||
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);
|
||||
}
|
||||
|
||||
const RouterInfo * NetDb::GetClosestFloodfill (const IdentHash& destination,
|
||||
const std::set<IdentHash>& excluded) const
|
||||
{
|
||||
RouterInfo * r = nullptr;
|
||||
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.get ();
|
||||
}
|
||||
}
|
||||
}
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::PublishLeaseSet (const LeaseSet * leaseSet, i2p::tunnel::TunnelPool * pool)
|
||||
{
|
||||
if (!leaseSet || !pool) return;
|
||||
auto outbound = pool->GetNextOutboundTunnel ();
|
||||
if (!outbound)
|
||||
{
|
||||
LogPrint ("Can't publish LeaseSet. No outbound tunnels");
|
||||
return;
|
||||
}
|
||||
std::set<IdentHash> excluded;
|
||||
auto floodfill = GetClosestFloodfill (leaseSet->GetIdentHash (), excluded);
|
||||
if (!floodfill)
|
||||
{
|
||||
LogPrint ("Can't publish LeaseSet. No floodfills found");
|
||||
return;
|
||||
}
|
||||
uint32_t replyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
||||
auto msg = pool->GetGarlicDestination ().WrapMessage (*floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, replyToken));
|
||||
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
130
NetDb.h
130
NetDb.h
@@ -1,130 +0,0 @@
|
||||
#ifndef NETDB_H__
|
||||
#define NETDB_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#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_LastRouter (nullptr), 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 ();
|
||||
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 (const RouterInfo * router, 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;
|
||||
const RouterInfo * m_LastRouter;
|
||||
uint64_t m_CreationTime;
|
||||
};
|
||||
|
||||
class NetDb
|
||||
{
|
||||
public:
|
||||
|
||||
NetDb ();
|
||||
~NetDb ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
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);
|
||||
RouterInfo * FindRouter (const IdentHash& ident) const;
|
||||
LeaseSet * FindLeaseSet (const IdentHash& destination) const;
|
||||
|
||||
void PublishLeaseSet (const LeaseSet * leaseSet, i2p::tunnel::TunnelPool * pool);
|
||||
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 (const RouterInfo * compatibleWith) const;
|
||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (const RouterInfo * compatibleWith) 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 ();
|
||||
const RouterInfo * GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
|
||||
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;
|
||||
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
|
||||
mutable std::mutex m_FloodfillsMutex;
|
||||
std::vector<std::shared_ptr<RouterInfo> > m_Floodfills;
|
||||
std::mutex m_RequestedDestinationsMutex;
|
||||
std::map<IdentHash, RequestedDestination *> m_RequestedDestinations;
|
||||
|
||||
bool m_IsRunning;
|
||||
int m_ReseedRetries;
|
||||
std::thread * m_Thread;
|
||||
i2p::util::Queue<I2NPMessage> m_Queue; // of I2NPDatabaseStoreMsg
|
||||
|
||||
static const char m_NetDbPath[];
|
||||
};
|
||||
|
||||
extern NetDb netdb;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
131
README.md
131
README.md
@@ -1,71 +1,86 @@
|
||||
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)
|
||||
* CentOS / Fedora - [](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
|
||||
* Docker image - [](https://hub.docker.com/r/meeh/i2pd/builds/)
|
||||
* 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
|
||||
|
||||
|
||||
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
|
||||
* --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
|
||||
|
||||
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.
|
||||
|
||||
179
Reseed.cpp
179
Reseed.cpp
@@ -1,179 +0,0 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <cryptopp/gzip.h>
|
||||
#include "I2PEndian.h"
|
||||
#include "Reseed.h"
|
||||
#include "Log.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;
|
||||
}
|
||||
|
||||
void ProcessSU3File (const char * filename)
|
||||
{
|
||||
static uint32_t headerSignature = htole32 (0x04044B50);
|
||||
|
||||
std::ifstream s(filename, std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
{
|
||||
while (!s.eof ())
|
||||
{
|
||||
uint32_t signature;
|
||||
s.read ((char *)&signature, 4);
|
||||
if (signature == headerSignature)
|
||||
{
|
||||
// next local file
|
||||
s.seekg (14, std::ios::cur); // skip field we don't care about
|
||||
uint32_t compressedSize, uncompressedSize;
|
||||
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 = le32toh (fileNameLength);
|
||||
s.read ((char *)&extraFieldLength, 2);
|
||||
extraFieldLength = le32toh (extraFieldLength);
|
||||
char localFileName[255];
|
||||
s.read (localFileName, fileNameLength);
|
||||
localFileName[fileNameLength] = 0;
|
||||
s.seekg (extraFieldLength, std::ios::cur);
|
||||
|
||||
uint8_t * compressed = new uint8_t[compressedSize];
|
||||
s.read ((char *)compressed, compressedSize);
|
||||
CryptoPP::Gunzip decompressor;
|
||||
decompressor.Put (compressed, compressedSize);
|
||||
delete[] compressed;
|
||||
if (decompressor.MaxRetrievable () <= uncompressedSize)
|
||||
{
|
||||
uint8_t * uncompressed = new uint8_t[uncompressedSize];
|
||||
decompressor.Get (uncompressed, decompressor.MaxRetrievable ());
|
||||
// TODO: save file
|
||||
delete[] uncompressed;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header");
|
||||
}
|
||||
else
|
||||
break; // no more files
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Can't open file ", filename);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
24
Reseed.h
24
Reseed.h
@@ -1,24 +0,0 @@
|
||||
#ifndef RESEED_H
|
||||
#define RESEED_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
|
||||
class Reseeder
|
||||
{
|
||||
public:
|
||||
Reseeder();
|
||||
~Reseeder();
|
||||
bool reseedNow();
|
||||
};
|
||||
|
||||
void ProcessSU3File (const char * filename);
|
||||
}
|
||||
}
|
||||
|
||||
#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
|
||||
642
RouterInfo.cpp
642
RouterInfo.cpp
@@ -1,642 +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");
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
757
SAM.cpp
757
SAM.cpp
@@ -1,757 +0,0 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <boost/bind.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 ()
|
||||
{
|
||||
if (m_Stream)
|
||||
{
|
||||
m_Stream->Close ();
|
||||
i2p::stream::DeleteStream (m_Stream);
|
||||
m_Stream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::Terminate ()
|
||||
{
|
||||
if (m_Stream)
|
||||
{
|
||||
m_Stream->Close ();
|
||||
i2p::stream::DeleteStream (m_Stream);
|
||||
m_Stream = nullptr;
|
||||
}
|
||||
switch (m_SocketType)
|
||||
{
|
||||
case eSAMSocketTypeSession:
|
||||
m_Owner.CloseSession (m_ID);
|
||||
break;
|
||||
case eSAMSocketTypeStream:
|
||||
{
|
||||
if (m_Session)
|
||||
m_Session->sockets.remove (this);
|
||||
break;
|
||||
}
|
||||
case eSAMSocketTypeAcceptor:
|
||||
{
|
||||
if (m_Session)
|
||||
{
|
||||
m_Session->sockets.remove (this);
|
||||
m_Session->localDestination->StopAcceptingStreams ();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
;
|
||||
}
|
||||
m_Socket.close ();
|
||||
// delete this;
|
||||
}
|
||||
|
||||
void SAMSocket::ReceiveHandshake ()
|
||||
{
|
||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
||||
boost::bind(&SAMSocket::HandleHandshakeReceived, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
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);
|
||||
if (!memcmp (m_Buffer, SAM_HANDSHAKE, strlen (SAM_HANDSHAKE)))
|
||||
{
|
||||
// TODO: check version
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (SAM_HANDSHAKE_REPLY, strlen (SAM_HANDSHAKE_REPLY)), boost::asio::transfer_all (),
|
||||
boost::bind(&SAMSocket::HandleHandshakeReplySent, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
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),
|
||||
boost::bind(&SAMSocket::HandleMessage, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
}
|
||||
|
||||
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 (),
|
||||
boost::bind(&SAMSocket::HandleMessageReplySent, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred, 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;
|
||||
}
|
||||
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination);
|
||||
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, 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 (boost::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
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 (boost::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint8_t ident[1024];
|
||||
size_t l = i2p::data::Base64ToByteStream (destination.c_str (), destination.length (), ident, 1024);
|
||||
i2p::data::IdentityEx dest;
|
||||
dest.FromBuffer (ident, l);
|
||||
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 (boost::bind (&SAMSocket::HandleStreamDestinationRequestTimer,
|
||||
this, boost::asio::placeholders::error, 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 (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::HandleNamingLookupDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident)
|
||||
{
|
||||
if (!ecode) // timeout expired
|
||||
{
|
||||
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
SendNamingLookupReply (leaseSet);
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM name destination not found");
|
||||
#ifdef _MSC_VER
|
||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_KEY_NOT_FOUND, (ident.ToBase32 () + ".b32.i2p").c_str ());
|
||||
#else
|
||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_KEY_NOT_FOUND, (ident.ToBase32 () + ".b32.i2p").c_str ());
|
||||
#endif
|
||||
SendMessageReply (m_Buffer, len, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (this);
|
||||
m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, 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)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
char priv[1024], pub[1024];
|
||||
size_t l = localDestination->GetPrivateKeys ().ToBuffer (buf, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
|
||||
priv[l1] = 0;
|
||||
|
||||
l = localDestination->GetIdentity ().ToBuffer (buf, 1024);
|
||||
l1 = i2p::data::ByteStreamToBase64 (buf, l, pub, 1024);
|
||||
pub[l1] = 0;
|
||||
#ifdef _MSC_VER
|
||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, pub, priv);
|
||||
#else
|
||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY, pub, priv);
|
||||
#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;
|
||||
if (name == "ME")
|
||||
SendNamingLookupReply (nullptr);
|
||||
else if (m_Session && context.GetAddressBook ().GetIdentHash (name, ident))
|
||||
{
|
||||
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
SendNamingLookupReply (leaseSet);
|
||||
else
|
||||
{
|
||||
i2p::data::netdb.RequestDestination (ident, true, m_Session->localDestination->GetTunnelPool ());
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_NAMING_LOOKUP_TIMEOUT));
|
||||
m_Timer.async_wait (boost::bind (&SAMSocket::HandleNamingLookupDestinationRequestTimer,
|
||||
this, boost::asio::placeholders::error, ident));
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
char pub[1024];
|
||||
const i2p::data::IdentityEx& identity = leaseSet ? leaseSet->GetIdentity () : m_Session->localDestination->GetIdentity ();
|
||||
size_t l = identity.ToBuffer (buf, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, pub, 1024);
|
||||
pub[l1] = 0;
|
||||
#ifdef _MSC_VER
|
||||
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, pub);
|
||||
#else
|
||||
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, pub);
|
||||
#endif
|
||||
SendMessageReply (m_Buffer, l2, 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),
|
||||
boost::bind((m_SocketType == eSAMSocketTypeSession) ? &SAMSocket::HandleMessage : &SAMSocket::HandleReceived,
|
||||
this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
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),
|
||||
boost::bind (&SAMSocket::HandleI2PReceive, this,
|
||||
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred),
|
||||
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),
|
||||
boost::bind (&SAMSocket::HandleWriteI2PData, this, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
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 (i2p::stream::Stream * stream)
|
||||
{
|
||||
if (stream)
|
||||
{
|
||||
LogPrint ("SAM incoming I2P connection for session ", m_ID);
|
||||
m_Stream = stream;
|
||||
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)
|
||||
{
|
||||
uint8_t identBuf[1024];
|
||||
size_t l = ident.ToBuffer (identBuf, 1024);
|
||||
size_t l1 = i2p::data::ByteStreamToBase64 (identBuf, l, m_Buffer, SAM_SOCKET_BUFFER_SIZE);
|
||||
m_Buffer[l1] = 0;
|
||||
#ifdef _MSC_VER
|
||||
size_t l2 = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, m_Buffer, len);
|
||||
#else
|
||||
size_t l2 = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, m_Buffer, len);
|
||||
#endif
|
||||
if (len < SAM_SOCKET_BUFFER_SIZE - l2)
|
||||
{
|
||||
memcpy (m_StreamBuffer + l2, buf, len);
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l2),
|
||||
boost::bind (&SAMSocket::HandleWriteI2PData, this, boost::asio::placeholders::error));
|
||||
}
|
||||
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),
|
||||
m_NewSocket (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SAMBridge::~SAMBridge ()
|
||||
{
|
||||
Stop ();
|
||||
delete m_NewSocket;
|
||||
}
|
||||
|
||||
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 ()
|
||||
{
|
||||
m_NewSocket = new SAMSocket (*this);
|
||||
m_Acceptor.async_accept (m_NewSocket->GetSocket (), boost::bind (&SAMBridge::HandleAccept, this,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
void SAMBridge::HandleAccept(const boost::system::error_code& ecode)
|
||||
{
|
||||
if (!ecode)
|
||||
{
|
||||
LogPrint ("New SAM connection from ", m_NewSocket->GetSocket ().remote_endpoint ());
|
||||
m_NewSocket->ReceiveHandshake ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("SAM accept error: ", ecode.message ());
|
||||
delete m_NewSocket;
|
||||
m_NewSocket = nullptr;
|
||||
}
|
||||
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Accept ();
|
||||
}
|
||||
|
||||
SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination)
|
||||
{
|
||||
ClientDestination * localDestination = nullptr;
|
||||
if (destination != "")
|
||||
{
|
||||
uint8_t * buf = new uint8_t[destination.length ()];
|
||||
size_t l = i2p::data::Base64ToByteStream (destination.c_str (), destination.length (), buf, destination.length ());
|
||||
i2p::data::PrivateKeys keys;
|
||||
keys.FromBuffer (buf, l);
|
||||
delete[] buf;
|
||||
localDestination = i2p::client::context.CreateNewLocalDestination (keys);
|
||||
}
|
||||
else // transient
|
||||
localDestination = i2p::client::context.CreateNewLocalDestination ();
|
||||
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)
|
||||
delete it1;
|
||||
it->second.sockets.clear ();
|
||||
it->second.localDestination->Stop ();
|
||||
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,
|
||||
boost::bind (&SAMBridge::HandleReceivedDatagram, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint8_t ident[1024];
|
||||
size_t l = i2p::data::Base64ToByteStream (destination, strlen(destination), ident, 1024);
|
||||
i2p::data::IdentityEx dest;
|
||||
dest.FromBuffer (ident, l);
|
||||
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 ());
|
||||
}
|
||||
}
|
||||
}
|
||||
170
SAM.h
170
SAM.h
@@ -1,170 +0,0 @@
|
||||
#ifndef SAM_H__
|
||||
#define SAM_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "Streaming.h"
|
||||
#include "Destination.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
const size_t SAM_SOCKET_BUFFER_SIZE = 4096;
|
||||
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
|
||||
const int SAM_CONNECT_TIMEOUT = 5; // in seconds
|
||||
const int SAM_NAMING_LOOKUP_TIMEOUT = 5; // in seconds
|
||||
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds
|
||||
const char SAM_HANDSHAKE[] = "HELLO VERSION";
|
||||
const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=3.0\n";
|
||||
const char SAM_SESSION_CREATE[] = "SESSION CREATE";
|
||||
const char SAM_SESSION_CREATE_REPLY_OK[] = "SESSION STATUS RESULT=OK DESTINATION=%s\n";
|
||||
const char SAM_SESSION_CREATE_DUPLICATED_ID[] = "SESSION STATUS RESULT=DUPLICATED_ID\n";
|
||||
const char SAM_SESSION_CREATE_DUPLICATED_DEST[] = "SESSION STATUS RESULT=DUPLICATED_DEST\n";
|
||||
const char SAM_STREAM_CONNECT[] = "STREAM CONNECT";
|
||||
const char SAM_STREAM_STATUS_OK[] = "STREAM STATUS RESULT=OK\n";
|
||||
const char SAM_STREAM_STATUS_INVALID_ID[] = "STREAM STATUS RESULT=INVALID_ID\n";
|
||||
const char SAM_STREAM_STATUS_CANT_REACH_PEER[] = "STREAM STATUS RESULT=CANT_REACH_PEER\n";
|
||||
const char SAM_STREAM_STATUS_I2P_ERROR[] = "STREAM STATUS RESULT=I2P_ERROR\n";
|
||||
const char SAM_STREAM_ACCEPT[] = "STREAM ACCEPT";
|
||||
const char SAM_DEST_GENERATE[] = "DEST GENERATE";
|
||||
const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n";
|
||||
const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
|
||||
const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP";
|
||||
const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n";
|
||||
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM_RECEIVED DESTINATION=%s SIZE=%i\n";
|
||||
const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n";
|
||||
const char SAM_NAMING_REPLY_KEY_NOT_FOUND[] = "NAMING REPLY RESULT=INVALID_KEY_NOT_FOUND NAME=%s\n";
|
||||
const char SAM_PARAM_STYLE[] = "STYLE";
|
||||
const char SAM_PARAM_ID[] = "ID";
|
||||
const char SAM_PARAM_SILENT[] = "SILENT";
|
||||
const char SAM_PARAM_DESTINATION[] = "DESTINATION";
|
||||
const char SAM_PARAM_NAME[] = "NAME";
|
||||
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
|
||||
const char SAM_VALUE_STREAM[] = "STREAM";
|
||||
const char SAM_VALUE_DATAGRAM[] = "DATAGRAM";
|
||||
const char SAM_VALUE_RAW[] = "RAW";
|
||||
const char SAM_VALUE_TRUE[] = "true";
|
||||
const char SAM_VALUE_FALSE[] = "false";
|
||||
|
||||
enum SAMSocketType
|
||||
{
|
||||
eSAMSocketTypeUnknown,
|
||||
eSAMSocketTypeSession,
|
||||
eSAMSocketTypeStream,
|
||||
eSAMSocketTypeAcceptor
|
||||
};
|
||||
|
||||
class SAMBridge;
|
||||
class SAMSession;
|
||||
class SAMSocket
|
||||
{
|
||||
public:
|
||||
|
||||
SAMSocket (SAMBridge& owner);
|
||||
~SAMSocket ();
|
||||
|
||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
||||
void ReceiveHandshake ();
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
void HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void SendMessageReply (const char * msg, size_t len, bool close);
|
||||
void HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close);
|
||||
void Receive ();
|
||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
|
||||
void I2PReceive ();
|
||||
void HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleI2PAccept (i2p::stream::Stream * stream);
|
||||
void HandleWriteI2PData (const boost::system::error_code& ecode);
|
||||
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& ident, const uint8_t * buf, size_t len);
|
||||
|
||||
void ProcessSessionCreate (char * buf, size_t len);
|
||||
void ProcessStreamConnect (char * buf, size_t len);
|
||||
void ProcessStreamAccept (char * buf, size_t len);
|
||||
void ProcessDestGenerate ();
|
||||
void ProcessNamingLookup (char * buf, size_t len);
|
||||
void ExtractParams (char * buf, size_t len, std::map<std::string, std::string>& params);
|
||||
|
||||
void Connect (const i2p::data::LeaseSet& remote);
|
||||
void HandleStreamDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident);
|
||||
void HandleNamingLookupDestinationRequestTimer (const boost::system::error_code& ecode, i2p::data::IdentHash ident);
|
||||
void SendNamingLookupReply (const i2p::data::LeaseSet * leaseSet);
|
||||
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
|
||||
void SendSessionCreateReplyOk ();
|
||||
|
||||
private:
|
||||
|
||||
SAMBridge& m_Owner;
|
||||
boost::asio::ip::tcp::socket m_Socket;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
char m_Buffer[SAM_SOCKET_BUFFER_SIZE + 1];
|
||||
uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE];
|
||||
SAMSocketType m_SocketType;
|
||||
std::string m_ID; // nickname
|
||||
bool m_IsSilent;
|
||||
i2p::stream::Stream * m_Stream;
|
||||
SAMSession * m_Session;
|
||||
};
|
||||
|
||||
struct SAMSession
|
||||
{
|
||||
ClientDestination * localDestination;
|
||||
std::list<SAMSocket *> sockets;
|
||||
};
|
||||
|
||||
class SAMBridge
|
||||
{
|
||||
public:
|
||||
|
||||
SAMBridge (int port);
|
||||
~SAMBridge ();
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
SAMSession * CreateSession (const std::string& id, const std::string& destination = ""); // empty string means transient
|
||||
void CloseSession (const std::string& id);
|
||||
SAMSession * FindSession (const std::string& id);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
|
||||
void Accept ();
|
||||
void HandleAccept(const boost::system::error_code& ecode);
|
||||
|
||||
void ReceiveDatagram ();
|
||||
void HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
||||
boost::asio::ip::udp::endpoint m_DatagramEndpoint, m_SenderEndpoint;
|
||||
boost::asio::ip::udp::socket m_DatagramSocket;
|
||||
SAMSocket * m_NewSocket;
|
||||
std::mutex m_SessionsMutex;
|
||||
std::map<std::string, SAMSession> m_Sessions;
|
||||
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
284
SOCKS.cpp
284
SOCKS.cpp
@@ -1,284 +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");
|
||||
delete m_stream;
|
||||
m_stream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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 "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;
|
||||
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
|
||||
365
SSU.cpp
365
SSU.cpp
@@ -1,365 +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_Work (m_Service),
|
||||
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_Service), 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 ()
|
||||
{
|
||||
for (auto it: m_Sessions)
|
||||
delete it.second;
|
||||
}
|
||||
|
||||
void SSUServer::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
||||
m_Service.post (boost::bind (&SSUServer::Receive, this));
|
||||
if (context.SupportsV6 ())
|
||||
m_Service.post (boost::bind (&SSUServer::ReceiveV6, this));
|
||||
if (i2p::context.IsUnreachable ())
|
||||
ScheduleIntroducersUpdateTimer ();
|
||||
}
|
||||
|
||||
void SSUServer::Stop ()
|
||||
{
|
||||
DeleteAllSessions ();
|
||||
m_IsRunning = false;
|
||||
m_Service.stop ();
|
||||
m_Socket.close ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU server: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay)
|
||||
{
|
||||
m_Relays[tag] = relay;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
SSUSession * session = nullptr;
|
||||
auto it = m_Sessions.find (from);
|
||||
if (it != m_Sessions.end ())
|
||||
session = it->second;
|
||||
if (!session)
|
||||
{
|
||||
session = new SSUSession (*this, from);
|
||||
m_Sessions[from] = session;
|
||||
LogPrint ("New SSU session from ", from.address ().to_string (), ":", from.port (), " created");
|
||||
}
|
||||
session->ProcessNextMessage (buf, bytes_transferred, from);
|
||||
}
|
||||
|
||||
SSUSession * SSUServer::FindSession (const i2p::data::RouterInfo * router)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
SSUSession * SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e)
|
||||
{
|
||||
auto it = m_Sessions.find (e);
|
||||
if (it != m_Sessions.end ())
|
||||
return it->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SSUSession * SSUServer::GetSession (const i2p::data::RouterInfo * router, bool peerTest)
|
||||
{
|
||||
SSUSession * session = nullptr;
|
||||
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 = new 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)
|
||||
{
|
||||
SSUSession * introducerSession = nullptr;
|
||||
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 = new 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);
|
||||
delete session;
|
||||
session = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address");
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
void SSUServer::DeleteSession (SSUSession * session)
|
||||
{
|
||||
if (session)
|
||||
{
|
||||
session->Close ();
|
||||
m_Sessions.erase (session->GetRemoteEndpoint ());
|
||||
delete session;
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::DeleteAllSessions ()
|
||||
{
|
||||
for (auto it: m_Sessions)
|
||||
{
|
||||
it.second->Close ();
|
||||
delete it.second;
|
||||
}
|
||||
m_Sessions.clear ();
|
||||
}
|
||||
|
||||
template<typename Filter>
|
||||
SSUSession * SSUServer::GetRandomSession (Filter filter)
|
||||
{
|
||||
std::vector<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;
|
||||
}
|
||||
|
||||
SSUSession * SSUServer::GetRandomEstablishedSession (const SSUSession * excluded)
|
||||
{
|
||||
return GetRandomSession (
|
||||
[excluded](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](SSUSession * session)->bool
|
||||
{
|
||||
return session->GetRelayTag () && !ret.count (session) &&
|
||||
session->GetState () == eSessionStateEstablished &&
|
||||
ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION;
|
||||
}
|
||||
);
|
||||
if (session)
|
||||
{
|
||||
ret.insert (session);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleIntroducersUpdateTimer ()
|
||||
{
|
||||
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
||||
m_IntroducersUpdateTimer.async_wait (boost::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
87
SSU.h
87
SSU.h
@@ -1,87 +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 ();
|
||||
SSUSession * GetSession (const i2p::data::RouterInfo * router, bool peerTest = false);
|
||||
SSUSession * FindSession (const i2p::data::RouterInfo * router);
|
||||
SSUSession * FindSession (const boost::asio::ip::udp::endpoint& e);
|
||||
SSUSession * GetRandomEstablishedSession (const SSUSession * excluded);
|
||||
void DeleteSession (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);
|
||||
SSUSession * FindRelaySession (uint32_t tag);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
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>
|
||||
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;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
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, 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
|
||||
|
||||
429
SSUData.cpp
429
SSUData.cpp
@@ -1,429 +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));
|
||||
m_ResendTimer.async_wait (boost::bind (&SSUData::HandleResendTimer,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
1012
SSUSession.cpp
1012
SSUSession.cpp
File diff suppressed because it is too large
Load Diff
145
SSUSession.h
145
SSUSession.h
@@ -1,145 +0,0 @@
|
||||
#ifndef SSU_SESSION_H__
|
||||
#define SSU_SESSION_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <boost/asio.hpp>
|
||||
#include "aes.h"
|
||||
#include "hmac.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "TransportSession.h"
|
||||
#include "SSUData.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace transport
|
||||
{
|
||||
#pragma pack(1)
|
||||
struct SSUHeader
|
||||
{
|
||||
uint8_t mac[16];
|
||||
uint8_t iv[16];
|
||||
uint8_t flag;
|
||||
uint32_t time;
|
||||
|
||||
uint8_t GetPayloadType () const { return flag >> 4; };
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
||||
|
||||
// payload types (4 bits)
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_CREATED = 1;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_CONFIRMED = 2;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_REQUEST = 3;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_RESPONSE = 4;
|
||||
const uint8_t PAYLOAD_TYPE_RELAY_INTRO = 5;
|
||||
const uint8_t PAYLOAD_TYPE_DATA = 6;
|
||||
const uint8_t PAYLOAD_TYPE_PEER_TEST = 7;
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_DESTROYED = 8;
|
||||
|
||||
enum SessionState
|
||||
{
|
||||
eSessionStateUnknown,
|
||||
eSessionStateIntroduced,
|
||||
eSessionStateEstablished,
|
||||
eSessionStateFailed
|
||||
};
|
||||
|
||||
class SSUServer;
|
||||
class SSUSession: public TransportSession
|
||||
{
|
||||
public:
|
||||
|
||||
SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
|
||||
const i2p::data::RouterInfo * router = nullptr, bool peerTest = false);
|
||||
void ProcessNextMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
~SSUSession ();
|
||||
|
||||
void Connect ();
|
||||
void Introduce (uint32_t iTag, const uint8_t * iKey);
|
||||
void WaitForIntroduction ();
|
||||
void Close ();
|
||||
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
||||
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
|
||||
void SendI2NPMessage (I2NPMessage * msg);
|
||||
void SendPeerTest (); // Alice
|
||||
|
||||
SessionState GetState () const { return m_State; };
|
||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
|
||||
void SendKeepAlive ();
|
||||
uint32_t GetRelayTag () const { return m_RelayTag; };
|
||||
uint32_t GetCreationTime () const { return m_CreationTime; };
|
||||
|
||||
private:
|
||||
|
||||
void CreateAESandMacKey (const uint8_t * pubKey);
|
||||
|
||||
void PostI2NPMessage (I2NPMessage * msg);
|
||||
void ProcessMessage (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint); // call for established session
|
||||
void ProcessSessionRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void SendSessionRequest ();
|
||||
void SendRelayRequest (uint32_t iTag, const uint8_t * iKey);
|
||||
void ProcessSessionCreated (uint8_t * buf, size_t len);
|
||||
void SendSessionCreated (const uint8_t * x);
|
||||
void ProcessSessionConfirmed (uint8_t * buf, size_t len);
|
||||
void SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen);
|
||||
void ProcessRelayRequest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& from);
|
||||
void SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
|
||||
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to);
|
||||
void SendRelayIntro (SSUSession * session, const boost::asio::ip::udp::endpoint& from);
|
||||
void ProcessRelayResponse (uint8_t * buf, size_t len);
|
||||
void ProcessRelayIntro (uint8_t * buf, size_t len);
|
||||
void Established ();
|
||||
void Failed ();
|
||||
void ScheduleConnectTimer ();
|
||||
void HandleConnectTimer (const boost::system::error_code& ecode);
|
||||
void ProcessPeerTest (uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& senderEndpoint);
|
||||
void SendPeerTest (uint32_t nonce, uint32_t address, uint16_t port, const uint8_t * introKey, bool toAddress = true);
|
||||
void ProcessData (uint8_t * buf, size_t len);
|
||||
void SendSesionDestroyed ();
|
||||
void Send (uint8_t type, const uint8_t * payload, size_t len); // with session key
|
||||
void Send (const uint8_t * buf, size_t size);
|
||||
|
||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len, const uint8_t * aesKey, const uint8_t * iv, const uint8_t * macKey);
|
||||
void FillHeaderAndEncrypt (uint8_t payloadType, uint8_t * buf, size_t len); // with session key
|
||||
void Decrypt (uint8_t * buf, size_t len, const uint8_t * aesKey);
|
||||
void DecryptSessionKey (uint8_t * buf, size_t len);
|
||||
bool Validate (uint8_t * buf, size_t len, const uint8_t * macKey);
|
||||
const uint8_t * GetIntroKey () const;
|
||||
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
friend class SSUData; // TODO: change in later
|
||||
SSUServer& m_Server;
|
||||
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
bool m_PeerTest;
|
||||
SessionState m_State;
|
||||
bool m_IsSessionKey;
|
||||
uint32_t m_RelayTag;
|
||||
std::set<uint32_t> m_PeerTestNonces;
|
||||
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
|
||||
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
i2p::crypto::MACKey m_MacKey;
|
||||
std::list<i2p::I2NPMessage *> m_DelayedMessages;
|
||||
SSUData m_Data;
|
||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
||||
uint32_t m_CreationTime; // seconds since epoch
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
155
Signature.h
155
Signature.h
@@ -1,155 +0,0 @@
|
||||
#ifndef SIGNATURE_H__
|
||||
#define SIGNATURE_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <cryptopp/dsa.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;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
const size_t ECDSAP256_PUBLIC_KEY_LENGTH = 64;
|
||||
const size_t ECDSAP256_PUBLIC_KEY_HALF_LENGTH = ECDSAP256_PUBLIC_KEY_LENGTH/2;
|
||||
const size_t ECDSAP256_SIGNATURE_LENGTH = 64;
|
||||
const size_t ECDSAP256_PRIVATE_KEY_LENGTH = ECDSAP256_SIGNATURE_LENGTH/2;
|
||||
class ECDSAP256Verifier: public Verifier
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP256Verifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey.Initialize (CryptoPP::ASN1::secp256r1(),
|
||||
CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, ECDSAP256_PUBLIC_KEY_HALF_LENGTH),
|
||||
CryptoPP::Integer (signingKey + ECDSAP256_PUBLIC_KEY_HALF_LENGTH, ECDSAP256_PUBLIC_KEY_HALF_LENGTH)));
|
||||
}
|
||||
|
||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
||||
{
|
||||
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::Verifier verifier (m_PublicKey);
|
||||
return verifier.VerifyMessage (buf, len, signature, ECDSAP256_SIGNATURE_LENGTH);
|
||||
}
|
||||
|
||||
size_t GetPublicKeyLen () const { return ECDSAP256_PUBLIC_KEY_LENGTH; };
|
||||
size_t GetSignatureLen () const { return ECDSAP256_SIGNATURE_LENGTH; };
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PublicKey m_PublicKey;
|
||||
};
|
||||
|
||||
class ECDSAP256Signer: public Signer
|
||||
{
|
||||
public:
|
||||
|
||||
ECDSAP256Signer (const uint8_t * signingPrivateKey)
|
||||
{
|
||||
m_PrivateKey.Initialize (CryptoPP::ASN1::secp256r1(), CryptoPP::Integer (signingPrivateKey, ECDSAP256_PRIVATE_KEY_LENGTH));
|
||||
}
|
||||
|
||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::Signer signer (m_PrivateKey);
|
||||
signer.SignMessage (rnd, buf, len, signature);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PrivateKey m_PrivateKey;
|
||||
};
|
||||
|
||||
inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
||||
{
|
||||
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PrivateKey privateKey;
|
||||
CryptoPP::ECDSA<CryptoPP::ECP, CryptoPP::SHA256>::PublicKey publicKey;
|
||||
privateKey.Initialize (rnd, CryptoPP::ASN1::secp256r1());
|
||||
privateKey.MakePublicKey (publicKey);
|
||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, ECDSAP256_PRIVATE_KEY_LENGTH);
|
||||
auto q = publicKey.GetPublicElement ();
|
||||
q.x.Encode (signingPublicKey, ECDSAP256_PUBLIC_KEY_HALF_LENGTH);
|
||||
q.y.Encode (signingPublicKey + ECDSAP256_PUBLIC_KEY_HALF_LENGTH, ECDSAP256_PUBLIC_KEY_HALF_LENGTH);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
660
Streaming.cpp
660
Streaming.cpp
@@ -1,660 +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_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_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 ();
|
||||
|
||||
Close ();
|
||||
}
|
||||
|
||||
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 ("Plain ACK received");
|
||||
delete packet;
|
||||
return;
|
||||
}
|
||||
|
||||
LogPrint ("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 (boost::bind (&Stream::HandleAckSendTimer,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
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 ("Duplicate message ", receivedSeqn, " received");
|
||||
m_LocalDestination.GetOwner ().ResetCurrentOutboundTunnel (); // pick another outbound tunnel
|
||||
UpdateCurrentRemoteLease (); // pick another lease
|
||||
SendQuickAck (); // resend ack for previous message again
|
||||
delete packet; // packet dropped
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint ("Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
|
||||
// save message and wait for missing message again
|
||||
SavePacket (packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 ("Process seqn=", receivedSeqn, ", flags=", flags);
|
||||
|
||||
const uint8_t * optionData = packet->GetOptionData ();
|
||||
if (flags & PACKET_FLAG_SYNCHRONIZE)
|
||||
LogPrint ("Synchronize");
|
||||
|
||||
if (flags & PACKET_FLAG_DELAY_REQUESTED)
|
||||
{
|
||||
optionData += 2;
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_FROM_INCLUDED)
|
||||
{
|
||||
optionData += m_RemoteIdentity.FromBuffer (optionData, packet->GetOptionSize ());
|
||||
LogPrint ("From identity ", m_RemoteIdentity.GetIdentHash ().ToBase64 ());
|
||||
if (!m_RemoteLeaseSet)
|
||||
LogPrint ("Incoming stream from ", m_RemoteIdentity.GetIdentHash ().ToBase64 ());
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
|
||||
{
|
||||
uint16_t maxPacketSize = be16toh (*(uint16_t *)optionData);
|
||||
LogPrint ("Max packet size ", maxPacketSize);
|
||||
optionData += 2;
|
||||
}
|
||||
|
||||
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
|
||||
{
|
||||
LogPrint ("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 ("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 ("Closed");
|
||||
SendQuickAck (); // send ack for close explicitly?
|
||||
m_IsOpen = false;
|
||||
m_IsReset = true;
|
||||
m_ReceiveTimer.cancel ();
|
||||
m_ResendTimer.cancel ();
|
||||
m_AckSendTimer.cancel ();
|
||||
}
|
||||
}
|
||||
|
||||
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 ("Packet ", seqn, " NACK");
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto sentPacket = *it;
|
||||
LogPrint ("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
|
||||
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;
|
||||
m_Service.post (boost::bind (&Stream::SendPacket, this, p));
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
void Stream::SendQuickAck ()
|
||||
{
|
||||
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 (m_LastReceivedSequenceNumber);
|
||||
size += 4; // ack Through
|
||||
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;
|
||||
SendPacket (p);
|
||||
LogPrint ("FIN sent");
|
||||
}
|
||||
}
|
||||
|
||||
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::SendPackets (const std::vector<Packet *>& packets)
|
||||
{
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
UpdateCurrentRemoteLease ();
|
||||
if (!m_RemoteLeaseSet)
|
||||
{
|
||||
LogPrint ("Can't send packets. Missing remote LeaseSet");
|
||||
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_LocalDestination.GetOwner ().SendTunnelDataMsgs (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 (boost::bind (&Stream::HandleResendTimer,
|
||||
this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
Close ();
|
||||
m_IsReset = true;
|
||||
m_ReceiveTimer.cancel ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (packets.size () > 0)
|
||||
{
|
||||
m_LocalDestination.GetOwner ().ResetCurrentOutboundTunnel (); // 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);
|
||||
for (auto it: m_Streams)
|
||||
delete it.second;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Stream * StreamingDestination::CreateNewOutgoingStream (const i2p::data::LeaseSet& remote, int port)
|
||||
{
|
||||
Stream * s = new Stream (*m_Owner.GetService (), *this, remote, port);
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams[s->GetRecvStreamID ()] = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
Stream * StreamingDestination::CreateNewIncomingStream ()
|
||||
{
|
||||
Stream * s = new Stream (*m_Owner.GetService (), *this);
|
||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
||||
m_Streams[s->GetRecvStreamID ()] = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
void StreamingDestination::DeleteStream (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);
|
||||
if (m_Owner.GetService ())
|
||||
m_Owner.GetService ()->post ([stream](void) { delete stream; });
|
||||
else
|
||||
delete stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteStream (Stream * stream)
|
||||
{
|
||||
if (stream)
|
||||
stream->GetLocalDestination ().DeleteStream (stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
234
Streaming.h
234
Streaming.h
@@ -1,234 +0,0 @@
|
||||
#ifndef STREAMING_H__
|
||||
#define STREAMING_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
#include <functional>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.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
|
||||
{
|
||||
uint8_t buf[MAX_PACKET_SIZE];
|
||||
size_t len, offset;
|
||||
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:
|
||||
|
||||
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 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;
|
||||
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 (Stream *)> Acceptor;
|
||||
|
||||
StreamingDestination (i2p::client::ClientDestination& owner): m_Owner (owner) {};
|
||||
~StreamingDestination () {};
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
Stream * CreateNewOutgoingStream (const i2p::data::LeaseSet& remote, int port = 0);
|
||||
void DeleteStream (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);
|
||||
Stream * CreateNewIncomingStream ();
|
||||
|
||||
private:
|
||||
|
||||
i2p::client::ClientDestination& m_Owner;
|
||||
std::mutex m_StreamsMutex;
|
||||
std::map<uint32_t, Stream *> m_Streams;
|
||||
Acceptor m_Acceptor;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
|
||||
};
|
||||
|
||||
void DeleteStream (Stream * stream);
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
|
||||
{
|
||||
if (!m_ReceiveQueue.empty ())
|
||||
{
|
||||
m_Service.post ([=](void) { this->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));
|
||||
m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
|
||||
{ this->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,75 +0,0 @@
|
||||
#ifndef TRANSPORT_SESSION_H__
|
||||
#define TRANSPORT_SESSION_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <iostream>
|
||||
#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 (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; };
|
||||
|
||||
const i2p::data::RouterInfo * GetRemoteRouter () { return m_RemoteRouter; };
|
||||
const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; };
|
||||
|
||||
protected:
|
||||
|
||||
const i2p::data::RouterInfo * m_RemoteRouter;
|
||||
i2p::data::IdentityEx m_RemoteIdentity;
|
||||
DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
378
Transports.cpp
378
Transports.cpp
@@ -1,378 +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 = new NTCPServerConnection (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 = new NTCPServerConnection (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;
|
||||
}
|
||||
|
||||
for (auto session: m_NTCPSessions)
|
||||
delete session.second;
|
||||
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 (NTCPSession * session)
|
||||
{
|
||||
if (session)
|
||||
m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session;
|
||||
}
|
||||
|
||||
void Transports::RemoveNTCPSession (NTCPSession * session)
|
||||
{
|
||||
if (session)
|
||||
m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ());
|
||||
}
|
||||
|
||||
void Transports::HandleAccept (NTCPServerConnection * conn, const boost::system::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ());
|
||||
conn->ServerLogin ();
|
||||
}
|
||||
else
|
||||
delete conn;
|
||||
|
||||
if (error != boost::asio::error::operation_aborted)
|
||||
{
|
||||
conn = new NTCPServerConnection (m_Service);
|
||||
m_NTCPAcceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAccept, this,
|
||||
conn, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::HandleAcceptV6 (NTCPServerConnection * conn, const boost::system::error_code& error)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
LogPrint ("Connected from ", conn->GetSocket ().remote_endpoint().address ().to_string ());
|
||||
conn->ServerLogin ();
|
||||
}
|
||||
else
|
||||
delete conn;
|
||||
|
||||
if (error != boost::asio::error::operation_aborted)
|
||||
{
|
||||
conn = new NTCPServerConnection (m_Service);
|
||||
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), boost::bind (&Transports::HandleAcceptV6, this,
|
||||
conn, boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
NTCPSession * Transports::GetNextNTCPSession ()
|
||||
{
|
||||
for (auto session: m_NTCPSessions)
|
||||
if (session.second->IsEstablished ())
|
||||
return session.second;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
RouterInfo * 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 = new NTCPClient (m_Service, address->host, address->port, *r);
|
||||
AddNTCPSession (s);
|
||||
s->SendI2NPMessage (msg);
|
||||
}
|
||||
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)
|
||||
{
|
||||
RouterInfo * 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 (const i2p::data::RouterInfo * router)
|
||||
{
|
||||
if (!router) return;
|
||||
m_Service.post (boost::bind (&Transports::PostCloseSession, this, router));
|
||||
}
|
||||
|
||||
void Transports::PostCloseSession (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.get (), true); // peer test
|
||||
}
|
||||
}
|
||||
|
||||
DHKeysPair * Transports::GetNextDHKeysPair ()
|
||||
{
|
||||
return m_DHKeysPairSupplier.Acquire ();
|
||||
}
|
||||
|
||||
void Transports::ReuseDHKeysPair (DHKeysPair * pair)
|
||||
{
|
||||
m_DHKeysPairSupplier.Return (pair);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
111
Transports.h
111
Transports.h
@@ -1,111 +0,0 @@
|
||||
#ifndef TRANSPORTS_H__
|
||||
#define TRANSPORTS_H__
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#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 (NTCPSession * session);
|
||||
void RemoveNTCPSession (NTCPSession * session);
|
||||
|
||||
NTCPSession * GetNextNTCPSession ();
|
||||
NTCPSession * FindNTCPSession (const i2p::data::IdentHash& ident);
|
||||
|
||||
void SendMessage (const i2p::data::IdentHash& ident, i2p::I2NPMessage * msg);
|
||||
void CloseSession (const i2p::data::RouterInfo * router);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
void HandleAccept (NTCPServerConnection * conn, const boost::system::error_code& error);
|
||||
void HandleAcceptV6 (NTCPServerConnection * 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 (const i2p::data::RouterInfo * router);
|
||||
|
||||
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, 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
|
||||
618
Tunnel.cpp
618
Tunnel.cpp
@@ -1,618 +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.second;
|
||||
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 numHops)
|
||||
{
|
||||
auto pool = new TunnelPool (localDestination, numHops);
|
||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
||||
m_Pools[pool->GetIdentHash ()] = pool;
|
||||
return pool;
|
||||
}
|
||||
|
||||
void Tunnels::DeleteTunnelPool (TunnelPool * pool)
|
||||
{
|
||||
if (pool)
|
||||
{
|
||||
StopTunnelPool (pool);
|
||||
m_Pools.erase (pool->GetLocalDestination ().GetIdentHash ());
|
||||
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<const i2p::data::RouterInfo *>
|
||||
{
|
||||
i2p::data::netdb.GetRandomRouter ().get ()
|
||||
},
|
||||
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-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<const i2p::data::RouterInfo *>
|
||||
{
|
||||
i2p::data::netdb.GetRandomRouter ().get ()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
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.second;
|
||||
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<const i2p::data::RouterInfo *>
|
||||
{
|
||||
&i2p::context.GetRouterInfo ()
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
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 numHops);
|
||||
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::map<i2p::data::IdentHash, 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
|
||||
222
TunnelConfig.h
222
TunnelConfig.h
@@ -1,222 +0,0 @@
|
||||
#ifndef TUNNEL_CONFIG_H__
|
||||
#define TUNNEL_CONFIG_H__
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include "aes.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "RouterContext.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
struct TunnelHopConfig
|
||||
{
|
||||
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 (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 = 0;
|
||||
nextTunnelID = 0;
|
||||
|
||||
next = 0;
|
||||
prev = 0;
|
||||
}
|
||||
|
||||
void SetNextRouter (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<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.GetRouterInfo ());
|
||||
}
|
||||
|
||||
~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.GetRouterInfo ());
|
||||
}
|
||||
if (!hop->next) newConfig->m_FirstHop = newHop; // last hop
|
||||
|
||||
hop = hop->next;
|
||||
}
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
TunnelConfig * Clone (const TunnelConfig * replyTunnelConfig = nullptr) const
|
||||
{
|
||||
std::vector<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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
330
TunnelPool.cpp
330
TunnelPool.cpp
@@ -1,330 +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 numHops, int numTunnels):
|
||||
m_LocalDestination (localDestination), m_NumHops (numHops), 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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
m_LocalDestination.SetLeaseSetUpdated ();
|
||||
}
|
||||
else
|
||||
it.second.second->SetState (eTunnelStateTestFailed);
|
||||
}
|
||||
}
|
||||
m_Tests.clear ();
|
||||
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::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
|
||||
m_LocalDestination.ProcessDeliveryStatusMessage (msg);
|
||||
}
|
||||
|
||||
const i2p::data::RouterInfo * TunnelPool::SelectNextHop (const i2p::data::RouterInfo * prevHop) const
|
||||
{
|
||||
auto hop = m_NumHops >= 3 ? i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop).get () :
|
||||
i2p::data::netdb.GetRandomRouter (prevHop).get ();
|
||||
if (!hop)
|
||||
hop = i2p::data::netdb.GetRandomRouter ().get ();
|
||||
return hop;
|
||||
}
|
||||
|
||||
void TunnelPool::CreateInboundTunnel ()
|
||||
{
|
||||
OutboundTunnel * outboundTunnel = GetNextOutboundTunnel ();
|
||||
if (!outboundTunnel)
|
||||
outboundTunnel = tunnels.GetNextOutboundTunnel ();
|
||||
LogPrint ("Creating destination inbound tunnel...");
|
||||
const i2p::data::RouterInfo * prevHop = &i2p::context.GetRouterInfo ();
|
||||
std::vector<const i2p::data::RouterInfo *> hops;
|
||||
int numHops = m_NumHops;
|
||||
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...");
|
||||
|
||||
const i2p::data::RouterInfo * prevHop = &i2p::context.GetRouterInfo ();
|
||||
std::vector<const i2p::data::RouterInfo *> hops;
|
||||
for (int i = 0; i < m_NumHops; 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");
|
||||
}
|
||||
}
|
||||
}
|
||||
88
TunnelPool.h
88
TunnelPool.h
@@ -1,88 +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 numHops, int numTunnels = 5);
|
||||
~TunnelPool ();
|
||||
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_LocalDestination.GetEncryptionPrivateKey (); };
|
||||
const uint8_t * GetEncryptionPublicKey () const { return m_LocalDestination.GetEncryptionPublicKey (); };
|
||||
const i2p::data::LocalDestination& GetLocalDestination () const { return m_LocalDestination; };
|
||||
i2p::garlic::GarlicDestination& GetGarlicDestination () const { return m_LocalDestination; };
|
||||
bool IsExploratory () const { return GetIdentHash () == i2p::context.GetIdentHash (); };
|
||||
|
||||
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;
|
||||
const i2p::data::IdentHash& GetIdentHash () const { return m_LocalDestination.GetIdentHash (); };
|
||||
|
||||
void TestTunnels ();
|
||||
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;
|
||||
const i2p::data::RouterInfo * SelectNextHop (const i2p::data::RouterInfo * prevHop) const;
|
||||
|
||||
private:
|
||||
|
||||
i2p::garlic::GarlicDestination& m_LocalDestination;
|
||||
int m_NumHops, 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
|
||||
113
Win32/DaemonWin32.cpp
Normal file
113
Win32/DaemonWin32.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#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");
|
||||
setlocale(LC_TIME, "C");
|
||||
|
||||
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");
|
||||
setlocale(LC_TIME, "C");
|
||||
#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.0
|
||||
Name PurpleI2P
|
||||
|
||||
RequestExecutionLevel highest
|
||||
SetCompressor /SOLID lzma
|
||||
ShowInstDetails show
|
||||
|
||||
# General Symbol Definitions
|
||||
!define REGKEY "SOFTWARE\$(^Name)"
|
||||
!define VERSION 0.2.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.2.0.0-win32-setup.exe
|
||||
InstallDir $PROGRAMFILES\PurpleI2P
|
||||
CRCCheck on
|
||||
XPStyle on
|
||||
BrandingText " "
|
||||
ShowInstDetails show
|
||||
VIProductVersion 0.2.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,281 +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="..\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="..\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.19.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 |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user