mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
6487 Commits
0.10.0
...
cd2cf62373
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd2cf62373 | ||
|
|
3d19fa12f6 | ||
|
|
48aaecacce | ||
|
|
4bb86b6a86 | ||
|
|
0588116489 | ||
|
|
78a37cc00f | ||
|
|
fb90b01f6c | ||
|
|
ea55215668 | ||
|
|
58a86fa2dc | ||
|
|
8a7c4040b6 | ||
|
|
ea279ef45a | ||
|
|
8aa18add4b | ||
|
|
5ff52e6c93 | ||
|
|
ef19a85fc0 | ||
|
|
2ce3145195 | ||
|
|
b8d74dab47 | ||
|
|
e8f5efd156 | ||
|
|
57aa8b3de8 | ||
|
|
972b66f9a5 | ||
|
|
eadeea76e7 | ||
|
|
da7d3c55b0 | ||
|
|
ff0b6a6a6a | ||
|
|
60d3e4d963 | ||
|
|
adc230acde | ||
|
|
e4ba07a540 | ||
|
|
93ec5ac5c4 | ||
|
|
774c606b09 | ||
|
|
1bff42042d | ||
|
|
daeb177579 | ||
|
|
5d7a062f1b | ||
|
|
35f7bd5127 | ||
|
|
d411da451a | ||
|
|
45bab06f37 | ||
|
|
588855c6a7 | ||
|
|
c3fa0ae8cc | ||
|
|
bf85a69a2f | ||
|
|
72ff0b9fbb | ||
|
|
b9c9988ff4 | ||
|
|
1bb5ad22af | ||
|
|
4fa5cec0dc | ||
|
|
1e7254dfaa | ||
|
|
ca0818af7e | ||
|
|
b3d09513b8 | ||
|
|
2857a163e9 | ||
|
|
cba7e5350d | ||
|
|
29a5effabb | ||
|
|
39e07ac265 | ||
|
|
57986bd348 | ||
|
|
5e301937f2 | ||
|
|
4edde333ad | ||
|
|
c600b834e3 | ||
|
|
b6319d78bf | ||
|
|
e4fc2789fe | ||
|
|
4c5a1e064d | ||
|
|
4bb82110ab | ||
|
|
8c555fe592 | ||
|
|
5f1c599f81 | ||
|
|
f2b5606583 | ||
|
|
08a680b53d | ||
|
|
634ceceb1c | ||
|
|
efd8e6e65b | ||
|
|
915429bb49 | ||
|
|
3e3e0e0a62 | ||
|
|
c023051fe4 | ||
|
|
0b788de627 | ||
|
|
fce4fab071 | ||
|
|
3236de0d5a | ||
|
|
18707dd844 | ||
|
|
fc16a70f7b | ||
|
|
619ec5d9c1 | ||
|
|
1293e122bc | ||
|
|
24bcc651e0 | ||
|
|
8713974f40 | ||
|
|
6e639f0e6a | ||
|
|
d48bf33fc5 | ||
|
|
0f14f9a302 | ||
|
|
55708d2a6d | ||
|
|
3bdfa5562b | ||
|
|
3995448014 | ||
|
|
7497741846 | ||
|
|
36939898fe | ||
|
|
b4bcd9914a | ||
|
|
833e0a936e | ||
|
|
bdc5eaa824 | ||
|
|
e76d09e1a1 | ||
|
|
3264704a23 | ||
|
|
cec68a2447 | ||
|
|
73ba1afc20 | ||
|
|
dcbe6cfaf2 | ||
|
|
3534b9c499 | ||
|
|
a7021a8283 | ||
|
|
946e523554 | ||
|
|
cdd528c51f | ||
|
|
1a748aebf1 | ||
|
|
f23a7f569b | ||
|
|
48b62340cc | ||
|
|
65da550d19 | ||
|
|
786da057f2 | ||
|
|
097813a6ca | ||
|
|
226257aa71 | ||
|
|
13604ccbb6 | ||
|
|
e996db03c0 | ||
|
|
f79a2e81ff | ||
|
|
4b1ac7420c | ||
|
|
e518b92a89 | ||
|
|
1a32ed9088 | ||
|
|
b17bbd754a | ||
|
|
7b0ff2850c | ||
|
|
31ff0ff1cb | ||
|
|
fcc70025fd | ||
|
|
56145d0f3c | ||
|
|
8b9f427aa4 | ||
|
|
cc768de8ea | ||
|
|
ffd18baf30 | ||
|
|
3474538697 | ||
|
|
5f1b31213f | ||
|
|
6fb3c7c3ba | ||
|
|
a248a2a732 | ||
|
|
09ae278306 | ||
|
|
d241e5d5cb | ||
|
|
b80278421d | ||
|
|
f2596e0187 | ||
|
|
5265dc71e9 | ||
|
|
a05bb93792 | ||
|
|
5d5970bed4 | ||
|
|
86080b26ae | ||
|
|
5b2d0c579b | ||
|
|
3c4926f377 | ||
|
|
391e3b7814 | ||
|
|
72a39609ed | ||
|
|
0c5f39ad81 | ||
|
|
3c608ec07c | ||
|
|
ce0461bf86 | ||
|
|
ce96f93c80 | ||
|
|
d88ba768d7 | ||
|
|
574d12298b | ||
|
|
7285caa4f1 | ||
|
|
2778b092e3 | ||
|
|
dbef3fe9d2 | ||
|
|
09b7d44dad | ||
|
|
a411fff1d9 | ||
|
|
e574354896 | ||
|
|
0a08383471 | ||
|
|
c5e464a8b5 | ||
|
|
002d8c7773 | ||
|
|
32921ead80 | ||
|
|
be24a3e336 | ||
|
|
d99a7d9b20 | ||
|
|
2f6bdd1c84 | ||
|
|
5a4ce66d42 | ||
|
|
76190ea365 | ||
|
|
f90386803f | ||
|
|
29d77113cc | ||
|
|
0d09a8be00 | ||
|
|
b8d61e04f0 | ||
|
|
4432c5a2c4 | ||
|
|
2419f52af4 | ||
|
|
b2a10ac82b | ||
|
|
0086f8e27a | ||
|
|
8a8277edda | ||
|
|
3f10f6651d | ||
|
|
9bc595a9a2 | ||
|
|
f04048717d | ||
|
|
361f364966 | ||
|
|
4c90a88b85 | ||
|
|
23e66671c2 | ||
|
|
ec67f48d85 | ||
|
|
3a229ea65c | ||
|
|
0e8d624d86 | ||
|
|
8f9874570a | ||
|
|
43939cedf4 | ||
|
|
4c66608caf | ||
|
|
ec4fe9a1e6 | ||
|
|
79e8ccbb5b | ||
|
|
608056dcd2 | ||
|
|
7461b640e3 | ||
|
|
743126b2ad | ||
|
|
f611136ea7 | ||
|
|
87ae9c4b74 | ||
|
|
d3630fb2b2 | ||
|
|
500afe745f | ||
|
|
26901e2945 | ||
|
|
64bde69967 | ||
|
|
ddf30784ec | ||
|
|
ea14b00d63 | ||
|
|
a24e0eb2dc | ||
|
|
0cb677a2c0 | ||
|
|
e6cbc842bf | ||
|
|
f087654f25 | ||
|
|
10335b90c5 | ||
|
|
8a234f70e6 | ||
|
|
f98a310235 | ||
|
|
1419745a5d | ||
|
|
890fe77b10 | ||
|
|
bc9d25ec3b | ||
|
|
fe71776b6f | ||
|
|
0213f058d1 | ||
|
|
0ccf0a6339 | ||
|
|
e26682f4cb | ||
|
|
8981e406f5 | ||
|
|
50d9252ba9 | ||
|
|
4f73f60e51 | ||
|
|
d69e957213 | ||
|
|
97fdedfbe3 | ||
|
|
ec1f41b13c | ||
|
|
7104d334fd | ||
|
|
4e581af3ba | ||
|
|
48f7131a7d | ||
|
|
fbd07a5276 | ||
|
|
8210911bc5 | ||
|
|
4a5406b803 | ||
|
|
ab02f722af | ||
|
|
c86e0ec371 | ||
|
|
ac4c58bbe9 | ||
|
|
23bac4a403 | ||
|
|
2321a897f5 | ||
|
|
88a5f8b125 | ||
|
|
78847306e9 | ||
|
|
1a6109109a | ||
|
|
905c6debf2 | ||
|
|
d7c4d0ff3e | ||
|
|
da3e83138a | ||
|
|
c6eba73653 | ||
|
|
0d224dfc54 | ||
|
|
dc48fb0180 | ||
|
|
cc05f9c5d9 | ||
|
|
e4c8cc300d | ||
|
|
0710f62948 | ||
|
|
58245bf121 | ||
|
|
4436c49ccc | ||
|
|
bce9630ff8 | ||
|
|
7f3a04a72f | ||
|
|
34f1ba5bd9 | ||
|
|
dc4cd34893 | ||
|
|
1fb45c4b0d | ||
|
|
514be6d048 | ||
|
|
8c292727da | ||
|
|
d5c40bb6be | ||
|
|
eed48c43fd | ||
|
|
600f36539f | ||
|
|
e1e530b4a9 | ||
|
|
98e93468a6 | ||
|
|
0f5e8d8424 | ||
|
|
d521350588 | ||
|
|
237d9474d8 | ||
|
|
5466983b36 | ||
|
|
ba41f7107d | ||
|
|
c2234599cd | ||
|
|
6ebb019e15 | ||
|
|
15cd4feade | ||
|
|
abbe1fea64 | ||
|
|
62b811c2c1 | ||
|
|
64e4b3871a | ||
|
|
c3a1631319 | ||
|
|
a06cce0aaf | ||
|
|
75b1c144b4 | ||
|
|
32ad4b4858 | ||
|
|
98669eff4f | ||
|
|
67763248cc | ||
|
|
262a803d10 | ||
|
|
0912de5b77 | ||
|
|
edb2ba7107 | ||
|
|
74f0330730 | ||
|
|
5cd0248494 | ||
|
|
816771dd00 | ||
|
|
189d7179c0 | ||
|
|
2dfc9003a7 | ||
|
|
9968afc038 | ||
|
|
5073c9637e | ||
|
|
2c594dc67a | ||
|
|
11bca5c3cd | ||
|
|
9d1e526812 | ||
|
|
018fa0ec00 | ||
|
|
f733f0a636 | ||
|
|
fd2b15fe81 | ||
|
|
c8958d71a2 | ||
|
|
e4962b855f | ||
|
|
9f30499984 | ||
|
|
5324197e43 | ||
|
|
715e063550 | ||
|
|
7ef1fdf634 | ||
|
|
db19c32381 | ||
|
|
ac1c28cb39 | ||
|
|
2fa4237acd | ||
|
|
ae26758170 | ||
|
|
a723405fb0 | ||
|
|
f20391d460 | ||
|
|
ca4db7aab2 | ||
|
|
13b2fc3266 | ||
|
|
d4c1a1c0bb | ||
|
|
d5aca85a35 | ||
|
|
17d0e59d02 | ||
|
|
d20475e3d0 | ||
|
|
ebec4d8a5e | ||
|
|
cb0801fc16 | ||
|
|
a5e9d9c6a3 | ||
|
|
3d0a1afd64 | ||
|
|
78ec5b2c6e | ||
|
|
272bf7dbc1 | ||
|
|
261acbbd66 | ||
|
|
a65dd218da | ||
|
|
50d297fa29 | ||
|
|
699e17b594 | ||
|
|
a91caa6559 | ||
|
|
07d108bb6f | ||
|
|
bcace3fb29 | ||
|
|
cd648b9b3f | ||
|
|
ba451eeca5 | ||
|
|
8d1c186665 | ||
|
|
d539c9677e | ||
|
|
855fd4d471 | ||
|
|
e0af7b077f | ||
|
|
fde301deaf | ||
|
|
9a77c0a4b1 | ||
|
|
306ea2df37 | ||
|
|
cab671e177 | ||
|
|
2ee5af0c06 | ||
|
|
911620bcd3 | ||
|
|
d1620d70bb | ||
|
|
53db54dafb | ||
|
|
ead1b72886 | ||
|
|
ae65af07c2 | ||
|
|
0046a8b3ec | ||
|
|
06e3a1b57a | ||
|
|
cc59003560 | ||
|
|
a3e0b3710c | ||
|
|
56b8534e0c | ||
|
|
c21cf0565b | ||
|
|
9668ea9338 | ||
|
|
a837e5c502 | ||
|
|
bbadbdbfdb | ||
|
|
509c039e2f | ||
|
|
8cf9cc1a01 | ||
|
|
a1f40d3048 | ||
|
|
83c0764ed4 | ||
|
|
2f5f39aaf2 | ||
|
|
5cc15fac31 | ||
|
|
ea3f356856 | ||
|
|
8189ff0f48 | ||
|
|
2679e8cfd8 | ||
|
|
3679c6aea0 | ||
|
|
604bdf314f | ||
|
|
937809bc0f | ||
|
|
d71f3d40fa | ||
|
|
e87ace0c3d | ||
|
|
bc48e6881d | ||
|
|
e957d7bbfb | ||
|
|
b3aa5ad998 | ||
|
|
ac876a0cd5 | ||
|
|
d85cb6e30a | ||
|
|
4a4b76141a | ||
|
|
a93043f064 | ||
|
|
ae309ca632 | ||
|
|
9037e8b2b1 | ||
|
|
3ff79038b5 | ||
|
|
da0e527777 | ||
|
|
66223792f3 | ||
|
|
a69eade1f4 | ||
|
|
0992a5124f | ||
|
|
e7423b1ffc | ||
|
|
65ceb08290 | ||
|
|
879d54fad4 | ||
|
|
ff5c76f8f2 | ||
|
|
0191e58b05 | ||
|
|
c43926083e | ||
|
|
bd98f2c3ee | ||
|
|
02c52f59cb | ||
|
|
fa218d3cf5 | ||
|
|
d169b422da | ||
|
|
7be64dad89 | ||
|
|
3720a5fce3 | ||
|
|
0df895b6a7 | ||
|
|
32ab95478e | ||
|
|
28adb54c0a | ||
|
|
b4fcf76480 | ||
|
|
fb8e0e1b5b | ||
|
|
41dd8b527d | ||
|
|
7376f7c399 | ||
|
|
d47ae3012a | ||
|
|
09dbe9fc03 | ||
|
|
11328a429d | ||
|
|
0c924836cf | ||
|
|
52a313bb65 | ||
|
|
d75f15104e | ||
|
|
b306bf2db9 | ||
|
|
349c4e30b6 | ||
|
|
3c69e0b2af | ||
|
|
8e1fb8ca9f | ||
|
|
42782944fb | ||
|
|
efd754eb93 | ||
|
|
81cc3e3de8 | ||
|
|
db4208e2e2 | ||
|
|
fe740249a5 | ||
|
|
4ad6cef5a5 | ||
|
|
86f86fc711 | ||
|
|
e5dac605f6 | ||
|
|
ab1abf584f | ||
|
|
8a3d6ddb3e | ||
|
|
1410fa5c21 | ||
|
|
ea19d2296c | ||
|
|
3a4833aa67 | ||
|
|
4a66624b04 | ||
|
|
0153748134 | ||
|
|
d3062d2994 | ||
|
|
d7ff459f12 | ||
|
|
e0ac8a7298 | ||
|
|
96ea630274 | ||
|
|
f232c8f2df | ||
|
|
2f54d95187 | ||
|
|
830e49f2c5 | ||
|
|
23e323438a | ||
|
|
48f1514053 | ||
|
|
a1aa6c62d7 | ||
|
|
9e1ea289c2 | ||
|
|
f6ddcd432e | ||
|
|
9a6654943d | ||
|
|
bed5a18294 | ||
|
|
45221da1dc | ||
|
|
8440633614 | ||
|
|
50f455e0a2 | ||
|
|
5fbcfadd6d | ||
|
|
5af13849a9 | ||
|
|
a9c486d7a1 | ||
|
|
8c0a1197d7 | ||
|
|
4e5f5c218a | ||
|
|
64cc59d1e9 | ||
|
|
0c943f4405 | ||
|
|
37d3d9e604 | ||
|
|
d23451fdf6 | ||
|
|
d843502832 | ||
|
|
0428b5ece1 | ||
|
|
df787060c3 | ||
|
|
ea9c69cd53 | ||
|
|
199d149bed | ||
|
|
ff8941af71 | ||
|
|
f125936b2e | ||
|
|
697d831441 | ||
|
|
457b64f92d | ||
|
|
6caec6b551 | ||
|
|
362edc68ad | ||
|
|
29872fc003 | ||
|
|
81d383c99e | ||
|
|
12653f2fe4 | ||
|
|
43f5ba286c | ||
|
|
f990a2f69f | ||
|
|
0b97b4294c | ||
|
|
4178ac8eac | ||
|
|
6a590bf970 | ||
|
|
2f847d62bb | ||
|
|
df6d48dbae | ||
|
|
bacce7dc60 | ||
|
|
b3314380cc | ||
|
|
d4eea61b82 | ||
|
|
a1995c13cd | ||
|
|
bc8adf1433 | ||
|
|
a1322d4667 | ||
|
|
4100249313 | ||
|
|
acbd3f897b | ||
|
|
7dc5a04b8d | ||
|
|
03635f4444 | ||
|
|
0fae04f96a | ||
|
|
bb531a878d | ||
|
|
0f7db8e418 | ||
|
|
9a724b2af9 | ||
|
|
f4ea6138e8 | ||
|
|
e74272781f | ||
|
|
b75e418879 | ||
|
|
927123188c | ||
|
|
c00eb8cf44 | ||
|
|
265bb8b779 | ||
|
|
e3be409945 | ||
|
|
d8707ceb57 | ||
|
|
39e16824b9 | ||
|
|
285e693a4e | ||
|
|
940628bf36 | ||
|
|
b5994e058a | ||
|
|
22dabfd79e | ||
|
|
0e41c3fa36 | ||
|
|
124698854f | ||
|
|
f223e668ce | ||
|
|
f5b823a712 | ||
|
|
4163542125 | ||
|
|
6921c8391e | ||
|
|
47a2020472 | ||
|
|
9d38facf3b | ||
|
|
a1be1aa9ec | ||
|
|
c4bbe2bb4a | ||
|
|
601695dede | ||
|
|
cdc81e19a0 | ||
|
|
b98b3a87b0 | ||
|
|
425ef2cfe5 | ||
|
|
c454685605 | ||
|
|
c98926abf2 | ||
|
|
7aacc97351 | ||
|
|
c49e17ad40 | ||
|
|
296b721929 | ||
|
|
d0cf385f4b | ||
|
|
b91f5a7430 | ||
|
|
4d8431907d | ||
|
|
b0cf5130a2 | ||
|
|
396aa6944d | ||
|
|
8a20d3219b | ||
|
|
13a746162a | ||
|
|
7e5370fbe5 | ||
|
|
ec59308fad | ||
|
|
5ed76b997c | ||
|
|
535fbdb4c9 | ||
|
|
034332a0ef | ||
|
|
a1eac6f28e | ||
|
|
ba22a940f1 | ||
|
|
8439f6dc57 | ||
|
|
a21bec0ed8 | ||
|
|
5adbc2c3fe | ||
|
|
c515f49903 | ||
|
|
845b14f581 | ||
|
|
cdfdfc9e24 | ||
|
|
62d279e1b0 | ||
|
|
a1fcd8af39 | ||
|
|
8c6c954ea2 | ||
|
|
77bb7432bc | ||
|
|
720ffa8a31 | ||
|
|
c1c69258c3 | ||
|
|
733a4a2869 | ||
|
|
ca3ac8c11d | ||
|
|
d4e3991257 | ||
|
|
648a884a18 | ||
|
|
8fe989050e | ||
|
|
bb6212ccc1 | ||
|
|
5f39f65540 | ||
|
|
146b3f52c0 | ||
|
|
cc75ccd070 | ||
|
|
0ddc514221 | ||
|
|
d3b699d7cd | ||
|
|
6592fab41c | ||
|
|
02895d4cf5 | ||
|
|
8b7941c4ce | ||
|
|
de673464d1 | ||
|
|
6ce2c30522 | ||
|
|
c5a1e8cac8 | ||
|
|
f67c38d8d2 | ||
|
|
1f1a3270f7 | ||
|
|
ffee29272f | ||
|
|
2d2469c23d | ||
|
|
8e80a8b06f | ||
|
|
26fac94d05 | ||
|
|
9a30068ae5 | ||
|
|
46c72a7137 | ||
|
|
04bccedd9b | ||
|
|
b2e21a4f12 | ||
|
|
57e46ba0cf | ||
|
|
df3dc1f574 | ||
|
|
1b5f67e185 | ||
|
|
89064b6fb4 | ||
|
|
c49dd712de | ||
|
|
4f1cb74f75 | ||
|
|
75df8d3c7b | ||
|
|
85be76b01a | ||
|
|
835c480269 | ||
|
|
ac9d92c681 | ||
|
|
a30d1972e5 | ||
|
|
0c2330bf14 | ||
|
|
a703d31893 | ||
|
|
4f8f3a386f | ||
|
|
83f0b9c041 | ||
|
|
cf77be0eeb | ||
|
|
b2aa34baa6 | ||
|
|
4def0b6ea5 | ||
|
|
25592a00b6 | ||
|
|
edaf162f9c | ||
|
|
e7ff15c573 | ||
|
|
161ff3579b | ||
|
|
f2085ecc8d | ||
|
|
0c5dee69ba | ||
|
|
d74033dd2b | ||
|
|
5412e29ff5 | ||
|
|
0236769134 | ||
|
|
530a078535 | ||
|
|
fbca27fe73 | ||
|
|
08cc256c54 | ||
|
|
6432963294 | ||
|
|
59beb5e4e4 | ||
|
|
217aa0c882 | ||
|
|
e889dc1508 | ||
|
|
0141489d34 | ||
|
|
2d06c0cbe6 | ||
|
|
51446f0324 | ||
|
|
66d0b7aec4 | ||
|
|
92b49fb969 | ||
|
|
17dd5c1285 | ||
|
|
ce97ec1534 | ||
|
|
3ceb64db2e | ||
|
|
bb702700f7 | ||
|
|
ff3fec9a00 | ||
|
|
3873e60cbb | ||
|
|
2dbf094433 | ||
|
|
e85e96bc35 | ||
|
|
98543af92b | ||
|
|
af0d853ccd | ||
|
|
20a5e19ea1 | ||
|
|
f1058410fb | ||
|
|
6ba42a0912 | ||
|
|
1292ec67c0 | ||
|
|
2f2f14e3a7 | ||
|
|
edd9dd2c39 | ||
|
|
f8722f17c6 | ||
|
|
38cc01e13d | ||
|
|
6ca266ff3b | ||
|
|
b9773c88e4 | ||
|
|
3311fe62bb | ||
|
|
a284c85153 | ||
|
|
e5f75eb61c | ||
|
|
89f9bec49a | ||
|
|
dbc3952654 | ||
|
|
f3c052ed0c | ||
|
|
692f495adc | ||
|
|
aa1de7fe94 | ||
|
|
ca45fe73e9 | ||
|
|
a8af683643 | ||
|
|
b86c83a068 | ||
|
|
6656ef3c8d | ||
|
|
6898d04a1d | ||
|
|
3215125950 | ||
|
|
612f51ba7f | ||
|
|
3d03732555 | ||
|
|
d6d440ba8a | ||
|
|
821a76a7c5 | ||
|
|
34154596f2 | ||
|
|
a1dce017f4 | ||
|
|
78af34237e | ||
|
|
8874ea8033 | ||
|
|
6bd1ee36f7 | ||
|
|
f07c4bd1dd | ||
|
|
8524a67895 | ||
|
|
43d880752e | ||
|
|
d4246edb82 | ||
|
|
fd4513ebb2 | ||
|
|
ee8449fa05 | ||
|
|
5415598f60 | ||
|
|
7d73c304b5 | ||
|
|
d25206abce | ||
|
|
5d7c6fb0b3 | ||
|
|
36a060d50f | ||
|
|
577ed56af0 | ||
|
|
695dc96a83 | ||
|
|
e5251bf3c3 | ||
|
|
2692aef53d | ||
|
|
d524105727 | ||
|
|
26463c50fc | ||
|
|
b092e712ec | ||
|
|
cb8fbb0135 | ||
|
|
19e23b34da | ||
|
|
a4a3f8e96b | ||
|
|
f2b720617c | ||
|
|
d677d67676 | ||
|
|
7e3157b162 | ||
|
|
2b6a95cbee | ||
|
|
900153765a | ||
|
|
441e847de8 | ||
|
|
6439e227f6 | ||
|
|
def404b61a | ||
|
|
d8be5b8ce1 | ||
|
|
dddbca6ffb | ||
|
|
56619caa71 | ||
|
|
0e502c49b5 | ||
|
|
710b27688b | ||
|
|
a9ad6fc31e | ||
|
|
967627e58a | ||
|
|
7691a5b4a9 | ||
|
|
d9b6262a6e | ||
|
|
075f80aea2 | ||
|
|
b07530a8a1 | ||
|
|
0ae7931a6f | ||
|
|
158160f5c0 | ||
|
|
4cb2ad48be | ||
|
|
adba3987f8 | ||
|
|
36dbc15bca | ||
|
|
d96803a290 | ||
|
|
592d6ae4f4 | ||
|
|
7dd9a7a0af | ||
|
|
1b23aa2d7b | ||
|
|
f980277552 | ||
|
|
586695673b | ||
|
|
c158bbe90a | ||
|
|
c01fd3299f | ||
|
|
f64b136f5a | ||
|
|
37e67cbcaa | ||
|
|
be815804e6 | ||
|
|
ce35637866 | ||
|
|
de2b0f6e09 | ||
|
|
7b776666a3 | ||
|
|
47578b69c6 | ||
|
|
8f28cee32f | ||
|
|
96cf6ca531 | ||
|
|
83cb3a1820 | ||
|
|
ffdd5935e9 | ||
|
|
2e9f2d4a3b | ||
|
|
0ca782ed71 | ||
|
|
e40b656ecf | ||
|
|
85f5f5b91e | ||
|
|
f008478505 | ||
|
|
d926a31064 | ||
|
|
822cb35efe | ||
|
|
faaa8115d9 | ||
|
|
81015a5228 | ||
|
|
a3246cd9dc | ||
|
|
530c353b00 | ||
|
|
75c2cb751f | ||
|
|
67c4d4bcaa | ||
|
|
25e82105b2 | ||
|
|
4b167fdbaf | ||
|
|
5b93558bd0 | ||
|
|
70639f1139 | ||
|
|
c5a1806528 | ||
|
|
a2249f0a82 | ||
|
|
9f217f8a11 | ||
|
|
5e19e361e7 | ||
|
|
0b47f65b06 | ||
|
|
140146e433 | ||
|
|
b4484c8e8f | ||
|
|
1e5604ed43 | ||
|
|
39e378a03d | ||
|
|
7cfcb12c7b | ||
|
|
49f4dc53ad | ||
|
|
4afdca090d | ||
|
|
d8f6c4a93d | ||
|
|
d724948d03 | ||
|
|
34d75b08dd | ||
|
|
ca9782dd0d | ||
|
|
2ad26dd4c9 | ||
|
|
828facab57 | ||
|
|
cd087568b5 | ||
|
|
dfe8b25e5e | ||
|
|
1e9bcd6b8b | ||
|
|
8bc58daa5a | ||
|
|
3b97feb89f | ||
|
|
a8135b8d18 | ||
|
|
5cf1961fa4 | ||
|
|
577c71b930 | ||
|
|
c5cab05a6b | ||
|
|
b855c71891 | ||
|
|
21f41a2b2a | ||
|
|
8319dd6b25 | ||
|
|
d4c47d90cb | ||
|
|
302af823a3 | ||
|
|
69ee6112b3 | ||
|
|
816a58f292 | ||
|
|
43e130ee34 | ||
|
|
8ffc1486a4 | ||
|
|
0e98dd5c70 | ||
|
|
beffdb9fe1 | ||
|
|
05eda2bc9e | ||
|
|
c3429bb1a9 | ||
|
|
53c6b10177 | ||
|
|
c2d7133174 | ||
|
|
9b82265cd8 | ||
|
|
c802c2deb0 | ||
|
|
c215f2e8d1 | ||
|
|
d327533b56 | ||
|
|
94255ebaf4 | ||
|
|
21259204b1 | ||
|
|
8d6eb5b6b2 | ||
|
|
9632e7ba03 | ||
|
|
1a02819187 | ||
|
|
6b33250c59 | ||
|
|
083034fa35 | ||
|
|
0021501d75 | ||
|
|
379be2a29e | ||
|
|
a3f62e1d71 | ||
|
|
a6ee1e648e | ||
|
|
575268d360 | ||
|
|
69b0bef206 | ||
|
|
e1b4feb618 | ||
|
|
3286bdb4a7 | ||
|
|
c8ae15041f | ||
|
|
e090b9052a | ||
|
|
0ef9c08326 | ||
|
|
d04b19d77c | ||
|
|
04adc14b76 | ||
|
|
0dee0609ea | ||
|
|
0a519d8072 | ||
|
|
0cc91dd2d2 | ||
|
|
606e35eec1 | ||
|
|
e926b0392f | ||
|
|
9980bfa0e7 | ||
|
|
efdbe26755 | ||
|
|
bb3ae8f5e4 | ||
|
|
1e6edf06a2 | ||
|
|
0cf656cd76 | ||
|
|
fb420bb563 | ||
|
|
3da5061426 | ||
|
|
dd32f1ed6d | ||
|
|
1857f14be8 | ||
|
|
9c25a88707 | ||
|
|
c599d65307 | ||
|
|
4d573f6655 | ||
|
|
3a4238729a | ||
|
|
df6bb6d9b8 | ||
|
|
918aa556ef | ||
|
|
1e7feae0df | ||
|
|
70829ee79e | ||
|
|
4ed19c05f6 | ||
|
|
4a5e16b994 | ||
|
|
c53ad2012c | ||
|
|
7b6aa41ca8 | ||
|
|
32c5ff23a6 | ||
|
|
c112276eea | ||
|
|
662a59d0fd | ||
|
|
72919ec076 | ||
|
|
627b8dca83 | ||
|
|
7bcc905f05 | ||
|
|
8447822c35 | ||
|
|
411063e3b5 | ||
|
|
9bac680f2a | ||
|
|
38795a41cb | ||
|
|
e170c39dfc | ||
|
|
68f4961f1a | ||
|
|
ae5239de43 | ||
|
|
2064504cce | ||
|
|
4aa631c33f | ||
|
|
d6834d6a9a | ||
|
|
413e25f20e | ||
|
|
b8e19bf5f1 | ||
|
|
c7efd465fa | ||
|
|
6e9a3422e9 | ||
|
|
d3cd8517b7 | ||
|
|
f13cc0b862 | ||
|
|
0754255c1f | ||
|
|
902899ae24 | ||
|
|
c620fc1232 | ||
|
|
e7157cf15e | ||
|
|
940a97db11 | ||
|
|
8e63f8f333 | ||
|
|
5e97b54d1b | ||
|
|
ea7cf1cf69 | ||
|
|
17c4038c60 | ||
|
|
5022a9c610 | ||
|
|
ca8ec6286a | ||
|
|
5142459e12 | ||
|
|
8590dbd3d3 | ||
|
|
c4be5f7fdb | ||
|
|
47dc5591b9 | ||
|
|
2bb48b4546 | ||
|
|
638e9b4d7f | ||
|
|
b8f998f76a | ||
|
|
5f43026986 | ||
|
|
13f263b791 | ||
|
|
07c529173e | ||
|
|
fee940238a | ||
|
|
ca02b5d860 | ||
|
|
fba23a4528 | ||
|
|
a0795d8534 | ||
|
|
03cc6e0524 | ||
|
|
3af1f4bc76 | ||
|
|
52b2d6c393 | ||
|
|
bb52056aec | ||
|
|
75aa9f2c0c | ||
|
|
4e426727e9 | ||
|
|
09f233dbfb | ||
|
|
dfe5df29e1 | ||
|
|
d5ee1f602f | ||
|
|
86173400d5 | ||
|
|
ae439b5385 | ||
|
|
dc6a42c26f | ||
|
|
fdf38f45d9 | ||
|
|
a2726cf206 | ||
|
|
e56d243c3f | ||
|
|
e95fb3ab89 | ||
|
|
c984f89dfb | ||
|
|
5a09c11e90 | ||
|
|
d44be2fd73 | ||
|
|
a158647153 | ||
|
|
623c3f4605 | ||
|
|
8a52295882 | ||
|
|
f2bc2598dc | ||
|
|
1d8a91c5cc | ||
|
|
3bc56ba423 | ||
|
|
e068a3cf22 | ||
|
|
2e2cbe7803 | ||
|
|
1c1452e06b | ||
|
|
c757b6d020 | ||
|
|
8fcab7b0c1 | ||
|
|
7646147ed2 | ||
|
|
b6de474fda | ||
|
|
2f74e670a5 | ||
|
|
c1168c2aa0 | ||
|
|
1bd6390f78 | ||
|
|
a9e9e14c42 | ||
|
|
19471dbb90 | ||
|
|
3b13a3f2a1 | ||
|
|
4ce2ef1d83 | ||
|
|
12d0abda55 | ||
|
|
7418f11651 | ||
|
|
fb83ca42f4 | ||
|
|
dab34e9051 | ||
|
|
ec2297ed9d | ||
|
|
7c535159bc | ||
|
|
2af4a2b58d | ||
|
|
ead6a6dca3 | ||
|
|
0ce15ffc91 | ||
|
|
76adac31c3 | ||
|
|
edfcd23b01 | ||
|
|
f21e1c75d5 | ||
|
|
786c27c8ec | ||
|
|
c34df2090c | ||
|
|
5769a41208 | ||
|
|
dc265367dc | ||
|
|
c22fc75370 | ||
|
|
6206616347 | ||
|
|
7a12b5ca4b | ||
|
|
b77ae08388 | ||
|
|
527ee3b3c5 | ||
|
|
cd5bfaabb5 | ||
|
|
e20acb93cf | ||
|
|
132557f941 | ||
|
|
b8d21a1282 | ||
|
|
4ebc7c970a | ||
|
|
572694b141 | ||
|
|
c1f19cb258 | ||
|
|
caff003a85 | ||
|
|
d580c0155a | ||
|
|
9475a22728 | ||
|
|
245e6b6efd | ||
|
|
e3fb9d8483 | ||
|
|
220ef283de | ||
|
|
8178df752b | ||
|
|
922e5915b1 | ||
|
|
dc6499aa98 | ||
|
|
0c8a80b6f8 | ||
|
|
7c2da75197 | ||
|
|
b8032e7fbf | ||
|
|
b42be2b391 | ||
|
|
a3bdc4ddc1 | ||
|
|
2abc997af8 | ||
|
|
710a35993d | ||
|
|
e36d5634e7 | ||
|
|
354a04f0f6 | ||
|
|
a80aeb6715 | ||
|
|
cd1af85e39 | ||
|
|
60e648bf9a | ||
|
|
c45e31b1b2 | ||
|
|
6d204b4d7b | ||
|
|
71bad23906 | ||
|
|
d91d734b5c | ||
|
|
5fee6df87a | ||
|
|
644d65054d | ||
|
|
f41563a700 | ||
|
|
32b54fa1f8 | ||
|
|
084663d6ea | ||
|
|
55b2f2c625 | ||
|
|
5ad9c8e740 | ||
|
|
e96f0bfb14 | ||
|
|
8677cd54bd | ||
|
|
b97ef1af49 | ||
|
|
227697c388 | ||
|
|
a6bd8275ca | ||
|
|
6f01ebc0a5 | ||
|
|
98f06e3ba1 | ||
|
|
b6d1d8117b | ||
|
|
7b35c793f3 | ||
|
|
58ef08310d | ||
|
|
ec5c13a95e | ||
|
|
0a564d153a | ||
|
|
0a8da6bc2f | ||
|
|
46e4f4aea5 | ||
|
|
6d7f20961f | ||
|
|
4b7fcdc719 | ||
|
|
716926f0d7 | ||
|
|
24a14e3440 | ||
|
|
5470a3a453 | ||
|
|
c02c9c3c24 | ||
|
|
3154eda6a6 | ||
|
|
d50cf0ad83 | ||
|
|
d6ed9152ee | ||
|
|
a4759694c8 | ||
|
|
6610af08c2 | ||
|
|
4db643aa8e | ||
|
|
75c9f596b2 | ||
|
|
064c013a72 | ||
|
|
75ab385ab7 | ||
|
|
9e0389df1b | ||
|
|
1b921a2eac | ||
|
|
f2729b3efe | ||
|
|
231c02c058 | ||
|
|
a199084c99 | ||
|
|
64f0a545fd | ||
|
|
34617bcb44 | ||
|
|
5f8820d9d6 | ||
|
|
10dfe39f5f | ||
|
|
b7f0d87daf | ||
|
|
3ff1adf597 | ||
|
|
93d89a1fe0 | ||
|
|
3c9a574e90 | ||
|
|
de82b3ae19 | ||
|
|
4b421d3feb | ||
|
|
f84d88ac63 | ||
|
|
2e62f9532f | ||
|
|
5657079355 | ||
|
|
bd63383d7f | ||
|
|
db0f2fab11 | ||
|
|
f33b0cad2e | ||
|
|
e3fbc246f4 | ||
|
|
d602ea1061 | ||
|
|
d40cd00cdb | ||
|
|
6930106d26 | ||
|
|
692600dfac | ||
|
|
95cc544c92 | ||
|
|
8544283110 | ||
|
|
abf687ff09 | ||
|
|
be2629aa5a | ||
|
|
ebee6c5f13 | ||
|
|
3adff82d4a | ||
|
|
200ad5524d | ||
|
|
82ac0aa492 | ||
|
|
b8a99878b6 | ||
|
|
eaca435a5b | ||
|
|
ce05cce331 | ||
|
|
e190dab7d6 | ||
|
|
289c308600 | ||
|
|
bff12b06f4 | ||
|
|
b8590075e6 | ||
|
|
1da9e2e1c0 | ||
|
|
a1fb97cfad | ||
|
|
e53fe2d988 | ||
|
|
182a721674 | ||
|
|
00c75a7afa | ||
|
|
385e592045 | ||
|
|
359781c698 | ||
|
|
c6c7191682 | ||
|
|
fa286a6fb3 | ||
|
|
02a36a9fa8 | ||
|
|
77142e59ee | ||
|
|
4156900fb5 | ||
|
|
76d39cc7c9 | ||
|
|
9e0c5d67c7 | ||
|
|
03111ad0df | ||
|
|
abb81c353d | ||
|
|
1ad38c0b95 | ||
|
|
532a29b0c4 | ||
|
|
4f3a416e37 | ||
|
|
4d0ad47491 | ||
|
|
7b632bd03d | ||
|
|
c2faa5c614 | ||
|
|
d15581d95e | ||
|
|
7bde4de1f5 | ||
|
|
88f19f551d | ||
|
|
d79bdc9f66 | ||
|
|
48a3c767e5 | ||
|
|
86fc12e395 | ||
|
|
3c65012a63 | ||
|
|
97b7ea0da5 | ||
|
|
1c4cc02c23 | ||
|
|
ac94341203 | ||
|
|
234540f9e7 | ||
|
|
cb44a35fde | ||
|
|
6ac849fe37 | ||
|
|
0829b186b7 | ||
|
|
993900aa77 | ||
|
|
aac9a8d18f | ||
|
|
2baaa8bb8f | ||
|
|
f29ef2c057 | ||
|
|
d1c98c58b9 | ||
|
|
4930157e93 | ||
|
|
d30d1e8a7d | ||
|
|
80a55c5b75 | ||
|
|
3c02cade58 | ||
|
|
21123e2afa | ||
|
|
f1415c2234 | ||
|
|
212a1156a1 | ||
|
|
54fb234424 | ||
|
|
5a6b50ae51 | ||
|
|
21542e8150 | ||
|
|
544fcd147f | ||
|
|
9bc3b11b96 | ||
|
|
02e7f6b0c3 | ||
|
|
f7101cc260 | ||
|
|
e9f0ed6473 | ||
|
|
4960587f46 | ||
|
|
bf8eecf407 | ||
|
|
09a1a78bd6 | ||
|
|
7df2ed6114 | ||
|
|
22b1066b0a | ||
|
|
89695a7040 | ||
|
|
2ed281472f | ||
|
|
fafdb0c590 | ||
|
|
2a6883e305 | ||
|
|
3cee8bfcb2 | ||
|
|
86dbfdb536 | ||
|
|
70fec2bc99 | ||
|
|
8a106eb09e | ||
|
|
f9331897b8 | ||
|
|
126ca0209b | ||
|
|
ad5540c9f3 | ||
|
|
e91d0bbec8 | ||
|
|
8ee461f60a | ||
|
|
4de6201b84 | ||
|
|
273aa31b1e | ||
|
|
b31f52e332 | ||
|
|
4fae7b8d65 | ||
|
|
5479ddd03c | ||
|
|
1f23584c24 | ||
|
|
067fb45a25 | ||
|
|
ac287a896c | ||
|
|
8baf62eb2c | ||
|
|
e1ec79daf2 | ||
|
|
d4426118c5 | ||
|
|
64fe56aa07 | ||
|
|
47eb49c34e | ||
|
|
cd6d86c8c3 | ||
|
|
84d4e074ce | ||
|
|
a0e71c4173 | ||
|
|
533c8a8a55 | ||
|
|
a57ae4dc56 | ||
|
|
88dfe3ca4e | ||
|
|
d68c7f8ea7 | ||
|
|
e8ace998ba | ||
|
|
e8be39af17 | ||
|
|
7196db09d6 | ||
|
|
b290ee1aa0 | ||
|
|
d105ab11af | ||
|
|
bc888167a7 | ||
|
|
6ca6591c43 | ||
|
|
36cb707e47 | ||
|
|
013d5ff74f | ||
|
|
9af5a90757 | ||
|
|
d8b6f5438c | ||
|
|
10030a4e0d | ||
|
|
993dc72ce6 | ||
|
|
324ace103b | ||
|
|
d530269e4f | ||
|
|
7146a4dbae | ||
|
|
f79900653b | ||
|
|
f172f44f32 | ||
|
|
f34abe60fa | ||
|
|
a3c305032a | ||
|
|
2921eaa055 | ||
|
|
1cc68ea402 | ||
|
|
c18e8f6c78 | ||
|
|
e59ca8420e | ||
|
|
a6f9a56e40 | ||
|
|
4011502f5f | ||
|
|
acd6af709e | ||
|
|
55704ece3a | ||
|
|
0d3ede56cb | ||
|
|
06fae976b3 | ||
|
|
e95035c143 | ||
|
|
55be5c74f0 | ||
|
|
a1c16e129d | ||
|
|
321c7cb7cf | ||
|
|
5f8d45154d | ||
|
|
503f522cc3 | ||
|
|
22179400c7 | ||
|
|
66f82cb43f | ||
|
|
1df67bd43c | ||
|
|
d5b03f214b | ||
|
|
cfb773351d | ||
|
|
6942c20879 | ||
|
|
7b341d5d30 | ||
|
|
e93718456f | ||
|
|
af838196be | ||
|
|
f8ba5b8c63 | ||
|
|
446bbf6b93 | ||
|
|
2c19da9aa7 | ||
|
|
01fc21ffb9 | ||
|
|
3e3e2c41bd | ||
|
|
cb139226df | ||
|
|
84d6028454 | ||
|
|
126dc0ebe0 | ||
|
|
099d9d977f | ||
|
|
5a167316cb | ||
|
|
85e31f84ec | ||
|
|
edb7a0e23c | ||
|
|
f401ccf5dd | ||
|
|
9f9e8bfa14 | ||
|
|
61168d5c9d | ||
|
|
8500aaa26f | ||
|
|
445cff0025 | ||
|
|
99356fd24d | ||
|
|
5ef5f5a170 | ||
|
|
54b7d4ef32 | ||
|
|
6376328c98 | ||
|
|
b6f83dfe9f | ||
|
|
3f728149ab | ||
|
|
36501fe31e | ||
|
|
e4ddc883d2 | ||
|
|
5ac01ddce8 | ||
|
|
d3656fcb3f | ||
|
|
d6c101d261 | ||
|
|
eeea02d834 | ||
|
|
0e0cd555eb | ||
|
|
5dfe483152 | ||
|
|
c210553a39 | ||
|
|
a315e4ce62 | ||
|
|
96cfd9acc2 | ||
|
|
d869bb25ed | ||
|
|
476e6aae35 | ||
|
|
d30ee99cf1 | ||
|
|
84d9c8f1b8 | ||
|
|
9a2f744630 | ||
|
|
df737a65b2 | ||
|
|
c5230ca44b | ||
|
|
3471e6fe16 | ||
|
|
f1437feede | ||
|
|
0d523bd2a6 | ||
|
|
8943200ffa | ||
|
|
a902d68669 | ||
|
|
f6ca7c19af | ||
|
|
4540d22de8 | ||
|
|
3458665df8 | ||
|
|
8320987124 | ||
|
|
648b09d45f | ||
|
|
857df5c734 | ||
|
|
737603e81b | ||
|
|
53ca5dc67a | ||
|
|
8ad5696e50 | ||
|
|
ef9d27e424 | ||
|
|
2bb5ff7184 | ||
|
|
753c7efde8 | ||
|
|
d0d0cd8445 | ||
|
|
410d2c2fa9 | ||
|
|
6a743f66e8 | ||
|
|
709c451400 | ||
|
|
cb73c7c72e | ||
|
|
50abeea82a | ||
|
|
8db352b4d0 | ||
|
|
6589bdf6b5 | ||
|
|
1ac171152a | ||
|
|
629c718527 | ||
|
|
519c0fe81d | ||
|
|
6e3aef0b9b | ||
|
|
eb0ef80a17 | ||
|
|
9763499dbe | ||
|
|
949c38f5f0 | ||
|
|
2a6f906177 | ||
|
|
b4c226f4b3 | ||
|
|
64c3282aae | ||
|
|
f5d511ae0f | ||
|
|
aa9a9ef18d | ||
|
|
aa5e6400e4 | ||
|
|
61bcfebcc8 | ||
|
|
73b9c0302b | ||
|
|
aead9db971 | ||
|
|
d8230644b2 | ||
|
|
97ef908b0c | ||
|
|
fb8be32c28 | ||
|
|
a298588943 | ||
|
|
ccfeca728e | ||
|
|
7705423c42 | ||
|
|
379075c594 | ||
|
|
6a23153c0b | ||
|
|
74b2ba7ae2 | ||
|
|
9e02c99db5 | ||
|
|
ba3cee1cf1 | ||
|
|
9f59ff2df4 | ||
|
|
8df4082d6f | ||
|
|
cf005821d7 | ||
|
|
39b3996596 | ||
|
|
78357baca4 | ||
|
|
85b78dfb9b | ||
|
|
9fd60b52f1 | ||
|
|
851be41d0d | ||
|
|
c6a6a4e0e8 | ||
|
|
28aeebd4c7 | ||
|
|
c88638afe4 | ||
|
|
7f98a8b972 | ||
|
|
e1e4924592 | ||
|
|
8299f80ea5 | ||
|
|
b8ce0b0838 | ||
|
|
e13f151474 | ||
|
|
4ed4e8708e | ||
|
|
1738d118f7 | ||
|
|
f1f66d7b8f | ||
|
|
4ed5e44de7 | ||
|
|
3e3f92c616 | ||
|
|
5fb1247b87 | ||
|
|
016222463d | ||
|
|
0e477bf938 | ||
|
|
eb75eb0e55 | ||
|
|
2a703e0844 | ||
|
|
2b6d9eaa8b | ||
|
|
f9b0bb0383 | ||
|
|
c6e8873d57 | ||
|
|
b2767304e9 | ||
|
|
3d4d3ce80d | ||
|
|
01ea1854bc | ||
|
|
f3aada9e1a | ||
|
|
08fd32b3bf | ||
|
|
39a86ce5c9 | ||
|
|
fe25260ee2 | ||
|
|
63fd05c7d3 | ||
|
|
6c2aec8854 | ||
|
|
e5553f7528 | ||
|
|
6e3cec653d | ||
|
|
55976fd9dc | ||
|
|
bcbd5201e9 | ||
|
|
c2f91ea63b | ||
|
|
1d9d89b115 | ||
|
|
798dd8b27b | ||
|
|
3544f77e90 | ||
|
|
96c4463d39 | ||
|
|
650b7abef6 | ||
|
|
714b3856a2 | ||
|
|
6b939eba59 | ||
|
|
e82662b389 | ||
|
|
8f9dae8556 | ||
|
|
69ca3bc75d | ||
|
|
3945f34e96 | ||
|
|
549dcbee32 | ||
|
|
0a0c2350f2 | ||
|
|
cef2263a7f | ||
|
|
e338ce7da9 | ||
|
|
638c376e5b | ||
|
|
8eade86624 | ||
|
|
24ae8d5443 | ||
|
|
030af11d86 | ||
|
|
857a2bc399 | ||
|
|
09e6e2940f | ||
|
|
23e18a34d4 | ||
|
|
3bdef5f58d | ||
|
|
cf27581c76 | ||
|
|
cf41df82e2 | ||
|
|
4634bff9f0 | ||
|
|
1a9c658836 | ||
|
|
1a32c55ca3 | ||
|
|
f4e230f1ad | ||
|
|
9abc4cf359 | ||
|
|
c54fc7ee44 | ||
|
|
9df757a3fd | ||
|
|
9b5a885b3b | ||
|
|
f32b288785 | ||
|
|
f378119889 | ||
|
|
8fd466c5a9 | ||
|
|
36eddd48c3 | ||
|
|
2470ba76f0 | ||
|
|
d32475440a | ||
|
|
b4d73683d1 | ||
|
|
95f19a5fb2 | ||
|
|
f98780b1d7 | ||
|
|
150c89e48a | ||
|
|
c85bf82749 | ||
|
|
63227ab2f1 | ||
|
|
5b19237a85 | ||
|
|
150b8f8cbd | ||
|
|
79b97ef2f7 | ||
|
|
e45d68ad3a | ||
|
|
b40f1b67b9 | ||
|
|
4fa7e43162 | ||
|
|
66fcbcae96 | ||
|
|
7f0845dfd3 | ||
|
|
f875823357 | ||
|
|
75611866eb | ||
|
|
c3dd7ed73a | ||
|
|
3ae885d120 | ||
|
|
81f53d313c | ||
|
|
d10c86b849 | ||
|
|
9d123fa5ad | ||
|
|
f4d6a08d57 | ||
|
|
e9e641afbe | ||
|
|
8f5768f85b | ||
|
|
3dd78a2589 | ||
|
|
df92a85159 | ||
|
|
ab606a1121 | ||
|
|
457b3cf168 | ||
|
|
c6f898b8ca | ||
|
|
b9970e1908 | ||
|
|
8bb9a57908 | ||
|
|
53934a470b | ||
|
|
a94ae7d77d | ||
|
|
f43e860998 | ||
|
|
3e40852999 | ||
|
|
df073bb306 | ||
|
|
771c4a0d02 | ||
|
|
cb959ab14c | ||
|
|
34b75dac02 | ||
|
|
fbb590d9a9 | ||
|
|
ed5c533982 | ||
|
|
98d2ce5845 | ||
|
|
9d9d5e3e5d | ||
|
|
eba4626589 | ||
|
|
ff5fa1d137 | ||
|
|
71766ecd16 | ||
|
|
fc63ca6982 | ||
|
|
0e6d888ed3 | ||
|
|
9afe3b5f39 | ||
|
|
3bd40fc8b3 | ||
|
|
01fe642beb | ||
|
|
e70d57dcb4 | ||
|
|
fd41fba069 | ||
|
|
8a6fe0f321 | ||
|
|
ae73e8a305 | ||
|
|
a344c09d0d | ||
|
|
991e37d0bf | ||
|
|
fdeb884fe5 | ||
|
|
4b1f5c9c9b | ||
|
|
6b513a0f95 | ||
|
|
b574aaf99c | ||
|
|
bc0cdaa669 | ||
|
|
f9106b77bb | ||
|
|
a0419e4f34 | ||
|
|
46a549c875 | ||
|
|
f8a609f692 | ||
|
|
987497bb10 | ||
|
|
e537878b8a | ||
|
|
617f45bc59 | ||
|
|
fe744f8f81 | ||
|
|
93d879b297 | ||
|
|
dbb9295063 | ||
|
|
09aa96e486 | ||
|
|
4d0047ae7c | ||
|
|
b860a4799d | ||
|
|
6ff64352d3 | ||
|
|
3683ec6a95 | ||
|
|
454fa9ee9b | ||
|
|
d33aeb4bb2 | ||
|
|
5f9f23eb3f | ||
|
|
5dbc7a8ca4 | ||
|
|
33a5968eb7 | ||
|
|
5ff34b93c0 | ||
|
|
098fdf0596 | ||
|
|
2eb929fe05 | ||
|
|
ea0ed9e844 | ||
|
|
4a3e481a83 | ||
|
|
2197cd8620 | ||
|
|
cf0d3b5f61 | ||
|
|
6f7ab49346 | ||
|
|
000e0358a7 | ||
|
|
a3e19931f0 | ||
|
|
9fec1a86cf | ||
|
|
ffab29890b | ||
|
|
206c068d8e | ||
|
|
dc30cd1112 | ||
|
|
412a245e88 | ||
|
|
16290bf66f | ||
|
|
4f8b0e6484 | ||
|
|
5026dbc1b3 | ||
|
|
014e4b0e1d | ||
|
|
14a6947b02 | ||
|
|
665a914dc3 | ||
|
|
8feac310af | ||
|
|
3394bb4b8d | ||
|
|
1dd2bd0013 | ||
|
|
5c62726992 | ||
|
|
90981f628e | ||
|
|
0c34189d94 | ||
|
|
f1d3d6a7b5 | ||
|
|
b0d962b49a | ||
|
|
c50e453af6 | ||
|
|
efbaf02016 | ||
|
|
3cf809e99d | ||
|
|
8b649aaaf8 | ||
|
|
fdebbc4498 | ||
|
|
3ff3417ff2 | ||
|
|
bb6227281a | ||
|
|
2f44d99a74 | ||
|
|
ca4414d15a | ||
|
|
fbb961b43c | ||
|
|
fa9c174264 | ||
|
|
83f43ab166 | ||
|
|
f7e9e6a1c4 | ||
|
|
aa21748e9a | ||
|
|
a2f4e08b00 | ||
|
|
66bc29d075 | ||
|
|
e3eebe537b | ||
|
|
3ed625f949 | ||
|
|
a1e414c3b7 | ||
|
|
a5a35b1fa6 | ||
|
|
2a24584d45 | ||
|
|
6039cdceb0 | ||
|
|
473159be0f | ||
|
|
0e6ad548b2 | ||
|
|
6143515ac6 | ||
|
|
50419f200d | ||
|
|
455390f121 | ||
|
|
d375299fa9 | ||
|
|
28db337166 | ||
|
|
d5e1d56fde | ||
|
|
6ca9a599ff | ||
|
|
83bd3b6f0b | ||
|
|
a68765e021 | ||
|
|
f5ed9129cd | ||
|
|
5e3115a614 | ||
|
|
624c46f925 | ||
|
|
52d1ee161f | ||
|
|
d3bc9eb110 | ||
|
|
72b61a29c2 | ||
|
|
a99fcfe54f | ||
|
|
b5d139f7b2 | ||
|
|
463ed12ce8 | ||
|
|
baf74cb582 | ||
|
|
63d7cffefe | ||
|
|
d7d74666b2 | ||
|
|
078d76c6f3 | ||
|
|
3539ee9be6 | ||
|
|
437282b148 | ||
|
|
5394b747a1 | ||
|
|
dd1dd3b7cf | ||
|
|
ae77d4ad22 | ||
|
|
821987fed7 | ||
|
|
18ddba4332 | ||
|
|
aaad6dece6 | ||
|
|
ed04747b9d | ||
|
|
827a88d772 | ||
|
|
24e325db62 | ||
|
|
38e43bc9c8 | ||
|
|
c3c5c7ae63 | ||
|
|
578a15bbe5 | ||
|
|
6b3d7372ae | ||
|
|
55f7529167 | ||
|
|
bceae244c1 | ||
|
|
5de224d6bf | ||
|
|
694b936f30 | ||
|
|
dda25d431c | ||
|
|
22f9abc2f1 | ||
|
|
c6c3de9164 | ||
|
|
58186f0283 | ||
|
|
0253e2d3f6 | ||
|
|
ee20d5b804 | ||
|
|
3a5295dbb9 | ||
|
|
39f14fd952 | ||
|
|
4d59df9f59 | ||
|
|
b8bc114502 | ||
|
|
74d29770e1 | ||
|
|
e4d5788cdc | ||
|
|
2a5cf3e4a8 | ||
|
|
c348736058 | ||
|
|
5bb20cb039 | ||
|
|
dd602a27b5 | ||
|
|
2067de162a | ||
|
|
2cc106b43e | ||
|
|
b15bfd99b3 | ||
|
|
67252b90b3 | ||
|
|
079f7e515c | ||
|
|
e8c58270c4 | ||
|
|
0c64f278d7 | ||
|
|
03518ec94f | ||
|
|
93b5dc2dff | ||
|
|
3bef6383d9 | ||
|
|
605ccf3e02 | ||
|
|
17892238a9 | ||
|
|
b678c989e2 | ||
|
|
b72d1237d2 | ||
|
|
f7b6db5dad | ||
|
|
b744a0cc38 | ||
|
|
b918499f14 | ||
|
|
2cfd054f2c | ||
|
|
dddc7ab039 | ||
|
|
2e4d8cdc8b | ||
|
|
0640bec026 | ||
|
|
cbcee5fb45 | ||
|
|
47460d86b2 | ||
|
|
3cd74f0d4f | ||
|
|
690c9f7c6f | ||
|
|
e2718e5a12 | ||
|
|
d9fefe757e | ||
|
|
55e4bf6b65 | ||
|
|
0176e5cf18 | ||
|
|
4670b12d49 | ||
|
|
321ec8ae4d | ||
|
|
1ccbb8d10b | ||
|
|
86c0accdce | ||
|
|
38d6c29ce9 | ||
|
|
0cf9478cd4 | ||
|
|
a04abd304a | ||
|
|
84aec9fe31 | ||
|
|
593b9bb6c5 | ||
|
|
d3a9cc8fde | ||
|
|
87a434c377 | ||
|
|
56022c9442 | ||
|
|
593d6bf466 | ||
|
|
29a4366dcf | ||
|
|
0a42f414bf | ||
|
|
9b2ac4349e | ||
|
|
2d4c7729ad | ||
|
|
6ecab66b0e | ||
|
|
1dded57a1c | ||
|
|
1d6104ecf3 | ||
|
|
14da941ff4 | ||
|
|
06b87311ea | ||
|
|
3b31773117 | ||
|
|
9c87fe79ea | ||
|
|
bd00112562 | ||
|
|
1c9160c37d | ||
|
|
e2ef88229f | ||
|
|
fd7b889a0f | ||
|
|
a7aa056ec1 | ||
|
|
4f74acb2d3 | ||
|
|
22ef1be82b | ||
|
|
9ddbf255ba | ||
|
|
dfb171d32a | ||
|
|
6b4ffcff5a | ||
|
|
d31cd2e5d6 | ||
|
|
396c74e6c6 | ||
|
|
609c658a9b | ||
|
|
ee6bb40736 | ||
|
|
f8c5ea2b42 | ||
|
|
923eb9fdb3 | ||
|
|
2cd3ebbdb3 | ||
|
|
5e25e30330 | ||
|
|
5aa2a8f60f | ||
|
|
0a1e302e8a | ||
|
|
bb705a77cf | ||
|
|
cb6155b946 | ||
|
|
714d1cc993 | ||
|
|
bc8e4494c4 | ||
|
|
c3a064f980 | ||
|
|
eb3feb7dbd | ||
|
|
da3f3ccac9 | ||
|
|
1a1871e8cd | ||
|
|
c22ab7e1fc | ||
|
|
436992b069 | ||
|
|
18cb3912e5 | ||
|
|
a818b0ba02 | ||
|
|
3716b6f988 | ||
|
|
c9e4e78f41 | ||
|
|
9b4e8bf64b | ||
|
|
5aebefe73f | ||
|
|
8f2124beab | ||
|
|
8b8b43df28 | ||
|
|
c42b991bc9 | ||
|
|
ec08333bf9 | ||
|
|
9e5b4e14c9 | ||
|
|
1f5ed89a88 | ||
|
|
2304a2bc2e | ||
|
|
dc82105226 | ||
|
|
5221f3ddc9 | ||
|
|
e970deb92b | ||
|
|
9db7ec6bb0 | ||
|
|
2e691b6655 | ||
|
|
f22e10537b | ||
|
|
6e532c494c | ||
|
|
f9ed0d4aa2 | ||
|
|
78b1afcc8c | ||
|
|
40340cf9c2 | ||
|
|
eb6437050f | ||
|
|
45ebfe378b | ||
|
|
1326597226 | ||
|
|
751da92c13 | ||
|
|
e10ca637da | ||
|
|
c5d9d71a8a | ||
|
|
3e0f5d231d | ||
|
|
6990f177ba | ||
|
|
98e713166b | ||
|
|
4c91ae0085 | ||
|
|
43f74d4d5a | ||
|
|
8c3e716c3f | ||
|
|
05946125b5 | ||
|
|
1e2a0a4549 | ||
|
|
f9f5084dd7 | ||
|
|
b7e7c6db7b | ||
|
|
f9d67b28ec | ||
|
|
46b77cc280 | ||
|
|
2f10decf56 | ||
|
|
678a1ae0fb | ||
|
|
51cbffd097 | ||
|
|
207b13dcab | ||
|
|
3052dbd1e8 | ||
|
|
5891b1ceb2 | ||
|
|
07e14ddda8 | ||
|
|
db5e90787c | ||
|
|
67e501f5c7 | ||
|
|
2160001167 | ||
|
|
f5f4150d17 | ||
|
|
887f292612 | ||
|
|
f5f282af97 | ||
|
|
82f9585b7a | ||
|
|
eb561bb0c2 | ||
|
|
81207999eb | ||
|
|
2fef595b83 | ||
|
|
2024e790ca | ||
|
|
f9925c7374 | ||
|
|
dd774b8dfd | ||
|
|
064b8042a5 | ||
|
|
7923ed9567 | ||
|
|
30b83414ef | ||
|
|
990906c57f | ||
|
|
4c323a666a | ||
|
|
a3f165d374 | ||
|
|
4977f9e6b4 | ||
|
|
7d5f51e357 | ||
|
|
371a339b18 | ||
|
|
7e7aee27b6 | ||
|
|
53148fe58f | ||
|
|
56b6de6962 | ||
|
|
44735681af | ||
|
|
ee1c4f4fdc | ||
|
|
fb6ecdde1e | ||
|
|
861166d8a9 | ||
|
|
70dca81c40 | ||
|
|
2774d72888 | ||
|
|
2440ffbfc9 | ||
|
|
77c5dde320 | ||
|
|
aa49cad279 | ||
|
|
f56ae240ab | ||
|
|
e871a30a78 | ||
|
|
30e6984889 | ||
|
|
324932c758 | ||
|
|
421800bc8f | ||
|
|
86fb47b2b4 | ||
|
|
715f83bf84 | ||
|
|
87bf5c2418 | ||
|
|
5c9af1c613 | ||
|
|
765e0e5c6b | ||
|
|
cc296e16dc | ||
|
|
ab9901525b | ||
|
|
3643a46a0c | ||
|
|
d467e6869d | ||
|
|
db36018849 | ||
|
|
3c5c375f71 | ||
|
|
7473d8c9aa | ||
|
|
33645d7f09 | ||
|
|
9f1106b14a | ||
|
|
3dd952b49b | ||
|
|
6b85bd2cb8 | ||
|
|
60b164c853 | ||
|
|
40c8a1bc1d | ||
|
|
22de695f12 | ||
|
|
e91f588cd7 | ||
|
|
7b72d91549 | ||
|
|
b3c2e86436 | ||
|
|
908bdc7624 | ||
|
|
21c1ec9c8c | ||
|
|
6d7d71bb16 | ||
|
|
6eba061c2a | ||
|
|
f184f550b9 | ||
|
|
bb7c0fef20 | ||
|
|
5c15a12116 | ||
|
|
68d015763e | ||
|
|
7faa732f38 | ||
|
|
11f9eeabf1 | ||
|
|
a152f36894 | ||
|
|
d4ede6ff01 | ||
|
|
35542d803c | ||
|
|
f6ba776c12 | ||
|
|
1511dcb309 | ||
|
|
35afa98112 | ||
|
|
df62b40ca7 | ||
|
|
9f1a125ed9 | ||
|
|
b7e20b9b86 | ||
|
|
a5d6972913 | ||
|
|
e4cb42c599 | ||
|
|
0a34f1f3ad | ||
|
|
7bdeaa9611 | ||
|
|
ab2577ce0a | ||
|
|
34544be423 | ||
|
|
6bf0fdd344 | ||
|
|
6a177cdd1c | ||
|
|
a51ef0cfc6 | ||
|
|
48374d97df | ||
|
|
bf3d7e74f5 | ||
|
|
ab3f3890e4 | ||
|
|
dceb0fb8c5 | ||
|
|
05c1856389 | ||
|
|
dc5cba60d1 | ||
|
|
70409dcdcc | ||
|
|
a92c29e04c | ||
|
|
c4b4dc79cf | ||
|
|
510fe43ec4 | ||
|
|
73e572b66b | ||
|
|
a272a2cb7e | ||
|
|
43b990afe6 | ||
|
|
90130b5492 | ||
|
|
f22faaefeb | ||
|
|
ac25649425 | ||
|
|
04388325a8 | ||
|
|
61ec873842 | ||
|
|
ea1f2d4e26 | ||
|
|
4211c733a2 | ||
|
|
450266818a | ||
|
|
1e019157bb | ||
|
|
c9a1066f02 | ||
|
|
0062f7d764 | ||
|
|
95e994e171 | ||
|
|
31242401e5 | ||
|
|
19cc1c3b3f | ||
|
|
03bcdceb9b | ||
|
|
33ca836ad0 | ||
|
|
53f19e4050 | ||
|
|
54b7d46f5a | ||
|
|
40e6d675c5 | ||
|
|
73b77c83b8 | ||
|
|
632d41e50c | ||
|
|
17acadbfb9 | ||
|
|
2ab5924ec9 | ||
|
|
ac09a4cf0f | ||
|
|
c10ee59de3 | ||
|
|
afad405ed9 | ||
|
|
5a35de8dc9 | ||
|
|
58cf26c304 | ||
|
|
a2de5564ac | ||
|
|
338b17ccf1 | ||
|
|
843a968959 | ||
|
|
dc45c13eef | ||
|
|
0d6e801595 | ||
|
|
2cd50ebaee | ||
|
|
cb6f6a6596 | ||
|
|
e4ab0acc92 | ||
|
|
10237c41d3 | ||
|
|
ac2c6c6010 | ||
|
|
c6b2ce93c4 | ||
|
|
401b7fe883 | ||
|
|
f567417bb3 | ||
|
|
ae5cb3bbe7 | ||
|
|
0eb8e15796 | ||
|
|
1c95c7856f | ||
|
|
daf7551e59 | ||
|
|
5b63d3692e | ||
|
|
5f9972af78 | ||
|
|
1be4cce074 | ||
|
|
935e93eb36 | ||
|
|
5d924cd35a | ||
|
|
27116b9f30 | ||
|
|
8ac2ee49a8 | ||
|
|
8a8e328fcf | ||
|
|
c4207e7672 | ||
|
|
73642703bd | ||
|
|
b3bd175e64 | ||
|
|
742032907a | ||
|
|
edc0162163 | ||
|
|
94661f697b | ||
|
|
4ecf36fab6 | ||
|
|
96cdb3bca3 | ||
|
|
6c57ba36f7 | ||
|
|
cb61897236 | ||
|
|
8b931dd40b | ||
|
|
6b81478bd2 | ||
|
|
9d94eb83c1 | ||
|
|
13374f8b7b | ||
|
|
7bf7aae3d1 | ||
|
|
e5f39d0caf | ||
|
|
174983548d | ||
|
|
a9ec4d916b | ||
|
|
a55c346af5 | ||
|
|
8e0d8c96bb | ||
|
|
9abd383014 | ||
|
|
8ee9c437e1 | ||
|
|
4052b1ea6d | ||
|
|
fdde197c58 | ||
|
|
49883dc3ac | ||
|
|
d798faa1ca | ||
|
|
3f63f15b16 | ||
|
|
f8c390cdd3 | ||
|
|
8f0978cfd6 | ||
|
|
1a8a32a773 | ||
|
|
c0400bfd07 | ||
|
|
c6e4758187 | ||
|
|
56ec8fe95b | ||
|
|
67863cfcf9 | ||
|
|
4c5ec68ff1 | ||
|
|
58b7b7d731 | ||
|
|
1de1c79d4f | ||
|
|
7073a6bf38 | ||
|
|
26db88d89b | ||
|
|
876e98d91e | ||
|
|
8566f6c127 | ||
|
|
bb8dc67942 | ||
|
|
9965d72990 | ||
|
|
921ec9ec12 | ||
|
|
f1990bc2ab | ||
|
|
cdc8e463b7 | ||
|
|
0a62a962d7 | ||
|
|
b0f043ec86 | ||
|
|
ae0cf2e831 | ||
|
|
4ce7e192d6 | ||
|
|
04ca916aac | ||
|
|
a348e10620 | ||
|
|
af794f901f | ||
|
|
8a58572b34 | ||
|
|
0c25e8f1eb | ||
|
|
ff3d2db85e | ||
|
|
efd84a2404 | ||
|
|
278fd2d8d5 | ||
|
|
197882a4c9 | ||
|
|
d310efcb5c | ||
|
|
1af9117b80 | ||
|
|
9685754511 | ||
|
|
44e01b41f8 | ||
|
|
7def2fa6a3 | ||
|
|
48131f4597 | ||
|
|
49e8cf89d8 | ||
|
|
e6bcd04a36 | ||
|
|
af133f4968 | ||
|
|
d723faaaa3 | ||
|
|
cc75efcbca | ||
|
|
2eded7cdd7 | ||
|
|
b10e5ce358 | ||
|
|
1bb1d89fab | ||
|
|
b9dd4aee8d | ||
|
|
518e53a61c | ||
|
|
18b6ba80f2 | ||
|
|
8debdc264c | ||
|
|
31bdce1f1f | ||
|
|
317d8cdc48 | ||
|
|
5b2b9e00a2 | ||
|
|
3dd9e81296 | ||
|
|
d2faec70be | ||
|
|
e5c773a3eb | ||
|
|
ec86c4611d | ||
|
|
247b6a0ed2 | ||
|
|
f7f36568ef | ||
|
|
e054c6e82c | ||
|
|
5e2e1a1e3d | ||
|
|
ad036de69d | ||
|
|
20652f7995 | ||
|
|
2f88a75325 | ||
|
|
292fe94352 | ||
|
|
76dca1b46b | ||
|
|
a54b5c18c6 | ||
|
|
c763472914 | ||
|
|
3a77e7ba2d | ||
|
|
41d6c117ee | ||
|
|
e8f4c42bfb | ||
|
|
bce8469e59 | ||
|
|
3f46ca41ca | ||
|
|
6b1ef6e1b9 | ||
|
|
349022ae42 | ||
|
|
bb518d3d51 | ||
|
|
c45e202fab | ||
|
|
541464b705 | ||
|
|
c762acd780 | ||
|
|
ec98ff297c | ||
|
|
af2c6c5575 | ||
|
|
7d220fb2eb | ||
|
|
f0c49b58fb | ||
|
|
24eeadea76 | ||
|
|
455c71ff25 | ||
|
|
96850da31e | ||
|
|
6ba992dabd | ||
|
|
2bdfcedd0e | ||
|
|
c93ab8f829 | ||
|
|
8abd08bd1b | ||
|
|
33355c0abe | ||
|
|
b830babcf4 | ||
|
|
97765ef895 | ||
|
|
8943d212ee | ||
|
|
86e118f2b7 | ||
|
|
8c3823fc92 | ||
|
|
b0874410f1 | ||
|
|
797f5eb714 | ||
|
|
fc29911ffd | ||
|
|
1e17ef2f21 | ||
|
|
b3e7b1b5ac | ||
|
|
38a2d45a3c | ||
|
|
49b3ac7f77 | ||
|
|
d124d4cace | ||
|
|
ba369d9b30 | ||
|
|
bef8587d8f | ||
|
|
fcbc16f2fd | ||
|
|
a3b172bbcb | ||
|
|
2f945a4fce | ||
|
|
dc9e5dc2f1 | ||
|
|
9396827379 | ||
|
|
28a055bd78 | ||
|
|
37f1a55147 | ||
|
|
64ec7dd559 | ||
|
|
367df4d0db | ||
|
|
da7e41c188 | ||
|
|
d88fe203e1 | ||
|
|
1e01c30e63 | ||
|
|
b16b753ed2 | ||
|
|
7a55d1fc38 | ||
|
|
f8623b6121 | ||
|
|
9a3c22f47d | ||
|
|
e68cff8bba | ||
|
|
513493fa78 | ||
|
|
a6937c792f | ||
|
|
99c7d5c23a | ||
|
|
cd8e8970de | ||
|
|
26d5ced2ef | ||
|
|
c7234f705a | ||
|
|
c153471c49 | ||
|
|
28369faa00 | ||
|
|
445c5f47ae | ||
|
|
7078ca53c3 | ||
|
|
d6ce5f9fa1 | ||
|
|
f28024cfe8 | ||
|
|
911ab9813e | ||
|
|
cfbf5862f9 | ||
|
|
5cb1f5986d | ||
|
|
0b14c810fb | ||
|
|
c2334db8f8 | ||
|
|
4807092df6 | ||
|
|
bdc1107c96 | ||
|
|
db9223b0d5 | ||
|
|
6ecfe0789f | ||
|
|
a37cf058cd | ||
|
|
f4902e6642 | ||
|
|
5d022c25ba | ||
|
|
0cd9f1b002 | ||
|
|
cd0751d3f1 | ||
|
|
a1d1a5df74 | ||
|
|
197f13f9c0 | ||
|
|
41bfc7899d | ||
|
|
2c129b6d39 | ||
|
|
dbe427d5eb | ||
|
|
3e281d4790 | ||
|
|
15c3d46492 | ||
|
|
6a467a09bd | ||
|
|
ba1b8c7c2b | ||
|
|
1e9eb30aa3 | ||
|
|
d47bf1bada | ||
|
|
59dd60f5cb | ||
|
|
c02a0c4da9 | ||
|
|
84f6024cc9 | ||
|
|
d73b42b726 | ||
|
|
ed0c2e68a5 | ||
|
|
847225c6bf | ||
|
|
a6294df9e8 | ||
|
|
431265a86a | ||
|
|
4255c4901d | ||
|
|
9000b3df4e | ||
|
|
a717542733 | ||
|
|
aace644815 | ||
|
|
0ae170531e | ||
|
|
5d01ee9581 | ||
|
|
8b35ce3320 | ||
|
|
ff0e23d2c4 | ||
|
|
d62d2ed269 | ||
|
|
abee29719d | ||
|
|
5781335814 | ||
|
|
f036b8df2d | ||
|
|
25f63ac22a | ||
|
|
12d6f03dc9 | ||
|
|
6d2c9e367b | ||
|
|
66422d6d83 | ||
|
|
b9476791f4 | ||
|
|
9fb8e8a582 | ||
|
|
377a50fa13 | ||
|
|
da20cae25c | ||
|
|
d0c5732e16 | ||
|
|
f9d9aa0306 | ||
|
|
f5db34b98b | ||
|
|
3c07665479 | ||
|
|
f7f50d049b | ||
|
|
7d51b4c6ed | ||
|
|
35ba16ff3b | ||
|
|
6971b1e9da | ||
|
|
84d987810f | ||
|
|
fed04c1a19 | ||
|
|
f5e7d87f5b | ||
|
|
6ca28adcbb | ||
|
|
8e5d2e1b73 | ||
|
|
e8ad7b4f79 | ||
|
|
d3a49e513c | ||
|
|
5bfab0a796 | ||
|
|
739d1aa9e9 | ||
|
|
e575c6e94d | ||
|
|
7bc2e74683 | ||
|
|
2185019b59 | ||
|
|
5d097651c1 | ||
|
|
81c83f0d54 | ||
|
|
5013ce5649 | ||
|
|
5e11a03f0a | ||
|
|
e14d358420 | ||
|
|
82bb3a9b25 | ||
|
|
669720d8f5 | ||
|
|
45ef6cba9d | ||
|
|
3330d2bb0c | ||
|
|
a97d2bbb63 | ||
|
|
f56f75bb3f | ||
|
|
08a82a0bcd | ||
|
|
3dc19bfd31 | ||
|
|
970f47ce33 | ||
|
|
2ee7ed8dda | ||
|
|
d058b9a595 | ||
|
|
1dda832e39 | ||
|
|
a6af4908d5 | ||
|
|
88145eaf94 | ||
|
|
2c7fff077b | ||
|
|
71df1fc4d6 | ||
|
|
064ecdb5ec | ||
|
|
8ec4783249 | ||
|
|
f9d378f1ce | ||
|
|
f07241bff7 | ||
|
|
a6be32392d | ||
|
|
ac594dbd26 | ||
|
|
954711e980 | ||
|
|
2ba3f4758a | ||
|
|
6be4d508f3 | ||
|
|
dc75868bd3 | ||
|
|
c06a560946 | ||
|
|
0bacd4df5f | ||
|
|
b91eaf5487 | ||
|
|
eebea7b342 | ||
|
|
29c1173e14 | ||
|
|
b962a330ad | ||
|
|
1d973bc3ac | ||
|
|
631c8c9870 | ||
|
|
bce6685d0c | ||
|
|
e412b17f70 | ||
|
|
a92b93192d | ||
|
|
8708a0076f | ||
|
|
83fd289e46 | ||
|
|
ed53cbb7b7 | ||
|
|
3b051dbba3 | ||
|
|
8e4781b0f7 | ||
|
|
d599502b1a | ||
|
|
8571830485 | ||
|
|
48d9a03aa8 | ||
|
|
5fb426b336 | ||
|
|
d752a83eb5 | ||
|
|
e740d5fc4f | ||
|
|
8e3e35a36d | ||
|
|
5ce9c0f1e2 | ||
|
|
ef8c4389e1 | ||
|
|
0547d590e1 | ||
|
|
be31640010 | ||
|
|
39319853ab | ||
|
|
ed42948051 | ||
|
|
a0e545a6f1 | ||
|
|
e77e383efa | ||
|
|
8ce5ceef59 | ||
|
|
3a53e049bd | ||
|
|
5011ecaaa6 | ||
|
|
35b1842a72 | ||
|
|
0292227a6b | ||
|
|
ebce1e34d8 | ||
|
|
cc1244126c | ||
|
|
bdf63cf82c | ||
|
|
0275f7f574 | ||
|
|
779f2fa451 | ||
|
|
1a4250d8cc | ||
|
|
08fafe267a | ||
|
|
d06924b339 | ||
|
|
585116a51f | ||
|
|
b676d7034f | ||
|
|
69a0fe3040 | ||
|
|
5207dd4c9e | ||
|
|
919bf4e144 | ||
|
|
7ed440ba75 | ||
|
|
2db035d23c | ||
|
|
a4b84517dc | ||
|
|
e687773b41 | ||
|
|
df66c2d2dc | ||
|
|
f321eb66c0 | ||
|
|
80b44fc9a9 | ||
|
|
0e68fe4a57 | ||
|
|
59b471b9a2 | ||
|
|
1b3c3fae89 | ||
|
|
f22eaa6db5 | ||
|
|
e37244fa0d | ||
|
|
c359c6e634 | ||
|
|
d299cbaabd | ||
|
|
2b22bfadbc | ||
|
|
baec22610e | ||
|
|
43b587636b | ||
|
|
c6cdb26f47 | ||
|
|
1285e30b3e | ||
|
|
a8e1cd9a13 | ||
|
|
d6f5640685 | ||
|
|
79dbf2a43e | ||
|
|
5ad4c2a65e | ||
|
|
fffa550bb0 | ||
|
|
0b9cb4e75b | ||
|
|
7f143a7f23 | ||
|
|
d8d8a68814 | ||
|
|
4018cf9d76 | ||
|
|
bd33ac202f | ||
|
|
e091eba831 | ||
|
|
4a0dbec4fb | ||
|
|
90dee900f0 | ||
|
|
94555b9c43 | ||
|
|
db93a7315f | ||
|
|
7a19533380 | ||
|
|
9d79b26506 | ||
|
|
b43a9cc80d | ||
|
|
b5618af308 | ||
|
|
9c8c3b9174 | ||
|
|
01e591b261 | ||
|
|
060e30d283 | ||
|
|
ad019da553 | ||
|
|
69afd3a1da | ||
|
|
7978adc577 | ||
|
|
ca77ca6ef0 | ||
|
|
d5b61ed544 | ||
|
|
5edb256990 | ||
|
|
74d0c04314 | ||
|
|
39d4464be0 | ||
|
|
be48dc6e87 | ||
|
|
2783337284 | ||
|
|
727743979c | ||
|
|
4543e14c57 | ||
|
|
83fc1b0b8e | ||
|
|
df858d9143 | ||
|
|
ac47c9c673 | ||
|
|
b9a2d5df02 | ||
|
|
3e873f88c9 | ||
|
|
277cef5ec4 | ||
|
|
5c9b478e46 | ||
|
|
ff89edf127 | ||
|
|
2cc9791bf2 | ||
|
|
67b32005f6 | ||
|
|
0f166973ca | ||
|
|
4f3333c841 | ||
|
|
bea384abea | ||
|
|
43033695f6 | ||
|
|
51ef7ef61c | ||
|
|
823b499a02 | ||
|
|
bb5ed0b40c | ||
|
|
94ca2514af | ||
|
|
5412352dec | ||
|
|
c94e8c7df4 | ||
|
|
094541caa6 | ||
|
|
8c59977e34 | ||
|
|
881bca6ae3 | ||
|
|
22865f8ee4 | ||
|
|
f3b728d828 | ||
|
|
bd7328345f | ||
|
|
25eae3c116 | ||
|
|
5cca5472e6 | ||
|
|
8462d382f4 | ||
|
|
2b0d18a6d7 | ||
|
|
edf3b7e2fc | ||
|
|
167d3a0e3c | ||
|
|
86415bc61f | ||
|
|
a6ea37a21e | ||
|
|
3695aa924b | ||
|
|
9e050d1a23 | ||
|
|
34eee2fc26 | ||
|
|
ac10f3055d | ||
|
|
991b74f036 | ||
|
|
589049ef0f | ||
|
|
6b0c7c2313 | ||
|
|
a9c7d0d598 | ||
|
|
ef1dfb153c | ||
|
|
ff9ee5873f | ||
|
|
a7b56bbbb7 | ||
|
|
820a365474 | ||
|
|
1d5d06f731 | ||
|
|
43d458cf72 | ||
|
|
fde79eecc6 | ||
|
|
436a3e7f54 | ||
|
|
7015bad905 | ||
|
|
cf8665748b | ||
|
|
1b8da90cbb | ||
|
|
6012585067 | ||
|
|
f162876600 | ||
|
|
6555ae5b0a | ||
|
|
f5af059ef4 | ||
|
|
cb8651ec68 | ||
|
|
7c0b0a4e3e | ||
|
|
880d1a7ccd | ||
|
|
744b25190a | ||
|
|
3792bb4928 | ||
|
|
9049902ced | ||
|
|
5f93dc72fd | ||
|
|
09dadd7e01 | ||
|
|
60b92f98db | ||
|
|
97f315d488 | ||
|
|
f3676d7f18 | ||
|
|
742dbdb68a | ||
|
|
2d59c968ca | ||
|
|
ad22247c9e | ||
|
|
f38920c338 | ||
|
|
8f90b21a5d | ||
|
|
ff0e6813c6 | ||
|
|
fa5e4d57fd | ||
|
|
876973f071 | ||
|
|
b994af9209 | ||
|
|
1f6cde652e | ||
|
|
3bf6db1c08 | ||
|
|
e70ffc9d7c | ||
|
|
065cfe3b9d | ||
|
|
def9873a70 | ||
|
|
618aa26454 | ||
|
|
924a7bc533 | ||
|
|
ef85277a1b | ||
|
|
876375f2c3 | ||
|
|
f70ee480ba | ||
|
|
a518320e3b | ||
|
|
6d88c3ab05 | ||
|
|
57c969b0ed | ||
|
|
ae58a7007b | ||
|
|
11c924bbe7 | ||
|
|
8bab4f60ef | ||
|
|
bef9a54f4a | ||
|
|
288b19c3f7 | ||
|
|
40f7e9d33e | ||
|
|
fab53dda66 | ||
|
|
a4e8bf9857 | ||
|
|
2cdf84cdab | ||
|
|
fbe83f729d | ||
|
|
4371a084ec | ||
|
|
d13f58088a | ||
|
|
f75bef7c03 | ||
|
|
3d7e93a688 | ||
|
|
a4dda304d2 | ||
|
|
124c3ef2d7 | ||
|
|
c3a2fca76a | ||
|
|
b60ebfe1c6 | ||
|
|
1d7639b3f4 | ||
|
|
2d972752ff | ||
|
|
616f0b2a21 | ||
|
|
94659ba890 | ||
|
|
d65bc068de | ||
|
|
1ca0354cf2 | ||
|
|
b1fcd4d27b | ||
|
|
f6a09f59a3 | ||
|
|
74aa07eba8 | ||
|
|
d1a98212ee | ||
|
|
75a31c79ae | ||
|
|
da0b36cb91 | ||
|
|
aa206d034d | ||
|
|
765ab60753 | ||
|
|
44e4ec573d | ||
|
|
0ed793d6d0 | ||
|
|
272e25ff07 | ||
|
|
63127ab181 | ||
|
|
004f3532a0 | ||
|
|
abe1af7b4f | ||
|
|
01df1647bc | ||
|
|
9d8eaf0ccb | ||
|
|
7e4c33d27e | ||
|
|
c164601acf | ||
|
|
3b32da4f5c | ||
|
|
1bc3de8df4 | ||
|
|
374e0cbbc3 | ||
|
|
313921da56 | ||
|
|
2d0e219197 | ||
|
|
dc64d1738a | ||
|
|
89e8d99294 | ||
|
|
66a238045f | ||
|
|
33b82b5669 | ||
|
|
f59d509b15 | ||
|
|
6966539b86 | ||
|
|
0e5dc15005 | ||
|
|
a74f685a5d | ||
|
|
05c7aacfa5 | ||
|
|
ace80c29e7 | ||
|
|
bfb1380dd2 | ||
|
|
ea19802d3f | ||
|
|
fef4f13b8f | ||
|
|
c4fc0f4ecf | ||
|
|
ba3acdac75 | ||
|
|
aad2d68edb | ||
|
|
9e5935aea5 | ||
|
|
129b4a2135 | ||
|
|
82649ab2a7 | ||
|
|
1ba5d25819 | ||
|
|
daa3f8699b | ||
|
|
df7fda9e0c | ||
|
|
484f69f16b | ||
|
|
7c8280934a | ||
|
|
85902b358a | ||
|
|
5931cb59ab | ||
|
|
fd73aab7d0 | ||
|
|
d13fbe5549 | ||
|
|
ed4c00e4f4 | ||
|
|
07282ec39f | ||
|
|
2d998aba43 | ||
|
|
2e0019c8c8 | ||
|
|
96e9608036 | ||
|
|
9d5bb1b2b6 | ||
|
|
67dab9b6d2 | ||
|
|
6fc5f88a3b | ||
|
|
e0cec79ad6 | ||
|
|
1a9e11d86d | ||
|
|
1235d18d67 | ||
|
|
8f25b66760 | ||
|
|
2bc0850b0f | ||
|
|
29176dd9bf | ||
|
|
aedcd1bcc0 | ||
|
|
b1262d54de | ||
|
|
bc4a97774f | ||
|
|
ee3cd44f97 | ||
|
|
726bd0d63b | ||
|
|
a2c28d0837 | ||
|
|
ce9640773c | ||
|
|
7ce92118e4 | ||
|
|
e12c5fe007 | ||
|
|
86ff0d86db | ||
|
|
b4236b04c6 | ||
|
|
d34dc397e8 | ||
|
|
f2e4d5f06c | ||
|
|
da7e2f2580 | ||
|
|
e07a20a771 | ||
|
|
ae1b1da342 | ||
|
|
a61d7fe115 | ||
|
|
b4d1e89696 | ||
|
|
a0d90717c3 | ||
|
|
5c2f1f36e8 | ||
|
|
0b084956e6 | ||
|
|
8c61e7d227 | ||
|
|
d7342586a6 | ||
|
|
242e3d007c | ||
|
|
d4b6485102 | ||
|
|
370ab6307a | ||
|
|
83b10fba62 | ||
|
|
1921bce4c7 | ||
|
|
669fb62a54 | ||
|
|
1a5920ee47 | ||
|
|
9c6e3ff1d7 | ||
|
|
ca78601ada | ||
|
|
2edce12759 | ||
|
|
ccc604c0f4 | ||
|
|
d3bf8c2417 | ||
|
|
dc774f0f94 | ||
|
|
f2059947bf | ||
|
|
eccd5b6ff0 | ||
|
|
776dc7ec52 | ||
|
|
082c4f1104 | ||
|
|
06a7e181cd | ||
|
|
bf91e16b5d | ||
|
|
65945b3462 | ||
|
|
31f0c35077 | ||
|
|
fc2dc9a019 | ||
|
|
04645aacc4 | ||
|
|
c91a8711e3 | ||
|
|
7373dae026 | ||
|
|
ca3b819151 | ||
|
|
ba79b94e06 | ||
|
|
bfc3acb834 | ||
|
|
ac67cd7f9a | ||
|
|
9a2c6a7619 | ||
|
|
3100d4f902 | ||
|
|
aace200899 | ||
|
|
a843165cb4 | ||
|
|
36473e3889 | ||
|
|
e2fcab34b7 | ||
|
|
abdf92c084 | ||
|
|
32fc6482cc | ||
|
|
ce14ea6fe5 | ||
|
|
2f57013e02 | ||
|
|
ad84944d20 | ||
|
|
2ff6f9d346 | ||
|
|
0ab95b1b87 | ||
|
|
58153c3579 | ||
|
|
746f53ba07 | ||
|
|
ff971563db | ||
|
|
242fb7db14 | ||
|
|
ad36738f57 | ||
|
|
c833b16544 | ||
|
|
1c5b350c2b | ||
|
|
9301e39af7 | ||
|
|
86e3b977e4 | ||
|
|
bc330ff0ea | ||
|
|
771480e368 | ||
|
|
c875ff923a | ||
|
|
3dfb44de31 | ||
|
|
2266c3877c | ||
|
|
f4486bc075 | ||
|
|
0436a65baa | ||
|
|
30d6bd144b | ||
|
|
d8381e9486 | ||
|
|
feaecbe177 | ||
|
|
85d796f906 | ||
|
|
0a3af12ee9 | ||
|
|
3925540517 | ||
|
|
3b630fe546 | ||
|
|
c69c4ae8a0 | ||
|
|
b4369470cb | ||
|
|
4a44b18b97 | ||
|
|
2bd6daeb8d | ||
|
|
1ae98b7fe1 | ||
|
|
44ca315c75 | ||
|
|
af20b13c7a | ||
|
|
1f6be38145 | ||
|
|
8b3a7486c7 | ||
|
|
62cd9fffa3 | ||
|
|
7e874eaa7c | ||
|
|
1c7780a423 | ||
|
|
07b77443dd | ||
|
|
4ba1be2dc0 | ||
|
|
6362a7bba5 | ||
|
|
1740715c00 | ||
|
|
21501cbf81 | ||
|
|
d5f3d6111e | ||
|
|
bd04f92087 | ||
|
|
942b2b05e7 | ||
|
|
b8064b9b4b | ||
|
|
f94d03465a | ||
|
|
4e7aafeec1 | ||
|
|
d820b8036e | ||
|
|
3907c17cf5 | ||
|
|
b12fa97a38 | ||
|
|
b9b431e82d | ||
|
|
9f2a2e44a3 | ||
|
|
aaf6c1ea8b | ||
|
|
b2f0278180 | ||
|
|
530eba1b91 | ||
|
|
812d312a9e | ||
|
|
5d256e1d80 | ||
|
|
d02a0c9b3a | ||
|
|
bdbd060229 | ||
|
|
bf04962994 | ||
|
|
33f2ddb696 | ||
|
|
e444519889 | ||
|
|
a47aa8c282 | ||
|
|
0c29aeb9be | ||
|
|
2b4a91cc80 | ||
|
|
9ffc4155dd | ||
|
|
979282a0d4 | ||
|
|
c63818f355 | ||
|
|
c400372a79 | ||
|
|
56f3bdd746 | ||
|
|
cc0367b079 | ||
|
|
e41bbcb2bb | ||
|
|
b35f43d79e | ||
|
|
e9f11e204e | ||
|
|
1b63c9f6ad | ||
|
|
975d5f44b6 | ||
|
|
21d99e355c | ||
|
|
f0adbcd5e1 | ||
|
|
bfcf3cfbf1 | ||
|
|
ef5495bfb2 | ||
|
|
c93ee0d65d | ||
|
|
db3e48a81a | ||
|
|
d9b87e877d | ||
|
|
b6175132eb | ||
|
|
57d6c7a3b3 | ||
|
|
d65a282e9d | ||
|
|
801ecaa41c | ||
|
|
49bf735c22 | ||
|
|
cb55944ff6 | ||
|
|
9c225f8d77 | ||
|
|
365fce922c | ||
|
|
fbfc5ecda3 | ||
|
|
4001f48a28 | ||
|
|
22124c25d1 | ||
|
|
17f5bcbd1c | ||
|
|
b7ebb3ea3d | ||
|
|
387830e07a | ||
|
|
da94d40738 | ||
|
|
417b5ed6cc | ||
|
|
005581ef62 | ||
|
|
050390c5c4 | ||
|
|
2648f1ba89 | ||
|
|
d9d31521f9 | ||
|
|
8e24d1b909 | ||
|
|
36fc0daa12 | ||
|
|
44d3854a13 | ||
|
|
1dbc35f13d | ||
|
|
11691fb44a | ||
|
|
acc5592f59 | ||
|
|
614921276e | ||
|
|
3f45a11f12 | ||
|
|
e3464add50 | ||
|
|
18bb4a71c2 | ||
|
|
85e9da82b0 | ||
|
|
99d046ca11 | ||
|
|
0b372a344c | ||
|
|
ffa0f0afd9 | ||
|
|
e21dac21c8 | ||
|
|
260564345a | ||
|
|
0108745065 | ||
|
|
e2a1cd12c3 | ||
|
|
f6ff232106 | ||
|
|
b0c690d836 | ||
|
|
7246624983 | ||
|
|
471c46ad8e | ||
|
|
59032d515b | ||
|
|
d218c9a983 | ||
|
|
243f6e755b | ||
|
|
67b76809ea | ||
|
|
77231bfc6c | ||
|
|
e614226926 | ||
|
|
65e15d74fc | ||
|
|
7ceb81cc83 | ||
|
|
d3f7eea0a3 | ||
|
|
c2f13a1496 | ||
|
|
faae2709d9 | ||
|
|
d595006d1f | ||
|
|
a8d23b5439 | ||
|
|
cfda807057 | ||
|
|
c601a2986f | ||
|
|
8483464aab | ||
|
|
dca69e9b6e | ||
|
|
9450dc84da | ||
|
|
3a2724ec58 | ||
|
|
ee84291997 | ||
|
|
fd9229c467 | ||
|
|
ac5a4fe70f | ||
|
|
873b4f3178 | ||
|
|
beb5d26e6d | ||
|
|
221b7cbf76 | ||
|
|
7d34f1e883 | ||
|
|
208707c00b | ||
|
|
cb41c04551 | ||
|
|
730c6aff11 | ||
|
|
3d40c7603d | ||
|
|
dec7a9a01c | ||
|
|
5f42888b61 | ||
|
|
489c38ec5b | ||
|
|
949fc47f31 | ||
|
|
4d85079372 | ||
|
|
335f9394a5 | ||
|
|
f939a7b349 | ||
|
|
09fdb068d2 | ||
|
|
024c29b180 | ||
|
|
2b0d1a2190 | ||
|
|
d0d71c93af | ||
|
|
da1e52357f | ||
|
|
a05a54b38e | ||
|
|
305a654a37 | ||
|
|
a0685d804d | ||
|
|
954781262c | ||
|
|
0777bad2c3 | ||
|
|
3159b06988 | ||
|
|
e7ff6fbffc | ||
|
|
6fec92c012 | ||
|
|
e50abbb250 | ||
|
|
8e25226574 | ||
|
|
9636d82b37 | ||
|
|
c3aa6b9cda | ||
|
|
3ef8b3dcbb | ||
|
|
c41554109b | ||
|
|
67b94d3533 | ||
|
|
d52c0633c8 | ||
|
|
4e4c117023 | ||
|
|
cb0d30cf42 | ||
|
|
2d65402ced | ||
|
|
e15b2cc5d6 | ||
|
|
c024905d56 | ||
|
|
6f17624742 | ||
|
|
6f2e6ed887 | ||
|
|
5f1e66d64b | ||
|
|
1f31fdc257 | ||
|
|
df9965e129 | ||
|
|
61e9c31f0d | ||
|
|
a0b35ebd3e | ||
|
|
951ec567c7 | ||
|
|
31494267e5 | ||
|
|
70e4cbc023 | ||
|
|
8d903a09e2 | ||
|
|
63451fb781 | ||
|
|
1e609acb03 | ||
|
|
69194118df | ||
|
|
0f309377ec | ||
|
|
49a19a52c8 | ||
|
|
1a39f7e5c6 | ||
|
|
a5fed64f38 | ||
|
|
79858d4372 | ||
|
|
61897ae16c | ||
|
|
5e0a8ed232 | ||
|
|
5993cc857a | ||
|
|
6a0174293e | ||
|
|
44bb8f6f16 | ||
|
|
a33cad4b70 | ||
|
|
0639cce784 | ||
|
|
a8f227f759 | ||
|
|
f077836bf5 | ||
|
|
6d7847f2df | ||
|
|
221c14cf0e | ||
|
|
6735b2686b | ||
|
|
55ff6beb7d | ||
|
|
4ae41513ac | ||
|
|
438a225487 | ||
|
|
e135696530 | ||
|
|
d510f7e473 | ||
|
|
9135772f89 | ||
|
|
b992fbab52 | ||
|
|
0dc212d97c | ||
|
|
45e8d5c50e | ||
|
|
37ec90c436 | ||
|
|
010541197c | ||
|
|
bdb918cdb3 | ||
|
|
64c986ebbb | ||
|
|
a4c4bf4b58 | ||
|
|
60b1b2ca4a | ||
|
|
8e0f1de25a | ||
|
|
dba6d68108 | ||
|
|
d226834eef | ||
|
|
2facf14443 | ||
|
|
6bd44f0e4b | ||
|
|
1dcb878796 | ||
|
|
0e0169d22b | ||
|
|
50c8a84037 | ||
|
|
1975adc48f | ||
|
|
71564f0d10 | ||
|
|
5a32082624 | ||
|
|
45aa78d953 | ||
|
|
86e8614934 | ||
|
|
ead89c767a | ||
|
|
8bae4975fb | ||
|
|
7a5146ea74 | ||
|
|
9633c247f0 | ||
|
|
78640532e1 | ||
|
|
46ee427ee3 | ||
|
|
0c2b0081b5 | ||
|
|
f133a7f9fd | ||
|
|
a6c9ee446a | ||
|
|
153aaa6d21 | ||
|
|
e5901dad91 | ||
|
|
9318388007 | ||
|
|
bdd75e1171 | ||
|
|
7d22ddd710 | ||
|
|
7e82c8e279 | ||
|
|
db6a0e6ad9 | ||
|
|
648d035a0f | ||
|
|
7ebf2f010c | ||
|
|
3db4421aa7 | ||
|
|
9fb59e128b | ||
|
|
c7c6e5917a | ||
|
|
7b418b3adf | ||
|
|
1c8d662e30 | ||
|
|
d4bfeab36c | ||
|
|
e1b1032df9 | ||
|
|
329439d0ae | ||
|
|
0b1cfb2102 | ||
|
|
1e4d2fd053 | ||
|
|
716378bd6b | ||
|
|
de48d3aaec | ||
|
|
b5b195e628 | ||
|
|
23be4c01df | ||
|
|
86782f3479 | ||
|
|
a96c205830 | ||
|
|
9274881c18 | ||
|
|
24c5f07153 | ||
|
|
789ff702ac | ||
|
|
9b6facf3b0 | ||
|
|
d503190647 | ||
|
|
c4d9c03930 | ||
|
|
d7d70b707f | ||
|
|
dbe1e3f577 | ||
|
|
bb7f03857c | ||
|
|
53b43353eb | ||
|
|
b197556447 | ||
|
|
42d4781a96 | ||
|
|
d991cc3b96 | ||
|
|
4d48d35ad7 | ||
|
|
b7ba8f8e93 | ||
|
|
dff510c181 | ||
|
|
1eead0e885 | ||
|
|
e301387896 | ||
|
|
c49e544781 | ||
|
|
d48db501e0 | ||
|
|
ec4e17f75c | ||
|
|
17e69e67b1 | ||
|
|
c4f9f7da06 | ||
|
|
c367476036 | ||
|
|
f5712c4198 | ||
|
|
1aa0da3382 | ||
|
|
27d69894d4 | ||
|
|
7133a07f38 | ||
|
|
627d8cfe69 | ||
|
|
16b992d705 | ||
|
|
3d9c844dca | ||
|
|
c0de9455bb | ||
|
|
65e1871cd7 | ||
|
|
0a431594f8 | ||
|
|
7b22ef4270 | ||
|
|
f77a58b2dc | ||
|
|
142a138cfc | ||
|
|
e6fdf5ad8d | ||
|
|
5700e18257 | ||
|
|
50a77fedca | ||
|
|
51e3d5f7bc | ||
|
|
7f859978dd | ||
|
|
d8134e8a21 | ||
|
|
614d91e0b1 | ||
|
|
30067fc7d7 | ||
|
|
ec29597dbd | ||
|
|
ad211a63f3 | ||
|
|
6b596bd05f | ||
|
|
510b85fd23 | ||
|
|
e3c8f3fd6f | ||
|
|
f9175db28e | ||
|
|
f4798d05e7 | ||
|
|
a2a0f62135 | ||
|
|
d923f0e01b | ||
|
|
2fc16ee13d | ||
|
|
152e579f7e | ||
|
|
90914bb2de | ||
|
|
95fa835191 | ||
|
|
4e37df26a3 | ||
|
|
5e606573b1 | ||
|
|
ddf3774aec | ||
|
|
b3974cb52a | ||
|
|
b7c206c44b | ||
|
|
49c1e47736 | ||
|
|
4e1319d874 | ||
|
|
a9436aa9af | ||
|
|
d503f07564 | ||
|
|
aa7750bfd3 | ||
|
|
8872d1f389 | ||
|
|
f21af4068f | ||
|
|
f4ca6bbb52 | ||
|
|
814f854c5a | ||
|
|
869d0156ce | ||
|
|
744e893dce | ||
|
|
fe9ac10f02 | ||
|
|
6fb80f226a | ||
|
|
ff19bab800 | ||
|
|
962c2160c7 | ||
|
|
168da33d8b | ||
|
|
b6b25dc9f3 | ||
|
|
3ca17fdc03 | ||
|
|
2249708097 | ||
|
|
2fcaa7d260 | ||
|
|
f3b0e57a54 | ||
|
|
5da92437a1 | ||
|
|
b5bc05ac2b | ||
|
|
45145fa50a | ||
|
|
dd8200e8b0 | ||
|
|
2f56547d5f | ||
|
|
82bdcfbbcb | ||
|
|
0e38e43315 | ||
|
|
63746be4d5 | ||
|
|
ee73ee365f | ||
|
|
3c53479864 | ||
|
|
4adc741de3 | ||
|
|
64da62dbe6 | ||
|
|
dd9b5faa5c | ||
|
|
51d018acc6 | ||
|
|
5eec580727 | ||
|
|
1e9a53da3f | ||
|
|
8dae044600 | ||
|
|
2d3fad2cdb | ||
|
|
a59a8f62ca | ||
|
|
35cfa7d9fb | ||
|
|
c6ccb373a2 | ||
|
|
b6368170ed | ||
|
|
35e8424293 | ||
|
|
e969d58689 | ||
|
|
ae20e3aa95 | ||
|
|
de4cb74173 | ||
|
|
4f0da87a7a | ||
|
|
97f0347715 | ||
|
|
2ffe62ba41 | ||
|
|
fe1724e7e6 | ||
|
|
2ac2da41cf | ||
|
|
ed574f9d79 | ||
|
|
e0cb26bd9e | ||
|
|
1893127e84 | ||
|
|
b02c9fb118 | ||
|
|
bca0809918 | ||
|
|
00db527377 | ||
|
|
2c6e041ae2 | ||
|
|
d3bbdb1011 | ||
|
|
a0d6c654cc | ||
|
|
5115c27e72 | ||
|
|
d09c3ccb2d | ||
|
|
5c308026ac | ||
|
|
91919c6d64 | ||
|
|
7168738835 | ||
|
|
9c9b723cf5 | ||
|
|
50450923df | ||
|
|
f392edd66c | ||
|
|
24b48e5d50 | ||
|
|
47f384a0e0 | ||
|
|
88594887f9 | ||
|
|
32e2f0b1fa | ||
|
|
09ed57ad42 | ||
|
|
53a6162b0c | ||
|
|
694d851cdb | ||
|
|
8e53c30a00 | ||
|
|
63e807b0b4 | ||
|
|
012f22cc47 | ||
|
|
9d891ab5dd | ||
|
|
d0e78be867 | ||
|
|
cbedebc9dd | ||
|
|
969f9aa436 | ||
|
|
b982be5ff5 | ||
|
|
2d154ee640 | ||
|
|
49810eb153 | ||
|
|
85b88b8749 | ||
|
|
239c8b5172 | ||
|
|
8c800dc178 | ||
|
|
cdd068d99a | ||
|
|
48fa10b080 | ||
|
|
a1dbec0fcb | ||
|
|
abe668f1c3 | ||
|
|
77440c235d | ||
|
|
fd1ee48dbe | ||
|
|
205e807b66 | ||
|
|
34295adb05 | ||
|
|
7c212bef63 | ||
|
|
76f95644b7 | ||
|
|
928b90d5bc | ||
|
|
09c6c2a4f3 | ||
|
|
2b2bd733e9 | ||
|
|
0d2d7e5e71 | ||
|
|
6142e93252 | ||
|
|
ccec3376ba | ||
|
|
f497a74ec4 | ||
|
|
0e666e7d6a | ||
|
|
f498fabd27 | ||
|
|
8b49a55442 | ||
|
|
a26eb942a9 | ||
|
|
eabcafa516 | ||
|
|
6cc388c1bc | ||
|
|
62e39ddfbd | ||
|
|
80373623cd | ||
|
|
451c3945f0 | ||
|
|
00cb15d9b4 | ||
|
|
67dd59125e | ||
|
|
b6800dd125 | ||
|
|
dc9da69509 | ||
|
|
d7d964bf57 | ||
|
|
d6d9f05443 | ||
|
|
bcfe44db54 | ||
|
|
376bf6ba72 | ||
|
|
f651baab25 | ||
|
|
61752e2aab | ||
|
|
b7d3fd959e | ||
|
|
7ac05f8487 | ||
|
|
5cfc574f9a | ||
|
|
4f70822b13 | ||
|
|
0007f304d0 | ||
|
|
4afef91359 | ||
|
|
815b6db0bf | ||
|
|
433d3bf582 | ||
|
|
a335841509 | ||
|
|
26ad793d82 | ||
|
|
5337aa10f7 | ||
|
|
9f79bdae9b | ||
|
|
db84be2488 | ||
|
|
599ec62bb0 | ||
|
|
19a88300c6 | ||
|
|
b5d55e1ffb | ||
|
|
521fb83e38 | ||
|
|
553d59c32b | ||
|
|
9ed58e5186 | ||
|
|
36eaaa748c | ||
|
|
4d7b86ca26 | ||
|
|
5faf84c732 | ||
|
|
d7b819267f | ||
|
|
7417867d0f | ||
|
|
8d74905257 | ||
|
|
c38298c06e | ||
|
|
3100d587d1 | ||
|
|
ba849d0300 | ||
|
|
95df3e4b39 | ||
|
|
72492e33a0 | ||
|
|
934f1269f5 | ||
|
|
e6956d9bb0 | ||
|
|
2877900233 | ||
|
|
df1aa52e08 | ||
|
|
5fa2485a7d | ||
|
|
29f0e10411 | ||
|
|
39300a5bbf | ||
|
|
35d6268675 | ||
|
|
0abb871f3f | ||
|
|
704fca969f | ||
|
|
95debf8c80 | ||
|
|
dd94b77b2a | ||
|
|
6cfe4fa580 | ||
|
|
515c086099 | ||
|
|
34ce06ac17 | ||
|
|
a104c9881e | ||
|
|
c3e3c091cc | ||
|
|
651240113c | ||
|
|
77189bf8e9 | ||
|
|
60fd3a4542 | ||
|
|
c66f9c8d6d | ||
|
|
569088eaca | ||
|
|
13e09e231d | ||
|
|
a7e8dd04fe | ||
|
|
dfdd76a1bb | ||
|
|
28aac6f93b | ||
|
|
c2f47119ce | ||
|
|
d6b1d0d4fb | ||
|
|
03a861745b | ||
|
|
9a7aed20e9 | ||
|
|
b7f17d4cb1 | ||
|
|
2497c3d187 | ||
|
|
f7a084969a | ||
|
|
2900bc26a5 | ||
|
|
2334c56a96 | ||
|
|
90a5d02bf6 | ||
|
|
81d9626da9 | ||
|
|
7866f644d3 | ||
|
|
44a2549b81 | ||
|
|
a2b8d468bc | ||
|
|
d523f0cadd | ||
|
|
99116ff097 | ||
|
|
3939ca9eb4 | ||
|
|
b5aa67b491 | ||
|
|
e42efec220 | ||
|
|
9d06aa2f6a | ||
|
|
80765a797b | ||
|
|
0b5509a1ed | ||
|
|
478d7b4a83 | ||
|
|
9d3b38141a | ||
|
|
ab3a4d902e | ||
|
|
5eab5f2437 | ||
|
|
80f632c19a | ||
|
|
6e4f18543d | ||
|
|
54586c9076 | ||
|
|
351c899807 | ||
|
|
fe45d431d7 | ||
|
|
488c2f6d05 | ||
|
|
75ab0909b3 | ||
|
|
8f82d563c1 | ||
|
|
9bbce5dba6 | ||
|
|
099adab9ed | ||
|
|
c8cbf425ac | ||
|
|
ad9c11cd92 | ||
|
|
3872c2a3f5 | ||
|
|
e6a09b49c9 | ||
|
|
db107602bd | ||
|
|
a6558a61a7 | ||
|
|
254d2b82b3 | ||
|
|
2c9fa2f738 | ||
|
|
97d9795fc9 | ||
|
|
54071b0e5d | ||
|
|
925e8316c7 | ||
|
|
99e1b74023 | ||
|
|
7d68ccca53 | ||
|
|
a090114066 | ||
|
|
a204841abb | ||
|
|
cc451809cc | ||
|
|
a605e4bab6 | ||
|
|
3f0534134d | ||
|
|
84de3f081f | ||
|
|
3acfb129cd | ||
|
|
6ccef66920 | ||
|
|
e9fa4e94a6 | ||
|
|
fecc0c4640 | ||
|
|
b759294975 | ||
|
|
a23e845c03 | ||
|
|
cb8373e487 | ||
|
|
8e919ddc8e | ||
|
|
832a9ab6b5 | ||
|
|
13732ac333 | ||
|
|
3e932a55f4 | ||
|
|
74e8610ec9 | ||
|
|
089a60ded6 | ||
|
|
c8eeefe194 | ||
|
|
85eeba14c1 | ||
|
|
f6f45eab39 | ||
|
|
a74065f775 | ||
|
|
48d02f7e09 | ||
|
|
e60549f8df | ||
|
|
41f4f4713e | ||
|
|
213a292fd5 | ||
|
|
79630e844b | ||
|
|
f0725c9b40 | ||
|
|
1c9e46dbb3 | ||
|
|
0a299284f8 | ||
|
|
347a5f7346 | ||
|
|
c6a903572c | ||
|
|
14f0d6d26b | ||
|
|
485f105555 | ||
|
|
686c0b776f | ||
|
|
828862ea49 | ||
|
|
c4dffa4dc8 | ||
|
|
3c1906e3d4 | ||
|
|
7147a3694c | ||
|
|
64707dbb22 | ||
|
|
554e8eeef3 | ||
|
|
5e10549543 | ||
|
|
685f45bd76 | ||
|
|
61d84dd4c1 | ||
|
|
4d10593bb1 | ||
|
|
fbb8903774 | ||
|
|
e8cac91bb7 | ||
|
|
7328ffa036 | ||
|
|
a03e828317 | ||
|
|
93d4dc70cf | ||
|
|
8e3d16e9fb | ||
|
|
07405e57b9 | ||
|
|
354c9187db | ||
|
|
af33df3004 | ||
|
|
78bfde237f | ||
|
|
7b9033d678 | ||
|
|
f784cfad46 | ||
|
|
e40c139ff1 | ||
|
|
edf4f7695d | ||
|
|
60ec03237e | ||
|
|
a91641e427 | ||
|
|
5c3992018f | ||
|
|
f5b682619f | ||
|
|
743fa745b7 | ||
|
|
39400fd381 | ||
|
|
5299ac35a6 | ||
|
|
ef76ed394c | ||
|
|
1472637de7 | ||
|
|
f176f1909b | ||
|
|
3b8baa85a3 | ||
|
|
73921b1024 | ||
|
|
ece140f18c | ||
|
|
5e42947fbd | ||
|
|
1bfb9b02f5 | ||
|
|
16a14c2b76 | ||
|
|
f6199c6c17 | ||
|
|
d7e7f06e88 | ||
|
|
4c4e856a1a | ||
|
|
07bbbbaf61 | ||
|
|
3236827781 | ||
|
|
0be664cc3d | ||
|
|
6cc6849ccc | ||
|
|
5d5cd71714 | ||
|
|
d248343517 | ||
|
|
64d800427f | ||
|
|
c4c896a833 | ||
|
|
b6b5bb3f75 | ||
|
|
5d69bb7383 | ||
|
|
76e222079a | ||
|
|
73abb9278d | ||
|
|
8fd843e7ce | ||
|
|
6a497a23d9 | ||
|
|
3ac74e1091 | ||
|
|
ef0fb48f1f | ||
|
|
414ef2bc3d | ||
|
|
ea791309ad | ||
|
|
706da6e431 | ||
|
|
ed116e7cea | ||
|
|
5b56f4007b | ||
|
|
e2071542bf | ||
|
|
cdb217b774 | ||
|
|
079798940b | ||
|
|
f1c24689bf | ||
|
|
1f9cf6ed7c | ||
|
|
43f218410f | ||
|
|
3fd9d5f641 | ||
|
|
f5ab8f2062 | ||
|
|
8774a8fbc2 | ||
|
|
6f4f0f03d2 | ||
|
|
00b5fdce03 | ||
|
|
baee6a0d91 | ||
|
|
ff44bcc489 | ||
|
|
c797ac4268 | ||
|
|
d22a76d4d1 | ||
|
|
a6642e0ebc | ||
|
|
3d4d260a34 | ||
|
|
8e4b9da97d | ||
|
|
2be80ba30f | ||
|
|
d231f944c0 | ||
|
|
2e44c88d6c | ||
|
|
21eb1ce6c9 | ||
|
|
cdfd411df7 | ||
|
|
a6149ca90c | ||
|
|
642435486c | ||
|
|
fc84d6c4b7 | ||
|
|
aa4bddd6ec | ||
|
|
8ec12a1b65 | ||
|
|
0fbf552e95 | ||
|
|
09b1b120d7 | ||
|
|
557244bc3f | ||
|
|
24c5ed1cff | ||
|
|
32e55ebd0c | ||
|
|
ea3070d02b | ||
|
|
9aaba49a9f | ||
|
|
9b64be07a9 | ||
|
|
42c3c28ea7 | ||
|
|
9e9236badb | ||
|
|
560ebcec8d | ||
|
|
9b1fe4338b | ||
|
|
9188e3ad3f | ||
|
|
af65af5be9 | ||
|
|
2f0115c300 | ||
|
|
0646461342 | ||
|
|
ec30ec0996 | ||
|
|
cdecb7a43c | ||
|
|
aa9c1b66a0 | ||
|
|
846eac29dc | ||
|
|
0f9e3c5b33 | ||
|
|
aa27746982 | ||
|
|
cc48436794 | ||
|
|
d8a4954bf1 | ||
|
|
d40a029dae | ||
|
|
96d961c393 | ||
|
|
7b6814e32d | ||
|
|
6fee2d3536 | ||
|
|
636fc633d4 | ||
|
|
72a239838e | ||
|
|
a463dbc5fb | ||
|
|
016ae3b9e9 | ||
|
|
7d0d421724 | ||
|
|
83b5856a19 | ||
|
|
f617b27110 | ||
|
|
a91a0263cf | ||
|
|
80ffe13f3e | ||
|
|
1eb726c9bb | ||
|
|
1fa3ba8b42 | ||
|
|
b6bfd66a49 | ||
|
|
1be0e7ddaa | ||
|
|
2cac9b03ff | ||
|
|
f5f4190803 | ||
|
|
a14d554947 | ||
|
|
6d9e5147b5 | ||
|
|
841452cb9e | ||
|
|
9c76368dbc | ||
|
|
bd5122c6ea | ||
|
|
6643258618 | ||
|
|
bc3f02cb6b | ||
|
|
d848ae332a | ||
|
|
08ddc98303 | ||
|
|
ea51fc8410 | ||
|
|
a3344c4290 | ||
|
|
22c1ce3ea5 | ||
|
|
afb14e6782 | ||
|
|
e177363377 | ||
|
|
ce213934c9 | ||
|
|
af286ec52e | ||
|
|
f7f2b7607b | ||
|
|
60a282826c | ||
|
|
3eba599aec | ||
|
|
74d876f145 | ||
|
|
d7609f119c | ||
|
|
65c2c7d80b | ||
|
|
468a32a819 | ||
|
|
b89cf73ae2 | ||
|
|
9cf43dea1a | ||
|
|
670ffe2078 | ||
|
|
884cf756ed | ||
|
|
e44ba54857 | ||
|
|
3712749a94 | ||
|
|
6569c4aa03 | ||
|
|
d6b2b3c996 | ||
|
|
06c7900ece | ||
|
|
52a6a12a9a | ||
|
|
e647603dce | ||
|
|
dadf6174ba | ||
|
|
84de7675c4 | ||
|
|
6311a80d0e | ||
|
|
9504e69598 | ||
|
|
5398b651f7 | ||
|
|
b5596c4596 | ||
|
|
fdcea5537c | ||
|
|
8ca8bc810d | ||
|
|
8f909b051f | ||
|
|
90f2b2d249 | ||
|
|
f74b27c58c | ||
|
|
3f091f4748 | ||
|
|
d84c9ad611 | ||
|
|
e55e15693d | ||
|
|
c54e6bafdb | ||
|
|
2e56c4895d | ||
|
|
bce4224d6e | ||
|
|
812e2814bc | ||
|
|
7cd17f8e1f | ||
|
|
6193b06708 | ||
|
|
12af68bdb5 | ||
|
|
881f7e9062 | ||
|
|
1db4076bbd | ||
|
|
1933e44719 | ||
|
|
25441cb650 | ||
|
|
bc755ac32f | ||
|
|
1fa34be52a | ||
|
|
f7a6d57855 | ||
|
|
8a987af244 | ||
|
|
65cbb06080 | ||
|
|
979ea9c252 | ||
|
|
aa1f4ee72a | ||
|
|
74ce485b73 | ||
|
|
165e6508f8 | ||
|
|
c7af2889fa | ||
|
|
5ab3390434 | ||
|
|
67f60f1889 | ||
|
|
985a468d0f | ||
|
|
34dc6fbdc1 | ||
|
|
b57152cc25 | ||
|
|
dc9562e430 | ||
|
|
05689fe183 | ||
|
|
8f6f95211e | ||
|
|
f30b6c9e6e | ||
|
|
12ac7d6a00 | ||
|
|
10251a6447 | ||
|
|
089cbbc20a | ||
|
|
95ab68acd1 | ||
|
|
abc4f6c70b | ||
|
|
8fc3a1f9c9 | ||
|
|
5c3d0fc02c | ||
|
|
7efb47fed4 | ||
|
|
c212a30d33 | ||
|
|
7692332f0e | ||
|
|
ef6db64e9f | ||
|
|
e68f1dbc99 | ||
|
|
0c9ebc36d4 | ||
|
|
fcd6eb7801 | ||
|
|
328c2182c2 | ||
|
|
08706f5dfb | ||
|
|
d49f165f0d | ||
|
|
cf0fc3a4a9 | ||
|
|
72c8fd257c | ||
|
|
fa620e41a4 | ||
|
|
b07f851ce7 | ||
|
|
16b3108719 | ||
|
|
f385c624c7 | ||
|
|
f7e9975192 | ||
|
|
cde989b59d | ||
|
|
c0e263abd3 | ||
|
|
79c0c11e80 | ||
|
|
ca671551c8 | ||
|
|
42ed312384 | ||
|
|
0e9074aaba | ||
|
|
7c1961d4ef | ||
|
|
71e57717c2 | ||
|
|
8a549b83a2 | ||
|
|
d7081c5f23 | ||
|
|
588d64a30b | ||
|
|
8335bdf3d4 | ||
|
|
85394f2438 | ||
|
|
42b556574f | ||
|
|
f34e65ad9e | ||
|
|
51352a6819 | ||
|
|
d9887ec370 | ||
|
|
670bf16cd0 | ||
|
|
c994950aaf | ||
|
|
a26ed6fe6c | ||
|
|
a12a7e73f9 | ||
|
|
779228857e | ||
|
|
8d0b696d33 | ||
|
|
23ae220aa7 | ||
|
|
b7940e0002 | ||
|
|
b3fd8bd0ae | ||
|
|
bffeb237de | ||
|
|
23e3602ea1 | ||
|
|
34cfd205f6 | ||
|
|
df3da8be7a | ||
|
|
940243f45e | ||
|
|
75d6599143 | ||
|
|
929a27a5ac | ||
|
|
82ddee2104 | ||
|
|
a141678119 | ||
|
|
96d109af81 | ||
|
|
a309eb9f3c | ||
|
|
d034dab265 | ||
|
|
883a035e5c | ||
|
|
08603091c5 | ||
|
|
a2e84e5a1e | ||
|
|
d148898ad7 | ||
|
|
9439621849 | ||
|
|
36cf622979 | ||
|
|
15ded89618 | ||
|
|
b84f74c167 | ||
|
|
a97300f8be | ||
|
|
9e12cff317 | ||
|
|
ecdf1f4ddc | ||
|
|
2fa7a48163 | ||
|
|
5e31e533e2 | ||
|
|
8adf76dcc9 | ||
|
|
15899c10b2 | ||
|
|
05ff05ea4b | ||
|
|
bd62df48c2 | ||
|
|
2366cbc833 | ||
|
|
25fb609544 | ||
|
|
af793395f0 | ||
|
|
8f41776858 | ||
|
|
139b13b8d1 | ||
|
|
4c611a5be1 | ||
|
|
5e7a21e177 | ||
|
|
5f7dda5ba8 | ||
|
|
2dfa1ca0f2 | ||
|
|
358cdcf4c4 | ||
|
|
c8f4ace5c4 | ||
|
|
5cac6ca8bb | ||
|
|
fccad71df1 | ||
|
|
97ae2674dc | ||
|
|
7c70affd7f | ||
|
|
52ff568d86 | ||
|
|
b917aeaa0b | ||
|
|
8de443ec4c | ||
|
|
7d9893c614 | ||
|
|
3540712517 | ||
|
|
a8b1a86bd7 | ||
|
|
1babd3a5a2 | ||
|
|
5ecd04dd4f | ||
|
|
50399e5194 | ||
|
|
b734acf1b1 | ||
|
|
33aa8e2471 | ||
|
|
2c58fe736b | ||
|
|
6fe1de5d86 | ||
|
|
064460b95f | ||
|
|
2c3b19a539 | ||
|
|
dc30a4c1ae | ||
|
|
86e9901bf2 | ||
|
|
6519e0835a | ||
|
|
a52344fc01 | ||
|
|
b67424643d | ||
|
|
575a4c01c9 | ||
|
|
f0d4ee6618 | ||
|
|
8753186a0d | ||
|
|
ff8fb8000d | ||
|
|
9dd38b99d6 | ||
|
|
dfe08c1ec9 | ||
|
|
fb26e78ecc | ||
|
|
4c687036c4 | ||
|
|
062d8d0f4f | ||
|
|
73b6338f62 | ||
|
|
c0d1e2c07a | ||
|
|
e70feceafe | ||
|
|
71ac0286b1 | ||
|
|
022f4d2c11 | ||
|
|
a83a839cff | ||
|
|
b259ee89aa | ||
|
|
65cf14bfce | ||
|
|
d9476fb5ca | ||
|
|
9882365ab4 | ||
|
|
2d758ce963 | ||
|
|
1dd003d26a | ||
|
|
0df5b77595 | ||
|
|
e190a005db | ||
|
|
45596a0342 | ||
|
|
405429a300 | ||
|
|
d009a29426 | ||
|
|
f1fb42460a | ||
|
|
5e110e9f7b | ||
|
|
77a409935d | ||
|
|
863baeb68b | ||
|
|
11142690a0 | ||
|
|
02e8c5faca | ||
|
|
c41081d35c | ||
|
|
db4c26a400 | ||
|
|
331a23fc20 | ||
|
|
db5a40d743 | ||
|
|
e4ab51329d | ||
|
|
8490e7ca7c | ||
|
|
86782aeb1b | ||
|
|
49a44fc92e | ||
|
|
cd39a52c25 | ||
|
|
634101ceb5 | ||
|
|
55555c8787 | ||
|
|
d36d825ac1 | ||
|
|
9bb01cd67c | ||
|
|
29b91075d2 | ||
|
|
6d46fc9f9f | ||
|
|
a2c41c9e36 | ||
|
|
ee700ac861 | ||
|
|
9884a4336f | ||
|
|
5b83d4bef8 | ||
|
|
d320a89590 | ||
|
|
f7e4afc282 | ||
|
|
88e87d589b | ||
|
|
d8c6dede7e | ||
|
|
5cc84133e3 | ||
|
|
f7728aa1f6 | ||
|
|
2b61f9a731 | ||
|
|
f407022fe6 | ||
|
|
41b9f19b01 | ||
|
|
09c6faf923 | ||
|
|
26d0177c01 | ||
|
|
f7415c8a8f | ||
|
|
4cf79088f9 | ||
|
|
50cd321818 | ||
|
|
83bbe6a9d9 | ||
|
|
0a33c18e36 | ||
|
|
6cf158ac63 | ||
|
|
f96bfa6afa | ||
|
|
2b64cf9126 | ||
|
|
a8dcfc44f5 | ||
|
|
0ff9c9da27 | ||
|
|
07e7c2d852 | ||
|
|
10e4b5b2a3 | ||
|
|
998653ea9d | ||
|
|
1a38e925bf | ||
|
|
c8f51380e6 | ||
|
|
2406d57d51 | ||
|
|
cb1e47eb71 | ||
|
|
c0a650f28b | ||
|
|
460cf6fd20 | ||
|
|
5bedfc1c84 | ||
|
|
5001592fb4 | ||
|
|
f6495e59c5 | ||
|
|
66bf431481 | ||
|
|
d9685e991e | ||
|
|
e0790700cd | ||
|
|
910a9600bd | ||
|
|
fc52b2b940 | ||
|
|
b99f828583 | ||
|
|
f38891cace | ||
|
|
8c5111e11a | ||
|
|
5575b981c8 | ||
|
|
0b36732911 | ||
|
|
52f3081a40 | ||
|
|
00c71dc26a | ||
|
|
5218c8584f | ||
|
|
6054bd6621 | ||
|
|
55af4ed385 | ||
|
|
64aee9c8ae | ||
|
|
5233e72205 | ||
|
|
db5b45222a | ||
|
|
fc4787da4e | ||
|
|
4ffbb46cf9 | ||
|
|
c3c2550f17 | ||
|
|
41e8ab5383 | ||
|
|
a802940616 | ||
|
|
dec848f072 | ||
|
|
fb229d4064 | ||
|
|
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 |
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
.editorconfig
Normal file
39
.editorconfig
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
# Unix style files
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[Makefile,Makefile.*]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.cmd]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = crlf
|
||||||
|
|
||||||
|
[*.{h,cpp}]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.rc]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.{md,markdown}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.patch]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build/build_mingw.cmd eol=crlf
|
||||||
61
.github/workflows/build-deb.yml
vendored
Normal file
61
.github/workflows/build-deb.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
name: Build Debian packages
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- .github/workflows/build-deb.yml
|
||||||
|
- contrib/**
|
||||||
|
- daemon/**
|
||||||
|
- debian/**
|
||||||
|
- i18n/**
|
||||||
|
- libi2pd/**
|
||||||
|
- libi2pd_client/**
|
||||||
|
- Makefile
|
||||||
|
- Makefile.linux
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: ${{ matrix.dist }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
dist: ['buster', 'bullseye', 'bookworm']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Commit Hash
|
||||||
|
id: commit
|
||||||
|
uses: prompt/actions-commit-hash@v3.0.0
|
||||||
|
|
||||||
|
- name: Build package
|
||||||
|
uses: jtdor/build-deb-action@v1
|
||||||
|
with:
|
||||||
|
docker-image: debian:${{ matrix.dist }}-slim
|
||||||
|
buildpackage-opts: --build=binary --no-sign
|
||||||
|
before-build-hook: debchange --controlmaint --local "+${{ steps.commit.outputs.short }}~${{ matrix.dist }}" -b --distribution ${{ matrix.dist }} "CI build"
|
||||||
|
extra-build-deps: devscripts git
|
||||||
|
|
||||||
|
- name: Upload package
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: i2pd_${{ matrix.dist }}
|
||||||
|
path: debian/artifacts/i2pd_*.deb
|
||||||
|
|
||||||
|
- name: Upload debugging symbols
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: i2pd-dbgsym_${{ matrix.dist }}
|
||||||
|
path: debian/artifacts/i2pd-dbgsym_*.deb
|
||||||
50
.github/workflows/build-freebsd.yml
vendored
Normal file
50
.github/workflows/build-freebsd.yml
vendored
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
name: Build on FreeBSD
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- .github/workflows/build-freebsd.yml
|
||||||
|
- build/CMakeLists.txt
|
||||||
|
- build/cmake_modules/**
|
||||||
|
- daemon/**
|
||||||
|
- i18n/**
|
||||||
|
- libi2pd/**
|
||||||
|
- libi2pd_client/**
|
||||||
|
- Makefile
|
||||||
|
- Makefile.bsd
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
name: with UPnP
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Test in FreeBSD
|
||||||
|
id: test
|
||||||
|
uses: vmactions/freebsd-vm@v1
|
||||||
|
with:
|
||||||
|
usesh: true
|
||||||
|
mem: 2048
|
||||||
|
sync: rsync
|
||||||
|
copyback: true
|
||||||
|
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
cmake -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release .
|
||||||
|
gmake -j2
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: i2pd-freebsd
|
||||||
|
path: build/i2pd
|
||||||
45
.github/workflows/build-osx.yml
vendored
Normal file
45
.github/workflows/build-osx.yml
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
name: Build on OSX
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- .github/workflows/build-osx.yml
|
||||||
|
- daemon/**
|
||||||
|
- i18n/**
|
||||||
|
- libi2pd/**
|
||||||
|
- libi2pd_client/**
|
||||||
|
- Makefile
|
||||||
|
- Makefile.homebrew
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: With USE_UPNP=${{ matrix.with_upnp }}
|
||||||
|
runs-on: macOS-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
with_upnp: ['yes', 'no']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install required formulae
|
||||||
|
run: |
|
||||||
|
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
|
||||||
|
brew update
|
||||||
|
brew install boost miniupnpc openssl@1.1
|
||||||
|
|
||||||
|
- name: List installed formulae
|
||||||
|
run: brew list
|
||||||
|
|
||||||
|
- name: Build application
|
||||||
|
run: make HOMEBREW=1 USE_UPNP=${{ matrix.with_upnp }} PREFIX=$GITHUB_WORKSPACE/output -j3
|
||||||
80
.github/workflows/build-windows-msvc.yml-disabled
vendored
Normal file
80
.github/workflows/build-windows-msvc.yml-disabled
vendored
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
name: Build on Windows with MSVC
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- .github/workflows/build-windows-msvc.yml
|
||||||
|
- build/CMakeLists.txt
|
||||||
|
- build/cmake_modules/**
|
||||||
|
- daemon/**
|
||||||
|
- i18n/**
|
||||||
|
- libi2pd/**
|
||||||
|
- libi2pd_client/**
|
||||||
|
- Win32/**
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: windows-latest
|
||||||
|
env:
|
||||||
|
boost_path: ${{ github.workspace }}\boost_1_83_0
|
||||||
|
openssl_path: ${{ github.workspace }}\openssl_3_2_1
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Build and install zlib
|
||||||
|
run: |
|
||||||
|
powershell -Command "(Invoke-WebRequest -Uri https://raw.githubusercontent.com/r4sas/zlib.install/master/install.bat -OutFile install_zlib.bat)"
|
||||||
|
powershell -Command "(Get-Content install_zlib.bat) | Set-Content install_zlib.bat" # fixing line endings
|
||||||
|
set BUILD_TYPE=Debug
|
||||||
|
./install_zlib.bat
|
||||||
|
set BUILD_TYPE=Release
|
||||||
|
./install_zlib.bat
|
||||||
|
del install_zlib.bat
|
||||||
|
|
||||||
|
- name: Install Boost
|
||||||
|
run: |
|
||||||
|
powershell -Command "(Start-BitsTransfer -Source https://sourceforge.net/projects/boost/files/boost-binaries/1.83.0/boost_1_83_0-msvc-14.3-64.exe/download -Destination boost_1_83_0-msvc-14.3-64.exe)"
|
||||||
|
./boost_1_83_0-msvc-14.3-64.exe /DIR="${{env.boost_path}}" /VERYSILENT /SUPPRESSMSGBOXES /SP-
|
||||||
|
|
||||||
|
- name: Install OpenSSL
|
||||||
|
run: |
|
||||||
|
powershell -Command "(Start-BitsTransfer -Source https://slproweb.com/download/Win64OpenSSL-3_2_1.exe -Destination Win64OpenSSL-3_2_1.exe)"
|
||||||
|
./Win64OpenSSL-3_2_1.exe /DIR="${{env.openssl_path}}" /TASKS="copytobin" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART /SP-
|
||||||
|
|
||||||
|
- name: Make copy of the OpenSSL libraries for CMake
|
||||||
|
run: |
|
||||||
|
dir ${{ github.workspace }}
|
||||||
|
dir ${{env.openssl_path}}\lib\VC
|
||||||
|
dir ${{env.openssl_path}}\lib\VC\x64\
|
||||||
|
dir ${{env.openssl_path}}\lib\VC\x64\MTd\
|
||||||
|
xcopy /s /y "${{env.openssl_path}}\lib\VC\x64\MTd" "${{env.openssl_path}}\lib"
|
||||||
|
|
||||||
|
- name: Configure
|
||||||
|
working-directory: build
|
||||||
|
run: cmake -DBoost_ROOT="${{env.boost_path}}" -DOPENSSL_ROOT_DIR="${{env.openssl_path}}" -DWITH_STATIC=ON .
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
working-directory: build
|
||||||
|
run: cmake --build . --config Debug -- -m
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: i2pd-msvc
|
||||||
|
path: build/Debug/i2pd.*
|
||||||
|
|
||||||
250
.github/workflows/build-windows.yml
vendored
Normal file
250
.github/workflows/build-windows.yml
vendored
Normal file
@@ -0,0 +1,250 @@
|
|||||||
|
name: Build on Windows
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- .github/workflows/build-windows.yml
|
||||||
|
- build/CMakeLists.txt
|
||||||
|
- build/cmake_modules/**
|
||||||
|
- daemon/**
|
||||||
|
- i18n/**
|
||||||
|
- libi2pd/**
|
||||||
|
- libi2pd_client/**
|
||||||
|
- Win32/**
|
||||||
|
- Makefile
|
||||||
|
- Makefile.mingw
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: msys2 {0}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: ${{ matrix.arch }}
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include: [
|
||||||
|
{ msystem: UCRT64, arch: ucrt-x86_64, arch_short: x64-ucrt, compiler: gcc },
|
||||||
|
{ msystem: CLANG64, arch: clang-x86_64, arch_short: x64-clang, compiler: clang },
|
||||||
|
{ msystem: MINGW64, arch: x86_64, arch_short: x64, compiler: gcc },
|
||||||
|
{ msystem: MINGW32, arch: i686, arch_short: x86, compiler: gcc }
|
||||||
|
]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup MSYS2
|
||||||
|
uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
msystem: ${{ matrix.msystem }}
|
||||||
|
install: base-devel git mingw-w64-${{ matrix.arch }}-${{ matrix.compiler }} mingw-w64-${{ matrix.arch }}-boost mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-miniupnpc
|
||||||
|
update: true
|
||||||
|
|
||||||
|
- name: Install additional clang packages
|
||||||
|
if: ${{ matrix.msystem == 'CLANG64' }}
|
||||||
|
run: pacman --noconfirm -S mingw-w64-${{ matrix.arch }}-gcc-compat
|
||||||
|
|
||||||
|
- name: Build application
|
||||||
|
run: |
|
||||||
|
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
|
||||||
|
make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes -j3
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: i2pd-${{ matrix.arch_short }}.exe
|
||||||
|
path: i2pd.exe
|
||||||
|
|
||||||
|
build-cmake:
|
||||||
|
name: CMake ${{ matrix.arch }}
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include: [
|
||||||
|
{ msystem: UCRT64, arch: ucrt-x86_64, arch_short: x64-ucrt, compiler: gcc },
|
||||||
|
{ msystem: CLANG64, arch: clang-x86_64, arch_short: x64-clang, compiler: clang },
|
||||||
|
{ msystem: MINGW64, arch: x86_64, arch_short: x64, compiler: gcc },
|
||||||
|
{ msystem: MINGW32, arch: i686, arch_short: x86, compiler: gcc }
|
||||||
|
]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup MSYS2
|
||||||
|
uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
msystem: ${{ matrix.msystem }}
|
||||||
|
install: base-devel git mingw-w64-${{ matrix.arch }}-cmake mingw-w64-${{ matrix.arch }}-ninja mingw-w64-${{ matrix.arch }}-${{ matrix.compiler }} mingw-w64-${{ matrix.arch }}-boost mingw-w64-${{ matrix.arch }}-openssl mingw-w64-${{ matrix.arch }}-miniupnpc
|
||||||
|
update: true
|
||||||
|
|
||||||
|
- name: Build application
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
cmake -DWITH_GIT_VERSION=ON -DWITH_STATIC=ON -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release .
|
||||||
|
cmake --build . -- -j3
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: i2pd-cmake-${{ matrix.arch_short }}.exe
|
||||||
|
path: build/i2pd.exe
|
||||||
|
|
||||||
|
build-xp:
|
||||||
|
name: XP
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Setup MSYS2
|
||||||
|
uses: msys2/setup-msys2@v2
|
||||||
|
with:
|
||||||
|
msystem: MINGW32
|
||||||
|
install: base-devel git mingw-w64-i686-gcc mingw-w64-i686-boost mingw-w64-i686-openssl mingw-w64-i686-miniupnpc
|
||||||
|
cache: true
|
||||||
|
update: true
|
||||||
|
|
||||||
|
- name: Clone MinGW packages repository and revert boost to 1.85.0
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/msys2/MINGW-packages
|
||||||
|
cd MINGW-packages
|
||||||
|
git checkout 4cbb366edf2f268ac3146174b40ce38604646fc5 mingw-w64-boost
|
||||||
|
cd mingw-w64-boost
|
||||||
|
sed -i 's/boostorg.jfrog.io\/artifactory\/main/archives.boost.io/' PKGBUILD
|
||||||
|
|
||||||
|
# headers
|
||||||
|
- name: Get headers package version
|
||||||
|
id: version-headers
|
||||||
|
run: |
|
||||||
|
echo "version=$(pacman -Si mingw-w64-i686-headers-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache headers package
|
||||||
|
uses: actions/cache@v4
|
||||||
|
id: cache-headers
|
||||||
|
with:
|
||||||
|
path: MINGW-packages/mingw-w64-headers-git/*.zst
|
||||||
|
key: winxp-headers-${{ steps.version-headers.outputs.version }}
|
||||||
|
- name: Build WinXP-capable headers package
|
||||||
|
if: steps.cache-headers.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd MINGW-packages/mingw-w64-headers-git
|
||||||
|
sed -i 's/0x601/0x501/' PKGBUILD
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||||
|
- name: Install headers package
|
||||||
|
run: pacman --noconfirm -U MINGW-packages/mingw-w64-headers-git/mingw-w64-i686-*-any.pkg.tar.zst
|
||||||
|
|
||||||
|
# CRT
|
||||||
|
- name: Get crt package version
|
||||||
|
id: version-crt
|
||||||
|
run: |
|
||||||
|
echo "version=$(pacman -Si mingw-w64-i686-crt-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache crt package
|
||||||
|
uses: actions/cache@v4
|
||||||
|
id: cache-crt
|
||||||
|
with:
|
||||||
|
path: MINGW-packages/mingw-w64-crt-git/*.zst
|
||||||
|
key: winxp-crt-${{ steps.version-crt.outputs.version }}
|
||||||
|
- name: Build WinXP-capable crt package
|
||||||
|
if: steps.cache-crt.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd MINGW-packages/mingw-w64-crt-git
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||||
|
- name: Install crt package
|
||||||
|
run: pacman --noconfirm -U MINGW-packages/mingw-w64-crt-git/mingw-w64-i686-*-any.pkg.tar.zst
|
||||||
|
|
||||||
|
# winpthreads
|
||||||
|
- name: Get winpthreads package version
|
||||||
|
id: version-winpthreads
|
||||||
|
run: |
|
||||||
|
echo "version=$(pacman -Si mingw-w64-i686-winpthreads-git | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache winpthreads package
|
||||||
|
uses: actions/cache@v4
|
||||||
|
id: cache-winpthreads
|
||||||
|
with:
|
||||||
|
path: MINGW-packages/mingw-w64-winpthreads-git/*.zst
|
||||||
|
key: winxp-winpthreads-${{ steps.version-winpthreads.outputs.version }}
|
||||||
|
- name: Build WinXP-capable winpthreads package
|
||||||
|
if: steps.cache-winpthreads.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd MINGW-packages/mingw-w64-winpthreads-git
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||||
|
- name: Install winpthreads package
|
||||||
|
run: pacman --noconfirm -U MINGW-packages/mingw-w64-winpthreads-git/mingw-w64-i686-*-any.pkg.tar.zst
|
||||||
|
|
||||||
|
# OpenSSL
|
||||||
|
- name: Get openssl package version
|
||||||
|
id: version-openssl
|
||||||
|
run: |
|
||||||
|
echo "version=$(pacman -Si mingw-w64-i686-openssl | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache openssl package
|
||||||
|
uses: actions/cache@v4
|
||||||
|
id: cache-openssl
|
||||||
|
with:
|
||||||
|
path: MINGW-packages/mingw-w64-openssl/*.zst
|
||||||
|
key: winxp-openssl-${{ steps.version-openssl.outputs.version }}
|
||||||
|
- name: Build WinXP-capable openssl package
|
||||||
|
if: steps.cache-openssl.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd MINGW-packages/mingw-w64-openssl
|
||||||
|
gpg --recv-keys D894E2CE8B3D79F5
|
||||||
|
gpg --recv-keys 216094DFD0CB81EF
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||||
|
- name: Install openssl package
|
||||||
|
run: pacman --noconfirm -U MINGW-packages/mingw-w64-openssl/mingw-w64-i686-*-any.pkg.tar.zst
|
||||||
|
|
||||||
|
# Boost
|
||||||
|
#- name: Get boost package version
|
||||||
|
# id: version-boost
|
||||||
|
# run: |
|
||||||
|
# echo "version=$(pacman -Si mingw-w64-i686-boost | grep -Po '^Version\s*: \K.+')" >> $GITHUB_OUTPUT
|
||||||
|
- name: Cache boost package
|
||||||
|
uses: actions/cache@v4
|
||||||
|
id: cache-boost
|
||||||
|
with:
|
||||||
|
path: MINGW-packages/mingw-w64-boost/*.zst
|
||||||
|
key: winxp-boost-1.85.0+crt-${{ steps.version-headers.outputs.version }}+ossl-${{ steps.version-openssl.outputs.version }}
|
||||||
|
# Rebuild package if packages above has changed
|
||||||
|
- name: Build WinXP-capable boost package
|
||||||
|
if: steps.cache-boost.outputs.cache-hit != 'true'
|
||||||
|
run: |
|
||||||
|
cd MINGW-packages/mingw-w64-boost
|
||||||
|
MINGW_ARCH=mingw32 makepkg-mingw -sCLf --noconfirm --nocheck
|
||||||
|
- name: Remove boost packages
|
||||||
|
run: pacman --noconfirm -R mingw-w64-i686-boost mingw-w64-i686-boost-libs
|
||||||
|
- name: Install boost package
|
||||||
|
run: pacman --noconfirm -U MINGW-packages/mingw-w64-boost/mingw-w64-i686-*-any.pkg.tar.zst
|
||||||
|
|
||||||
|
# Building i2pd
|
||||||
|
- name: Build application
|
||||||
|
run: |
|
||||||
|
mkdir -p obj/Win32 obj/libi2pd obj/libi2pd_client obj/daemon
|
||||||
|
make USE_UPNP=yes DEBUG=no USE_GIT_VERSION=yes USE_WINXP_FLAGS=yes -j3
|
||||||
|
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: i2pd-xp.exe
|
||||||
|
path: i2pd.exe
|
||||||
67
.github/workflows/build.yml
vendored
Normal file
67
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
name: Build on Ubuntu
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
paths:
|
||||||
|
- .github/workflows/build.yml
|
||||||
|
- build/CMakeLists.txt
|
||||||
|
- build/cmake_modules/**
|
||||||
|
- daemon/**
|
||||||
|
- i18n/**
|
||||||
|
- libi2pd/**
|
||||||
|
- libi2pd_client/**
|
||||||
|
- Makefile
|
||||||
|
- Makefile.linux
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-make:
|
||||||
|
name: Make with USE_UPNP=${{ matrix.with_upnp }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
with_upnp: ['yes', 'no']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: install packages
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||||
|
|
||||||
|
- name: build application
|
||||||
|
run: make USE_UPNP=${{ matrix.with_upnp }} -j3
|
||||||
|
|
||||||
|
build-cmake:
|
||||||
|
name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: true
|
||||||
|
matrix:
|
||||||
|
with_upnp: ['ON', 'OFF']
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: install packages
|
||||||
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install build-essential cmake libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
|
||||||
|
|
||||||
|
- name: build application
|
||||||
|
run: |
|
||||||
|
cd build
|
||||||
|
cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
|
||||||
|
make -j3
|
||||||
140
.github/workflows/docker.yml
vendored
Normal file
140
.github/workflows/docker.yml
vendored
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
name: Build containers
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- openssl
|
||||||
|
- docker
|
||||||
|
paths:
|
||||||
|
- .github/workflows/docker.yml
|
||||||
|
- contrib/docker/**
|
||||||
|
- contrib/certificates/**
|
||||||
|
- daemon/**
|
||||||
|
- i18n/**
|
||||||
|
- libi2pd/**
|
||||||
|
- libi2pd_client/**
|
||||||
|
- Makefile
|
||||||
|
- Makefile.linux
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: Building container for ${{ matrix.platform }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
include: [
|
||||||
|
{ platform: 'linux/amd64', archname: 'amd64' },
|
||||||
|
{ platform: 'linux/386', archname: 'i386' },
|
||||||
|
{ platform: 'linux/arm64', archname: 'arm64' },
|
||||||
|
{ platform: 'linux/arm/v7', archname: 'armv7' },
|
||||||
|
]
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build container for ${{ matrix.archname }}
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: ./contrib/docker
|
||||||
|
file: ./contrib/docker/Dockerfile
|
||||||
|
platforms: ${{ matrix.platform }}
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
purplei2p/i2pd:latest-${{ matrix.archname }}
|
||||||
|
ghcr.io/purplei2p/i2pd:latest-${{ matrix.archname }}
|
||||||
|
provenance: false
|
||||||
|
|
||||||
|
push:
|
||||||
|
name: Pushing merged manifest
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
packages: write
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
needs: build
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Login to DockerHub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Login to GitHub Container registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Create and push latest manifest image to Docker Hub
|
||||||
|
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
inputs: purplei2p/i2pd:latest
|
||||||
|
images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Create and push latest manifest image to GHCR
|
||||||
|
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
inputs: ghcr.io/purplei2p/i2pd:latest
|
||||||
|
images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Store release version to env
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
run: echo "RELEASE_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create and push release manifest to Docker Hub
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
inputs: purplei2p/i2pd:latest,purplei2p/i2pd:latest-release,purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
|
images: purplei2p/i2pd:latest-amd64,purplei2p/i2pd:latest-i386,purplei2p/i2pd:latest-arm64,purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
|
|
||||||
|
- name: Create and push release manifest to GHCR
|
||||||
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
|
uses: Noelware/docker-manifest-action@master
|
||||||
|
with:
|
||||||
|
inputs: ghcr.io/purplei2p/i2pd:latest,ghcr.io/purplei2p/i2pd:latest-release,ghcr.io/purplei2p/i2pd:release-${{ env.RELEASE_VERSION }}
|
||||||
|
images: ghcr.io/purplei2p/i2pd:latest-amd64,ghcr.io/purplei2p/i2pd:latest-i386,ghcr.io/purplei2p/i2pd:latest-arm64,ghcr.io/purplei2p/i2pd:latest-armv7
|
||||||
|
push: true
|
||||||
53
.gitignore
vendored
53
.gitignore
vendored
@@ -1,16 +1,27 @@
|
|||||||
# i2pd
|
# i2pd
|
||||||
obj/*.o
|
*.o
|
||||||
router.info
|
router.info
|
||||||
router.keys
|
router.keys
|
||||||
i2p
|
i2p
|
||||||
libi2pd.so
|
|
||||||
netDb
|
netDb
|
||||||
|
/i2pd
|
||||||
|
/libi2pd.a
|
||||||
|
/libi2pdclient.a
|
||||||
|
/libi2pdlang.a
|
||||||
|
/libi2pd.so
|
||||||
|
/libi2pdclient.so
|
||||||
|
/libi2pdlang.so
|
||||||
|
/libi2pd.dll
|
||||||
|
/libi2pdclient.dll
|
||||||
|
/libi2pdlang.dll
|
||||||
|
*.exe
|
||||||
|
|
||||||
|
|
||||||
# Autotools
|
# Autotools
|
||||||
autom4te.cache
|
autom4te.cache
|
||||||
.deps
|
.deps
|
||||||
stamp-h1
|
stamp-h1
|
||||||
Makefile
|
#Makefile
|
||||||
config.h
|
config.h
|
||||||
config.h.in~
|
config.h.in~
|
||||||
config.log
|
config.log
|
||||||
@@ -230,3 +241,39 @@ pip-log.txt
|
|||||||
|
|
||||||
#Mr Developer
|
#Mr Developer
|
||||||
.mr.developer.cfg
|
.mr.developer.cfg
|
||||||
|
|
||||||
|
# Sphinx
|
||||||
|
docs/_build
|
||||||
|
/androidIdea/
|
||||||
|
|
||||||
|
# Doxygen
|
||||||
|
docs/generated
|
||||||
|
|
||||||
|
# emacs files
|
||||||
|
*~
|
||||||
|
*\#*
|
||||||
|
|
||||||
|
# gdb files
|
||||||
|
.gdb_history
|
||||||
|
|
||||||
|
# cmake makefile
|
||||||
|
build/Makefile
|
||||||
|
|
||||||
|
# debian stuff
|
||||||
|
debian/i2pd.1.gz
|
||||||
|
.pc/
|
||||||
|
|
||||||
|
# qt
|
||||||
|
|
||||||
|
qt/i2pd_qt/*.autosave
|
||||||
|
qt/i2pd_qt/*.ui.bk*
|
||||||
|
qt/i2pd_qt/*.ui_*
|
||||||
|
|
||||||
|
#unknown android stuff
|
||||||
|
android/libs/
|
||||||
|
|
||||||
|
#various logs
|
||||||
|
*LOGS/
|
||||||
|
|
||||||
|
qt/build-*.sh*
|
||||||
|
|
||||||
|
|||||||
569
AddressBook.cpp
569
AddressBook.cpp
@@ -1,569 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <fstream>
|
|
||||||
#include <chrono>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include <cryptopp/osrng.h>
|
|
||||||
#include "base64.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "AddressBook.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
|
|
||||||
class AddressBookFilesystemStorage: public AddressBookStorage
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
AddressBookFilesystemStorage ();
|
|
||||||
bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const;
|
|
||||||
void AddAddress (const i2p::data::IdentityEx& address);
|
|
||||||
void RemoveAddress (const i2p::data::IdentHash& ident);
|
|
||||||
|
|
||||||
int Load (std::map<std::string, i2p::data::IdentHash>& addresses);
|
|
||||||
int Save (const std::map<std::string, i2p::data::IdentHash>& addresses);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
boost::filesystem::path GetPath () const { return i2p::util::filesystem::GetDefaultDataDir() / "addressbook"; };
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
AddressBookFilesystemStorage::AddressBookFilesystemStorage ()
|
|
||||||
{
|
|
||||||
auto path = GetPath ();
|
|
||||||
if (!boost::filesystem::exists (path))
|
|
||||||
{
|
|
||||||
// Create directory is necessary
|
|
||||||
if (!boost::filesystem::create_directory (path))
|
|
||||||
LogPrint (eLogError, "Failed to create addressbook directory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddressBookFilesystemStorage::GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const
|
|
||||||
{
|
|
||||||
auto filename = GetPath () / (ident.ToBase32() + ".b32");
|
|
||||||
std::ifstream f(filename.c_str (), std::ifstream::binary);
|
|
||||||
if (f.is_open ())
|
|
||||||
{
|
|
||||||
f.seekg (0,std::ios::end);
|
|
||||||
size_t len = f.tellg ();
|
|
||||||
if (len < i2p::data::DEFAULT_IDENTITY_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "File ", filename, " is too short. ", len);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
f.seekg(0, std::ios::beg);
|
|
||||||
uint8_t * buf = new uint8_t[len];
|
|
||||||
f.read((char *)buf, len);
|
|
||||||
address.FromBuffer (buf, len);
|
|
||||||
delete[] buf;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBookFilesystemStorage::AddAddress (const i2p::data::IdentityEx& address)
|
|
||||||
{
|
|
||||||
auto filename = GetPath () / (address.GetIdentHash ().ToBase32() + ".b32");
|
|
||||||
std::ofstream f (filename.c_str (), std::ofstream::binary | std::ofstream::out);
|
|
||||||
if (f.is_open ())
|
|
||||||
{
|
|
||||||
size_t len = address.GetFullLen ();
|
|
||||||
uint8_t * buf = new uint8_t[len];
|
|
||||||
address.ToBuffer (buf, len);
|
|
||||||
f.write ((char *)buf, len);
|
|
||||||
delete[] buf;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Can't open file ", filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBookFilesystemStorage::RemoveAddress (const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
auto filename = GetPath () / (ident.ToBase32() + ".b32");
|
|
||||||
if (boost::filesystem::exists (filename))
|
|
||||||
boost::filesystem::remove (filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
int AddressBookFilesystemStorage::Load (std::map<std::string, i2p::data::IdentHash>& addresses)
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
auto filename = GetPath () / "addresses.csv";
|
|
||||||
std::ifstream f (filename.c_str (), std::ofstream::in); // in text mode
|
|
||||||
if (f.is_open ())
|
|
||||||
{
|
|
||||||
addresses.clear ();
|
|
||||||
while (!f.eof ())
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
getline(f, s);
|
|
||||||
if (!s.length())
|
|
||||||
continue; // skip empty line
|
|
||||||
|
|
||||||
size_t pos = s.find(',');
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
std::string name = s.substr(0, pos++);
|
|
||||||
std::string addr = s.substr(pos);
|
|
||||||
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
ident.FromBase32 (addr);
|
|
||||||
addresses[name] = ident;
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, num, " addresses loaded");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, filename, " not found");
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
int AddressBookFilesystemStorage::Save (const std::map<std::string, i2p::data::IdentHash>& addresses)
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
auto filename = GetPath () / "addresses.csv";
|
|
||||||
std::ofstream f (filename.c_str (), std::ofstream::out); // in text mode
|
|
||||||
if (f.is_open ())
|
|
||||||
{
|
|
||||||
for (auto it: addresses)
|
|
||||||
{
|
|
||||||
f << it.first << "," << it.second.ToBase32 () << std::endl;
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, num, " addresses saved");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Can't open file ", filename);
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
|
||||||
AddressBook::AddressBook (): m_Storage (nullptr), m_IsLoaded (false), m_IsDownloading (false),
|
|
||||||
m_DefaultSubscription (nullptr), m_SubscriptionsUpdateTimer (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressBook::~AddressBook ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::Start ()
|
|
||||||
{
|
|
||||||
StartSubscriptions ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::Stop ()
|
|
||||||
{
|
|
||||||
StopSubscriptions ();
|
|
||||||
if (m_SubscriptionsUpdateTimer)
|
|
||||||
{
|
|
||||||
delete m_SubscriptionsUpdateTimer;
|
|
||||||
m_SubscriptionsUpdateTimer = nullptr;
|
|
||||||
}
|
|
||||||
if (m_IsDownloading)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Subscription is downloading. Waiting for temination...");
|
|
||||||
for (int i = 0; i < 30; i++)
|
|
||||||
{
|
|
||||||
if (!m_IsDownloading)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Subscription download complete");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
std::this_thread::sleep_for (std::chrono::seconds (1)); // wait for 1 seconds
|
|
||||||
}
|
|
||||||
LogPrint (eLogError, "Subscription download hangs");
|
|
||||||
m_IsDownloading = false;
|
|
||||||
}
|
|
||||||
if (m_Storage)
|
|
||||||
{
|
|
||||||
m_Storage->Save (m_Addresses);
|
|
||||||
delete m_Storage;
|
|
||||||
m_Storage = nullptr;
|
|
||||||
}
|
|
||||||
if (m_DefaultSubscription)
|
|
||||||
{
|
|
||||||
delete m_DefaultSubscription;
|
|
||||||
m_DefaultSubscription = nullptr;
|
|
||||||
}
|
|
||||||
for (auto it: m_Subscriptions)
|
|
||||||
delete it;
|
|
||||||
m_Subscriptions.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressBookStorage * AddressBook::CreateStorage ()
|
|
||||||
{
|
|
||||||
return new AddressBookFilesystemStorage ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddressBook::GetIdentHash (const std::string& address, i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
auto pos = address.find(".b32.i2p");
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
Base32ToByteStream (address.c_str(), pos, ident, 32);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pos = address.find (".i2p");
|
|
||||||
if (pos != std::string::npos)
|
|
||||||
{
|
|
||||||
auto identHash = FindAddress (address);
|
|
||||||
if (identHash)
|
|
||||||
{
|
|
||||||
ident = *identHash;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if not .b32 we assume full base64 address
|
|
||||||
i2p::data::IdentityEx dest;
|
|
||||||
if (!dest.FromBase64 (address))
|
|
||||||
return false;
|
|
||||||
ident = dest.GetIdentHash ();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const i2p::data::IdentHash * AddressBook::FindAddress (const std::string& address)
|
|
||||||
{
|
|
||||||
if (!m_IsLoaded)
|
|
||||||
LoadHosts ();
|
|
||||||
if (m_IsLoaded)
|
|
||||||
{
|
|
||||||
auto it = m_Addresses.find (address);
|
|
||||||
if (it != m_Addresses.end ())
|
|
||||||
return &it->second;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::InsertAddress (const std::string& address, const std::string& base64)
|
|
||||||
{
|
|
||||||
i2p::data::IdentityEx ident;
|
|
||||||
ident.FromBase64 (base64);
|
|
||||||
if (!m_Storage)
|
|
||||||
m_Storage = CreateStorage ();
|
|
||||||
m_Storage->AddAddress (ident);
|
|
||||||
m_Addresses[address] = ident.GetIdentHash ();
|
|
||||||
LogPrint (address,"->", ToAddress(ident.GetIdentHash ()), " added");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::InsertAddress (const i2p::data::IdentityEx& address)
|
|
||||||
{
|
|
||||||
if (!m_Storage)
|
|
||||||
m_Storage = CreateStorage ();
|
|
||||||
m_Storage->AddAddress (address);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddressBook::GetAddress (const std::string& address, i2p::data::IdentityEx& identity)
|
|
||||||
{
|
|
||||||
if (!m_Storage)
|
|
||||||
m_Storage = CreateStorage ();
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
if (!GetIdentHash (address, ident)) return false;
|
|
||||||
return m_Storage->GetAddress (ident, identity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::LoadHosts ()
|
|
||||||
{
|
|
||||||
if (!m_Storage)
|
|
||||||
m_Storage = CreateStorage ();
|
|
||||||
if (m_Storage->Load (m_Addresses) > 0)
|
|
||||||
{
|
|
||||||
m_IsLoaded = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try hosts.txt first
|
|
||||||
std::ifstream f (i2p::util::filesystem::GetFullPath ("hosts.txt").c_str (), std::ofstream::in); // in text mode
|
|
||||||
if (f.is_open ())
|
|
||||||
{
|
|
||||||
LoadHostsFromStream (f);
|
|
||||||
m_IsLoaded = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if not found download it from http://i2p-projekt.i2p/hosts.txt
|
|
||||||
LogPrint (eLogInfo, "hosts.txt not found. Try to download it from default subscription...");
|
|
||||||
if (!m_IsDownloading)
|
|
||||||
{
|
|
||||||
m_IsDownloading = true;
|
|
||||||
if (!m_DefaultSubscription)
|
|
||||||
m_DefaultSubscription = new AddressBookSubscription (*this, DEFAULT_SUBSCRIPTION_ADDRESS);
|
|
||||||
m_DefaultSubscription->CheckSubscription ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::LoadHostsFromStream (std::istream& f)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_AddressBookMutex);
|
|
||||||
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;
|
|
||||||
if (ident.FromBase64(addr))
|
|
||||||
{
|
|
||||||
m_Addresses[name] = ident.GetIdentHash ();
|
|
||||||
m_Storage->AddAddress (ident);
|
|
||||||
numAddresses++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Malformed address ", addr, " for ", name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, numAddresses, " addresses processed");
|
|
||||||
if (numAddresses > 0)
|
|
||||||
{
|
|
||||||
m_IsLoaded = true;
|
|
||||||
m_Storage->Save (m_Addresses);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::LoadSubscriptions ()
|
|
||||||
{
|
|
||||||
if (!m_Subscriptions.size ())
|
|
||||||
{
|
|
||||||
std::ifstream f (i2p::util::filesystem::GetFullPath ("subscriptions.txt").c_str (), std::ofstream::in); // in text mode
|
|
||||||
if (f.is_open ())
|
|
||||||
{
|
|
||||||
std::string s;
|
|
||||||
while (!f.eof ())
|
|
||||||
{
|
|
||||||
getline(f, s);
|
|
||||||
if (!s.length()) continue; // skip empty line
|
|
||||||
m_Subscriptions.push_back (new AddressBookSubscription (*this, s));
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, m_Subscriptions.size (), " subscriptions loaded");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "subscriptions.txt not found");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Subscriptions already loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::DownloadComplete (bool success)
|
|
||||||
{
|
|
||||||
m_IsDownloading = false;
|
|
||||||
if (m_SubscriptionsUpdateTimer)
|
|
||||||
{
|
|
||||||
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(
|
|
||||||
success ? CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT : CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT));
|
|
||||||
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::StartSubscriptions ()
|
|
||||||
{
|
|
||||||
LoadSubscriptions ();
|
|
||||||
if (!m_Subscriptions.size ()) return;
|
|
||||||
|
|
||||||
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
|
||||||
if (dest)
|
|
||||||
{
|
|
||||||
m_SubscriptionsUpdateTimer = new boost::asio::deadline_timer (dest->GetService ());
|
|
||||||
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT));
|
|
||||||
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Can't start subscriptions: missing shared local destination");
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::StopSubscriptions ()
|
|
||||||
{
|
|
||||||
if (m_SubscriptionsUpdateTimer)
|
|
||||||
m_SubscriptionsUpdateTimer->cancel ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBook::HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
|
||||||
if (!dest) return;
|
|
||||||
if (m_IsLoaded && !m_IsDownloading && dest->IsReady ())
|
|
||||||
{
|
|
||||||
// pick random subscription
|
|
||||||
CryptoPP::AutoSeededRandomPool rnd;
|
|
||||||
auto ind = rnd.GenerateWord32 (0, m_Subscriptions.size() - 1);
|
|
||||||
m_IsDownloading = true;
|
|
||||||
m_Subscriptions[ind]->CheckSubscription ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!m_IsLoaded)
|
|
||||||
LoadHosts ();
|
|
||||||
// try it again later
|
|
||||||
m_SubscriptionsUpdateTimer->expires_from_now (boost::posix_time::minutes(INITIAL_SUBSCRIPTION_RETRY_TIMEOUT));
|
|
||||||
m_SubscriptionsUpdateTimer->async_wait (std::bind (&AddressBook::HandleSubscriptionsUpdateTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AddressBookSubscription::AddressBookSubscription (AddressBook& book, const std::string& link):
|
|
||||||
m_Book (book), m_Link (link)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBookSubscription::CheckSubscription ()
|
|
||||||
{
|
|
||||||
std::thread load_hosts(&AddressBookSubscription::Request, this);
|
|
||||||
load_hosts.detach(); // TODO: use join
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddressBookSubscription::Request ()
|
|
||||||
{
|
|
||||||
// must be run in separate thread
|
|
||||||
LogPrint (eLogInfo, "Downloading hosts from ", m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
|
|
||||||
bool success = false;
|
|
||||||
i2p::util::http::url u (m_Link);
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
if (m_Book.GetIdentHash (u.host_, ident))
|
|
||||||
{
|
|
||||||
std::condition_variable newDataReceived;
|
|
||||||
std::mutex newDataReceivedMutex;
|
|
||||||
auto leaseSet = i2p::client::context.GetSharedLocalDestination ()->FindLeaseSet (ident);
|
|
||||||
if (!leaseSet)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(newDataReceivedMutex);
|
|
||||||
i2p::client::context.GetSharedLocalDestination ()->RequestDestination (ident,
|
|
||||||
[&newDataReceived, &leaseSet](std::shared_ptr<i2p::data::LeaseSet> ls)
|
|
||||||
{
|
|
||||||
leaseSet = ls;
|
|
||||||
newDataReceived.notify_all ();
|
|
||||||
});
|
|
||||||
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
|
|
||||||
LogPrint (eLogError, "Subscription LeseseSet request timeout expired");
|
|
||||||
}
|
|
||||||
if (leaseSet)
|
|
||||||
{
|
|
||||||
std::stringstream request, response;
|
|
||||||
// standard header
|
|
||||||
request << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
|
|
||||||
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n";
|
|
||||||
if (m_Etag.length () > 0) // etag
|
|
||||||
request << i2p::util::http::IF_NONE_MATCH << ": \"" << m_Etag << "\"\r\n";
|
|
||||||
if (m_LastModified.length () > 0) // if-modfief-since
|
|
||||||
request << i2p::util::http::IF_MODIFIED_SINCE << ": " << m_LastModified << "\r\n";
|
|
||||||
request << "\r\n"; // end of header
|
|
||||||
auto stream = i2p::client::context.GetSharedLocalDestination ()->CreateStream (leaseSet, u.port_);
|
|
||||||
stream->Send ((uint8_t *)request.str ().c_str (), request.str ().length ());
|
|
||||||
|
|
||||||
uint8_t buf[4096];
|
|
||||||
bool end = false;
|
|
||||||
while (!end)
|
|
||||||
{
|
|
||||||
stream->AsyncReceive (boost::asio::buffer (buf, 4096),
|
|
||||||
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (bytes_transferred)
|
|
||||||
response.write ((char *)buf, bytes_transferred);
|
|
||||||
if (ecode == boost::asio::error::timed_out || !stream->IsOpen ())
|
|
||||||
end = true;
|
|
||||||
newDataReceived.notify_all ();
|
|
||||||
},
|
|
||||||
30); // wait for 30 seconds
|
|
||||||
std::unique_lock<std::mutex> l(newDataReceivedMutex);
|
|
||||||
if (newDataReceived.wait_for (l, std::chrono::seconds (SUBSCRIPTION_REQUEST_TIMEOUT)) == std::cv_status::timeout)
|
|
||||||
LogPrint (eLogError, "Subscription timeout expired");
|
|
||||||
}
|
|
||||||
// process remaining buffer
|
|
||||||
while (size_t len = stream->ReadSome (buf, 4096))
|
|
||||||
response.write ((char *)buf, len);
|
|
||||||
|
|
||||||
// parse response
|
|
||||||
std::string version;
|
|
||||||
response >> version; // HTTP version
|
|
||||||
int status = 0;
|
|
||||||
response >> status; // status
|
|
||||||
if (status == 200) // OK
|
|
||||||
{
|
|
||||||
bool isChunked = false;
|
|
||||||
std::string header, statusMessage;
|
|
||||||
std::getline (response, statusMessage);
|
|
||||||
// read until new line meaning end of header
|
|
||||||
while (!response.eof () && header != "\r")
|
|
||||||
{
|
|
||||||
std::getline (response, header);
|
|
||||||
auto colon = header.find (':');
|
|
||||||
if (colon != std::string::npos)
|
|
||||||
{
|
|
||||||
std::string field = header.substr (0, colon);
|
|
||||||
header.resize (header.length () - 1); // delete \r
|
|
||||||
if (field == i2p::util::http::ETAG)
|
|
||||||
m_Etag = header.substr (colon + 1);
|
|
||||||
else if (field == i2p::util::http::LAST_MODIFIED)
|
|
||||||
m_LastModified = header.substr (colon + 1);
|
|
||||||
else if (field == i2p::util::http::TRANSFER_ENCODING)
|
|
||||||
isChunked = !header.compare (colon + 1, std::string::npos, "chunked");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, m_Link, " ETag: ", m_Etag, " Last-Modified: ", m_LastModified);
|
|
||||||
if (!response.eof ())
|
|
||||||
{
|
|
||||||
success = true;
|
|
||||||
if (!isChunked)
|
|
||||||
m_Book.LoadHostsFromStream (response);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// merge chunks
|
|
||||||
std::stringstream merged;
|
|
||||||
i2p::util::http::MergeChunkedResponse (response, merged);
|
|
||||||
m_Book.LoadHostsFromStream (merged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (status == 304)
|
|
||||||
{
|
|
||||||
success = true;
|
|
||||||
LogPrint (eLogInfo, "No updates from ", m_Link);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Adressbook HTTP response ", status);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Address ", u.host_, " not found");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Can't resolve ", u.host_);
|
|
||||||
LogPrint (eLogInfo, "Download complete ", success ? "Success" : "Failed");
|
|
||||||
m_Book.DownloadComplete (success);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
105
AddressBook.h
105
AddressBook.h
@@ -1,105 +0,0 @@
|
|||||||
#ifndef ADDRESS_BOOK_H__
|
|
||||||
#define ADDRESS_BOOK_H__
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
|
||||||
#include <mutex>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "base64.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://udhdrtrcetjm5sxzskjyr5ztpeszydbh4dpl3pl4utgqqw2v4jna.b32.i2p/hosts.txt";
|
|
||||||
const int INITIAL_SUBSCRIPTION_UPDATE_TIMEOUT = 3; // in minutes
|
|
||||||
const int INITIAL_SUBSCRIPTION_RETRY_TIMEOUT = 1; // in minutes
|
|
||||||
const int CONTINIOUS_SUBSCRIPTION_UPDATE_TIMEOUT = 720; // in minutes (12 hours)
|
|
||||||
const int CONTINIOUS_SUBSCRIPTION_RETRY_TIMEOUT = 5; // in minutes
|
|
||||||
const int SUBSCRIPTION_REQUEST_TIMEOUT = 60; //in second
|
|
||||||
|
|
||||||
inline std::string GetB32Address(const i2p::data::IdentHash& ident) { return ident.ToBase32().append(".b32.i2p"); }
|
|
||||||
|
|
||||||
class AddressBookStorage // interface for storage
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~AddressBookStorage () {};
|
|
||||||
virtual bool GetAddress (const i2p::data::IdentHash& ident, i2p::data::IdentityEx& address) const = 0;
|
|
||||||
virtual void AddAddress (const i2p::data::IdentityEx& address) = 0;
|
|
||||||
virtual void RemoveAddress (const i2p::data::IdentHash& ident) = 0;
|
|
||||||
|
|
||||||
virtual int Load (std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
|
|
||||||
virtual int Save (const std::map<std::string, i2p::data::IdentHash>& addresses) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AddressBookSubscription;
|
|
||||||
class AddressBook
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
AddressBook ();
|
|
||||||
~AddressBook ();
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
bool GetIdentHash (const std::string& address, i2p::data::IdentHash& ident);
|
|
||||||
bool GetAddress (const std::string& address, i2p::data::IdentityEx& identity);
|
|
||||||
const i2p::data::IdentHash * FindAddress (const std::string& address);
|
|
||||||
void InsertAddress (const std::string& address, const std::string& base64); // for jump service
|
|
||||||
void InsertAddress (const i2p::data::IdentityEx& address);
|
|
||||||
|
|
||||||
void LoadHostsFromStream (std::istream& f);
|
|
||||||
void DownloadComplete (bool success);
|
|
||||||
//This method returns the ".b32.i2p" address
|
|
||||||
std::string ToAddress(const i2p::data::IdentHash& ident) { return GetB32Address(ident); }
|
|
||||||
std::string ToAddress(const i2p::data::IdentityEx& ident) { return ToAddress(ident.GetIdentHash ()); }
|
|
||||||
private:
|
|
||||||
|
|
||||||
void StartSubscriptions ();
|
|
||||||
void StopSubscriptions ();
|
|
||||||
|
|
||||||
AddressBookStorage * CreateStorage ();
|
|
||||||
void LoadHosts ();
|
|
||||||
void LoadSubscriptions ();
|
|
||||||
|
|
||||||
void HandleSubscriptionsUpdateTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::mutex m_AddressBookMutex;
|
|
||||||
std::map<std::string, i2p::data::IdentHash> m_Addresses;
|
|
||||||
AddressBookStorage * m_Storage;
|
|
||||||
volatile bool m_IsLoaded, m_IsDownloading;
|
|
||||||
std::vector<AddressBookSubscription *> m_Subscriptions;
|
|
||||||
AddressBookSubscription * m_DefaultSubscription; // in case if we don't know any addresses yet
|
|
||||||
boost::asio::deadline_timer * m_SubscriptionsUpdateTimer;
|
|
||||||
};
|
|
||||||
|
|
||||||
class AddressBookSubscription
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
AddressBookSubscription (AddressBook& book, const std::string& link);
|
|
||||||
void CheckSubscription ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Request ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
AddressBook& m_Book;
|
|
||||||
std::string m_Link, m_Etag, m_LastModified;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
643
BOB.cpp
643
BOB.cpp
@@ -1,643 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "BOB.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
BOBI2PInboundTunnel::BOBI2PInboundTunnel (int port, std::shared_ptr<ClientDestination> localDestination):
|
|
||||||
BOBI2PTunnel (localDestination),
|
|
||||||
m_Acceptor (localDestination->GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBI2PInboundTunnel::~BOBI2PInboundTunnel ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::Start ()
|
|
||||||
{
|
|
||||||
m_Acceptor.listen ();
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::Stop ()
|
|
||||||
{
|
|
||||||
m_Acceptor.close();
|
|
||||||
ClearHandlers ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::Accept ()
|
|
||||||
{
|
|
||||||
auto receiver = std::make_shared<AddressReceiver> ();
|
|
||||||
receiver->socket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
|
|
||||||
m_Acceptor.async_accept (*receiver->socket, std::bind (&BOBI2PInboundTunnel::HandleAccept, this,
|
|
||||||
std::placeholders::_1, receiver));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
ReceiveAddress (receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::ReceiveAddress (std::shared_ptr<AddressReceiver> receiver)
|
|
||||||
{
|
|
||||||
receiver->socket->async_read_some (boost::asio::buffer(
|
|
||||||
receiver->buffer + receiver->bufferOffset,
|
|
||||||
BOB_COMMAND_BUFFER_SIZE - receiver->bufferOffset),
|
|
||||||
std::bind(&BOBI2PInboundTunnel::HandleReceivedAddress, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, receiver));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
|
||||||
std::shared_ptr<AddressReceiver> receiver)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
LogPrint ("BOB inbound tunnel read error: ", ecode.message ());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
receiver->bufferOffset += bytes_transferred;
|
|
||||||
receiver->buffer[receiver->bufferOffset] = 0;
|
|
||||||
char * eol = strchr (receiver->buffer, '\n');
|
|
||||||
if (eol)
|
|
||||||
{
|
|
||||||
*eol = 0;
|
|
||||||
|
|
||||||
receiver->data = (uint8_t *)eol + 1;
|
|
||||||
receiver->dataLen = receiver->bufferOffset - (eol - receiver->buffer + 1);
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
if (!context.GetAddressBook ().GetIdentHash (receiver->buffer, ident))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "BOB address ", receiver->buffer, " not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto leaseSet = GetLocalDestination ()->FindLeaseSet (ident);
|
|
||||||
if (leaseSet)
|
|
||||||
CreateConnection (receiver, leaseSet);
|
|
||||||
else
|
|
||||||
GetLocalDestination ()->RequestDestination (ident,
|
|
||||||
std::bind (&BOBI2PInboundTunnel::HandleDestinationRequestComplete,
|
|
||||||
this, std::placeholders::_1, receiver));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (receiver->bufferOffset < BOB_COMMAND_BUFFER_SIZE)
|
|
||||||
ReceiveAddress (receiver);
|
|
||||||
else
|
|
||||||
LogPrint ("BOB missing inbound address ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver)
|
|
||||||
{
|
|
||||||
if (leaseSet)
|
|
||||||
CreateConnection (receiver, leaseSet);
|
|
||||||
else
|
|
||||||
LogPrint ("LeaseSet for BOB inbound destination not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2PInboundTunnel::CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet)
|
|
||||||
{
|
|
||||||
LogPrint ("New BOB inbound connection");
|
|
||||||
auto connection = std::make_shared<I2PTunnelConnection>(this, receiver->socket, leaseSet);
|
|
||||||
AddHandler (connection);
|
|
||||||
connection->I2PConnect (receiver->data, receiver->dataLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBI2POutboundTunnel::BOBI2POutboundTunnel (const std::string& address, int port,
|
|
||||||
std::shared_ptr<ClientDestination> localDestination, bool quiet): BOBI2PTunnel (localDestination),
|
|
||||||
m_Endpoint (boost::asio::ip::address::from_string (address), port), m_IsQuiet (quiet)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2POutboundTunnel::Start ()
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2POutboundTunnel::Stop ()
|
|
||||||
{
|
|
||||||
ClearHandlers ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2POutboundTunnel::Accept ()
|
|
||||||
{
|
|
||||||
auto localDestination = GetLocalDestination ();
|
|
||||||
if (localDestination)
|
|
||||||
localDestination->AcceptStreams (std::bind (&BOBI2POutboundTunnel::HandleAccept, this, std::placeholders::_1));
|
|
||||||
else
|
|
||||||
LogPrint ("Local destination not set for server tunnel");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBI2POutboundTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), m_Endpoint, m_IsQuiet);
|
|
||||||
AddHandler (conn);
|
|
||||||
conn->Connect ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBDestination::BOBDestination (std::shared_ptr<ClientDestination> localDestination):
|
|
||||||
m_LocalDestination (localDestination),
|
|
||||||
m_OutboundTunnel (nullptr), m_InboundTunnel (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBDestination::~BOBDestination ()
|
|
||||||
{
|
|
||||||
delete m_OutboundTunnel;
|
|
||||||
delete m_InboundTunnel;
|
|
||||||
i2p::client::context.DeleteLocalDestination (m_LocalDestination);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::Start ()
|
|
||||||
{
|
|
||||||
if (m_OutboundTunnel) m_OutboundTunnel->Start ();
|
|
||||||
if (m_InboundTunnel) m_InboundTunnel->Start ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::Stop ()
|
|
||||||
{
|
|
||||||
StopTunnels ();
|
|
||||||
m_LocalDestination->Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::StopTunnels ()
|
|
||||||
{
|
|
||||||
if (m_OutboundTunnel)
|
|
||||||
{
|
|
||||||
m_OutboundTunnel->Stop ();
|
|
||||||
delete m_OutboundTunnel;
|
|
||||||
m_OutboundTunnel = nullptr;
|
|
||||||
}
|
|
||||||
if (m_InboundTunnel)
|
|
||||||
{
|
|
||||||
m_InboundTunnel->Stop ();
|
|
||||||
delete m_InboundTunnel;
|
|
||||||
m_InboundTunnel = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::CreateInboundTunnel (int port)
|
|
||||||
{
|
|
||||||
if (!m_InboundTunnel)
|
|
||||||
m_InboundTunnel = new BOBI2PInboundTunnel (port, m_LocalDestination);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBDestination::CreateOutboundTunnel (const std::string& address, int port, bool quiet)
|
|
||||||
{
|
|
||||||
if (!m_OutboundTunnel)
|
|
||||||
m_OutboundTunnel = new BOBI2POutboundTunnel (address, port, m_LocalDestination, quiet);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBCommandSession::BOBCommandSession (BOBCommandChannel& owner):
|
|
||||||
m_Owner (owner), m_Socket (m_Owner.GetService ()), m_ReceiveBufferOffset (0),
|
|
||||||
m_IsOpen (true), m_IsQuiet (false), m_InPort (0), m_OutPort (0),
|
|
||||||
m_CurrentDestination (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBCommandSession::~BOBCommandSession ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::Terminate ()
|
|
||||||
{
|
|
||||||
m_Socket.close ();
|
|
||||||
m_IsOpen = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::Receive ()
|
|
||||||
{
|
|
||||||
m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, BOB_COMMAND_BUFFER_SIZE - m_ReceiveBufferOffset),
|
|
||||||
std::bind(&BOBCommandSession::HandleReceived, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("BOB command channel read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
size_t size = m_ReceiveBufferOffset + bytes_transferred;
|
|
||||||
m_ReceiveBuffer[size] = 0;
|
|
||||||
char * eol = strchr (m_ReceiveBuffer, '\n');
|
|
||||||
if (eol)
|
|
||||||
{
|
|
||||||
*eol = 0;
|
|
||||||
char * operand = strchr (m_ReceiveBuffer, ' ');
|
|
||||||
if (operand)
|
|
||||||
{
|
|
||||||
*operand = 0;
|
|
||||||
operand++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
operand = eol;
|
|
||||||
// process command
|
|
||||||
auto& handlers = m_Owner.GetCommandHandlers ();
|
|
||||||
auto it = handlers.find (m_ReceiveBuffer);
|
|
||||||
if (it != handlers.end ())
|
|
||||||
(this->*(it->second))(operand, eol - operand);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "BOB unknown command ", m_ReceiveBuffer);
|
|
||||||
SendReplyError ("unknown command");
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ReceiveBufferOffset = size - (eol - m_ReceiveBuffer) - 1;
|
|
||||||
memmove (m_ReceiveBuffer, eol + 1, m_ReceiveBufferOffset);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (size < BOB_COMMAND_BUFFER_SIZE)
|
|
||||||
m_ReceiveBufferOffset = size;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Malformed input of the BOB command channel");
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::Send (size_t len)
|
|
||||||
{
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_SendBuffer, len),
|
|
||||||
boost::asio::transfer_all (),
|
|
||||||
std::bind(&BOBCommandSession::HandleSent, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("BOB command channel send error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_IsOpen)
|
|
||||||
Receive ();
|
|
||||||
else
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SendReplyOK (const char * msg)
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg);
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_OK, msg);
|
|
||||||
#endif
|
|
||||||
Send (len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SendReplyError (const char * msg)
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg);
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_REPLY_ERROR, msg);
|
|
||||||
#endif
|
|
||||||
Send (len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SendVersion ()
|
|
||||||
{
|
|
||||||
size_t len = strlen (BOB_VERSION);
|
|
||||||
memcpy (m_SendBuffer, BOB_VERSION, len);
|
|
||||||
Send (len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SendData (const char * nickname)
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname);
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_SendBuffer, BOB_COMMAND_BUFFER_SIZE, BOB_DATA, nickname);
|
|
||||||
#endif
|
|
||||||
Send (len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::ZapCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: zap");
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::QuitCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: quit");
|
|
||||||
m_IsOpen = false;
|
|
||||||
SendReplyOK ("Bye!");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::StartCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: start ", m_Nickname);
|
|
||||||
if (!m_CurrentDestination)
|
|
||||||
{
|
|
||||||
m_CurrentDestination = new BOBDestination (i2p::client::context.CreateNewLocalDestination (m_Keys, true, &m_Options));
|
|
||||||
m_Owner.AddDestination (m_Nickname, m_CurrentDestination);
|
|
||||||
}
|
|
||||||
if (m_InPort)
|
|
||||||
m_CurrentDestination->CreateInboundTunnel (m_InPort);
|
|
||||||
if (m_OutPort && !m_Address.empty ())
|
|
||||||
m_CurrentDestination->CreateOutboundTunnel (m_Address, m_OutPort, m_IsQuiet);
|
|
||||||
m_CurrentDestination->Start ();
|
|
||||||
SendReplyOK ("tunnel starting");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::StopCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
auto dest = m_Owner.FindDestination (m_Nickname);
|
|
||||||
if (dest)
|
|
||||||
{
|
|
||||||
dest->StopTunnels ();
|
|
||||||
SendReplyOK ("tunnel stopping");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendReplyError ("tunnel not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SetNickCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: setnick ", operand);
|
|
||||||
m_Nickname = operand;
|
|
||||||
std::string msg ("Nickname set to ");
|
|
||||||
msg += operand;
|
|
||||||
SendReplyOK (msg.c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::GetNickCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: getnick ", operand);
|
|
||||||
m_CurrentDestination = m_Owner.FindDestination (operand);
|
|
||||||
if (m_CurrentDestination)
|
|
||||||
{
|
|
||||||
m_Keys = m_CurrentDestination->GetKeys ();
|
|
||||||
m_Nickname = operand;
|
|
||||||
std::string msg ("Nickname set to ");
|
|
||||||
msg += operand;
|
|
||||||
SendReplyOK (msg.c_str ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendReplyError ("tunnel not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::NewkeysCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: newkeys");
|
|
||||||
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys ();
|
|
||||||
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::SetkeysCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: setkeys ", operand);
|
|
||||||
m_Keys.FromBase64 (operand);
|
|
||||||
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: getkeys");
|
|
||||||
SendReplyOK (m_Keys.ToBase64 ().c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: getdest");
|
|
||||||
SendReplyOK (m_Keys.GetPublic ().ToBase64 ().c_str ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::OuthostCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: outhost ", operand);
|
|
||||||
m_Address = operand;
|
|
||||||
SendReplyOK ("outhost set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: outport ", operand);
|
|
||||||
m_OutPort = boost::lexical_cast<int>(operand);
|
|
||||||
SendReplyOK ("outbound port set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::InhostCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: inhost ", operand);
|
|
||||||
m_Address = operand;
|
|
||||||
SendReplyOK ("inhost set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::InportCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: inport ", operand);
|
|
||||||
m_InPort = boost::lexical_cast<int>(operand);
|
|
||||||
SendReplyOK ("inbound port set");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::QuietCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: quiet");
|
|
||||||
m_IsQuiet = true;
|
|
||||||
SendReplyOK ("quiet");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::LookupCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: lookup ", operand);
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
if (!context.GetAddressBook ().GetIdentHash (operand, ident) || !m_CurrentDestination)
|
|
||||||
{
|
|
||||||
SendReplyError ("Address Not found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto localDestination = m_CurrentDestination->GetLocalDestination ();
|
|
||||||
auto leaseSet = localDestination->FindLeaseSet (ident);
|
|
||||||
if (leaseSet)
|
|
||||||
SendReplyOK (leaseSet->GetIdentity ().ToBase64 ().c_str ());
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto s = shared_from_this ();
|
|
||||||
localDestination->RequestDestination (ident,
|
|
||||||
[s](std::shared_ptr<i2p::data::LeaseSet> ls)
|
|
||||||
{
|
|
||||||
if (ls)
|
|
||||||
s->SendReplyOK (ls->GetIdentity ().ToBase64 ().c_str ());
|
|
||||||
else
|
|
||||||
s->SendReplyError ("LeaseSet Not found");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::ClearCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: clear");
|
|
||||||
m_Owner.DeleteDestination (m_Nickname);
|
|
||||||
SendReplyOK ("cleared");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::ListCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: list");
|
|
||||||
auto& destinations = m_Owner.GetDestinations ();
|
|
||||||
for (auto it: destinations)
|
|
||||||
SendData (it.first.c_str ());
|
|
||||||
SendReplyOK ("Listing done");
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandSession::OptionCommandHandler (const char * operand, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "BOB: option ", operand);
|
|
||||||
const char * value = strchr (operand, '=');
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
*(const_cast<char *>(value)) = 0;
|
|
||||||
m_Options[operand] = value + 1;
|
|
||||||
*(const_cast<char *>(value)) = '=';
|
|
||||||
SendReplyOK ("option");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendReplyError ("malformed");
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBCommandChannel::BOBCommandChannel (int port):
|
|
||||||
m_IsRunning (false), m_Thread (nullptr),
|
|
||||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
|
|
||||||
{
|
|
||||||
// command -> handler
|
|
||||||
m_CommandHandlers[BOB_COMMAND_ZAP] = &BOBCommandSession::ZapCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_QUIT] = &BOBCommandSession::QuitCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_START] = &BOBCommandSession::StartCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_STOP] = &BOBCommandSession::StopCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_SETNICK] = &BOBCommandSession::SetNickCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_GETNICK] = &BOBCommandSession::GetNickCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_NEWKEYS] = &BOBCommandSession::NewkeysCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_GETKEYS] = &BOBCommandSession::GetkeysCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_SETKEYS] = &BOBCommandSession::SetkeysCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_GETDEST] = &BOBCommandSession::GetdestCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_OUTHOST] = &BOBCommandSession::OuthostCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_OUTPORT] = &BOBCommandSession::OutportCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_INHOST] = &BOBCommandSession::InhostCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_INPORT] = &BOBCommandSession::InportCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_QUIET] = &BOBCommandSession::QuietCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_LOOKUP] = &BOBCommandSession::LookupCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_CLEAR] = &BOBCommandSession::ClearCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_LIST] = &BOBCommandSession::ListCommandHandler;
|
|
||||||
m_CommandHandlers[BOB_COMMAND_OPTION] = &BOBCommandSession::OptionCommandHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBCommandChannel::~BOBCommandChannel ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
for (auto it: m_Destinations)
|
|
||||||
delete it.second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::Start ()
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Thread = new std::thread (std::bind (&BOBCommandChannel::Run, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::Stop ()
|
|
||||||
{
|
|
||||||
m_IsRunning = false;
|
|
||||||
for (auto it: m_Destinations)
|
|
||||||
it.second->Stop ();
|
|
||||||
m_Acceptor.cancel ();
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "BOB: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::AddDestination (const std::string& name, BOBDestination * dest)
|
|
||||||
{
|
|
||||||
m_Destinations[name] = dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::DeleteDestination (const std::string& name)
|
|
||||||
{
|
|
||||||
auto it = m_Destinations.find (name);
|
|
||||||
if (it != m_Destinations.end ())
|
|
||||||
{
|
|
||||||
it->second->Stop ();
|
|
||||||
delete it->second;
|
|
||||||
m_Destinations.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOBDestination * BOBCommandChannel::FindDestination (const std::string& name)
|
|
||||||
{
|
|
||||||
auto it = m_Destinations.find (name);
|
|
||||||
if (it != m_Destinations.end ())
|
|
||||||
return it->second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::Accept ()
|
|
||||||
{
|
|
||||||
auto newSession = std::make_shared<BOBCommandSession> (*this);
|
|
||||||
m_Acceptor.async_accept (newSession->GetSocket (), std::bind (&BOBCommandChannel::HandleAccept, this,
|
|
||||||
std::placeholders::_1, newSession));
|
|
||||||
}
|
|
||||||
|
|
||||||
void BOBCommandChannel::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<BOBCommandSession> session)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Accept ();
|
|
||||||
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "New BOB command connection from ", session->GetSocket ().remote_endpoint ());
|
|
||||||
session->SendVersion ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "BOB accept error: ", ecode.message ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
237
BOB.h
237
BOB.h
@@ -1,237 +0,0 @@
|
|||||||
#ifndef BOB_H__
|
|
||||||
#define BOB_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "I2PTunnel.h"
|
|
||||||
#include "I2PService.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
const size_t BOB_COMMAND_BUFFER_SIZE = 1024;
|
|
||||||
const char BOB_COMMAND_ZAP[] = "zap";
|
|
||||||
const char BOB_COMMAND_QUIT[] = "quit";
|
|
||||||
const char BOB_COMMAND_START[] = "start";
|
|
||||||
const char BOB_COMMAND_STOP[] = "stop";
|
|
||||||
const char BOB_COMMAND_SETNICK[] = "setnick";
|
|
||||||
const char BOB_COMMAND_GETNICK[] = "getnick";
|
|
||||||
const char BOB_COMMAND_NEWKEYS[] = "newkeys";
|
|
||||||
const char BOB_COMMAND_GETKEYS[] = "getkeys";
|
|
||||||
const char BOB_COMMAND_SETKEYS[] = "setkeys";
|
|
||||||
const char BOB_COMMAND_GETDEST[] = "getdest";
|
|
||||||
const char BOB_COMMAND_OUTHOST[] = "outhost";
|
|
||||||
const char BOB_COMMAND_OUTPORT[] = "outport";
|
|
||||||
const char BOB_COMMAND_INHOST[] = "inhost";
|
|
||||||
const char BOB_COMMAND_INPORT[] = "inport";
|
|
||||||
const char BOB_COMMAND_QUIET[] = "quiet";
|
|
||||||
const char BOB_COMMAND_LOOKUP[] = "lookup";
|
|
||||||
const char BOB_COMMAND_CLEAR[] = "clear";
|
|
||||||
const char BOB_COMMAND_LIST[] = "list";
|
|
||||||
const char BOB_COMMAND_OPTION[] = "option";
|
|
||||||
|
|
||||||
const char BOB_VERSION[] = "BOB 00.00.10\nOK\n";
|
|
||||||
const char BOB_REPLY_OK[] = "OK %s\n";
|
|
||||||
const char BOB_REPLY_ERROR[] = "ERROR %s\n";
|
|
||||||
const char BOB_DATA[] = "NICKNAME %s\n";
|
|
||||||
|
|
||||||
class BOBI2PTunnel: public I2PService
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
BOBI2PTunnel (std::shared_ptr<ClientDestination> localDestination):
|
|
||||||
I2PService (localDestination) {};
|
|
||||||
|
|
||||||
virtual void Start () {};
|
|
||||||
virtual void Stop () {};
|
|
||||||
};
|
|
||||||
|
|
||||||
class BOBI2PInboundTunnel: public BOBI2PTunnel
|
|
||||||
{
|
|
||||||
struct AddressReceiver
|
|
||||||
{
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket;
|
|
||||||
char buffer[BOB_COMMAND_BUFFER_SIZE + 1]; // for destination base64 address
|
|
||||||
uint8_t * data; // pointer to buffer
|
|
||||||
size_t dataLen, bufferOffset;
|
|
||||||
|
|
||||||
AddressReceiver (): data (nullptr), dataLen (0), bufferOffset (0) {};
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
BOBI2PInboundTunnel (int port, std::shared_ptr<ClientDestination> localDestination);
|
|
||||||
~BOBI2PInboundTunnel ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Accept ();
|
|
||||||
void HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<AddressReceiver> receiver);
|
|
||||||
|
|
||||||
void ReceiveAddress (std::shared_ptr<AddressReceiver> receiver);
|
|
||||||
void HandleReceivedAddress (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
|
||||||
std::shared_ptr<AddressReceiver> receiver);
|
|
||||||
|
|
||||||
void HandleDestinationRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<AddressReceiver> receiver);
|
|
||||||
|
|
||||||
void CreateConnection (std::shared_ptr<AddressReceiver> receiver, std::shared_ptr<const i2p::data::LeaseSet> leaseSet);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BOBI2POutboundTunnel: public BOBI2PTunnel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
BOBI2POutboundTunnel (const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, bool quiet);
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
void SetQuiet () { m_IsQuiet = true; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Accept ();
|
|
||||||
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
boost::asio::ip::tcp::endpoint m_Endpoint;
|
|
||||||
bool m_IsQuiet;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class BOBDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
BOBDestination (std::shared_ptr<ClientDestination> localDestination);
|
|
||||||
~BOBDestination ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
void StopTunnels ();
|
|
||||||
void CreateInboundTunnel (int port);
|
|
||||||
void CreateOutboundTunnel (const std::string& address, int port, bool quiet);
|
|
||||||
const i2p::data::PrivateKeys& GetKeys () const { return m_LocalDestination->GetPrivateKeys (); };
|
|
||||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDestination; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> m_LocalDestination;
|
|
||||||
BOBI2POutboundTunnel * m_OutboundTunnel;
|
|
||||||
BOBI2PInboundTunnel * m_InboundTunnel;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BOBCommandChannel;
|
|
||||||
class BOBCommandSession: public std::enable_shared_from_this<BOBCommandSession>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
BOBCommandSession (BOBCommandChannel& owner);
|
|
||||||
~BOBCommandSession ();
|
|
||||||
void Terminate ();
|
|
||||||
|
|
||||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
|
||||||
void SendVersion ();
|
|
||||||
|
|
||||||
// command handlers
|
|
||||||
void ZapCommandHandler (const char * operand, size_t len);
|
|
||||||
void QuitCommandHandler (const char * operand, size_t len);
|
|
||||||
void StartCommandHandler (const char * operand, size_t len);
|
|
||||||
void StopCommandHandler (const char * operand, size_t len);
|
|
||||||
void SetNickCommandHandler (const char * operand, size_t len);
|
|
||||||
void GetNickCommandHandler (const char * operand, size_t len);
|
|
||||||
void NewkeysCommandHandler (const char * operand, size_t len);
|
|
||||||
void SetkeysCommandHandler (const char * operand, size_t len);
|
|
||||||
void GetkeysCommandHandler (const char * operand, size_t len);
|
|
||||||
void GetdestCommandHandler (const char * operand, size_t len);
|
|
||||||
void OuthostCommandHandler (const char * operand, size_t len);
|
|
||||||
void OutportCommandHandler (const char * operand, size_t len);
|
|
||||||
void InhostCommandHandler (const char * operand, size_t len);
|
|
||||||
void InportCommandHandler (const char * operand, size_t len);
|
|
||||||
void QuietCommandHandler (const char * operand, size_t len);
|
|
||||||
void LookupCommandHandler (const char * operand, size_t len);
|
|
||||||
void ClearCommandHandler (const char * operand, size_t len);
|
|
||||||
void ListCommandHandler (const char * operand, size_t len);
|
|
||||||
void OptionCommandHandler (const char * operand, size_t len);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Receive ();
|
|
||||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
|
|
||||||
void Send (size_t len);
|
|
||||||
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void SendReplyOK (const char * msg);
|
|
||||||
void SendReplyError (const char * msg);
|
|
||||||
void SendData (const char * nickname);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
BOBCommandChannel& m_Owner;
|
|
||||||
boost::asio::ip::tcp::socket m_Socket;
|
|
||||||
char m_ReceiveBuffer[BOB_COMMAND_BUFFER_SIZE + 1], m_SendBuffer[BOB_COMMAND_BUFFER_SIZE + 1];
|
|
||||||
size_t m_ReceiveBufferOffset;
|
|
||||||
bool m_IsOpen, m_IsQuiet;
|
|
||||||
std::string m_Nickname, m_Address;
|
|
||||||
int m_InPort, m_OutPort;
|
|
||||||
i2p::data::PrivateKeys m_Keys;
|
|
||||||
std::map<std::string, std::string> m_Options;
|
|
||||||
BOBDestination * m_CurrentDestination;
|
|
||||||
};
|
|
||||||
typedef void (BOBCommandSession::*BOBCommandHandler)(const char * operand, size_t len);
|
|
||||||
|
|
||||||
class BOBCommandChannel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
BOBCommandChannel (int port);
|
|
||||||
~BOBCommandChannel ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
boost::asio::io_service& GetService () { return m_Service; };
|
|
||||||
void AddDestination (const std::string& name, BOBDestination * dest);
|
|
||||||
void DeleteDestination (const std::string& name);
|
|
||||||
BOBDestination * FindDestination (const std::string& name);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void Accept ();
|
|
||||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<BOBCommandSession> session);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
boost::asio::io_service m_Service;
|
|
||||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
|
||||||
std::map<std::string, BOBDestination *> m_Destinations;
|
|
||||||
std::map<std::string, BOBCommandHandler> m_CommandHandlers;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
const decltype(m_CommandHandlers)& GetCommandHandlers () const { return m_CommandHandlers; };
|
|
||||||
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,344 +0,0 @@
|
|||||||
#include <fstream>
|
|
||||||
#include <iostream>
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
|
||||||
#include "util.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
ClientContext context;
|
|
||||||
|
|
||||||
ClientContext::ClientContext (): m_SharedLocalDestination (nullptr),
|
|
||||||
m_HttpProxy (nullptr), m_SocksProxy (nullptr), m_SamBridge (nullptr),
|
|
||||||
m_BOBCommandChannel (nullptr), m_I2PControlService (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientContext::~ClientContext ()
|
|
||||||
{
|
|
||||||
delete m_HttpProxy;
|
|
||||||
delete m_SocksProxy;
|
|
||||||
delete m_SamBridge;
|
|
||||||
delete m_BOBCommandChannel;
|
|
||||||
delete m_I2PControlService;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::Start ()
|
|
||||||
{
|
|
||||||
if (!m_SharedLocalDestination)
|
|
||||||
{
|
|
||||||
m_SharedLocalDestination = CreateNewLocalDestination (); // non-public, DSA
|
|
||||||
m_Destinations[m_SharedLocalDestination->GetIdentity ().GetIdentHash ()] = m_SharedLocalDestination;
|
|
||||||
m_SharedLocalDestination->Start ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> localDestination;
|
|
||||||
// proxies
|
|
||||||
std::string proxyKeys = i2p::util::config::GetArg("-proxykeys", "");
|
|
||||||
if (proxyKeys.length () > 0)
|
|
||||||
localDestination = LoadLocalDestination (proxyKeys, false);
|
|
||||||
m_HttpProxy = new i2p::proxy::HTTPProxy(i2p::util::config::GetArg("-httpproxyport", 4446), localDestination);
|
|
||||||
m_HttpProxy->Start();
|
|
||||||
LogPrint("HTTP Proxy started");
|
|
||||||
m_SocksProxy = new i2p::proxy::SOCKSProxy(i2p::util::config::GetArg("-socksproxyport", 4447), localDestination);
|
|
||||||
m_SocksProxy->Start();
|
|
||||||
LogPrint("SOCKS Proxy Started");
|
|
||||||
|
|
||||||
// I2P tunnels
|
|
||||||
std::string ircDestination = i2p::util::config::GetArg("-ircdest", "");
|
|
||||||
if (ircDestination.length () > 0) // ircdest is presented
|
|
||||||
{
|
|
||||||
localDestination = nullptr;
|
|
||||||
std::string ircKeys = i2p::util::config::GetArg("-irckeys", "");
|
|
||||||
if (ircKeys.length () > 0)
|
|
||||||
localDestination = LoadLocalDestination (ircKeys, false);
|
|
||||||
auto ircPort = i2p::util::config::GetArg("-ircport", 6668);
|
|
||||||
auto ircTunnel = new I2PClientTunnel (ircDestination, ircPort, localDestination);
|
|
||||||
ircTunnel->Start ();
|
|
||||||
m_ClientTunnels.insert (std::make_pair(ircPort, std::unique_ptr<I2PClientTunnel>(ircTunnel)));
|
|
||||||
LogPrint("IRC tunnel started");
|
|
||||||
}
|
|
||||||
std::string eepKeys = i2p::util::config::GetArg("-eepkeys", "");
|
|
||||||
if (eepKeys.length () > 0) // eepkeys file is presented
|
|
||||||
{
|
|
||||||
localDestination = LoadLocalDestination (eepKeys, true);
|
|
||||||
auto serverTunnel = new I2PServerTunnel (i2p::util::config::GetArg("-eephost", "127.0.0.1"),
|
|
||||||
i2p::util::config::GetArg("-eepport", 80), localDestination);
|
|
||||||
serverTunnel->Start ();
|
|
||||||
m_ServerTunnels.insert (std::make_pair(localDestination->GetIdentHash (), std::unique_ptr<I2PServerTunnel>(serverTunnel)));
|
|
||||||
LogPrint("Server tunnel started");
|
|
||||||
}
|
|
||||||
ReadTunnels ();
|
|
||||||
|
|
||||||
// SAM
|
|
||||||
int samPort = i2p::util::config::GetArg("-samport", 0);
|
|
||||||
if (samPort)
|
|
||||||
{
|
|
||||||
m_SamBridge = new SAMBridge (samPort);
|
|
||||||
m_SamBridge->Start ();
|
|
||||||
LogPrint("SAM bridge started");
|
|
||||||
}
|
|
||||||
|
|
||||||
// BOB
|
|
||||||
int bobPort = i2p::util::config::GetArg("-bobport", 0);
|
|
||||||
if (bobPort)
|
|
||||||
{
|
|
||||||
m_BOBCommandChannel = new BOBCommandChannel (bobPort);
|
|
||||||
m_BOBCommandChannel->Start ();
|
|
||||||
LogPrint("BOB command channel started");
|
|
||||||
}
|
|
||||||
|
|
||||||
// I2P Control
|
|
||||||
int i2pcontrolPort = i2p::util::config::GetArg("-i2pcontrolport", 0);
|
|
||||||
if (i2pcontrolPort)
|
|
||||||
{
|
|
||||||
m_I2PControlService = new I2PControlService (i2pcontrolPort);
|
|
||||||
m_I2PControlService->Start ();
|
|
||||||
LogPrint("I2PControl started");
|
|
||||||
}
|
|
||||||
m_AddressBook.Start ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::Stop ()
|
|
||||||
{
|
|
||||||
m_HttpProxy->Stop();
|
|
||||||
delete m_HttpProxy;
|
|
||||||
m_HttpProxy = nullptr;
|
|
||||||
LogPrint("HTTP Proxy stopped");
|
|
||||||
m_SocksProxy->Stop();
|
|
||||||
delete m_SocksProxy;
|
|
||||||
m_SocksProxy = nullptr;
|
|
||||||
LogPrint("SOCKS Proxy stopped");
|
|
||||||
for (auto& it: m_ClientTunnels)
|
|
||||||
{
|
|
||||||
it.second->Stop ();
|
|
||||||
LogPrint("I2P client tunnel on port ", it.first, " stopped");
|
|
||||||
}
|
|
||||||
m_ClientTunnels.clear ();
|
|
||||||
for (auto& it: m_ServerTunnels)
|
|
||||||
{
|
|
||||||
it.second->Stop ();
|
|
||||||
LogPrint("I2P server tunnel stopped");
|
|
||||||
}
|
|
||||||
m_ServerTunnels.clear ();
|
|
||||||
if (m_SamBridge)
|
|
||||||
{
|
|
||||||
m_SamBridge->Stop ();
|
|
||||||
delete m_SamBridge;
|
|
||||||
m_SamBridge = nullptr;
|
|
||||||
LogPrint("SAM brdige stopped");
|
|
||||||
}
|
|
||||||
if (m_BOBCommandChannel)
|
|
||||||
{
|
|
||||||
m_BOBCommandChannel->Stop ();
|
|
||||||
delete m_BOBCommandChannel;
|
|
||||||
m_BOBCommandChannel = nullptr;
|
|
||||||
LogPrint("BOB command channel stopped");
|
|
||||||
}
|
|
||||||
if (m_I2PControlService)
|
|
||||||
{
|
|
||||||
m_I2PControlService->Stop ();
|
|
||||||
delete m_I2PControlService;
|
|
||||||
m_I2PControlService = nullptr;
|
|
||||||
LogPrint("I2PControl stopped");
|
|
||||||
}
|
|
||||||
m_AddressBook.Stop ();
|
|
||||||
for (auto it: m_Destinations)
|
|
||||||
it.second->Stop ();
|
|
||||||
m_Destinations.clear ();
|
|
||||||
m_SharedLocalDestination = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> ClientContext::LoadLocalDestination (const std::string& filename, bool isPublic)
|
|
||||||
{
|
|
||||||
i2p::data::PrivateKeys keys;
|
|
||||||
std::string fullPath = i2p::util::filesystem::GetFullPath (filename);
|
|
||||||
std::ifstream s(fullPath.c_str (), std::ifstream::binary);
|
|
||||||
if (s.is_open ())
|
|
||||||
{
|
|
||||||
s.seekg (0, std::ios::end);
|
|
||||||
size_t len = s.tellg();
|
|
||||||
s.seekg (0, std::ios::beg);
|
|
||||||
uint8_t * buf = new uint8_t[len];
|
|
||||||
s.read ((char *)buf, len);
|
|
||||||
keys.FromBuffer (buf, len);
|
|
||||||
delete[] buf;
|
|
||||||
LogPrint ("Local address ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " loaded");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("Can't open file ", fullPath, " Creating new one");
|
|
||||||
keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
|
||||||
std::ofstream f (fullPath, std::ofstream::binary | std::ofstream::out);
|
|
||||||
size_t len = keys.GetFullLen ();
|
|
||||||
uint8_t * buf = new uint8_t[len];
|
|
||||||
len = keys.ToBuffer (buf, len);
|
|
||||||
f.write ((char *)buf, len);
|
|
||||||
delete[] buf;
|
|
||||||
|
|
||||||
LogPrint ("New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " created");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
|
||||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
|
||||||
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
|
|
||||||
if (it != m_Destinations.end ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " alreday exists");
|
|
||||||
localDestination = it->second;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
localDestination = std::make_shared<ClientDestination> (keys, isPublic);
|
|
||||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
|
||||||
localDestination->Start ();
|
|
||||||
}
|
|
||||||
return localDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
|
|
||||||
const std::map<std::string, std::string> * params)
|
|
||||||
{
|
|
||||||
i2p::data::PrivateKeys keys = i2p::data::PrivateKeys::CreateRandomKeys (sigType);
|
|
||||||
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
|
|
||||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
|
||||||
m_Destinations[localDestination->GetIdentHash ()] = localDestination;
|
|
||||||
localDestination->Start ();
|
|
||||||
return localDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::DeleteLocalDestination (std::shared_ptr<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 ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
|
||||||
const std::map<std::string, std::string> * params)
|
|
||||||
{
|
|
||||||
auto it = m_Destinations.find (keys.GetPublic ().GetIdentHash ());
|
|
||||||
if (it != m_Destinations.end ())
|
|
||||||
{
|
|
||||||
LogPrint ("Local destination ", m_AddressBook.ToAddress(keys.GetPublic ().GetIdentHash ()), " exists");
|
|
||||||
if (!it->second->IsRunning ())
|
|
||||||
{
|
|
||||||
it->second->Start ();
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
auto localDestination = std::make_shared<ClientDestination> (keys, isPublic, params);
|
|
||||||
std::unique_lock<std::mutex> l(m_DestinationsMutex);
|
|
||||||
m_Destinations[keys.GetPublic ().GetIdentHash ()] = localDestination;
|
|
||||||
localDestination->Start ();
|
|
||||||
return localDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientContext::ReadTunnels ()
|
|
||||||
{
|
|
||||||
boost::property_tree::ptree pt;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
boost::property_tree::read_ini (i2p::util::filesystem::GetFullPath (TUNNELS_CONFIG_FILENAME), pt);
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Can't read ", TUNNELS_CONFIG_FILENAME, ": ", ex.what ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numClientTunnels = 0, numServerTunnels = 0;
|
|
||||||
for (auto& section: pt)
|
|
||||||
{
|
|
||||||
std::string name = section.first;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE);
|
|
||||||
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT)
|
|
||||||
{
|
|
||||||
// mandatory params
|
|
||||||
std::string dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
|
|
||||||
int port = section.second.get<int> (I2P_CLIENT_TUNNEL_PORT);
|
|
||||||
// optional params
|
|
||||||
std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "");
|
|
||||||
int destinationPort = section.second.get (I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0);
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
|
||||||
if (keys.length () > 0)
|
|
||||||
localDestination = LoadLocalDestination (keys, false);
|
|
||||||
auto clientTunnel = new I2PClientTunnel (dest, port, localDestination, destinationPort);
|
|
||||||
if (m_ClientTunnels.insert (std::make_pair (port, std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
|
|
||||||
clientTunnel->Start ();
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2P client tunnel with port ", port, " already exists");
|
|
||||||
numClientTunnels++;
|
|
||||||
}
|
|
||||||
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP)
|
|
||||||
{
|
|
||||||
// mandatory params
|
|
||||||
std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST);
|
|
||||||
int port = section.second.get<int> (I2P_SERVER_TUNNEL_PORT);
|
|
||||||
std::string keys = section.second.get<std::string> (I2P_SERVER_TUNNEL_KEYS);
|
|
||||||
// optional params
|
|
||||||
int inPort = section.second.get (I2P_SERVER_TUNNEL_INPORT, 0);
|
|
||||||
std::string accessList = section.second.get (I2P_SERVER_TUNNEL_ACCESS_LIST, "");
|
|
||||||
|
|
||||||
auto localDestination = LoadLocalDestination (keys, true);
|
|
||||||
I2PServerTunnel * serverTunnel = (type == I2P_TUNNELS_SECTION_TYPE_HTTP) ? new I2PServerTunnelHTTP (host, port, localDestination, inPort) : new I2PServerTunnel (host, port, localDestination, inPort);
|
|
||||||
if (accessList.length () > 0)
|
|
||||||
{
|
|
||||||
std::set<i2p::data::IdentHash> idents;
|
|
||||||
size_t pos = 0, comma;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
comma = accessList.find (',', pos);
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
ident.FromBase32 (accessList.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
|
|
||||||
idents.insert (ident);
|
|
||||||
pos = comma + 1;
|
|
||||||
}
|
|
||||||
while (comma != std::string::npos);
|
|
||||||
serverTunnel->SetAccessList (idents);
|
|
||||||
}
|
|
||||||
if (m_ServerTunnels.insert (std::make_pair (localDestination->GetIdentHash (), std::unique_ptr<I2PServerTunnel>(serverTunnel))).second)
|
|
||||||
serverTunnel->Start ();
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2P server tunnel for destination ", m_AddressBook.ToAddress(localDestination->GetIdentHash ()), " already exists");
|
|
||||||
numServerTunnels++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Unknown section type=", type, " of ", name, " in ", TUNNELS_CONFIG_FILENAME);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't read tunnel ", name, " params: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, numClientTunnels, " I2P client tunnels created");
|
|
||||||
LogPrint (eLogInfo, numServerTunnels, " I2P server tunnels created");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
#ifndef CLIENT_CONTEXT_H__
|
|
||||||
#define CLIENT_CONTEXT_H__
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "HTTPProxy.h"
|
|
||||||
#include "SOCKS.h"
|
|
||||||
#include "I2PTunnel.h"
|
|
||||||
#include "SAM.h"
|
|
||||||
#include "BOB.h"
|
|
||||||
#include "AddressBook.h"
|
|
||||||
#include "I2PControl.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE[] = "type";
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE_CLIENT[] = "client";
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server";
|
|
||||||
const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http";
|
|
||||||
const char I2P_CLIENT_TUNNEL_PORT[] = "port";
|
|
||||||
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination";
|
|
||||||
const char I2P_CLIENT_TUNNEL_KEYS[] = "keys";
|
|
||||||
const char I2P_CLIENT_TUNNEL_DESTINATION_PORT[] = "destinationport";
|
|
||||||
const char I2P_SERVER_TUNNEL_HOST[] = "host";
|
|
||||||
const char I2P_SERVER_TUNNEL_PORT[] = "port";
|
|
||||||
const char I2P_SERVER_TUNNEL_KEYS[] = "keys";
|
|
||||||
const char I2P_SERVER_TUNNEL_INPORT[] = "inport";
|
|
||||||
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
|
|
||||||
const char TUNNELS_CONFIG_FILENAME[] = "tunnels.cfg";
|
|
||||||
|
|
||||||
class ClientContext
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ClientContext ();
|
|
||||||
~ClientContext ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> GetSharedLocalDestination () const { return m_SharedLocalDestination; };
|
|
||||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (bool isPublic = false, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1,
|
|
||||||
const std::map<std::string, std::string> * params = nullptr); // transient
|
|
||||||
std::shared_ptr<ClientDestination> CreateNewLocalDestination (const i2p::data::PrivateKeys& keys, bool isPublic = true,
|
|
||||||
const std::map<std::string, std::string> * params = nullptr);
|
|
||||||
void DeleteLocalDestination (std::shared_ptr<ClientDestination> destination);
|
|
||||||
std::shared_ptr<ClientDestination> FindLocalDestination (const i2p::data::IdentHash& destination) const;
|
|
||||||
std::shared_ptr<ClientDestination> LoadLocalDestination (const std::string& filename, bool isPublic);
|
|
||||||
|
|
||||||
AddressBook& GetAddressBook () { return m_AddressBook; };
|
|
||||||
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void ReadTunnels ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::mutex m_DestinationsMutex;
|
|
||||||
std::map<i2p::data::IdentHash, std::shared_ptr<ClientDestination> > m_Destinations;
|
|
||||||
std::shared_ptr<ClientDestination> m_SharedLocalDestination;
|
|
||||||
|
|
||||||
AddressBook m_AddressBook;
|
|
||||||
|
|
||||||
i2p::proxy::HTTPProxy * m_HttpProxy;
|
|
||||||
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
|
||||||
std::map<int, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // port->tunnel
|
|
||||||
std::map<i2p::data::IdentHash, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // destination->tunnel
|
|
||||||
SAMBridge * m_SamBridge;
|
|
||||||
BOBCommandChannel * m_BOBCommandChannel;
|
|
||||||
I2PControlService * m_I2PControlService;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// for HTTP
|
|
||||||
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
|
|
||||||
};
|
|
||||||
|
|
||||||
extern ClientContext context;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#include <inttypes.h>
|
|
||||||
#include "CryptoConst.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
const uint8_t elgp_[256]=
|
|
||||||
{
|
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
|
|
||||||
0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
|
|
||||||
0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
|
|
||||||
0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
|
|
||||||
0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45, 0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
|
|
||||||
0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
|
|
||||||
0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
|
|
||||||
0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D, 0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
|
|
||||||
0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
|
|
||||||
0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
|
|
||||||
0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D, 0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
|
|
||||||
0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
|
|
||||||
0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
|
|
||||||
0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9, 0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
|
|
||||||
0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
|
|
||||||
0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint8_t dsap_[128]=
|
|
||||||
{
|
|
||||||
0x9c, 0x05, 0xb2, 0xaa, 0x96, 0x0d, 0x9b, 0x97, 0xb8, 0x93, 0x19, 0x63, 0xc9, 0xcc, 0x9e, 0x8c,
|
|
||||||
0x30, 0x26, 0xe9, 0xb8, 0xed, 0x92, 0xfa, 0xd0, 0xa6, 0x9c, 0xc8, 0x86, 0xd5, 0xbf, 0x80, 0x15,
|
|
||||||
0xfc, 0xad, 0xae, 0x31, 0xa0, 0xad, 0x18, 0xfa, 0xb3, 0xf0, 0x1b, 0x00, 0xa3, 0x58, 0xde, 0x23,
|
|
||||||
0x76, 0x55, 0xc4, 0x96, 0x4a, 0xfa, 0xa2, 0xb3, 0x37, 0xe9, 0x6a, 0xd3, 0x16, 0xb9, 0xfb, 0x1c,
|
|
||||||
0xc5, 0x64, 0xb5, 0xae, 0xc5, 0xb6, 0x9a, 0x9f, 0xf6, 0xc3, 0xe4, 0x54, 0x87, 0x07, 0xfe, 0xf8,
|
|
||||||
0x50, 0x3d, 0x91, 0xdd, 0x86, 0x02, 0xe8, 0x67, 0xe6, 0xd3, 0x5d, 0x22, 0x35, 0xc1, 0x86, 0x9c,
|
|
||||||
0xe2, 0x47, 0x9c, 0x3b, 0x9d, 0x54, 0x01, 0xde, 0x04, 0xe0, 0x72, 0x7f, 0xb3, 0x3d, 0x65, 0x11,
|
|
||||||
0x28, 0x5d, 0x4c, 0xf2, 0x95, 0x38, 0xd9, 0xe3, 0xb6, 0x05, 0x1f, 0x5b, 0x22, 0xcc, 0x1c, 0x93
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint8_t dsaq_[20]=
|
|
||||||
{
|
|
||||||
0xa5, 0xdf, 0xc2, 0x8f, 0xef, 0x4c, 0xa1, 0xe2, 0x86, 0x74, 0x4c, 0xd8, 0xee, 0xd9, 0xd2, 0x9d,
|
|
||||||
0x68, 0x40, 0x46, 0xb7
|
|
||||||
};
|
|
||||||
|
|
||||||
const uint8_t dsag_[128]=
|
|
||||||
{
|
|
||||||
0x0c, 0x1f, 0x4d, 0x27, 0xd4, 0x00, 0x93, 0xb4, 0x29, 0xe9, 0x62, 0xd7, 0x22, 0x38, 0x24, 0xe0,
|
|
||||||
0xbb, 0xc4, 0x7e, 0x7c, 0x83, 0x2a, 0x39, 0x23, 0x6f, 0xc6, 0x83, 0xaf, 0x84, 0x88, 0x95, 0x81,
|
|
||||||
0x07, 0x5f, 0xf9, 0x08, 0x2e, 0xd3, 0x23, 0x53, 0xd4, 0x37, 0x4d, 0x73, 0x01, 0xcd, 0xa1, 0xd2,
|
|
||||||
0x3c, 0x43, 0x1f, 0x46, 0x98, 0x59, 0x9d, 0xda, 0x02, 0x45, 0x18, 0x24, 0xff, 0x36, 0x97, 0x52,
|
|
||||||
0x59, 0x36, 0x47, 0xcc, 0x3d, 0xdc, 0x19, 0x7d, 0xe9, 0x85, 0xe4, 0x3d, 0x13, 0x6c, 0xdc, 0xfc,
|
|
||||||
0x6b, 0xd5, 0x40, 0x9c, 0xd2, 0xf4, 0x50, 0x82, 0x11, 0x42, 0xa5, 0xe6, 0xf8, 0xeb, 0x1c, 0x3a,
|
|
||||||
0xb5, 0xd0, 0x48, 0x4b, 0x81, 0x29, 0xfc, 0xf1, 0x7b, 0xce, 0x4f, 0x7f, 0x33, 0x32, 0x1c, 0x3c,
|
|
||||||
0xb3, 0xdb, 0xb1, 0x4a, 0x90, 0x5e, 0x7b, 0x2b, 0x3e, 0x93, 0xbe, 0x47, 0x08, 0xcb, 0xcc, 0x82
|
|
||||||
};
|
|
||||||
|
|
||||||
const CryptoConstants& GetCryptoConstants ()
|
|
||||||
{
|
|
||||||
static CryptoConstants cryptoConstants =
|
|
||||||
{
|
|
||||||
{elgp_, 256}, // elgp
|
|
||||||
{2}, // elgg
|
|
||||||
{dsap_, 128}, // dsap
|
|
||||||
{dsaq_, 20}, // dsaq
|
|
||||||
{dsag_, 128} // dsag
|
|
||||||
};
|
|
||||||
return cryptoConstants;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#ifndef CRYPTO_CONST_H__
|
|
||||||
#define CRYPTO_CONST_H__
|
|
||||||
|
|
||||||
#include <cryptopp/integer.h>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
struct CryptoConstants
|
|
||||||
{
|
|
||||||
// DH/ElGamal
|
|
||||||
const CryptoPP::Integer elgp;
|
|
||||||
const CryptoPP::Integer elgg;
|
|
||||||
|
|
||||||
// DSA
|
|
||||||
const CryptoPP::Integer dsap;
|
|
||||||
const CryptoPP::Integer dsaq;
|
|
||||||
const CryptoPP::Integer dsag;
|
|
||||||
};
|
|
||||||
|
|
||||||
const CryptoConstants& GetCryptoConstants ();
|
|
||||||
|
|
||||||
// DH/ElGamal
|
|
||||||
#define elgp GetCryptoConstants ().elgp
|
|
||||||
#define elgg GetCryptoConstants ().elgg
|
|
||||||
|
|
||||||
// DSA
|
|
||||||
#define dsap GetCryptoConstants ().dsap
|
|
||||||
#define dsaq GetCryptoConstants ().dsaq
|
|
||||||
#define dsag GetCryptoConstants ().dsag
|
|
||||||
|
|
||||||
// RSA
|
|
||||||
const int rsae = 65537;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
144
Daemon.cpp
144
Daemon.cpp
@@ -1,144 +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));
|
|
||||||
|
|
||||||
i2p::context.SetSupportsV6 (i2p::util::config::GetArg("-v6", 0));
|
|
||||||
i2p::context.SetFloodfill (i2p::util::config::GetArg("-floodfill", 0));
|
|
||||||
auto bandwidth = i2p::util::config::GetArg("-bandwidth", "");
|
|
||||||
if (bandwidth.length () > 0)
|
|
||||||
{
|
|
||||||
if (bandwidth[0] > 'L')
|
|
||||||
i2p::context.SetHighBandwidth ();
|
|
||||||
else
|
|
||||||
i2p::context.SetLowBandwidth ();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 stopped");
|
|
||||||
i2p::tunnel::tunnels.Stop();
|
|
||||||
LogPrint("Tunnels stopped");
|
|
||||||
i2p::transport::transports.Stop();
|
|
||||||
LogPrint("Transports stopped");
|
|
||||||
i2p::data::netdb.Stop();
|
|
||||||
LogPrint("NetDB stopped");
|
|
||||||
d.httpServer->Stop();
|
|
||||||
LogPrint("HTTP Server stopped");
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
119
DaemonLinux.cpp
119
DaemonLinux.cpp
@@ -1,119 +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;
|
|
||||||
}
|
|
||||||
std::string d(i2p::util::filesystem::GetDataDir().string ()); // make a copy
|
|
||||||
chdir(d.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
|
|
||||||
151
Datagram.cpp
151
Datagram.cpp
@@ -1,151 +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::IdentHash& ident, uint16_t fromPort, uint16_t toPort)
|
|
||||||
{
|
|
||||||
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 msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
|
|
||||||
auto remote = m_Owner.FindLeaseSet (ident);
|
|
||||||
if (remote)
|
|
||||||
m_Owner.GetService ().post (std::bind (&DatagramDestination::SendMsg, this, msg, remote));
|
|
||||||
else
|
|
||||||
m_Owner.RequestDestination (ident, std::bind (&DatagramDestination::HandleLeaseSetRequestComplete, this, std::placeholders::_1, msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> remote, I2NPMessage * msg)
|
|
||||||
{
|
|
||||||
if (remote)
|
|
||||||
SendMsg (msg, remote);
|
|
||||||
else
|
|
||||||
DeleteI2NPMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramDestination::SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote)
|
|
||||||
{
|
|
||||||
auto outboundTunnel = m_Owner.GetTunnelPool ()->GetNextOutboundTunnel ();
|
|
||||||
auto leases = remote->GetNonExpiredLeases ();
|
|
||||||
if (!leases.empty () && outboundTunnel)
|
|
||||||
{
|
|
||||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
|
||||||
uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1);
|
|
||||||
auto garlic = m_Owner.WrapMessage (remote, ToSharedI2NPMessage (msg), true);
|
|
||||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
|
||||||
{
|
|
||||||
i2p::tunnel::eDeliveryTypeTunnel,
|
|
||||||
leases[i].tunnelGateway, leases[i].tunnelID,
|
|
||||||
garlic
|
|
||||||
});
|
|
||||||
outboundTunnel->SendTunnelDataMsg (msgs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (outboundTunnel)
|
|
||||||
LogPrint (eLogWarning, "Failed to send datagram. All leases expired");
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels");
|
|
||||||
DeleteI2NPMessage (msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort, 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)
|
|
||||||
{
|
|
||||||
auto it = m_ReceiversByPorts.find (toPort);
|
|
||||||
if (it != m_ReceiversByPorts.end ())
|
|
||||||
it->second (identity, fromPort, toPort, buf + headerLen, len -headerLen);
|
|
||||||
else if (m_Receiver != nullptr)
|
|
||||||
m_Receiver (identity, fromPort, toPort, buf + headerLen, len -headerLen);
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Receiver for datagram is not set");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Datagram signature verification failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, 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 (fromPort, toPort, uncompressed, uncompressedLen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("Received datagram size ", uncompressedLen, " exceeds max size");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage * DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
|
|
||||||
{
|
|
||||||
I2NPMessage * msg = NewI2NPMessage ();
|
|
||||||
CryptoPP::Gzip compressor; // default level
|
|
||||||
compressor.Put (payload, len);
|
|
||||||
compressor.MessageEnd();
|
|
||||||
int size = compressor.MaxRetrievable ();
|
|
||||||
uint8_t * buf = msg->GetPayload ();
|
|
||||||
htobe32buf (buf, size); // length
|
|
||||||
buf += 4;
|
|
||||||
compressor.Get (buf, size);
|
|
||||||
htobe16buf (buf + 4, fromPort); // source port
|
|
||||||
htobe16buf (buf + 6, toPort); // destination port
|
|
||||||
buf[9] = i2p::client::PROTOCOL_TYPE_DATAGRAM; // datagram protocol
|
|
||||||
msg->len += size + 4;
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPData);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
57
Datagram.h
57
Datagram.h
@@ -1,57 +0,0 @@
|
|||||||
#ifndef DATAGRAM_H__
|
|
||||||
#define DATAGRAM_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#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& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)> Receiver;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
DatagramDestination (i2p::client::ClientDestination& owner);
|
|
||||||
~DatagramDestination () {};
|
|
||||||
|
|
||||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
|
||||||
void HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
void SetReceiver (const Receiver& receiver) { m_Receiver = receiver; };
|
|
||||||
void ResetReceiver () { m_Receiver = nullptr; };
|
|
||||||
|
|
||||||
void SetReceiver (const Receiver& receiver, uint16_t port) { m_ReceiversByPorts[port] = receiver; };
|
|
||||||
void ResetReceiver (uint16_t port) { m_ReceiversByPorts.erase (port); };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, I2NPMessage * msg);
|
|
||||||
|
|
||||||
I2NPMessage * CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
|
|
||||||
void SendMsg (I2NPMessage * msg, std::shared_ptr<const i2p::data::LeaseSet> remote);
|
|
||||||
void HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
i2p::client::ClientDestination& m_Owner;
|
|
||||||
Receiver m_Receiver; // default
|
|
||||||
std::map<uint16_t, Receiver> m_ReceiversByPorts;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
667
Destination.cpp
667
Destination.cpp
@@ -1,667 +0,0 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "ElGamal.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "AddressBook.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic,
|
|
||||||
const std::map<std::string, std::string> * params):
|
|
||||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
|
||||||
m_Keys (keys), m_IsPublic (isPublic), m_PublishReplyToken (0),
|
|
||||||
m_DatagramDestination (nullptr), m_PublishConfirmationTimer (m_Service), m_CleanupTimer (m_Service)
|
|
||||||
{
|
|
||||||
i2p::crypto::GenerateElGamalKeyPair(i2p::context.GetRandomNumberGenerator (), m_EncryptionPrivateKey, m_EncryptionPublicKey);
|
|
||||||
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
|
|
||||||
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
|
||||||
int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
|
||||||
int outboundTunnelsQuantity = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
|
|
||||||
std::shared_ptr<std::vector<i2p::data::IdentHash> > explicitPeers;
|
|
||||||
if (params)
|
|
||||||
{
|
|
||||||
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
|
|
||||||
if (it != params->end ())
|
|
||||||
{
|
|
||||||
int len = boost::lexical_cast<int>(it->second);
|
|
||||||
if (len > 0)
|
|
||||||
{
|
|
||||||
inboundTunnelLen = len;
|
|
||||||
LogPrint (eLogInfo, "Inbound tunnel length set to ", len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
|
|
||||||
if (it != params->end ())
|
|
||||||
{
|
|
||||||
int len = boost::lexical_cast<int>(it->second);
|
|
||||||
if (len > 0)
|
|
||||||
{
|
|
||||||
outboundTunnelLen = len;
|
|
||||||
LogPrint (eLogInfo, "Outbound tunnel length set to ", len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
|
|
||||||
if (it != params->end ())
|
|
||||||
{
|
|
||||||
int quantity = boost::lexical_cast<int>(it->second);
|
|
||||||
if (quantity > 0)
|
|
||||||
{
|
|
||||||
inboundTunnelsQuantity = quantity;
|
|
||||||
LogPrint (eLogInfo, "Inbound tunnels quantity set to ", quantity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
|
||||||
if (it != params->end ())
|
|
||||||
{
|
|
||||||
int quantity = boost::lexical_cast<int>(it->second);
|
|
||||||
if (quantity > 0)
|
|
||||||
{
|
|
||||||
outboundTunnelsQuantity = quantity;
|
|
||||||
LogPrint (eLogInfo, "Outbound tunnels quantity set to ", quantity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
|
|
||||||
if (it != params->end ())
|
|
||||||
{
|
|
||||||
explicitPeers = std::make_shared<std::vector<i2p::data::IdentHash> >();
|
|
||||||
std::stringstream ss(it->second);
|
|
||||||
std::string b64;
|
|
||||||
while (std::getline (ss, b64, ','))
|
|
||||||
{
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
ident.FromBase64 (b64);
|
|
||||||
explicitPeers->push_back (ident);
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, "Explicit peers set to ", it->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (this, inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
|
|
||||||
if (explicitPeers)
|
|
||||||
m_Pool->SetExplicitPeers (explicitPeers);
|
|
||||||
if (m_IsPublic)
|
|
||||||
LogPrint (eLogInfo, "Local address ", i2p::client::GetB32Address(GetIdentHash()), " created");
|
|
||||||
m_StreamingDestination = std::make_shared<i2p::stream::StreamingDestination> (*this); // TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
ClientDestination::~ClientDestination ()
|
|
||||||
{
|
|
||||||
if (m_IsRunning)
|
|
||||||
Stop ();
|
|
||||||
for (auto it: m_LeaseSetRequests)
|
|
||||||
delete it.second;
|
|
||||||
if (m_Pool)
|
|
||||||
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
|
|
||||||
if (m_DatagramDestination)
|
|
||||||
delete m_DatagramDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint ("Destination: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::Start ()
|
|
||||||
{
|
|
||||||
if (!m_IsRunning)
|
|
||||||
{
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Pool->SetLocalDestination (this);
|
|
||||||
m_Pool->SetActive (true);
|
|
||||||
m_Thread = new std::thread (std::bind (&ClientDestination::Run, this));
|
|
||||||
m_StreamingDestination->Start ();
|
|
||||||
for (auto it: m_StreamingDestinationsByPorts)
|
|
||||||
it.second->Start ();
|
|
||||||
|
|
||||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
|
||||||
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::Stop ()
|
|
||||||
{
|
|
||||||
if (m_IsRunning)
|
|
||||||
{
|
|
||||||
m_CleanupTimer.cancel ();
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_StreamingDestination->Stop ();
|
|
||||||
for (auto it: m_StreamingDestinationsByPorts)
|
|
||||||
it.second->Stop ();
|
|
||||||
if (m_DatagramDestination)
|
|
||||||
{
|
|
||||||
auto d = m_DatagramDestination;
|
|
||||||
m_DatagramDestination = nullptr;
|
|
||||||
delete d;
|
|
||||||
}
|
|
||||||
if (m_Pool)
|
|
||||||
{
|
|
||||||
m_Pool->SetLocalDestination (nullptr);
|
|
||||||
i2p::tunnel::tunnels.StopTunnelPool (m_Pool);
|
|
||||||
}
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<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");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto ls = i2p::data::netdb.FindLeaseSet (ident);
|
|
||||||
if (ls)
|
|
||||||
{
|
|
||||||
m_RemoteLeaseSets[ident] = ls;
|
|
||||||
return ls;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> ClientDestination::GetLeaseSet ()
|
|
||||||
{
|
|
||||||
if (!m_Pool) return nullptr;
|
|
||||||
if (!m_LeaseSet)
|
|
||||||
UpdateLeaseSet ();
|
|
||||||
return m_LeaseSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::UpdateLeaseSet ()
|
|
||||||
{
|
|
||||||
m_LeaseSet.reset (new i2p::data::LeaseSet (*m_Pool));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint8_t k[32], t[32];
|
|
||||||
} data;
|
|
||||||
memcpy (data.k, key, 32);
|
|
||||||
memcpy (data.t, tag, 32);
|
|
||||||
m_Service.post ([this,data](void)
|
|
||||||
{
|
|
||||||
this->AddSessionKey (data.k, data.t);
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
m_Service.post (std::bind (&ClientDestination::HandleGarlicMessage, this, msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
m_Service.post (std::bind (&ClientDestination::HandleDeliveryStatusMessage, this, msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
|
||||||
{
|
|
||||||
uint8_t typeID = buf[I2NP_HEADER_TYPEID_OFFSET];
|
|
||||||
switch (typeID)
|
|
||||||
{
|
|
||||||
case eI2NPData:
|
|
||||||
HandleDataMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
|
|
||||||
break;
|
|
||||||
case eI2NPDeliveryStatus:
|
|
||||||
// we assume tunnel tests non-encrypted
|
|
||||||
HandleDeliveryStatusMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
|
|
||||||
break;
|
|
||||||
case eI2NPDatabaseStore:
|
|
||||||
HandleDatabaseStoreMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
|
|
||||||
break;
|
|
||||||
case eI2NPDatabaseSearchReply:
|
|
||||||
HandleDatabaseSearchReplyMessage (buf + I2NP_HEADER_SIZE, bufbe16toh (buf + I2NP_HEADER_SIZE_OFFSET));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::HandleDatabaseStoreMessage (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
|
|
||||||
size_t offset = DATABASE_STORE_HEADER_SIZE;
|
|
||||||
if (replyToken)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Reply token is ignored for DatabaseStore");
|
|
||||||
offset += 36;
|
|
||||||
}
|
|
||||||
std::shared_ptr<i2p::data::LeaseSet> leaseSet;
|
|
||||||
if (buf[DATABASE_STORE_TYPE_OFFSET] == 1) // LeaseSet
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Remote LeaseSet");
|
|
||||||
auto it = m_RemoteLeaseSets.find (buf + DATABASE_STORE_KEY_OFFSET);
|
|
||||||
if (it != m_RemoteLeaseSets.end ())
|
|
||||||
{
|
|
||||||
leaseSet = it->second;
|
|
||||||
leaseSet->Update (buf + offset, len - offset);
|
|
||||||
if (leaseSet->IsValid ())
|
|
||||||
LogPrint (eLogDebug, "Remote LeaseSet updated");
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Remote LeaseSet update failed");
|
|
||||||
m_RemoteLeaseSets.erase (it);
|
|
||||||
leaseSet = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset);
|
|
||||||
if (leaseSet->IsValid ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "New remote LeaseSet added");
|
|
||||||
m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = leaseSet;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "New remote LeaseSet verification failed");
|
|
||||||
leaseSet = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ". Dropped");
|
|
||||||
|
|
||||||
auto it1 = m_LeaseSetRequests.find (buf + DATABASE_STORE_KEY_OFFSET);
|
|
||||||
if (it1 != m_LeaseSetRequests.end ())
|
|
||||||
{
|
|
||||||
it1->second->requestTimeoutTimer.cancel ();
|
|
||||||
if (it1->second->requestComplete) it1->second->requestComplete (leaseSet);
|
|
||||||
delete it1->second;
|
|
||||||
m_LeaseSetRequests.erase (it1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
i2p::data::IdentHash key (buf);
|
|
||||||
int num = buf[32]; // num
|
|
||||||
LogPrint ("DatabaseSearchReply for ", key.ToBase64 (), " num=", num);
|
|
||||||
auto it = m_LeaseSetRequests.find (key);
|
|
||||||
if (it != m_LeaseSetRequests.end ())
|
|
||||||
{
|
|
||||||
LeaseSetRequest * request = it->second;
|
|
||||||
bool found = false;
|
|
||||||
if (request->excluded.size () < MAX_NUM_FLOODFILLS_PER_REQUEST)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
i2p::data::IdentHash peerHash (buf + 33 + i*32);
|
|
||||||
auto floodfill = i2p::data::netdb.FindRouter (peerHash);
|
|
||||||
if (floodfill)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ());
|
|
||||||
if (SendLeaseSetRequest (key, floodfill, request))
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Found new floodfill. Request it");
|
|
||||||
i2p::data::netdb.RequestDestination (peerHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found)
|
|
||||||
LogPrint (eLogError, "Suggested floodfills are not presented in netDb");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogInfo, key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST," floodfills");
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
if (request->requestComplete) request->requestComplete (nullptr);
|
|
||||||
delete request;
|
|
||||||
m_LeaseSetRequests.erase (key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("Request for ", key.ToBase64 (), " not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
uint32_t msgID = bufbe32toh (msg->GetPayload () + DELIVERY_STATUS_MSGID_OFFSET);
|
|
||||||
if (msgID == m_PublishReplyToken)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Publishing confirmed");
|
|
||||||
m_ExcludedFloodfills.clear ();
|
|
||||||
m_PublishReplyToken = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::SetLeaseSetUpdated ()
|
|
||||||
{
|
|
||||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
|
||||||
UpdateLeaseSet ();
|
|
||||||
if (m_IsPublic)
|
|
||||||
Publish ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::Publish ()
|
|
||||||
{
|
|
||||||
if (!m_LeaseSet || !m_Pool)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't publish non-existing LeaseSet");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_PublishReplyToken)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Publishing is pending");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto outbound = m_Pool->GetNextOutboundTunnel ();
|
|
||||||
if (!outbound)
|
|
||||||
{
|
|
||||||
LogPrint ("Can't publish LeaseSet. No outbound tunnels");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
std::set<i2p::data::IdentHash> excluded;
|
|
||||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (m_LeaseSet->GetIdentHash (), m_ExcludedFloodfills);
|
|
||||||
if (!floodfill)
|
|
||||||
{
|
|
||||||
LogPrint ("Can't publish LeaseSet. No more floodfills found");
|
|
||||||
m_ExcludedFloodfills.clear ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
|
|
||||||
LogPrint (eLogDebug, "Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
|
|
||||||
m_PublishReplyToken = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
|
||||||
auto msg = WrapMessage (floodfill, ToSharedI2NPMessage (i2p::CreateDatabaseStoreMsg (m_LeaseSet, m_PublishReplyToken)));
|
|
||||||
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
|
|
||||||
m_PublishConfirmationTimer.async_wait (std::bind (&ClientDestination::HandlePublishConfirmationTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
if (m_PublishReplyToken)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Publish confirmation was not received in ", PUBLISH_CONFIRMATION_TIMEOUT, "seconds. Try again");
|
|
||||||
m_PublishReplyToken = 0;
|
|
||||||
Publish ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
uint32_t length = bufbe32toh (buf);
|
|
||||||
buf += 4;
|
|
||||||
// we assume I2CP payload
|
|
||||||
uint16_t fromPort = bufbe16toh (buf + 4), // source
|
|
||||||
toPort = bufbe16toh (buf + 6); // destination
|
|
||||||
switch (buf[9])
|
|
||||||
{
|
|
||||||
case PROTOCOL_TYPE_STREAMING:
|
|
||||||
{
|
|
||||||
// streaming protocol
|
|
||||||
auto dest = GetStreamingDestination (toPort);
|
|
||||||
if (dest)
|
|
||||||
dest->HandleDataMessagePayload (buf, length);
|
|
||||||
else
|
|
||||||
LogPrint ("Missing streaming destination");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case PROTOCOL_TYPE_DATAGRAM:
|
|
||||||
// datagram protocol
|
|
||||||
if (m_DatagramDestination)
|
|
||||||
m_DatagramDestination->HandleDataMessagePayload (fromPort, toPort, buf, length);
|
|
||||||
else
|
|
||||||
LogPrint ("Missing streaming destination");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint ("Data: unexpected protocol ", buf[9]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port) {
|
|
||||||
assert(streamRequestComplete);
|
|
||||||
auto leaseSet = FindLeaseSet (dest);
|
|
||||||
if (leaseSet)
|
|
||||||
streamRequestComplete(CreateStream (leaseSet, port));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RequestDestination (dest,
|
|
||||||
[this, streamRequestComplete, port](std::shared_ptr<i2p::data::LeaseSet> ls)
|
|
||||||
{
|
|
||||||
if (ls)
|
|
||||||
streamRequestComplete(CreateStream (ls, port));
|
|
||||||
else
|
|
||||||
streamRequestComplete (nullptr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
|
|
||||||
{
|
|
||||||
if (m_StreamingDestination)
|
|
||||||
return m_StreamingDestination->CreateNewOutgoingStream (remote, port);
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::GetStreamingDestination (int port) const
|
|
||||||
{
|
|
||||||
if (port)
|
|
||||||
{
|
|
||||||
auto it = m_StreamingDestinationsByPorts.find (port);
|
|
||||||
if (it != m_StreamingDestinationsByPorts.end ())
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
// if port is zero or not found, use default destination
|
|
||||||
return m_StreamingDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port)
|
|
||||||
{
|
|
||||||
auto dest = std::make_shared<i2p::stream::StreamingDestination> (*this, port);
|
|
||||||
if (port)
|
|
||||||
m_StreamingDestinationsByPorts[port] = dest;
|
|
||||||
else // update default
|
|
||||||
m_StreamingDestination = dest;
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
|
|
||||||
{
|
|
||||||
if (!m_DatagramDestination)
|
|
||||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (*this);
|
|
||||||
return m_DatagramDestination;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
|
||||||
{
|
|
||||||
if (!m_Pool || !IsReady ())
|
|
||||||
{
|
|
||||||
if (requestComplete) requestComplete (false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_Service.post (std::bind (&ClientDestination::RequestLeaseSet, this, dest, requestComplete));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
|
||||||
{
|
|
||||||
std::set<i2p::data::IdentHash> excluded;
|
|
||||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, excluded);
|
|
||||||
if (floodfill)
|
|
||||||
{
|
|
||||||
LeaseSetRequest * request = new LeaseSetRequest (m_Service);
|
|
||||||
request->requestComplete = requestComplete;
|
|
||||||
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, LeaseSetRequest *>(dest,request));
|
|
||||||
if (ret.second) // inserted
|
|
||||||
{
|
|
||||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
|
||||||
{
|
|
||||||
// request failed
|
|
||||||
if (request->requestComplete) request->requestComplete (nullptr);
|
|
||||||
delete request;
|
|
||||||
m_LeaseSetRequests.erase (dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // duplicate
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Request of ", dest.ToBase64 (), " is pending already");
|
|
||||||
// TODO: queue up requests
|
|
||||||
if (request->requestComplete) request->requestComplete (nullptr);
|
|
||||||
delete request;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "No floodfills found");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ClientDestination::SendLeaseSetRequest (const i2p::data::IdentHash& dest,
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, LeaseSetRequest * request)
|
|
||||||
{
|
|
||||||
auto replyTunnel = m_Pool->GetNextInboundTunnel ();
|
|
||||||
if (!replyTunnel) LogPrint (eLogError, "No inbound tunnels found");
|
|
||||||
|
|
||||||
auto outboundTunnel = m_Pool->GetNextOutboundTunnel ();
|
|
||||||
if (!outboundTunnel) LogPrint (eLogError, "No outbound tunnels found");
|
|
||||||
|
|
||||||
if (replyTunnel && outboundTunnel)
|
|
||||||
{
|
|
||||||
request->excluded.insert (nextFloodfill->GetIdentHash ());
|
|
||||||
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
request->requestTimeoutTimer.cancel ();
|
|
||||||
|
|
||||||
CryptoPP::AutoSeededRandomPool rnd;
|
|
||||||
uint8_t replyKey[32], replyTag[32];
|
|
||||||
rnd.GenerateBlock (replyKey, 32); // random session key
|
|
||||||
rnd.GenerateBlock (replyTag, 32); // random session tag
|
|
||||||
AddSessionKey (replyKey, replyTag);
|
|
||||||
|
|
||||||
auto msg = WrapMessage (nextFloodfill,
|
|
||||||
ToSharedI2NPMessage (CreateLeaseSetDatabaseLookupMsg (dest, request->excluded,
|
|
||||||
replyTunnel.get (), replyKey, replyTag)));
|
|
||||||
outboundTunnel->SendTunnelDataMsg (
|
|
||||||
{
|
|
||||||
i2p::tunnel::TunnelMessageBlock
|
|
||||||
{
|
|
||||||
i2p::tunnel::eDeliveryTypeRouter,
|
|
||||||
nextFloodfill->GetIdentHash (), 0, msg
|
|
||||||
}
|
|
||||||
});
|
|
||||||
request->requestTimeoutTimer.expires_from_now (boost::posix_time::seconds(LEASESET_REQUEST_TIMEOUT));
|
|
||||||
request->requestTimeoutTimer.async_wait (std::bind (&ClientDestination::HandleRequestTimoutTimer,
|
|
||||||
this, std::placeholders::_1, dest));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
auto it = m_LeaseSetRequests.find (dest);
|
|
||||||
if (it != m_LeaseSetRequests.end ())
|
|
||||||
{
|
|
||||||
bool done = false;
|
|
||||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
if (ts < it->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
|
||||||
{
|
|
||||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (dest, it->second->excluded);
|
|
||||||
if (floodfill)
|
|
||||||
done = !SendLeaseSetRequest (dest, floodfill, it->second);
|
|
||||||
else
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, dest.ToBase64 (), " was not found within ", MAX_LEASESET_REQUEST_TIMEOUT, " seconds");
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (done)
|
|
||||||
{
|
|
||||||
if (it->second->requestComplete) it->second->requestComplete (false);
|
|
||||||
delete it->second;
|
|
||||||
m_LeaseSetRequests.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::HandleCleanupTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
CleanupRoutingSessions ();
|
|
||||||
CleanupRemoteLeaseSets ();
|
|
||||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
|
||||||
m_CleanupTimer.async_wait (std::bind (&ClientDestination::HandleCleanupTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClientDestination::CleanupRemoteLeaseSets ()
|
|
||||||
{
|
|
||||||
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
|
|
||||||
{
|
|
||||||
if (!it->second->HasNonExpiredLeases ()) // all leases expired
|
|
||||||
{
|
|
||||||
LogPrint ("Remote LeaseSet ", it->second->GetIdentHash ().ToBase64 (), " expired");
|
|
||||||
it = m_RemoteLeaseSets.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
156
Destination.h
156
Destination.h
@@ -1,156 +0,0 @@
|
|||||||
#ifndef DESTINATION_H__
|
|
||||||
#define DESTINATION_H__
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <string>
|
|
||||||
#include <functional>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "TunnelPool.h"
|
|
||||||
#include "CryptoConst.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "Garlic.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "Streaming.h"
|
|
||||||
#include "Datagram.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
const uint8_t PROTOCOL_TYPE_STREAMING = 6;
|
|
||||||
const uint8_t PROTOCOL_TYPE_DATAGRAM = 17;
|
|
||||||
const uint8_t PROTOCOL_TYPE_RAW = 18;
|
|
||||||
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
|
|
||||||
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
|
|
||||||
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
|
|
||||||
const int MAX_NUM_FLOODFILLS_PER_REQUEST = 7;
|
|
||||||
const int DESTINATION_CLEANUP_TIMEOUT = 20; // in minutes
|
|
||||||
|
|
||||||
// I2CP
|
|
||||||
const char I2CP_PARAM_INBOUND_TUNNEL_LENGTH[] = "inbound.length";
|
|
||||||
const int DEFAULT_INBOUND_TUNNEL_LENGTH = 3;
|
|
||||||
const char I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH[] = "outbound.length";
|
|
||||||
const int DEFAULT_OUTBOUND_TUNNEL_LENGTH = 3;
|
|
||||||
const char I2CP_PARAM_INBOUND_TUNNELS_QUANTITY[] = "inbound.quantity";
|
|
||||||
const int DEFAULT_INBOUND_TUNNELS_QUANTITY = 5;
|
|
||||||
const char I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY[] = "outbound.quantity";
|
|
||||||
const int DEFAULT_OUTBOUND_TUNNELS_QUANTITY = 5;
|
|
||||||
const char I2CP_PARAM_EXPLICIT_PEERS[] = "explicitPeers";
|
|
||||||
const int STREAM_REQUEST_TIMEOUT = 60; //in seconds
|
|
||||||
|
|
||||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
|
||||||
|
|
||||||
class ClientDestination: public i2p::garlic::GarlicDestination
|
|
||||||
{
|
|
||||||
typedef std::function<void (std::shared_ptr<i2p::data::LeaseSet> leaseSet)> RequestComplete;
|
|
||||||
// leaseSet = nullptr means not found
|
|
||||||
struct LeaseSetRequest
|
|
||||||
{
|
|
||||||
LeaseSetRequest (boost::asio::io_service& service): requestTime (0), requestTimeoutTimer (service) {};
|
|
||||||
std::set<i2p::data::IdentHash> excluded;
|
|
||||||
uint64_t requestTime;
|
|
||||||
boost::asio::deadline_timer requestTimeoutTimer;
|
|
||||||
RequestComplete requestComplete;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
|
||||||
~ClientDestination ();
|
|
||||||
|
|
||||||
virtual void Start ();
|
|
||||||
virtual void Stop ();
|
|
||||||
bool IsRunning () const { return m_IsRunning; };
|
|
||||||
boost::asio::io_service& GetService () { return m_Service; };
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () { return m_Pool; };
|
|
||||||
bool IsReady () const { return m_LeaseSet && m_LeaseSet->HasNonExpiredLeases () && m_Pool->GetOutboundTunnels ().size () > 0; };
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> FindLeaseSet (const i2p::data::IdentHash& ident);
|
|
||||||
bool RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete = nullptr);
|
|
||||||
|
|
||||||
// streaming
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> CreateStreamingDestination (int port); // additional
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> GetStreamingDestination (int port = 0) const;
|
|
||||||
// following methods operate with default streaming destination
|
|
||||||
void CreateStream (StreamRequestComplete streamRequestComplete, const i2p::data::IdentHash& dest, int port = 0);
|
|
||||||
std::shared_ptr<i2p::stream::Stream> CreateStream (std::shared_ptr<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
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet ();
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const { return m_Pool; }
|
|
||||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
|
|
||||||
// override GarlicDestination
|
|
||||||
bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag);
|
|
||||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void SetLeaseSetUpdated ();
|
|
||||||
|
|
||||||
// I2CP
|
|
||||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void UpdateLeaseSet ();
|
|
||||||
void Publish ();
|
|
||||||
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
|
||||||
void HandleDatabaseStoreMessage (const uint8_t * buf, size_t len);
|
|
||||||
void HandleDatabaseSearchReplyMessage (const uint8_t * buf, size_t len);
|
|
||||||
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
void RequestLeaseSet (const i2p::data::IdentHash& dest, RequestComplete requestComplete);
|
|
||||||
bool SendLeaseSetRequest (const i2p::data::IdentHash& dest, std::shared_ptr<const i2p::data::RouterInfo> nextFloodfill, LeaseSetRequest * request);
|
|
||||||
void HandleRequestTimoutTimer (const boost::system::error_code& ecode, const i2p::data::IdentHash& dest);
|
|
||||||
void HandleCleanupTimer (const boost::system::error_code& ecode);
|
|
||||||
void CleanupRemoteLeaseSets ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
volatile 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, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
|
||||||
std::map<i2p::data::IdentHash, LeaseSetRequest *> m_LeaseSetRequests;
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
|
||||||
std::shared_ptr<i2p::data::LeaseSet> m_LeaseSet;
|
|
||||||
bool m_IsPublic;
|
|
||||||
uint32_t m_PublishReplyToken;
|
|
||||||
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> m_StreamingDestination; // default
|
|
||||||
std::map<uint16_t, std::shared_ptr<i2p::stream::StreamingDestination> > m_StreamingDestinationsByPorts;
|
|
||||||
i2p::datagram::DatagramDestination * m_DatagramDestination;
|
|
||||||
|
|
||||||
boost::asio::deadline_timer m_PublishConfirmationTimer, m_CleanupTimer;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP only
|
|
||||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
87
ElGamal.h
87
ElGamal.h
@@ -1,87 +0,0 @@
|
|||||||
#ifndef EL_GAMAL_H__
|
|
||||||
#define EL_GAMAL_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <cryptopp/integer.h>
|
|
||||||
#include <cryptopp/osrng.h>
|
|
||||||
#include <cryptopp/dh.h>
|
|
||||||
#include <cryptopp/sha.h>
|
|
||||||
#include "CryptoConst.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
|
|
||||||
class ElGamalEncryption
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ElGamalEncryption (const uint8_t * key)
|
|
||||||
{
|
|
||||||
CryptoPP::AutoSeededRandomPool rnd;
|
|
||||||
CryptoPP::Integer 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) const
|
|
||||||
{
|
|
||||||
// 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::Integer a, b1;
|
|
||||||
};
|
|
||||||
|
|
||||||
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];
|
|
||||||
a_times_b_mod_c (b, a_exp_b_mod_c (a, elgp - x - 1, elgp), elgp).Encode (m, 255);
|
|
||||||
if (!CryptoPP::SHA256().VerifyDigest (m + 1, m + 33, 222))
|
|
||||||
{
|
|
||||||
LogPrint ("ElGamal decrypt hash doesn't match");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
memcpy (data, m + 33, 222);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void GenerateElGamalKeyPair (CryptoPP::RandomNumberGenerator& rnd, uint8_t * priv, uint8_t * pub)
|
|
||||||
{
|
|
||||||
#if defined(__x86_64__) || defined(__i386__) || defined(_MSC_VER)
|
|
||||||
rnd.GenerateBlock (priv, 256);
|
|
||||||
a_exp_b_mod_c (elgg, CryptoPP::Integer (priv, 256), elgp).Encode (pub, 256);
|
|
||||||
#else
|
|
||||||
CryptoPP::DH dh (elgp, elgg);
|
|
||||||
dh.GenerateKeyPair(rnd, priv, pub);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
618
Garlic.cpp
618
Garlic.cpp
@@ -1,618 +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,
|
|
||||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, int numTags, bool attachLeaseSet):
|
|
||||||
m_Owner (owner), m_Destination (destination), m_NumTags (numTags),
|
|
||||||
m_LeaseSetUpdateStatus (attachLeaseSet ? eLeaseSetUpdated : eLeaseSetDoNotSend)
|
|
||||||
{
|
|
||||||
// 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_LeaseSetUpdateStatus (eLeaseSetDoNotSend)
|
|
||||||
{
|
|
||||||
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::MessageConfirmed (uint32_t msgID)
|
|
||||||
{
|
|
||||||
TagsConfirmed (msgID);
|
|
||||||
if (msgID == m_LeaseSetUpdateMsgID)
|
|
||||||
{
|
|
||||||
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
|
|
||||||
LogPrint (eLogInfo, "LeaseSet update confirmed");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
CleanupExpiredTags ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
|
|
||||||
{
|
|
||||||
auto it = m_UnconfirmedTagsMsgs.find (msgID);
|
|
||||||
if (it != m_UnconfirmedTagsMsgs.end ())
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GarlicRoutingSession::CleanupExpiredTags ()
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
|
|
||||||
{
|
|
||||||
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
|
||||||
it = m_SessionTags.erase (it);
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
// delete expired unconfirmed tags
|
|
||||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
|
||||||
{
|
|
||||||
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
if (m_Owner)
|
|
||||||
m_Owner->RemoveCreatedSession (it->first);
|
|
||||||
delete it->second;
|
|
||||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
return !m_SessionTags.empty () || m_UnconfirmedTagsMsgs.empty ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> GarlicRoutingSession::WrapSingleMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
auto m = ToSharedI2NPMessage(NewI2NPMessage ());
|
|
||||||
m->Align (12); // in order to get buf aligned to 16 (12 + 4)
|
|
||||||
size_t len = 0;
|
|
||||||
uint8_t * buf = m->GetPayload () + 4; // 4 bytes for length
|
|
||||||
|
|
||||||
// find non-expired tag
|
|
||||||
bool tagFound = false;
|
|
||||||
SessionTag tag;
|
|
||||||
if (m_NumTags > 0)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
while (!m_SessionTags.empty ())
|
|
||||||
{
|
|
||||||
if (ts < m_SessionTags.front ().creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
tag = m_SessionTags.front ();
|
|
||||||
m_SessionTags.pop_front (); // use same tag only once
|
|
||||||
tagFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_SessionTags.pop_front (); // remove expired tag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// create message
|
|
||||||
if (!tagFound) // new session
|
|
||||||
{
|
|
||||||
LogPrint ("No garlic tags available. Use ElGamal");
|
|
||||||
if (!m_Destination)
|
|
||||||
{
|
|
||||||
LogPrint ("Can't use ElGamal for unknown destination");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
// create ElGamal block
|
|
||||||
ElGamalBlock elGamal;
|
|
||||||
memcpy (elGamal.sessionKey, m_SessionKey, 32);
|
|
||||||
m_Rnd.GenerateBlock (elGamal.preIV, 32); // Pre-IV
|
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
|
||||||
CryptoPP::SHA256().CalculateDigest(iv, elGamal.preIV, 32);
|
|
||||||
m_Destination->GetElGamalEncryption ()->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
|
|
||||||
m_Encryption.SetIV (iv);
|
|
||||||
buf += 514;
|
|
||||||
len += 514;
|
|
||||||
}
|
|
||||||
else // existing session
|
|
||||||
{
|
|
||||||
// session tag
|
|
||||||
memcpy (buf, tag, 32);
|
|
||||||
uint8_t iv[32]; // IV is first 16 bytes
|
|
||||||
CryptoPP::SHA256().CalculateDigest(iv, tag, 32);
|
|
||||||
m_Encryption.SetIV (iv);
|
|
||||||
buf += 32;
|
|
||||||
len += 32;
|
|
||||||
}
|
|
||||||
// AES block
|
|
||||||
len += CreateAESBlock (buf, msg.get ()); // TODO
|
|
||||||
htobe32buf (m->GetPayload (), len);
|
|
||||||
m->len += len + 4;
|
|
||||||
m->FillI2NPMessageHeader (eI2NPGarlic);
|
|
||||||
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/3);
|
|
||||||
UnconfirmedTags * newTags = createNewTags ? GenerateSessionTags () : nullptr;
|
|
||||||
htobuf16 (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);
|
|
||||||
htobe32buf (payloadSize, 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)
|
|
||||||
{
|
|
||||||
// resubmit non-confirmed LeaseSet
|
|
||||||
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted &&
|
|
||||||
i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
|
||||||
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
|
||||||
|
|
||||||
// attach DeviveryStatus if necessary
|
|
||||||
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
|
|
||||||
{
|
|
||||||
// clove is DeliveryStatus
|
|
||||||
auto cloveSize = CreateDeliveryStatusClove (payload + size, msgID);
|
|
||||||
if (cloveSize > 0) // successive?
|
|
||||||
{
|
|
||||||
size += cloveSize;
|
|
||||||
(*numCloves)++;
|
|
||||||
if (newTags) // new tags created
|
|
||||||
m_UnconfirmedTagsMsgs[msgID] = newTags;
|
|
||||||
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("DeliveryStatus clove was not created");
|
|
||||||
}
|
|
||||||
// attach LeaseSet
|
|
||||||
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
|
|
||||||
{
|
|
||||||
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
|
|
||||||
m_LeaseSetUpdateMsgID = msgID;
|
|
||||||
m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
// 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;
|
|
||||||
htobe32buf (payload + size, msgID); // MessageID
|
|
||||||
size += 4;
|
|
||||||
htobe64buf (payload + size, 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 ();
|
|
||||||
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID
|
|
||||||
size += 4;
|
|
||||||
htobe64buf (buf + size, 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 inboundTunnel = m_Owner->GetTunnelPool ()->GetNextInboundTunnel ();
|
|
||||||
if (inboundTunnel)
|
|
||||||
{
|
|
||||||
buf[size] = eGarlicDeliveryTypeTunnel << 5; // delivery instructions flag tunnel
|
|
||||||
size++;
|
|
||||||
// hash and tunnelID sequence is reversed for Garlic
|
|
||||||
memcpy (buf + size, inboundTunnel->GetNextIdentHash (), 32); // To Hash
|
|
||||||
size += 32;
|
|
||||||
htobe32buf (buf + size, inboundTunnel->GetNextTunnelID ()); // tunnelID
|
|
||||||
size += 4;
|
|
||||||
// create msg
|
|
||||||
auto msg = CreateDeliveryStatusMsg (msgID);
|
|
||||||
if (m_Owner)
|
|
||||||
{
|
|
||||||
//encrypt
|
|
||||||
uint8_t key[32], tag[32];
|
|
||||||
m_Rnd.GenerateBlock (key, 32); // random session key
|
|
||||||
m_Rnd.GenerateBlock (tag, 32); // random session tag
|
|
||||||
m_Owner->SubmitSessionKey (key, tag);
|
|
||||||
GarlicRoutingSession garlic (key, tag);
|
|
||||||
msg = garlic.WrapSingleMessage (msg);
|
|
||||||
}
|
|
||||||
memcpy (buf + size, msg->GetBuffer (), msg->GetLength ());
|
|
||||||
size += msg->GetLength ();
|
|
||||||
// fill clove
|
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 5000; // 5 sec
|
|
||||||
htobe32buf (buf + size, m_Rnd.GenerateWord32 ()); // CloveID
|
|
||||||
size += 4;
|
|
||||||
htobe64buf (buf + size, ts); // Expiration of clove
|
|
||||||
size += 8;
|
|
||||||
memset (buf + size, 0, 3); // certificate of clove
|
|
||||||
size += 3;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "No inbound tunnels in the pool for DeliveryStatus");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("Missing local LeaseSet");
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
GarlicDestination::~GarlicDestination ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
|
|
||||||
{
|
|
||||||
if (key)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
auto decryption = std::make_shared<i2p::crypto::CBCDecryption>();
|
|
||||||
decryption->SetKey (key);
|
|
||||||
m_Tags[SessionTag(tag, ts)] = decryption;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GarlicDestination::SubmitSessionKey (const uint8_t * key, const uint8_t * tag)
|
|
||||||
{
|
|
||||||
AddSessionKey (key, tag);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
uint8_t * buf = msg->GetPayload ();
|
|
||||||
uint32_t length = bufbe32toh (buf);
|
|
||||||
if (length > msg->GetLength ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Garlic message length ", length, " exceeds I2NP message length ", msg->GetLength ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buf += 4; // length
|
|
||||||
auto it = m_Tags.find (SessionTag(buf));
|
|
||||||
if (it != m_Tags.end ())
|
|
||||||
{
|
|
||||||
// tag found. Use AES
|
|
||||||
if (length >= 32)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Garlic message length ", length, " is less than 32 bytes");
|
|
||||||
m_Tags.erase (it); // tag might be used only once
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// tag not found. Use ElGamal
|
|
||||||
ElGamalBlock elGamal;
|
|
||||||
if (length >= 514 && 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 (eLogError, "Failed to decrypt garlic");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
|
||||||
{
|
|
||||||
uint16_t tagCount = bufbe16toh (buf);
|
|
||||||
buf += 2; len -= 2;
|
|
||||||
if (tagCount > 0)
|
|
||||||
{
|
|
||||||
if (tagCount*32 > len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Tag count ", tagCount, " exceeds length ", len);
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (int i = 0; i < tagCount; i++)
|
|
||||||
m_Tags[SessionTag(buf + i*32, ts)] = decryption;
|
|
||||||
}
|
|
||||||
buf += tagCount*32;
|
|
||||||
len -= tagCount*32;
|
|
||||||
uint32_t payloadSize = bufbe32toh (buf);
|
|
||||||
if (payloadSize > len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Unexpected payload size ", payloadSize);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buf += 4;
|
|
||||||
uint8_t * payloadHash = buf;
|
|
||||||
buf += 32;// payload hash.
|
|
||||||
if (*buf) // session key?
|
|
||||||
buf += 32; // new session key
|
|
||||||
buf++; // flag
|
|
||||||
|
|
||||||
// payload
|
|
||||||
if (!CryptoPP::SHA256().VerifyDigest (payloadHash, buf, payloadSize)) // payload hash doesn't match
|
|
||||||
{
|
|
||||||
LogPrint ("Wrong payload hash");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
HandleGarlicPayload (buf, payloadSize, from);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
|
||||||
{
|
|
||||||
const uint8_t * buf1 = buf;
|
|
||||||
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 = bufbe32toh (buf);
|
|
||||||
buf += 4;
|
|
||||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> tunnel;
|
|
||||||
if (from && from->GetTunnelPool ())
|
|
||||||
tunnel = from->GetTunnelPool ()->GetNextOutboundTunnel ();
|
|
||||||
if (tunnel) // we have send it through an outbound tunnel
|
|
||||||
{
|
|
||||||
auto 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
|
|
||||||
if (buf - buf1 > (int)len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Garlic clove is too long");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> GarlicDestination::WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
|
||||||
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet)
|
|
||||||
{
|
|
||||||
auto session = GetRoutingSession (destination, attachLeaseSet); // 32 tags by default
|
|
||||||
return session->WrapSingleMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<GarlicRoutingSession> GarlicDestination::GetRoutingSession (
|
|
||||||
std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet)
|
|
||||||
{
|
|
||||||
auto it = m_Sessions.find (destination->GetIdentHash ());
|
|
||||||
std::shared_ptr<GarlicRoutingSession> session;
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
session = it->second;
|
|
||||||
if (!session)
|
|
||||||
{
|
|
||||||
session = std::make_shared<GarlicRoutingSession> (this, destination,
|
|
||||||
attachLeaseSet ? 40 : 4, attachLeaseSet); // 40 tags for connections and 4 for LS requests
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
m_Sessions[destination->GetIdentHash ()] = session;
|
|
||||||
}
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::CleanupRoutingSessions ()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
|
|
||||||
{
|
|
||||||
if (!it->second->CleanupExpiredTags ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
|
|
||||||
it = m_Sessions.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::RemoveCreatedSession (uint32_t msgID)
|
|
||||||
{
|
|
||||||
m_CreatedSessions.erase (msgID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::DeliveryStatusSent (std::shared_ptr<GarlicRoutingSession> session, uint32_t msgID)
|
|
||||||
{
|
|
||||||
m_CreatedSessions[msgID] = session;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
uint32_t msgID = bufbe32toh (msg->GetPayload ());
|
|
||||||
{
|
|
||||||
auto it = m_CreatedSessions.find (msgID);
|
|
||||||
if (it != m_CreatedSessions.end ())
|
|
||||||
{
|
|
||||||
it->second->MessageConfirmed (msgID);
|
|
||||||
m_CreatedSessions.erase (it);
|
|
||||||
LogPrint (eLogInfo, "Garlic message ", msgID, " acknowledged");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::SetLeaseSetUpdated ()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
for (auto it: m_Sessions)
|
|
||||||
it.second->SetLeaseSetUpdated ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
HandleGarlicMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GarlicDestination::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
HandleDeliveryStatusMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
169
Garlic.h
169
Garlic.h
@@ -1,169 +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
|
|
||||||
const int LEASET_CONFIRMATION_TIMEOUT = 4000; // in milliseconds
|
|
||||||
|
|
||||||
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: public std::enable_shared_from_this<GarlicRoutingSession>
|
|
||||||
{
|
|
||||||
enum LeaseSetUpdateStatus
|
|
||||||
{
|
|
||||||
eLeaseSetUpToDate = 0,
|
|
||||||
eLeaseSetUpdated,
|
|
||||||
eLeaseSetSubmitted,
|
|
||||||
eLeaseSetDoNotSend
|
|
||||||
};
|
|
||||||
|
|
||||||
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, std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
|
||||||
int numTags, bool attachLeaseSet);
|
|
||||||
GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag); // one time encryption
|
|
||||||
~GarlicRoutingSession ();
|
|
||||||
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void MessageConfirmed (uint32_t msgID);
|
|
||||||
bool CleanupExpiredTags (); // returns true if something left
|
|
||||||
|
|
||||||
void SetLeaseSetUpdated ()
|
|
||||||
{
|
|
||||||
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
|
||||||
};
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
void TagsConfirmed (uint32_t msgID);
|
|
||||||
UnconfirmedTags * GenerateSessionTags ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
GarlicDestination * m_Owner;
|
|
||||||
std::shared_ptr<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;
|
|
||||||
|
|
||||||
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
|
|
||||||
uint32_t m_LeaseSetUpdateMsgID;
|
|
||||||
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
|
|
||||||
|
|
||||||
i2p::crypto::CBCEncryption m_Encryption;
|
|
||||||
CryptoPP::AutoSeededRandomPool m_Rnd;
|
|
||||||
};
|
|
||||||
|
|
||||||
class GarlicDestination: public i2p::data::LocalDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
GarlicDestination (): m_LastTagsCleanupTime (0) {};
|
|
||||||
~GarlicDestination ();
|
|
||||||
|
|
||||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
|
||||||
void CleanupRoutingSessions ();
|
|
||||||
void RemoveCreatedSession (uint32_t msgID);
|
|
||||||
std::shared_ptr<I2NPMessage> WrapMessage (std::shared_ptr<const i2p::data::RoutingDestination> destination,
|
|
||||||
std::shared_ptr<I2NPMessage> msg, bool attachLeaseSet = false);
|
|
||||||
|
|
||||||
void AddSessionKey (const uint8_t * key, const uint8_t * tag); // one tag
|
|
||||||
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
|
|
||||||
void DeliveryStatusSent (std::shared_ptr<GarlicRoutingSession> session, uint32_t msgID);
|
|
||||||
|
|
||||||
virtual void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
virtual void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
virtual void SetLeaseSetUpdated ();
|
|
||||||
|
|
||||||
virtual std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () = 0; // TODO
|
|
||||||
virtual std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const = 0;
|
|
||||||
virtual void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void HandleGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleAESBlock (uint8_t * buf, size_t len, std::shared_ptr<i2p::crypto::CBCDecryption> decryption,
|
|
||||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
void HandleGarlicPayload (uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// outgoing sessions
|
|
||||||
std::mutex m_SessionsMutex;
|
|
||||||
std::map<i2p::data::IdentHash, std::shared_ptr<GarlicRoutingSession> > m_Sessions;
|
|
||||||
// incoming
|
|
||||||
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
|
|
||||||
uint32_t m_LastTagsCleanupTime;
|
|
||||||
// DeliveryStatus
|
|
||||||
std::map<uint32_t, std::shared_ptr<GarlicRoutingSession> > m_CreatedSessions; // msgID -> session
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
299
HTTPProxy.cpp
299
HTTPProxy.cpp
@@ -1,299 +0,0 @@
|
|||||||
#include <cstring>
|
|
||||||
#include <cassert>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include <boost/regex.hpp>
|
|
||||||
#include <string>
|
|
||||||
#include <atomic>
|
|
||||||
#include "HTTPProxy.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Streaming.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "I2PTunnel.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace proxy
|
|
||||||
{
|
|
||||||
static const size_t http_buffer_size = 8192;
|
|
||||||
class HTTPProxyHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<HTTPProxyHandler>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
enum state
|
|
||||||
{
|
|
||||||
GET_METHOD,
|
|
||||||
GET_HOSTNAME,
|
|
||||||
GET_HTTPV,
|
|
||||||
GET_HTTPVNL, //TODO: fallback to finding HOst: header if needed
|
|
||||||
DONE
|
|
||||||
};
|
|
||||||
|
|
||||||
void EnterState(state nstate);
|
|
||||||
bool HandleData(uint8_t *http_buff, std::size_t len);
|
|
||||||
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
|
||||||
void Terminate();
|
|
||||||
void AsyncSockRead();
|
|
||||||
void HTTPRequestFailed(/*std::string message*/);
|
|
||||||
void ExtractRequest();
|
|
||||||
bool ValidateHTTPRequest();
|
|
||||||
void HandleJumpServices();
|
|
||||||
bool CreateHTTPRequest(uint8_t *http_buff, std::size_t len);
|
|
||||||
void SentHTTPFailed(const boost::system::error_code & ecode);
|
|
||||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
|
|
||||||
uint8_t m_http_buff[http_buffer_size];
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
|
|
||||||
std::string m_request; //Data left to be sent
|
|
||||||
std::string m_url; //URL
|
|
||||||
std::string m_method; //Method
|
|
||||||
std::string m_version; //HTTP version
|
|
||||||
std::string m_address; //Address
|
|
||||||
std::string m_path; //Path
|
|
||||||
int m_port; //Port
|
|
||||||
state m_state;//Parsing state
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
HTTPProxyHandler(HTTPProxyServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
|
|
||||||
I2PServiceHandler(parent), m_sock(sock)
|
|
||||||
{ EnterState(GET_METHOD); }
|
|
||||||
~HTTPProxyHandler() { Terminate(); }
|
|
||||||
void Handle () { AsyncSockRead(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
void HTTPProxyHandler::AsyncSockRead()
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- HTTP Proxy async sock read");
|
|
||||||
if(m_sock) {
|
|
||||||
m_sock->async_receive(boost::asio::buffer(m_http_buff, http_buffer_size),
|
|
||||||
std::bind(&HTTPProxyHandler::HandleSockRecv, shared_from_this(),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
} else {
|
|
||||||
LogPrint(eLogError,"--- HTTP Proxy no socket for read");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPProxyHandler::Terminate() {
|
|
||||||
if (Kill()) return;
|
|
||||||
if (m_sock)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- HTTP Proxy close sock");
|
|
||||||
m_sock->close();
|
|
||||||
m_sock = nullptr;
|
|
||||||
}
|
|
||||||
Done(shared_from_this());
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All hope is lost beyond this point */
|
|
||||||
//TODO: handle this apropriately
|
|
||||||
void HTTPProxyHandler::HTTPRequestFailed(/*HTTPProxyHandler::errTypes error*/)
|
|
||||||
{
|
|
||||||
static std::string response = "HTTP/1.0 500 Internal Server Error\r\nContent-type: text/html\r\nContent-length: 0\r\n";
|
|
||||||
boost::asio::async_write(*m_sock, boost::asio::buffer(response,response.size()),
|
|
||||||
std::bind(&HTTPProxyHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPProxyHandler::EnterState(HTTPProxyHandler::state nstate)
|
|
||||||
{
|
|
||||||
m_state = nstate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPProxyHandler::ExtractRequest()
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- HTTP Proxy method is: ", m_method, "\nRequest is: ", m_url);
|
|
||||||
std::string server="";
|
|
||||||
std::string port="80";
|
|
||||||
boost::regex rHTTP("http://(.*?)(:(\\d+))?(/.*)");
|
|
||||||
boost::smatch m;
|
|
||||||
std::string path;
|
|
||||||
if(boost::regex_search(m_url, m, rHTTP, boost::match_extra))
|
|
||||||
{
|
|
||||||
server=m[1].str();
|
|
||||||
if (m[2].str() != "") port=m[3].str();
|
|
||||||
path=m[4].str();
|
|
||||||
}
|
|
||||||
LogPrint(eLogDebug,"--- HTTP Proxy server is: ",server, " port is: ", port, "\n path is: ",path);
|
|
||||||
m_address = server;
|
|
||||||
m_port = boost::lexical_cast<int>(port);
|
|
||||||
m_path = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HTTPProxyHandler::ValidateHTTPRequest()
|
|
||||||
{
|
|
||||||
if ( m_version != "HTTP/1.0" && m_version != "HTTP/1.1" )
|
|
||||||
{
|
|
||||||
LogPrint(eLogError,"--- HTTP Proxy unsupported version: ", m_version);
|
|
||||||
HTTPRequestFailed(); //TODO: send right stuff
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPProxyHandler::HandleJumpServices()
|
|
||||||
{
|
|
||||||
static const char * helpermark1 = "?i2paddresshelper=";
|
|
||||||
static const char * helpermark2 = "&i2paddresshelper=";
|
|
||||||
size_t addressHelperPos1 = m_path.rfind (helpermark1);
|
|
||||||
size_t addressHelperPos2 = m_path.rfind (helpermark2);
|
|
||||||
size_t addressHelperPos;
|
|
||||||
if (addressHelperPos1 == std::string::npos)
|
|
||||||
{
|
|
||||||
if (addressHelperPos2 == std::string::npos)
|
|
||||||
return; //Not a jump service
|
|
||||||
else
|
|
||||||
addressHelperPos = addressHelperPos2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (addressHelperPos2 == std::string::npos)
|
|
||||||
addressHelperPos = addressHelperPos1;
|
|
||||||
else if ( addressHelperPos1 > addressHelperPos2 )
|
|
||||||
addressHelperPos = addressHelperPos1;
|
|
||||||
else
|
|
||||||
addressHelperPos = addressHelperPos2;
|
|
||||||
}
|
|
||||||
auto base64 = m_path.substr (addressHelperPos + strlen(helpermark1));
|
|
||||||
base64 = i2p::util::http::urlDecode(base64); //Some of the symbols may be urlencoded
|
|
||||||
LogPrint (eLogDebug,"Jump service for ", m_address, " found at ", base64, ". Inserting to address book");
|
|
||||||
//TODO: this is very dangerous and broken. We should ask the user before doing anything see http://pastethis.i2p/raw/pn5fL4YNJL7OSWj3Sc6N/
|
|
||||||
//TODO: we could redirect the user again to avoid dirtiness in the browser
|
|
||||||
i2p::client::context.GetAddressBook ().InsertAddress (m_address, base64);
|
|
||||||
m_path.erase(addressHelperPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HTTPProxyHandler::CreateHTTPRequest(uint8_t *http_buff, std::size_t len)
|
|
||||||
{
|
|
||||||
ExtractRequest(); //TODO: parse earlier
|
|
||||||
if (!ValidateHTTPRequest()) return false;
|
|
||||||
HandleJumpServices();
|
|
||||||
m_request = m_method;
|
|
||||||
m_request.push_back(' ');
|
|
||||||
m_request += m_path;
|
|
||||||
m_request.push_back(' ');
|
|
||||||
m_request += m_version;
|
|
||||||
m_request.push_back('\r');
|
|
||||||
m_request.push_back('\n');
|
|
||||||
m_request.append("Connection: close\r\n");
|
|
||||||
m_request.append(reinterpret_cast<const char *>(http_buff),len);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HTTPProxyHandler::HandleData(uint8_t *http_buff, std::size_t len)
|
|
||||||
{
|
|
||||||
assert(len); // This should always be called with a least a byte left to parse
|
|
||||||
while (len > 0)
|
|
||||||
{
|
|
||||||
//TODO: fallback to finding HOst: header if needed
|
|
||||||
switch (m_state)
|
|
||||||
{
|
|
||||||
case GET_METHOD:
|
|
||||||
switch (*http_buff)
|
|
||||||
{
|
|
||||||
case ' ': EnterState(GET_HOSTNAME); break;
|
|
||||||
default: m_method.push_back(*http_buff); break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET_HOSTNAME:
|
|
||||||
switch (*http_buff)
|
|
||||||
{
|
|
||||||
case ' ': EnterState(GET_HTTPV); break;
|
|
||||||
default: m_url.push_back(*http_buff); break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET_HTTPV:
|
|
||||||
switch (*http_buff)
|
|
||||||
{
|
|
||||||
case '\r': EnterState(GET_HTTPVNL); break;
|
|
||||||
default: m_version.push_back(*http_buff); break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET_HTTPVNL:
|
|
||||||
switch (*http_buff)
|
|
||||||
{
|
|
||||||
case '\n': EnterState(DONE); break;
|
|
||||||
default:
|
|
||||||
LogPrint(eLogError,"--- HTTP Proxy rejected invalid request ending with: ", ((int)*http_buff));
|
|
||||||
HTTPRequestFailed(); //TODO: add correct code
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint(eLogError,"--- HTTP Proxy invalid state: ", m_state);
|
|
||||||
HTTPRequestFailed(); //TODO: add correct code 500
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
http_buff++;
|
|
||||||
len--;
|
|
||||||
if (m_state == DONE)
|
|
||||||
return CreateHTTPRequest(http_buff,len);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPProxyHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- HTTP Proxy sock recv: ", len);
|
|
||||||
if(ecode)
|
|
||||||
{
|
|
||||||
LogPrint(eLogWarning," --- HTTP Proxy sock recv got error: ", ecode);
|
|
||||||
Terminate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HandleData(m_http_buff, len))
|
|
||||||
{
|
|
||||||
if (m_state == DONE)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo,"--- HTTP Proxy requested: ", m_url);
|
|
||||||
GetOwner()->CreateStream (std::bind (&HTTPProxyHandler::HandleStreamRequestComplete,
|
|
||||||
shared_from_this(), std::placeholders::_1), m_address, m_port);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
AsyncSockRead();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPProxyHandler::SentHTTPFailed(const boost::system::error_code & ecode)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
Terminate();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError,"--- HTTP Proxy Closing socket after sending failure because: ", ecode.message ());
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HTTPProxyHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
if (Kill()) return;
|
|
||||||
LogPrint (eLogInfo,"--- HTTP Proxy New I2PTunnel connection");
|
|
||||||
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
|
|
||||||
GetOwner()->AddHandler (connection);
|
|
||||||
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_request.data()), m_request.size());
|
|
||||||
Done(shared_from_this());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError,"--- HTTP Proxy Issue when creating the stream, check the previous warnings for more info.");
|
|
||||||
HTTPRequestFailed(); // TODO: Send correct error message host unreachable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HTTPProxyServer::HTTPProxyServer(int port, std::shared_ptr<i2p::client::ClientDestination> localDestination):
|
|
||||||
TCPIPAcceptor(port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::client::I2PServiceHandler> HTTPProxyServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
return std::make_shared<HTTPProxyHandler> (this, socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
32
HTTPProxy.h
32
HTTPProxy.h
@@ -1,32 +0,0 @@
|
|||||||
#ifndef HTTP_PROXY_H__
|
|
||||||
#define HTTP_PROXY_H__
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <mutex>
|
|
||||||
#include "I2PService.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace proxy
|
|
||||||
{
|
|
||||||
class HTTPProxyServer: public i2p::client::TCPIPAcceptor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
HTTPProxyServer(int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
|
|
||||||
~HTTPProxyServer() {};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Implements TCPIPAcceptor
|
|
||||||
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
|
||||||
const char* GetName() { return "HTTP Proxy"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef HTTPProxyServer HTTPProxy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
1105
HTTPServer.cpp
1105
HTTPServer.cpp
File diff suppressed because it is too large
Load Diff
138
HTTPServer.h
138
HTTPServer.h
@@ -1,138 +0,0 @@
|
|||||||
#ifndef HTTP_SERVER_H__
|
|
||||||
#define HTTP_SERVER_H__
|
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/array.hpp>
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "Streaming.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace util
|
|
||||||
{
|
|
||||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
|
||||||
const int HTTP_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
|
||||||
class HTTPConnection: public std::enable_shared_from_this<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) {};
|
|
||||||
~HTTPConnection() { delete m_Socket; }
|
|
||||||
void Receive ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Terminate ();
|
|
||||||
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 ShowSAMSessions (std::stringstream& s);
|
|
||||||
void ShowSAMSession (const std::string& id, std::stringstream& s);
|
|
||||||
void StartAcceptingTunnels (std::stringstream& s);
|
|
||||||
void StopAcceptingTunnels (std::stringstream& s);
|
|
||||||
void FillContent (std::stringstream& s);
|
|
||||||
std::string ExtractAddress ();
|
|
||||||
void ExtractParams (const std::string& str, std::map<std::string, std::string>& params);
|
|
||||||
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
boost::asio::ip::tcp::socket * m_Socket;
|
|
||||||
boost::asio::deadline_timer m_Timer;
|
|
||||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
|
||||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1], m_StreamBuffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
|
||||||
size_t m_BufferLen;
|
|
||||||
request m_Request;
|
|
||||||
reply m_Reply;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
virtual void RunRequest ();
|
|
||||||
void HandleDestinationRequest(const std::string& address, const std::string& uri);
|
|
||||||
void SendToAddress (const std::string& address, int port, const char * buf, size_t len);
|
|
||||||
void HandleDestinationRequestTimeout (const boost::system::error_code& ecode,
|
|
||||||
i2p::data::IdentHash destination, int port, const char * buf, size_t len);
|
|
||||||
void SendToDestination (std::shared_ptr<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
|
|
||||||
|
|
||||||
|
|
||||||
626
I2NPProtocol.cpp
626
I2NPProtocol.cpp
@@ -1,626 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <atomic>
|
|
||||||
#include "I2PEndian.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> ToSharedI2NPMessage (I2NPMessage * msg)
|
|
||||||
{
|
|
||||||
return std::shared_ptr<I2NPMessage>(msg, DeleteI2NPMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2NPMessage::FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID)
|
|
||||||
{
|
|
||||||
SetTypeID (msgType);
|
|
||||||
if (replyMsgID) // for tunnel creation
|
|
||||||
SetMsgID (replyMsgID);
|
|
||||||
else
|
|
||||||
SetMsgID (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
|
|
||||||
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000); // TODO: 5 secs is a magic number
|
|
||||||
UpdateSize ();
|
|
||||||
UpdateChks ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2NPMessage::RenewI2NPMessageHeader ()
|
|
||||||
{
|
|
||||||
SetMsgID (i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
|
|
||||||
SetExpiration (i2p::util::GetMillisecondsSinceEpoch () + 5000);
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID)
|
|
||||||
{
|
|
||||||
I2NPMessage * msg = NewI2NPMessage (len);
|
|
||||||
if (msg->len + len < msg->maxLen)
|
|
||||||
{
|
|
||||||
memcpy (msg->GetPayload (), buf, len);
|
|
||||||
msg->len += len;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2NP message length ", len, " exceeds max length");
|
|
||||||
msg->FillI2NPMessageHeader (msgType, replyMsgID);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
|
||||||
{
|
|
||||||
I2NPMessage * msg = NewI2NPMessage ();
|
|
||||||
if (msg->offset + len < msg->maxLen)
|
|
||||||
{
|
|
||||||
memcpy (msg->GetBuffer (), buf, len);
|
|
||||||
msg->len = msg->offset + len;
|
|
||||||
msg->from = from;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2NP message length ", len, " exceeds max length");
|
|
||||||
return ToSharedI2NPMessage(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID)
|
|
||||||
{
|
|
||||||
I2NPMessage * m = NewI2NPShortMessage ();
|
|
||||||
uint8_t * buf = m->GetPayload ();
|
|
||||||
if (msgID)
|
|
||||||
{
|
|
||||||
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
|
|
||||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::util::GetMillisecondsSinceEpoch ());
|
|
||||||
}
|
|
||||||
else // for SSU establishment
|
|
||||||
{
|
|
||||||
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, i2p::context.GetRandomNumberGenerator ().GenerateWord32 ());
|
|
||||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, 2); // netID = 2
|
|
||||||
}
|
|
||||||
m->len += DELIVERY_STATUS_SIZE;
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
|
|
||||||
return ToSharedI2NPMessage (m);
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
|
||||||
uint32_t replyTunnelID, bool exploratory, std::set<i2p::data::IdentHash> * excludedPeers)
|
|
||||||
{
|
|
||||||
I2NPMessage * m = excludedPeers ? NewI2NPMessage () : NewI2NPShortMessage ();
|
|
||||||
uint8_t * buf = m->GetPayload ();
|
|
||||||
memcpy (buf, key, 32); // key
|
|
||||||
buf += 32;
|
|
||||||
memcpy (buf, from, 32); // from
|
|
||||||
buf += 32;
|
|
||||||
uint8_t flag = exploratory ? DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP : DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP;
|
|
||||||
if (replyTunnelID)
|
|
||||||
{
|
|
||||||
*buf = flag | DATABASE_LOOKUP_DELIVERY_FLAG; // set delivery flag
|
|
||||||
htobe32buf (buf+1, replyTunnelID);
|
|
||||||
buf += 5;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*buf = flag; // flag
|
|
||||||
buf++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (excludedPeers)
|
|
||||||
{
|
|
||||||
int cnt = excludedPeers->size ();
|
|
||||||
htobe16buf (buf, cnt);
|
|
||||||
buf += 2;
|
|
||||||
for (auto& it: *excludedPeers)
|
|
||||||
{
|
|
||||||
memcpy (buf, it, 32);
|
|
||||||
buf += 32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// nothing to exclude
|
|
||||||
htobuf16 (buf, 0);
|
|
||||||
buf += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
m->len += (buf - m->GetPayload ());
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
|
|
||||||
const std::set<i2p::data::IdentHash>& excludedFloodfills,
|
|
||||||
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag)
|
|
||||||
{
|
|
||||||
int cnt = excludedFloodfills.size ();
|
|
||||||
I2NPMessage * m = cnt > 0 ? NewI2NPMessage () : NewI2NPShortMessage ();
|
|
||||||
uint8_t * buf = m->GetPayload ();
|
|
||||||
memcpy (buf, dest, 32); // key
|
|
||||||
buf += 32;
|
|
||||||
memcpy (buf, replyTunnel->GetNextIdentHash (), 32); // reply tunnel GW
|
|
||||||
buf += 32;
|
|
||||||
*buf = DATABASE_LOOKUP_DELIVERY_FLAG | DATABASE_LOOKUP_ENCYPTION_FLAG | DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP; // flags
|
|
||||||
htobe32buf (buf + 1, replyTunnel->GetNextTunnelID ()); // reply tunnel ID
|
|
||||||
buf += 5;
|
|
||||||
|
|
||||||
// excluded
|
|
||||||
htobe16buf (buf, cnt);
|
|
||||||
buf += 2;
|
|
||||||
if (cnt > 0)
|
|
||||||
{
|
|
||||||
for (auto& it: excludedFloodfills)
|
|
||||||
{
|
|
||||||
memcpy (buf, it, 32);
|
|
||||||
buf += 32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// encryption
|
|
||||||
memcpy (buf, replyKey, 32);
|
|
||||||
buf[32] = 1; // 1 tag
|
|
||||||
memcpy (buf + 33, replyTag, 32);
|
|
||||||
buf += 65;
|
|
||||||
|
|
||||||
m->len += (buf - m->GetPayload ());
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseLookup);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident,
|
|
||||||
std::vector<i2p::data::IdentHash> routers)
|
|
||||||
{
|
|
||||||
I2NPMessage * m = NewI2NPShortMessage ();
|
|
||||||
uint8_t * buf = m->GetPayload ();
|
|
||||||
size_t len = 0;
|
|
||||||
memcpy (buf, ident, 32);
|
|
||||||
len += 32;
|
|
||||||
buf[len] = routers.size ();
|
|
||||||
len++;
|
|
||||||
for (auto it: routers)
|
|
||||||
{
|
|
||||||
memcpy (buf + len, it, 32);
|
|
||||||
len += 32;
|
|
||||||
}
|
|
||||||
memcpy (buf + len, i2p::context.GetRouterInfo ().GetIdentHash (), 32);
|
|
||||||
len += 32;
|
|
||||||
m->len += len;
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseSearchReply);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage * CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router, uint32_t replyToken)
|
|
||||||
{
|
|
||||||
if (!router) // we send own RouterInfo
|
|
||||||
router = context.GetSharedRouterInfo ();
|
|
||||||
|
|
||||||
I2NPMessage * m = NewI2NPShortMessage ();
|
|
||||||
uint8_t * payload = m->GetPayload ();
|
|
||||||
|
|
||||||
memcpy (payload + DATABASE_STORE_KEY_OFFSET, router->GetIdentHash (), 32);
|
|
||||||
payload[DATABASE_STORE_TYPE_OFFSET] = 0; // RouterInfo
|
|
||||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
|
|
||||||
uint8_t * buf = payload + DATABASE_STORE_HEADER_SIZE;
|
|
||||||
if (replyToken)
|
|
||||||
{
|
|
||||||
memset (buf, 0, 4); // zero tunnelID means direct reply
|
|
||||||
buf += 4;
|
|
||||||
memcpy (buf, router->GetIdentHash (), 32);
|
|
||||||
buf += 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoPP::Gzip compressor;
|
|
||||||
compressor.Put (router->GetBuffer (), router->GetBufferLen ());
|
|
||||||
compressor.MessageEnd();
|
|
||||||
auto size = compressor.MaxRetrievable ();
|
|
||||||
htobe16buf (buf, size); // size
|
|
||||||
buf += 2;
|
|
||||||
m->len += (buf - payload); // payload size
|
|
||||||
if (m->len + size > m->maxLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "DatabaseStore message size is not enough for ", m->len + size);
|
|
||||||
auto newMsg = NewI2NPMessage ();
|
|
||||||
*newMsg = *m;
|
|
||||||
DeleteI2NPMessage (m);
|
|
||||||
m = newMsg;
|
|
||||||
buf = m->buf + m->len;
|
|
||||||
}
|
|
||||||
compressor.Get (buf, size);
|
|
||||||
m->len += size;
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage * CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken)
|
|
||||||
{
|
|
||||||
if (!leaseSet) return nullptr;
|
|
||||||
I2NPMessage * m = NewI2NPShortMessage ();
|
|
||||||
uint8_t * payload = m->GetPayload ();
|
|
||||||
memcpy (payload + DATABASE_STORE_KEY_OFFSET, leaseSet->GetIdentHash (), 32);
|
|
||||||
payload[DATABASE_STORE_TYPE_OFFSET] = 1; // LeaseSet
|
|
||||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, replyToken);
|
|
||||||
size_t size = DATABASE_STORE_HEADER_SIZE;
|
|
||||||
if (replyToken)
|
|
||||||
{
|
|
||||||
auto leases = leaseSet->GetNonExpiredLeases ();
|
|
||||||
if (leases.size () > 0)
|
|
||||||
{
|
|
||||||
htobe32buf (payload + size, leases[0].tunnelID);
|
|
||||||
size += 4; // reply tunnelID
|
|
||||||
memcpy (payload + size, leases[0].tunnelGateway, 32);
|
|
||||||
size += 32; // reply tunnel gateway
|
|
||||||
}
|
|
||||||
else
|
|
||||||
htobe32buf (payload + DATABASE_STORE_REPLY_TOKEN_OFFSET, 0);
|
|
||||||
}
|
|
||||||
memcpy (payload + size, leaseSet->GetBuffer (), leaseSet->GetBufferLen ());
|
|
||||||
size += leaseSet->GetBufferLen ();
|
|
||||||
m->len += size;
|
|
||||||
m->FillI2NPMessageHeader (eI2NPDatabaseStore);
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
uint8_t * record = records + i*TUNNEL_BUILD_RECORD_SIZE;
|
|
||||||
if (!memcmp (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16))
|
|
||||||
{
|
|
||||||
LogPrint ("Record ",i," is ours");
|
|
||||||
|
|
||||||
i2p::crypto::ElGamalDecrypt (i2p::context.GetEncryptionPrivateKey (), record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText);
|
|
||||||
// replace record to reply
|
|
||||||
if (i2p::context.AcceptsTunnels () &&
|
|
||||||
i2p::tunnel::tunnels.GetTransitTunnels ().size () <= MAX_NUM_TRANSIT_TUNNELS &&
|
|
||||||
!i2p::transport::transports.IsBandwidthExceeded ())
|
|
||||||
{
|
|
||||||
i2p::tunnel::TransitTunnel * transitTunnel =
|
|
||||||
i2p::tunnel::CreateTransitTunnel (
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET),
|
|
||||||
clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
|
||||||
clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET,
|
|
||||||
clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET,
|
|
||||||
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80,
|
|
||||||
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET ] & 0x40);
|
|
||||||
i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel);
|
|
||||||
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
record[BUILD_RESPONSE_RECORD_RET_OFFSET] = 30; // always reject with bandwidth reason (30)
|
|
||||||
|
|
||||||
//TODO: fill filler
|
|
||||||
CryptoPP::SHA256().CalculateDigest(record + BUILD_RESPONSE_RECORD_HASH_OFFSET,
|
|
||||||
record + BUILD_RESPONSE_RECORD_PADDING_OFFSET, BUILD_RESPONSE_RECORD_PADDING_SIZE + 1); // + 1 byte of ret
|
|
||||||
// encrypt reply
|
|
||||||
i2p::crypto::CBCEncryption encryption;
|
|
||||||
for (int j = 0; j < num; j++)
|
|
||||||
{
|
|
||||||
encryption.SetKey (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET);
|
|
||||||
encryption.SetIV (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET);
|
|
||||||
uint8_t * reply = records + j*TUNNEL_BUILD_RECORD_SIZE;
|
|
||||||
encryption.Encrypt(reply, TUNNEL_BUILD_RECORD_SIZE, reply);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
int num = buf[0];
|
|
||||||
LogPrint ("VariableTunnelBuild ", num, " records");
|
|
||||||
|
|
||||||
auto tunnel = i2p::tunnel::tunnels.GetPendingInboundTunnel (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 (tunnel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("Inbound tunnel ", tunnel->GetTunnelID (), " has been declined");
|
|
||||||
tunnel->SetState (i2p::tunnel::eTunnelStateBuildFailed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
|
||||||
if (HandleBuildRequestRecords (num, buf + 1, clearText))
|
|
||||||
{
|
|
||||||
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel
|
|
||||||
{
|
|
||||||
// so we send it to reply tunnel
|
|
||||||
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
ToSharedI2NPMessage (CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
|
||||||
eI2NPVariableTunnelBuildReply, buf, len,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
ToSharedI2NPMessage (CreateI2NPMessage (eI2NPVariableTunnelBuild, buf, len,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleTunnelBuildMsg (uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
|
||||||
if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText))
|
|
||||||
{
|
|
||||||
if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel
|
|
||||||
{
|
|
||||||
// so we send it to reply tunnel
|
|
||||||
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
ToSharedI2NPMessage (CreateTunnelGatewayMsg (bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET),
|
|
||||||
eI2NPTunnelBuildReply, buf, len,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET,
|
|
||||||
ToSharedI2NPMessage (CreateI2NPMessage (eI2NPTunnelBuild, buf, len,
|
|
||||||
bufbe32toh (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint ("VariableTunnelBuildReplyMsg replyMsgID=", replyMsgID);
|
|
||||||
auto tunnel = i2p::tunnel::tunnels.GetPendingOutboundTunnel (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 (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 = NewI2NPShortMessage ();
|
|
||||||
memcpy (msg->GetPayload (), buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
|
|
||||||
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelData);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage * CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
|
|
||||||
{
|
|
||||||
I2NPMessage * msg = NewI2NPShortMessage ();
|
|
||||||
memcpy (msg->GetPayload () + 4, payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
|
|
||||||
htobe32buf (msg->GetPayload (), tunnelID);
|
|
||||||
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelData);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
|
|
||||||
{
|
|
||||||
I2NPMessage * msg = NewI2NPShortMessage ();
|
|
||||||
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
|
|
||||||
return ToSharedI2NPMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage * CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
I2NPMessage * msg = NewI2NPMessage (len);
|
|
||||||
uint8_t * payload = msg->GetPayload ();
|
|
||||||
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
|
|
||||||
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
|
|
||||||
memcpy (payload + TUNNEL_GATEWAY_HEADER_SIZE, buf, len);
|
|
||||||
msg->len += TUNNEL_GATEWAY_HEADER_SIZE + len;
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (msg->offset >= I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE)
|
|
||||||
{
|
|
||||||
// message is capable to be used without copying
|
|
||||||
uint8_t * payload = msg->GetBuffer () - TUNNEL_GATEWAY_HEADER_SIZE;
|
|
||||||
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
|
|
||||||
int len = msg->GetLength ();
|
|
||||||
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
|
|
||||||
msg->offset -= (I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE);
|
|
||||||
msg->len = msg->offset + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE +len;
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelGateway);
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
I2NPMessage * msg1 = CreateTunnelGatewayMsg (tunnelID, msg->GetBuffer (), msg->GetLength ());
|
|
||||||
return ToSharedI2NPMessage (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 = I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
|
|
||||||
msg->offset += gatewayMsgOffset;
|
|
||||||
msg->len += gatewayMsgOffset;
|
|
||||||
memcpy (msg->GetPayload (), buf, len);
|
|
||||||
msg->len += len;
|
|
||||||
msg->FillI2NPMessageHeader (msgType, replyMsgID); // create content message
|
|
||||||
len = msg->GetLength ();
|
|
||||||
msg->offset -= gatewayMsgOffset;
|
|
||||||
uint8_t * payload = msg->GetPayload ();
|
|
||||||
htobe32buf (payload + TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET, tunnelID);
|
|
||||||
htobe16buf (payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET, len);
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPTunnelGateway); // gateway message
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetI2NPMessageLength (const uint8_t * msg)
|
|
||||||
{
|
|
||||||
return bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET) + I2NP_HEADER_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleI2NPMessage (uint8_t * msg, size_t len)
|
|
||||||
{
|
|
||||||
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
|
|
||||||
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
|
|
||||||
LogPrint ("I2NP msg received len=", len,", type=", (int)typeID, ", msgID=", (unsigned int)msgID);
|
|
||||||
|
|
||||||
uint8_t * buf = msg + I2NP_HEADER_SIZE;
|
|
||||||
int size = bufbe16toh (msg + I2NP_HEADER_SIZE_OFFSET);
|
|
||||||
switch (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)typeID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (msg)
|
|
||||||
{
|
|
||||||
switch (msg->GetTypeID ())
|
|
||||||
{
|
|
||||||
case eI2NPTunnelData:
|
|
||||||
LogPrint ("TunnelData");
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
|
||||||
break;
|
|
||||||
case eI2NPTunnelGateway:
|
|
||||||
LogPrint ("TunnelGateway");
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
|
||||||
break;
|
|
||||||
case eI2NPGarlic:
|
|
||||||
{
|
|
||||||
LogPrint ("Garlic");
|
|
||||||
if (msg->from)
|
|
||||||
{
|
|
||||||
if (msg->from->GetTunnelPool ())
|
|
||||||
msg->from->GetTunnelPool ()->ProcessGarlicMessage (msg);
|
|
||||||
else
|
|
||||||
LogPrint (eLogInfo, "Local destination for garlic doesn't exist anymore");
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
case eI2NPVariableTunnelBuild:
|
|
||||||
case eI2NPVariableTunnelBuildReply:
|
|
||||||
case eI2NPTunnelBuild:
|
|
||||||
case eI2NPTunnelBuildReply:
|
|
||||||
// forward to tunnel thread
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (msg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessagesHandler::~I2NPMessagesHandler ()
|
|
||||||
{
|
|
||||||
Flush ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2NPMessagesHandler::PutNextMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (msg)
|
|
||||||
{
|
|
||||||
switch (msg->GetTypeID ())
|
|
||||||
{
|
|
||||||
case eI2NPTunnelData:
|
|
||||||
m_TunnelMsgs.push_back (msg);
|
|
||||||
break;
|
|
||||||
case eI2NPTunnelGateway:
|
|
||||||
m_TunnelGatewayMsgs.push_back (msg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
HandleI2NPMessage (msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2NPMessagesHandler::Flush ()
|
|
||||||
{
|
|
||||||
if (!m_TunnelMsgs.empty ())
|
|
||||||
{
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelMsgs);
|
|
||||||
m_TunnelMsgs.clear ();
|
|
||||||
}
|
|
||||||
if (!m_TunnelGatewayMsgs.empty ())
|
|
||||||
{
|
|
||||||
i2p::tunnel::tunnels.PostTunnelData (m_TunnelGatewayMsgs);
|
|
||||||
m_TunnelGatewayMsgs.clear ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
251
I2NPProtocol.h
251
I2NPProtocol.h
@@ -1,251 +0,0 @@
|
|||||||
#ifndef I2NP_PROTOCOL_H__
|
|
||||||
#define I2NP_PROTOCOL_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <set>
|
|
||||||
#include <memory>
|
|
||||||
#include <cryptopp/sha.h>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
// I2NP header
|
|
||||||
const size_t I2NP_HEADER_TYPEID_OFFSET = 0;
|
|
||||||
const size_t I2NP_HEADER_MSGID_OFFSET = I2NP_HEADER_TYPEID_OFFSET + 1;
|
|
||||||
const size_t I2NP_HEADER_EXPIRATION_OFFSET = I2NP_HEADER_MSGID_OFFSET + 4;
|
|
||||||
const size_t I2NP_HEADER_SIZE_OFFSET = I2NP_HEADER_EXPIRATION_OFFSET + 8;
|
|
||||||
const size_t I2NP_HEADER_CHKS_OFFSET = I2NP_HEADER_SIZE_OFFSET + 2;
|
|
||||||
const size_t I2NP_HEADER_SIZE = I2NP_HEADER_CHKS_OFFSET + 1;
|
|
||||||
|
|
||||||
// I2NP short header
|
|
||||||
const size_t I2NP_SHORT_HEADER_TYPEID_OFFSET = 0;
|
|
||||||
const size_t I2NP_SHORT_HEADER_EXPIRATION_OFFSET = I2NP_SHORT_HEADER_TYPEID_OFFSET + 1;
|
|
||||||
const size_t I2NP_SHORT_HEADER_SIZE = I2NP_SHORT_HEADER_EXPIRATION_OFFSET + 4;
|
|
||||||
|
|
||||||
// Tunnel Gateway header
|
|
||||||
const size_t TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET = 0;
|
|
||||||
const size_t TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET = TUNNEL_GATEWAY_HEADER_TUNNELID_OFFSET + 4;
|
|
||||||
const size_t TUNNEL_GATEWAY_HEADER_SIZE = TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET + 2;
|
|
||||||
|
|
||||||
// DeliveryStatus
|
|
||||||
const size_t DELIVERY_STATUS_MSGID_OFFSET = 0;
|
|
||||||
const size_t DELIVERY_STATUS_TIMESTAMP_OFFSET = DELIVERY_STATUS_MSGID_OFFSET + 4;
|
|
||||||
const size_t DELIVERY_STATUS_SIZE = DELIVERY_STATUS_TIMESTAMP_OFFSET + 8;
|
|
||||||
|
|
||||||
// DatabaseStore
|
|
||||||
const size_t DATABASE_STORE_KEY_OFFSET = 0;
|
|
||||||
const size_t DATABASE_STORE_TYPE_OFFSET = DATABASE_STORE_KEY_OFFSET + 32;
|
|
||||||
const size_t DATABASE_STORE_REPLY_TOKEN_OFFSET = DATABASE_STORE_TYPE_OFFSET + 1;
|
|
||||||
const size_t DATABASE_STORE_HEADER_SIZE = DATABASE_STORE_REPLY_TOKEN_OFFSET + 4;
|
|
||||||
|
|
||||||
// TunnelBuild
|
|
||||||
const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
|
|
||||||
|
|
||||||
//BuildRequestRecordClearText
|
|
||||||
const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET + 32;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET = BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET = BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_IV_KEY_OFFSET = BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET + 32;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET = BUILD_REQUEST_RECORD_IV_KEY_OFFSET + 32;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_REPLY_IV_OFFSET = BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET + 32;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_FLAG_OFFSET = BUILD_REQUEST_RECORD_REPLY_IV_OFFSET + 16;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET = BUILD_REQUEST_RECORD_FLAG_OFFSET + 1;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET = BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_PADDING_OFFSET = BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET + 4;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE = 222;
|
|
||||||
|
|
||||||
// BuildRequestRecordEncrypted
|
|
||||||
const size_t BUILD_REQUEST_RECORD_TO_PEER_OFFSET = 0;
|
|
||||||
const size_t BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET = BUILD_REQUEST_RECORD_TO_PEER_OFFSET + 16;
|
|
||||||
|
|
||||||
// BuildResponseRecord
|
|
||||||
const size_t BUILD_RESPONSE_RECORD_HASH_OFFSET = 0;
|
|
||||||
const size_t BUILD_RESPONSE_RECORD_PADDING_OFFSET = 32;
|
|
||||||
const size_t BUILD_RESPONSE_RECORD_PADDING_SIZE = 495;
|
|
||||||
const size_t BUILD_RESPONSE_RECORD_RET_OFFSET = BUILD_RESPONSE_RECORD_PADDING_OFFSET + BUILD_RESPONSE_RECORD_PADDING_SIZE;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// DatabaseLookup flags
|
|
||||||
const uint8_t DATABASE_LOOKUP_DELIVERY_FLAG = 0x01;
|
|
||||||
const uint8_t DATABASE_LOOKUP_ENCYPTION_FLAG = 0x02;
|
|
||||||
const uint8_t DATABASE_LOOKUP_TYPE_FLAGS_MASK = 0x0C;
|
|
||||||
const uint8_t DATABASE_LOOKUP_TYPE_NORMAL_LOOKUP = 0;
|
|
||||||
const uint8_t DATABASE_LOOKUP_TYPE_LEASESET_LOOKUP = 0x04; // 0100
|
|
||||||
const uint8_t DATABASE_LOOKUP_TYPE_ROUTERINFO_LOOKUP = 0x08; // 1000
|
|
||||||
const uint8_t DATABASE_LOOKUP_TYPE_EXPLORATORY_LOOKUP = 0x0C; // 1100
|
|
||||||
|
|
||||||
const int MAX_NUM_TRANSIT_TUNNELS = 2500;
|
|
||||||
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
class InboundTunnel;
|
|
||||||
class TunnelPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t I2NP_MAX_MESSAGE_SIZE = 32768;
|
|
||||||
const size_t I2NP_MAX_SHORT_MESSAGE_SIZE = 4096;
|
|
||||||
struct I2NPMessage
|
|
||||||
{
|
|
||||||
uint8_t * buf;
|
|
||||||
size_t len, offset, maxLen;
|
|
||||||
std::shared_ptr<i2p::tunnel::InboundTunnel> from;
|
|
||||||
|
|
||||||
I2NPMessage (): buf (nullptr),len (I2NP_HEADER_SIZE + 2),
|
|
||||||
offset(2), maxLen (0), from (nullptr) {}; // reserve 2 bytes for NTCP header
|
|
||||||
|
|
||||||
// header accessors
|
|
||||||
uint8_t * GetHeader () { return GetBuffer (); };
|
|
||||||
const uint8_t * GetHeader () const { return GetBuffer (); };
|
|
||||||
void SetTypeID (uint8_t typeID) { GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = typeID; };
|
|
||||||
uint8_t GetTypeID () const { return GetHeader ()[I2NP_HEADER_TYPEID_OFFSET]; };
|
|
||||||
void SetMsgID (uint32_t msgID) { htobe32buf (GetHeader () + I2NP_HEADER_MSGID_OFFSET, msgID); };
|
|
||||||
uint32_t GetMsgID () const { return bufbe32toh (GetHeader () + I2NP_HEADER_MSGID_OFFSET); };
|
|
||||||
void SetExpiration (uint64_t expiration) { htobe64buf (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET, expiration); };
|
|
||||||
uint64_t GetExpiration () const { return bufbe64toh (GetHeader () + I2NP_HEADER_EXPIRATION_OFFSET); };
|
|
||||||
void SetSize (uint16_t size) { htobe16buf (GetHeader () + I2NP_HEADER_SIZE_OFFSET, size); };
|
|
||||||
uint16_t GetSize () const { return bufbe16toh (GetHeader () + I2NP_HEADER_SIZE_OFFSET); };
|
|
||||||
void UpdateSize () { SetSize (GetPayloadLength ()); };
|
|
||||||
void SetChks (uint8_t chks) { GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = chks; };
|
|
||||||
void UpdateChks ()
|
|
||||||
{
|
|
||||||
uint8_t hash[32];
|
|
||||||
CryptoPP::SHA256().CalculateDigest(hash, GetPayload (), GetPayloadLength ());
|
|
||||||
GetHeader ()[I2NP_HEADER_CHKS_OFFSET] = hash[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// payload
|
|
||||||
uint8_t * GetPayload () { return GetBuffer () + I2NP_HEADER_SIZE; };
|
|
||||||
const uint8_t * GetPayload () const { return GetBuffer () + I2NP_HEADER_SIZE; };
|
|
||||||
uint8_t * GetBuffer () { return buf + offset; };
|
|
||||||
const uint8_t * GetBuffer () const { return buf + offset; };
|
|
||||||
size_t GetLength () const { return len - offset; };
|
|
||||||
size_t GetPayloadLength () const { return GetLength () - I2NP_HEADER_SIZE; };
|
|
||||||
|
|
||||||
void Align (size_t alignment)
|
|
||||||
{
|
|
||||||
if (len + alignment > maxLen) return;
|
|
||||||
size_t rem = ((size_t)GetBuffer ()) % alignment;
|
|
||||||
if (rem)
|
|
||||||
{
|
|
||||||
offset += (alignment - rem);
|
|
||||||
len += (alignment - rem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
I2NPMessage& operator=(const I2NPMessage& other)
|
|
||||||
{
|
|
||||||
memcpy (buf + offset, other.buf + other.offset, other.GetLength ());
|
|
||||||
len = offset + other.GetLength ();
|
|
||||||
from = other.from;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for SSU only
|
|
||||||
uint8_t * GetSSUHeader () { return buf + offset + I2NP_HEADER_SIZE - I2NP_SHORT_HEADER_SIZE; };
|
|
||||||
void FromSSU (uint32_t msgID) // we have received SSU message and convert it to regular
|
|
||||||
{
|
|
||||||
const uint8_t * ssu = GetSSUHeader ();
|
|
||||||
GetHeader ()[I2NP_HEADER_TYPEID_OFFSET] = ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET]; // typeid
|
|
||||||
SetMsgID (msgID);
|
|
||||||
SetExpiration (bufbe32toh (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET)*1000LL);
|
|
||||||
SetSize (len - offset - I2NP_HEADER_SIZE);
|
|
||||||
SetChks (0);
|
|
||||||
}
|
|
||||||
uint32_t ToSSU () // return msgID
|
|
||||||
{
|
|
||||||
uint8_t header[I2NP_HEADER_SIZE];
|
|
||||||
memcpy (header, GetHeader (), I2NP_HEADER_SIZE);
|
|
||||||
uint8_t * ssu = GetSSUHeader ();
|
|
||||||
ssu[I2NP_SHORT_HEADER_TYPEID_OFFSET] = header[I2NP_HEADER_TYPEID_OFFSET]; // typeid
|
|
||||||
htobe32buf (ssu + I2NP_SHORT_HEADER_EXPIRATION_OFFSET, bufbe64toh (header + I2NP_HEADER_EXPIRATION_OFFSET)/1000LL);
|
|
||||||
len = offset + I2NP_SHORT_HEADER_SIZE + bufbe16toh (header + I2NP_HEADER_SIZE_OFFSET);
|
|
||||||
return bufbe32toh (header + I2NP_HEADER_MSGID_OFFSET);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FillI2NPMessageHeader (I2NPMessageType msgType, uint32_t replyMsgID = 0);
|
|
||||||
void RenewI2NPMessageHeader ();
|
|
||||||
};
|
|
||||||
|
|
||||||
template<int sz>
|
|
||||||
struct I2NPMessageBuffer: public I2NPMessage
|
|
||||||
{
|
|
||||||
I2NPMessageBuffer () { buf = m_Buffer; maxLen = sz; };
|
|
||||||
uint8_t m_Buffer[sz + 16];
|
|
||||||
};
|
|
||||||
|
|
||||||
I2NPMessage * NewI2NPMessage ();
|
|
||||||
I2NPMessage * NewI2NPShortMessage ();
|
|
||||||
I2NPMessage * NewI2NPMessage (size_t len);
|
|
||||||
void DeleteI2NPMessage (I2NPMessage * msg);
|
|
||||||
std::shared_ptr<I2NPMessage> ToSharedI2NPMessage (I2NPMessage * msg);
|
|
||||||
|
|
||||||
I2NPMessage * CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, int len, uint32_t replyMsgID = 0);
|
|
||||||
std::shared_ptr<I2NPMessage> CreateI2NPMessage (const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from = nullptr);
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> CreateDeliveryStatusMsg (uint32_t msgID);
|
|
||||||
I2NPMessage * CreateRouterInfoDatabaseLookupMsg (const uint8_t * key, const uint8_t * from,
|
|
||||||
uint32_t replyTunnelID, bool exploratory = false, std::set<i2p::data::IdentHash> * excludedPeers = nullptr);
|
|
||||||
I2NPMessage * CreateLeaseSetDatabaseLookupMsg (const i2p::data::IdentHash& dest,
|
|
||||||
const std::set<i2p::data::IdentHash>& excludedFloodfills,
|
|
||||||
const i2p::tunnel::InboundTunnel * replyTunnel, const uint8_t * replyKey, const uint8_t * replyTag);
|
|
||||||
I2NPMessage * CreateDatabaseSearchReply (const i2p::data::IdentHash& ident, std::vector<i2p::data::IdentHash> routers);
|
|
||||||
|
|
||||||
I2NPMessage * CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::RouterInfo> router = nullptr, uint32_t replyToken = 0);
|
|
||||||
I2NPMessage * CreateDatabaseStoreMsg (std::shared_ptr<const i2p::data::LeaseSet> leaseSet, uint32_t replyToken = 0);
|
|
||||||
|
|
||||||
bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * 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);
|
|
||||||
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ();
|
|
||||||
|
|
||||||
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);
|
|
||||||
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
size_t GetI2NPMessageLength (const uint8_t * msg);
|
|
||||||
void HandleI2NPMessage (uint8_t * msg, size_t len);
|
|
||||||
void HandleI2NPMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
class I2NPMessagesHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
~I2NPMessagesHandler ();
|
|
||||||
void PutNextMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void Flush ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelMsgs, m_TunnelGatewayMsgs;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
416
I2PControl.cpp
416
I2PControl.cpp
@@ -1,416 +0,0 @@
|
|||||||
// There is bug in boost 1.49 with gcc 4.7 coming with Debian Wheezy
|
|
||||||
#define GCC47_BOOST149 ((BOOST_VERSION == 104900) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 7))
|
|
||||||
|
|
||||||
#include "I2PControl.h"
|
|
||||||
#include <sstream>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include <boost/date_time/local_time/local_time.hpp>
|
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
|
||||||
#if !GCC47_BOOST149
|
|
||||||
#include <boost/property_tree/json_parser.hpp>
|
|
||||||
#endif
|
|
||||||
#include "Log.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "Daemon.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
I2PControlService::I2PControlService (int port):
|
|
||||||
m_Password (I2P_CONTROL_DEFAULT_PASSWORD), m_IsRunning (false), m_Thread (nullptr),
|
|
||||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
|
|
||||||
m_ShutdownTimer (m_Service)
|
|
||||||
{
|
|
||||||
m_MethodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlService::AuthenticateHandler;
|
|
||||||
m_MethodHandlers[I2P_CONTROL_METHOD_ECHO] = &I2PControlService::EchoHandler;
|
|
||||||
m_MethodHandlers[I2P_CONTROL_METHOD_I2PCONTROL] = &I2PControlService::I2PControlHandler;
|
|
||||||
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_INFO] = &I2PControlService::RouterInfoHandler;
|
|
||||||
m_MethodHandlers[I2P_CONTROL_METHOD_ROUTER_MANAGER] = &I2PControlService::RouterManagerHandler;
|
|
||||||
m_MethodHandlers[I2P_CONTROL_METHOD_NETWORK_SETTING] = &I2PControlService::NetworkSettingHandler;
|
|
||||||
|
|
||||||
// RouterInfo
|
|
||||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_UPTIME] = &I2PControlService::UptimeHandler;
|
|
||||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_VERSION] = &I2PControlService::VersionHandler;
|
|
||||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_STATUS] = &I2PControlService::StatusHandler;
|
|
||||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS] = &I2PControlService::NetDbKnownPeersHandler;
|
|
||||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS] = &I2PControlService::NetDbActivePeersHandler;
|
|
||||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_NET_STATUS] = &I2PControlService::NetStatusHandler;
|
|
||||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING] = &I2PControlService::TunnelsParticipatingHandler;
|
|
||||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_IB_1S] = &I2PControlService::InboundBandwidth1S ;
|
|
||||||
m_RouterInfoHandlers[I2P_CONTROL_ROUTER_INFO_BW_OB_1S] = &I2PControlService::OutboundBandwidth1S ;
|
|
||||||
|
|
||||||
// RouterManager
|
|
||||||
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN] = &I2PControlService::ShutdownHandler;
|
|
||||||
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL] = &I2PControlService::ShutdownGracefulHandler;
|
|
||||||
m_RouterManagerHandlers[I2P_CONTROL_ROUTER_MANAGER_RESEED] = &I2PControlService::ReseedHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PControlService::~I2PControlService ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::Start ()
|
|
||||||
{
|
|
||||||
if (!m_IsRunning)
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Thread = new std::thread (std::bind (&I2PControlService::Run, this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::Stop ()
|
|
||||||
{
|
|
||||||
if (m_IsRunning)
|
|
||||||
{
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_Acceptor.cancel ();
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PControl: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::Accept ()
|
|
||||||
{
|
|
||||||
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (m_Service);
|
|
||||||
m_Acceptor.async_accept (*newSocket, std::bind (&I2PControlService::HandleAccept, this,
|
|
||||||
std::placeholders::_1, newSocket));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Accept ();
|
|
||||||
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "New I2PControl request from ", socket->remote_endpoint ());
|
|
||||||
std::this_thread::sleep_for (std::chrono::milliseconds(5));
|
|
||||||
ReadRequest (socket);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2PControl accept error: ", ecode.message ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::ReadRequest (std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
auto request = std::make_shared<I2PControlBuffer>();
|
|
||||||
socket->async_read_some (
|
|
||||||
#if BOOST_VERSION >= 104900
|
|
||||||
boost::asio::buffer (*request),
|
|
||||||
#else
|
|
||||||
boost::asio::buffer (request->data (), request->size ()),
|
|
||||||
#endif
|
|
||||||
std::bind(&I2PControlService::HandleRequestReceived, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, socket, request));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::HandleRequestReceived (const boost::system::error_code& ecode,
|
|
||||||
size_t bytes_transferred, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
std::shared_ptr<I2PControlBuffer> buf)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PControl read error: ", ecode.message ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bool isHtml = !memcmp (buf->data (), "POST", 4);
|
|
||||||
std::stringstream ss;
|
|
||||||
ss.write (buf->data (), bytes_transferred);
|
|
||||||
if (isHtml)
|
|
||||||
{
|
|
||||||
std::string header;
|
|
||||||
while (!ss.eof () && header != "\r")
|
|
||||||
std::getline(ss, header);
|
|
||||||
if (ss.eof ())
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Malformed I2PControl request. HTTP header expected");
|
|
||||||
return; // TODO:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if GCC47_BOOST149
|
|
||||||
LogPrint (eLogError, "json_read is not supported due bug in boost 1.49 with gcc 4.7");
|
|
||||||
#else
|
|
||||||
boost::property_tree::ptree pt;
|
|
||||||
boost::property_tree::read_json (ss, pt);
|
|
||||||
|
|
||||||
std::string method = pt.get<std::string>(I2P_CONTROL_PROPERTY_METHOD);
|
|
||||||
auto it = m_MethodHandlers.find (method);
|
|
||||||
if (it != m_MethodHandlers.end ())
|
|
||||||
{
|
|
||||||
std::ostringstream response;
|
|
||||||
response << "{\"id\":" << pt.get<std::string>(I2P_CONTROL_PROPERTY_ID) << ",\"result\":{";
|
|
||||||
|
|
||||||
(this->*(it->second))(pt.get_child (I2P_CONTROL_PROPERTY_PARAMS), response);
|
|
||||||
response << "},\"jsonrpc\":\"2.0\"}";
|
|
||||||
SendResponse (socket, buf, response, isHtml);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Unknown I2PControl method ", method);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PControl handle request: ", ex.what ());
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "I2PControl handle request unknown exception");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
|
|
||||||
{
|
|
||||||
ss << "\"" << name << "\":" << value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const
|
|
||||||
{
|
|
||||||
ss << "\"" << name << "\":";
|
|
||||||
if (value.length () > 0)
|
|
||||||
ss << "\"" << value << "\"";
|
|
||||||
else
|
|
||||||
ss << "null";
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, double value) const
|
|
||||||
{
|
|
||||||
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::SendResponse (std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
|
||||||
{
|
|
||||||
size_t len = response.str ().length (), offset = 0;
|
|
||||||
if (isHtml)
|
|
||||||
{
|
|
||||||
std::ostringstream header;
|
|
||||||
header << "HTTP/1.1 200 OK\r\n";
|
|
||||||
header << "Connection: close\r\n";
|
|
||||||
header << "Content-Length: " << boost::lexical_cast<std::string>(len) << "\r\n";
|
|
||||||
header << "Content-Type: application/json\r\n";
|
|
||||||
header << "Date: ";
|
|
||||||
auto facet = new boost::local_time::local_time_facet ("%a, %d %b %Y %H:%M:%S GMT");
|
|
||||||
header.imbue(std::locale (header.getloc(), facet));
|
|
||||||
header << boost::posix_time::second_clock::local_time() << "\r\n";
|
|
||||||
header << "\r\n";
|
|
||||||
offset = header.str ().size ();
|
|
||||||
memcpy (buf->data (), header.str ().c_str (), offset);
|
|
||||||
}
|
|
||||||
memcpy (buf->data () + offset, response.str ().c_str (), len);
|
|
||||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), offset + len),
|
|
||||||
boost::asio::transfer_all (),
|
|
||||||
std::bind(&I2PControlService::HandleResponseSent, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, socket, buf));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
LogPrint (eLogError, "I2PControl write error: ", ecode.message ());
|
|
||||||
socket->close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// handlers
|
|
||||||
|
|
||||||
void I2PControlService::AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
int api = params.get<int> (I2P_CONTROL_PARAM_API);
|
|
||||||
auto password = params.get<std::string> (I2P_CONTROL_PARAM_PASSWORD);
|
|
||||||
LogPrint (eLogDebug, "I2PControl Authenticate API=", api, " Password=", password);
|
|
||||||
if (password != m_Password)
|
|
||||||
LogPrint (eLogError, "I2PControl Authenticate Invalid password ", password, " expected ", m_Password);
|
|
||||||
InsertParam (results, I2P_CONTROL_PARAM_API, api);
|
|
||||||
results << ",";
|
|
||||||
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
|
|
||||||
m_Tokens.insert (token);
|
|
||||||
InsertParam (results, I2P_CONTROL_PARAM_TOKEN, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
auto echo = params.get<std::string> (I2P_CONTROL_PARAM_ECHO);
|
|
||||||
LogPrint (eLogDebug, "I2PControl Echo Echo=", echo);
|
|
||||||
InsertParam (results, I2P_CONTROL_PARAM_RESULT, echo);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// I2PControl
|
|
||||||
|
|
||||||
void I2PControlService::I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "I2PControl I2PControl");
|
|
||||||
for (auto& it: params)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, it.first);
|
|
||||||
auto it1 = m_I2PControlHandlers.find (it.first);
|
|
||||||
if (it1 != m_I2PControlHandlers.end ())
|
|
||||||
(this->*(it1->second))(it.second.data ());
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RouterInfo
|
|
||||||
|
|
||||||
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "I2PControl RouterInfo");
|
|
||||||
for (auto it = params.begin (); it != params.end (); it++)
|
|
||||||
{
|
|
||||||
if (it != params.begin ()) results << ",";
|
|
||||||
LogPrint (eLogDebug, it->first);
|
|
||||||
auto it1 = m_RouterInfoHandlers.find (it->first);
|
|
||||||
if (it1 != m_RouterInfoHandlers.end ())
|
|
||||||
(this->*(it1->second))(results);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2PControl RouterInfo unknown request ", it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::UptimeHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_INFO_UPTIME, (int)i2p::context.GetUptime ()*1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::VersionHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_INFO_VERSION, VERSION);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::StatusHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_INFO_STATUS, "???"); // TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS, i2p::data::netdb.GetNumRouters ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::NetDbActivePeersHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS, (int)i2p::transport::transports.GetPeers ().size ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::NetStatusHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_INFO_NET_STATUS, (int)i2p::context.GetStatus ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::TunnelsParticipatingHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING, (int)i2p::tunnel::tunnels.GetTransitTunnels ().size ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_IB_1S, (double)i2p::transport::transports.GetInBandwidth ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::OutboundBandwidth1S (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_INFO_BW_OB_1S, (double)i2p::transport::transports.GetOutBandwidth ());
|
|
||||||
}
|
|
||||||
|
|
||||||
// RouterManager
|
|
||||||
|
|
||||||
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "I2PControl RouterManager");
|
|
||||||
for (auto it = params.begin (); it != params.end (); it++)
|
|
||||||
{
|
|
||||||
if (it != params.begin ()) results << ",";
|
|
||||||
LogPrint (eLogDebug, it->first);
|
|
||||||
auto it1 = m_RouterManagerHandlers.find (it->first);
|
|
||||||
if (it1 != m_RouterManagerHandlers.end ())
|
|
||||||
(this->*(it1->second))(results);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2PControl RouterManager unknown request ", it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void I2PControlService::ShutdownHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Shutdown requested");
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, "");
|
|
||||||
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(1)); // 1 second to make sure response has been sent
|
|
||||||
m_ShutdownTimer.async_wait (
|
|
||||||
[](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
Daemon.running = 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::ShutdownGracefulHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
i2p::context.SetAcceptsTunnels (false);
|
|
||||||
int timeout = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout ();
|
|
||||||
LogPrint (eLogInfo, "Graceful shutdown requested. Will shutdown after ", timeout, " seconds");
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL, "");
|
|
||||||
m_ShutdownTimer.expires_from_now (boost::posix_time::seconds(timeout + 1)); // + 1 second
|
|
||||||
m_ShutdownTimer.async_wait (
|
|
||||||
[](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
Daemon.running = 0;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PControlService::ReseedHandler (std::ostringstream& results)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Reseed requested");
|
|
||||||
InsertParam (results, I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN, "");
|
|
||||||
i2p::data::netdb.Reseed ();
|
|
||||||
}
|
|
||||||
|
|
||||||
// network setting
|
|
||||||
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "I2PControl NetworkSetting");
|
|
||||||
for (auto it = params.begin (); it != params.end (); it++)
|
|
||||||
{
|
|
||||||
if (it != params.begin ()) results << ",";
|
|
||||||
LogPrint (eLogDebug, it->first);
|
|
||||||
auto it1 = m_NetworkSettingHandlers.find (it->first);
|
|
||||||
if (it1 != m_NetworkSettingHandlers.end ())
|
|
||||||
(this->*(it1->second))(it->second.data (), results);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "I2PControl NetworkSetting unknown request ", it->first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
149
I2PControl.h
149
I2PControl.h
@@ -1,149 +0,0 @@
|
|||||||
#ifndef I2P_CONTROL_H__
|
|
||||||
#define I2P_CONTROL_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
#include <array>
|
|
||||||
#include <string>
|
|
||||||
#include <sstream>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024;
|
|
||||||
typedef std::array<char, I2P_CONTROL_MAX_REQUEST_SIZE> I2PControlBuffer;
|
|
||||||
|
|
||||||
const char I2P_CONTROL_DEFAULT_PASSWORD[] = "itoopie";
|
|
||||||
|
|
||||||
const char I2P_CONTROL_PROPERTY_ID[] = "id";
|
|
||||||
const char I2P_CONTROL_PROPERTY_METHOD[] = "method";
|
|
||||||
const char I2P_CONTROL_PROPERTY_PARAMS[] = "params";
|
|
||||||
const char I2P_CONTROL_PROPERTY_RESULT[] = "result";
|
|
||||||
|
|
||||||
// methods
|
|
||||||
const char I2P_CONTROL_METHOD_AUTHENTICATE[] = "Authenticate";
|
|
||||||
const char I2P_CONTROL_METHOD_ECHO[] = "Echo";
|
|
||||||
const char I2P_CONTROL_METHOD_I2PCONTROL[] = "I2PControl";
|
|
||||||
const char I2P_CONTROL_METHOD_ROUTER_INFO[] = "RouterInfo";
|
|
||||||
const char I2P_CONTROL_METHOD_ROUTER_MANAGER[] = "RouterManager";
|
|
||||||
const char I2P_CONTROL_METHOD_NETWORK_SETTING[] = "NetworkSetting";
|
|
||||||
|
|
||||||
// params
|
|
||||||
const char I2P_CONTROL_PARAM_API[] = "API";
|
|
||||||
const char I2P_CONTROL_PARAM_PASSWORD[] = "Password";
|
|
||||||
const char I2P_CONTROL_PARAM_TOKEN[] = "Token";
|
|
||||||
const char I2P_CONTROL_PARAM_ECHO[] = "Echo";
|
|
||||||
const char I2P_CONTROL_PARAM_RESULT[] = "Result";
|
|
||||||
|
|
||||||
// I2PControl
|
|
||||||
const char I2P_CONTROL_I2PCONTROL_ADDRESS[] = "i2pcontrol.address";
|
|
||||||
const char I2P_CONTROL_I2PCONTROL_PASSWORD[] = "i2pcontrol.password";
|
|
||||||
const char I2P_CONTROL_I2PCONTROL_PORT[] = "i2pcontrol.port";
|
|
||||||
|
|
||||||
// RouterInfo requests
|
|
||||||
const char I2P_CONTROL_ROUTER_INFO_UPTIME[] = "i2p.router.uptime";
|
|
||||||
const char I2P_CONTROL_ROUTER_INFO_VERSION[] = "i2p.router.version";
|
|
||||||
const char I2P_CONTROL_ROUTER_INFO_STATUS[] = "i2p.router.status";
|
|
||||||
const char I2P_CONTROL_ROUTER_INFO_NETDB_KNOWNPEERS[] = "i2p.router.netdb.knownpeers";
|
|
||||||
const char I2P_CONTROL_ROUTER_INFO_NETDB_ACTIVEPEERS[] = "i2p.router.netdb.activepeers";
|
|
||||||
const char I2P_CONTROL_ROUTER_INFO_NET_STATUS[] = "i2p.router.net.status";
|
|
||||||
const char I2P_CONTROL_ROUTER_INFO_TUNNELS_PARTICIPATING[] = "i2p.router.net.tunnels.participating";
|
|
||||||
const char I2P_CONTROL_ROUTER_INFO_BW_IB_1S[] = "i2p.router.net.bw.inbound.1s";
|
|
||||||
const char I2P_CONTROL_ROUTER_INFO_BW_OB_1S[] = "i2p.router.net.bw.outbound.1s";
|
|
||||||
|
|
||||||
// RouterManager requests
|
|
||||||
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN[] = "Shutdown";
|
|
||||||
const char I2P_CONTROL_ROUTER_MANAGER_SHUTDOWN_GRACEFUL[] = "ShutdownGraceful";
|
|
||||||
const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed";
|
|
||||||
|
|
||||||
class I2PControlService
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PControlService (int port);
|
|
||||||
~I2PControlService ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void Accept ();
|
|
||||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
|
||||||
void ReadRequest (std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
|
||||||
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
|
||||||
void SendResponse (std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml);
|
|
||||||
void HandleResponseSent (const boost::system::error_code& ecode, std::size_t bytes_transferred,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void InsertParam (std::ostringstream& ss, const std::string& name, int value) const;
|
|
||||||
void InsertParam (std::ostringstream& ss, const std::string& name, double value) const;
|
|
||||||
void InsertParam (std::ostringstream& ss, const std::string& name, const std::string& value) const;
|
|
||||||
|
|
||||||
// methods
|
|
||||||
typedef void (I2PControlService::*MethodHandler)(const boost::property_tree::ptree& params, std::ostringstream& results);
|
|
||||||
|
|
||||||
void AuthenticateHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
|
||||||
void EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
|
||||||
void I2PControlHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
|
||||||
void RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
|
||||||
void RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
|
||||||
void NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results);
|
|
||||||
|
|
||||||
// I2PControl
|
|
||||||
typedef void (I2PControlService::*I2PControlRequestHandler)(const std::string& value);
|
|
||||||
|
|
||||||
// RouterInfo
|
|
||||||
typedef void (I2PControlService::*RouterInfoRequestHandler)(std::ostringstream& results);
|
|
||||||
void UptimeHandler (std::ostringstream& results);
|
|
||||||
void VersionHandler (std::ostringstream& results);
|
|
||||||
void StatusHandler (std::ostringstream& results);
|
|
||||||
void NetDbKnownPeersHandler (std::ostringstream& results);
|
|
||||||
void NetDbActivePeersHandler (std::ostringstream& results);
|
|
||||||
void NetStatusHandler (std::ostringstream& results);
|
|
||||||
void TunnelsParticipatingHandler (std::ostringstream& results);
|
|
||||||
void InboundBandwidth1S (std::ostringstream& results);
|
|
||||||
void OutboundBandwidth1S (std::ostringstream& results);
|
|
||||||
|
|
||||||
// RouterManager
|
|
||||||
typedef void (I2PControlService::*RouterManagerRequestHandler)(std::ostringstream& results);
|
|
||||||
void ShutdownHandler (std::ostringstream& results);
|
|
||||||
void ShutdownGracefulHandler (std::ostringstream& results);
|
|
||||||
void ReseedHandler (std::ostringstream& results);
|
|
||||||
|
|
||||||
// NetworkSetting
|
|
||||||
typedef void (I2PControlService::*NetworkSettingRequestHandler)(const std::string& value, std::ostringstream& results);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_Password;
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
|
|
||||||
boost::asio::io_service m_Service;
|
|
||||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
|
||||||
boost::asio::deadline_timer m_ShutdownTimer;
|
|
||||||
std::set<std::string> m_Tokens;
|
|
||||||
|
|
||||||
std::map<std::string, MethodHandler> m_MethodHandlers;
|
|
||||||
std::map<std::string, I2PControlRequestHandler> m_I2PControlHandlers;
|
|
||||||
std::map<std::string, RouterInfoRequestHandler> m_RouterInfoHandlers;
|
|
||||||
std::map<std::string, RouterManagerRequestHandler> m_RouterManagerHandlers;
|
|
||||||
std::map<std::string, NetworkSettingRequestHandler> m_NetworkSettingHandlers;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
#include "Destination.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "I2PService.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
static const i2p::data::SigningKeyType I2P_SERVICE_DEFAULT_KEY_TYPE = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256;
|
|
||||||
|
|
||||||
I2PService::I2PService (std::shared_ptr<ClientDestination> localDestination):
|
|
||||||
m_LocalDestination (localDestination ? localDestination :
|
|
||||||
i2p::client::context.CreateNewLocalDestination (false, I2P_SERVICE_DEFAULT_KEY_TYPE))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PService::I2PService (i2p::data::SigningKeyType kt):
|
|
||||||
m_LocalDestination (i2p::client::context.CreateNewLocalDestination (false, kt))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PService::CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port) {
|
|
||||||
assert(streamRequestComplete);
|
|
||||||
i2p::data::IdentHash identHash;
|
|
||||||
if (i2p::client::context.GetAddressBook ().GetIdentHash (dest, identHash))
|
|
||||||
m_LocalDestination->CreateStream (streamRequestComplete, identHash, port);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Remote destination ", dest, " not found");
|
|
||||||
streamRequestComplete (nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPAcceptor::Start ()
|
|
||||||
{
|
|
||||||
m_Acceptor.listen ();
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPAcceptor::Stop ()
|
|
||||||
{
|
|
||||||
m_Acceptor.close();
|
|
||||||
m_Timer.cancel ();
|
|
||||||
ClearHandlers();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPAcceptor::Accept ()
|
|
||||||
{
|
|
||||||
auto newSocket = std::make_shared<boost::asio::ip::tcp::socket> (GetService ());
|
|
||||||
m_Acceptor.async_accept (*newSocket, std::bind (&TCPIPAcceptor::HandleAccept, this,
|
|
||||||
std::placeholders::_1, newSocket));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TCPIPAcceptor::HandleAccept (const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- ",GetName()," accepted");
|
|
||||||
auto handler = CreateHandler(socket);
|
|
||||||
if (handler)
|
|
||||||
{
|
|
||||||
AddHandler(handler);
|
|
||||||
handler->Handle();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
socket->close();
|
|
||||||
Accept();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
LogPrint (eLogError,"--- ",GetName()," Closing socket on accept because: ", ecode.message ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
109
I2PService.h
109
I2PService.h
@@ -1,109 +0,0 @@
|
|||||||
#ifndef I2PSERVICE_H__
|
|
||||||
#define I2PSERVICE_H__
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <mutex>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
class I2PServiceHandler;
|
|
||||||
class I2PService
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
I2PService (std::shared_ptr<ClientDestination> localDestination = nullptr);
|
|
||||||
I2PService (i2p::data::SigningKeyType kt);
|
|
||||||
virtual ~I2PService () { ClearHandlers (); }
|
|
||||||
|
|
||||||
inline void AddHandler (std::shared_ptr<I2PServiceHandler> conn)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_HandlersMutex);
|
|
||||||
m_Handlers.insert(conn);
|
|
||||||
}
|
|
||||||
inline void RemoveHandler (std::shared_ptr<I2PServiceHandler> conn)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_HandlersMutex);
|
|
||||||
m_Handlers.erase(conn);
|
|
||||||
}
|
|
||||||
inline void ClearHandlers ()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_HandlersMutex);
|
|
||||||
m_Handlers.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::shared_ptr<ClientDestination> GetLocalDestination () { return m_LocalDestination; }
|
|
||||||
inline void SetLocalDestination (std::shared_ptr<ClientDestination> dest) { m_LocalDestination = dest; }
|
|
||||||
void CreateStream (StreamRequestComplete streamRequestComplete, const std::string& dest, int port = 0);
|
|
||||||
|
|
||||||
inline boost::asio::io_service& GetService () { return m_LocalDestination->GetService (); }
|
|
||||||
|
|
||||||
virtual void Start () = 0;
|
|
||||||
virtual void Stop () = 0;
|
|
||||||
|
|
||||||
virtual const char* GetName() { return "Generic I2P Service"; }
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::shared_ptr<ClientDestination> m_LocalDestination;
|
|
||||||
std::unordered_set<std::shared_ptr<I2PServiceHandler> > m_Handlers;
|
|
||||||
std::mutex m_HandlersMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*Simple interface for I2PHandlers, allows detection of finalization amongst other things */
|
|
||||||
class I2PServiceHandler
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
I2PServiceHandler(I2PService * parent) : m_Service(parent), m_Dead(false) { }
|
|
||||||
virtual ~I2PServiceHandler() { }
|
|
||||||
//If you override this make sure you call it from the children
|
|
||||||
virtual void Handle() {}; //Start handling the socket
|
|
||||||
protected:
|
|
||||||
// Call when terminating or handing over to avoid race conditions
|
|
||||||
inline bool Kill () { return m_Dead.exchange(true); }
|
|
||||||
// Call to know if the handler is dead
|
|
||||||
inline bool Dead () { return m_Dead; }
|
|
||||||
// Call when done to clean up (make sure Kill is called first)
|
|
||||||
inline void Done (std::shared_ptr<I2PServiceHandler> me) { if(m_Service) m_Service->RemoveHandler(me); }
|
|
||||||
// Call to talk with the owner
|
|
||||||
inline I2PService * GetOwner() { return m_Service; }
|
|
||||||
private:
|
|
||||||
I2PService *m_Service;
|
|
||||||
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
|
|
||||||
};
|
|
||||||
|
|
||||||
/* TODO: support IPv6 too */
|
|
||||||
//This is a service that listens for connections on the IP network and interacts with I2P
|
|
||||||
class TCPIPAcceptor: public I2PService
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TCPIPAcceptor (int port, std::shared_ptr<ClientDestination> localDestination = nullptr) :
|
|
||||||
I2PService(localDestination),
|
|
||||||
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
|
|
||||||
m_Timer (GetService ()) {}
|
|
||||||
TCPIPAcceptor (int port, i2p::data::SigningKeyType kt) :
|
|
||||||
I2PService(kt),
|
|
||||||
m_Acceptor (GetService (), boost::asio::ip::tcp::endpoint (boost::asio::ip::tcp::v4(), port)),
|
|
||||||
m_Timer (GetService ()) {}
|
|
||||||
virtual ~TCPIPAcceptor () { TCPIPAcceptor::Stop(); }
|
|
||||||
//If you override this make sure you call it from the children
|
|
||||||
void Start ();
|
|
||||||
//If you override this make sure you call it from the children
|
|
||||||
void Stop ();
|
|
||||||
protected:
|
|
||||||
virtual std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket) = 0;
|
|
||||||
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
|
|
||||||
private:
|
|
||||||
void Accept();
|
|
||||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
|
||||||
boost::asio::ip::tcp::acceptor m_Acceptor;
|
|
||||||
boost::asio::deadline_timer m_Timer;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
402
I2PTunnel.cpp
402
I2PTunnel.cpp
@@ -1,402 +0,0 @@
|
|||||||
#include <cassert>
|
|
||||||
#include "base64.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "I2PTunnel.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port):
|
|
||||||
I2PServiceHandler(owner), m_Socket (socket), m_RemoteEndpoint (socket->remote_endpoint ()),
|
|
||||||
m_IsQuiet (true)
|
|
||||||
{
|
|
||||||
m_Stream = GetOwner()->GetLocalDestination ()->CreateStream (leaseSet, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, std::shared_ptr<i2p::stream::Stream> stream):
|
|
||||||
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
|
|
||||||
m_RemoteEndpoint (socket->remote_endpoint ()), m_IsQuiet (true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnection::I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket, const boost::asio::ip::tcp::endpoint& target, bool quiet):
|
|
||||||
I2PServiceHandler(owner), m_Socket (socket), m_Stream (stream),
|
|
||||||
m_RemoteEndpoint (target), m_IsQuiet (quiet)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnection::~I2PTunnelConnection ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
if (msg)
|
|
||||||
m_Stream->Send (msg, len); // connect and send
|
|
||||||
else
|
|
||||||
m_Stream->Send (m_Buffer, 0); // connect
|
|
||||||
}
|
|
||||||
StreamReceive ();
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::Connect ()
|
|
||||||
{
|
|
||||||
if (m_Socket)
|
|
||||||
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::Terminate ()
|
|
||||||
{
|
|
||||||
if (Kill()) return;
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
m_Stream->Close ();
|
|
||||||
m_Stream.reset ();
|
|
||||||
}
|
|
||||||
m_Socket->close ();
|
|
||||||
Done(shared_from_this ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::Receive ()
|
|
||||||
{
|
|
||||||
m_Socket->async_read_some (boost::asio::buffer(m_Buffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
|
||||||
std::bind(&I2PTunnelConnection::HandleReceived, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("I2PTunnel read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
auto s = shared_from_this ();
|
|
||||||
m_Stream->AsyncSend (m_Buffer, bytes_transferred,
|
|
||||||
[s](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
s->Receive ();
|
|
||||||
else
|
|
||||||
s->Terminate ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::HandleWrite (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("I2PTunnel write error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
StreamReceive ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::StreamReceive ()
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, I2P_TUNNEL_CONNECTION_BUFFER_SIZE),
|
|
||||||
std::bind (&I2PTunnelConnection::HandleStreamReceive, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2),
|
|
||||||
I2P_TUNNEL_CONNECTION_MAX_IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("I2PTunnel stream read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Write (m_StreamBuffer, bytes_transferred);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::Write (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
m_Socket->async_send (boost::asio::buffer (buf, len),
|
|
||||||
std::bind (&I2PTunnelConnection::HandleWrite, shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnection::HandleConnect (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("I2PTunnel connect error: ", ecode.message ());
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("I2PTunnel connected");
|
|
||||||
if (m_IsQuiet)
|
|
||||||
StreamReceive ();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// send destination first like received from I2P
|
|
||||||
std::string dest = m_Stream->GetRemoteIdentity ().ToBase64 ();
|
|
||||||
dest += "\n";
|
|
||||||
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
|
|
||||||
HandleStreamReceive (boost::system::error_code (), dest.size ());
|
|
||||||
}
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PTunnelConnectionHTTP::I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host):
|
|
||||||
I2PTunnelConnection (owner, stream, socket, target), m_Host (host), m_HeaderSent (false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (m_HeaderSent)
|
|
||||||
I2PTunnelConnection::Write (buf, len);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_InHeader.clear ();
|
|
||||||
m_InHeader.write ((const char *)buf, len);
|
|
||||||
std::string line;
|
|
||||||
bool endOfHeader = false;
|
|
||||||
while (!endOfHeader)
|
|
||||||
{
|
|
||||||
std::getline(m_InHeader, line);
|
|
||||||
if (!m_InHeader.fail ())
|
|
||||||
{
|
|
||||||
if (line.find ("Host:") != std::string::npos)
|
|
||||||
m_OutHeader << "Host: " << m_Host << "\r\n";
|
|
||||||
else
|
|
||||||
m_OutHeader << line << "\n";
|
|
||||||
if (line == "\r") endOfHeader = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endOfHeader)
|
|
||||||
{
|
|
||||||
m_OutHeader << m_InHeader.str (); // data right after header
|
|
||||||
m_HeaderSent = true;
|
|
||||||
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This handler tries to stablish a connection with the desired server and dies if it fails to do so */
|
|
||||||
class I2PClientTunnelHandler: public I2PServiceHandler, public std::enable_shared_from_this<I2PClientTunnelHandler>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
I2PClientTunnelHandler (I2PClientTunnel * parent, i2p::data::IdentHash destination,
|
|
||||||
int destinationPort, std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
|
||||||
I2PServiceHandler(parent), m_DestinationIdentHash(destination),
|
|
||||||
m_DestinationPort (destinationPort), m_Socket(socket) {};
|
|
||||||
void Handle();
|
|
||||||
void Terminate();
|
|
||||||
private:
|
|
||||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
i2p::data::IdentHash m_DestinationIdentHash;
|
|
||||||
int m_DestinationPort;
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
|
||||||
};
|
|
||||||
|
|
||||||
void I2PClientTunnelHandler::Handle()
|
|
||||||
{
|
|
||||||
GetOwner()->GetLocalDestination ()->CreateStream (
|
|
||||||
std::bind (&I2PClientTunnelHandler::HandleStreamRequestComplete, shared_from_this(), std::placeholders::_1),
|
|
||||||
m_DestinationIdentHash, m_DestinationPort);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PClientTunnelHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
if (Kill()) return;
|
|
||||||
LogPrint (eLogInfo,"New I2PTunnel connection");
|
|
||||||
auto connection = std::make_shared<I2PTunnelConnection>(GetOwner(), m_Socket, stream);
|
|
||||||
GetOwner()->AddHandler (connection);
|
|
||||||
connection->I2PConnect ();
|
|
||||||
Done(shared_from_this());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError,"I2P Client Tunnel Issue when creating the stream, check the previous warnings for more info.");
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PClientTunnelHandler::Terminate()
|
|
||||||
{
|
|
||||||
if (Kill()) return;
|
|
||||||
if (m_Socket)
|
|
||||||
{
|
|
||||||
m_Socket->close();
|
|
||||||
m_Socket = nullptr;
|
|
||||||
}
|
|
||||||
Done(shared_from_this());
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PClientTunnel::I2PClientTunnel (const std::string& destination, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort):
|
|
||||||
TCPIPAcceptor (port,localDestination), m_Destination (destination), m_DestinationIdentHash (nullptr), m_DestinationPort (destinationPort)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void I2PClientTunnel::Start ()
|
|
||||||
{
|
|
||||||
TCPIPAcceptor::Start ();
|
|
||||||
GetIdentHash();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PClientTunnel::Stop ()
|
|
||||||
{
|
|
||||||
TCPIPAcceptor::Stop();
|
|
||||||
auto *originalIdentHash = m_DestinationIdentHash;
|
|
||||||
m_DestinationIdentHash = nullptr;
|
|
||||||
delete originalIdentHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HACK: maybe we should create a caching IdentHash provider in AddressBook */
|
|
||||||
const i2p::data::IdentHash * I2PClientTunnel::GetIdentHash ()
|
|
||||||
{
|
|
||||||
if (!m_DestinationIdentHash)
|
|
||||||
{
|
|
||||||
i2p::data::IdentHash identHash;
|
|
||||||
if (i2p::client::context.GetAddressBook ().GetIdentHash (m_Destination, identHash))
|
|
||||||
m_DestinationIdentHash = new i2p::data::IdentHash (identHash);
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning,"Remote destination ", m_Destination, " not found");
|
|
||||||
}
|
|
||||||
return m_DestinationIdentHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2PServiceHandler> I2PClientTunnel::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
const i2p::data::IdentHash *identHash = GetIdentHash();
|
|
||||||
if (identHash)
|
|
||||||
return std::make_shared<I2PClientTunnelHandler>(this, *identHash, m_DestinationPort, socket);
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PServerTunnel::I2PServerTunnel (const std::string& address, int port,
|
|
||||||
std::shared_ptr<ClientDestination> localDestination, int inport):
|
|
||||||
I2PService (localDestination), m_Address (address), m_Port (port), m_IsAccessList (false)
|
|
||||||
{
|
|
||||||
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port);
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::Start ()
|
|
||||||
{
|
|
||||||
m_Endpoint.port (m_Port);
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto addr = boost::asio::ip::address::from_string (m_Address, ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
m_Endpoint.address (addr);
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(GetService ());
|
|
||||||
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (m_Address, ""),
|
|
||||||
std::bind (&I2PServerTunnel::HandleResolve, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, resolver));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::Stop ()
|
|
||||||
{
|
|
||||||
ClearHandlers ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
auto addr = (*it).endpoint ().address ();
|
|
||||||
LogPrint (eLogInfo, "server tunnel ", (*it).host_name (), " has been resolved to ", addr);
|
|
||||||
m_Endpoint.address (addr);
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Unable to resolve server tunnel address: ", ecode.message ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::SetAccessList (const std::set<i2p::data::IdentHash>& accessList)
|
|
||||||
{
|
|
||||||
m_AccessList = accessList;
|
|
||||||
m_IsAccessList = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::Accept ()
|
|
||||||
{
|
|
||||||
if (m_PortDestination)
|
|
||||||
m_PortDestination->SetAcceptor (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
|
|
||||||
|
|
||||||
auto localDestination = GetLocalDestination ();
|
|
||||||
if (localDestination)
|
|
||||||
{
|
|
||||||
if (!localDestination->IsAcceptingStreams ()) // set it as default if not set yet
|
|
||||||
localDestination->AcceptStreams (std::bind (&I2PServerTunnel::HandleAccept, this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("Local destination not set for server tunnel");
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::HandleAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
if (m_IsAccessList)
|
|
||||||
{
|
|
||||||
if (!m_AccessList.count (stream->GetRemoteIdentity ().GetIdentHash ()))
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Address ", stream->GetRemoteIdentity ().GetIdentHash ().ToBase32 (), " is not in white list. Incoming connection dropped");
|
|
||||||
stream->Close ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CreateI2PConnection (stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
auto conn = std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ());
|
|
||||||
AddHandler (conn);
|
|
||||||
conn->Connect ();
|
|
||||||
}
|
|
||||||
|
|
||||||
I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& address, int port, std::shared_ptr<ClientDestination> localDestination, int inport):
|
|
||||||
I2PServerTunnel (address, port, localDestination, inport)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
auto conn = std::make_shared<I2PTunnelConnectionHTTP> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), GetAddress ());
|
|
||||||
AddHandler (conn);
|
|
||||||
conn->Connect ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
153
I2PTunnel.h
153
I2PTunnel.h
@@ -1,153 +0,0 @@
|
|||||||
#ifndef I2PTUNNEL_H__
|
|
||||||
#define I2PTUNNEL_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <set>
|
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "Streaming.h"
|
|
||||||
#include "I2PService.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 I2PTunnelConnection: public I2PServiceHandler, public std::enable_shared_from_this<I2PTunnelConnection>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> leaseSet, int port = 0); // to I2P
|
|
||||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
std::shared_ptr<i2p::stream::Stream> stream); // to I2P using simplified API
|
|
||||||
I2PTunnelConnection (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
const boost::asio::ip::tcp::endpoint& target, bool quiet = true); // from I2P
|
|
||||||
~I2PTunnelConnection ();
|
|
||||||
void I2PConnect (const uint8_t * msg = nullptr, size_t len = 0);
|
|
||||||
void Connect ();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void Terminate ();
|
|
||||||
|
|
||||||
void Receive ();
|
|
||||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
virtual void Write (const uint8_t * buf, size_t len); // can be overloaded
|
|
||||||
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];
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_Socket;
|
|
||||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
|
||||||
boost::asio::ip::tcp::endpoint m_RemoteEndpoint;
|
|
||||||
bool m_IsQuiet; // don't send destination
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PTunnelConnectionHTTP: public I2PTunnelConnection
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
|
||||||
const boost::asio::ip::tcp::endpoint& target, const std::string& host);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
void Write (const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_Host;
|
|
||||||
std::stringstream m_InHeader, m_OutHeader;
|
|
||||||
bool m_HeaderSent;
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PClientTunnel: public TCPIPAcceptor
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
|
|
||||||
// Implements TCPIPAcceptor
|
|
||||||
std::shared_ptr<I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
|
||||||
const char* GetName() { return "I2P Client Tunnel"; }
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PClientTunnel (const std::string& destination, int port, std::shared_ptr<ClientDestination> localDestination, int destinationPort = 0);
|
|
||||||
~I2PClientTunnel () {}
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
const i2p::data::IdentHash * GetIdentHash ();
|
|
||||||
|
|
||||||
std::string m_Destination;
|
|
||||||
const i2p::data::IdentHash * m_DestinationIdentHash;
|
|
||||||
int m_DestinationPort;
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PServerTunnel: public I2PService
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PServerTunnel (const std::string& address, int port,
|
|
||||||
std::shared_ptr<ClientDestination> localDestination, int inport = 0);
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
void SetAccessList (const std::set<i2p::data::IdentHash>& accessList);
|
|
||||||
|
|
||||||
const std::string& GetAddress() const { return m_Address; }
|
|
||||||
int GetPort () const { return m_Port; };
|
|
||||||
const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
|
||||||
|
|
||||||
void Accept ();
|
|
||||||
void HandleAccept (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
virtual void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::string m_Address;
|
|
||||||
int m_Port;
|
|
||||||
boost::asio::ip::tcp::endpoint m_Endpoint;
|
|
||||||
std::shared_ptr<i2p::stream::StreamingDestination> m_PortDestination;
|
|
||||||
std::set<i2p::data::IdentHash> m_AccessList;
|
|
||||||
bool m_IsAccessList;
|
|
||||||
};
|
|
||||||
|
|
||||||
class I2PServerTunnelHTTP: public I2PServerTunnel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
I2PServerTunnelHTTP (const std::string& address, int port,
|
|
||||||
std::shared_ptr<ClientDestination> localDestination, int inport = 0);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
566
Identity.cpp
566
Identity.cpp
@@ -1,566 +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 "ElGamal.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
Identity& Identity::operator=(const Keys& keys)
|
|
||||||
{
|
|
||||||
// copy public and signing keys together
|
|
||||||
memcpy (publicKey, keys.publicKey, sizeof (publicKey) + sizeof (signingKey));
|
|
||||||
memset (&certificate, 0, sizeof (certificate));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Identity::FromBuffer (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
memcpy (publicKey, buf, DEFAULT_IDENTITY_SIZE);
|
|
||||||
return DEFAULT_IDENTITY_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentHash Identity::Hash () const
|
|
||||||
{
|
|
||||||
IdentHash hash;
|
|
||||||
CryptoPP::SHA256().CalculateDigest(hash, publicKey, DEFAULT_IDENTITY_SIZE);
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::IdentityEx ():
|
|
||||||
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type)
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey));
|
|
||||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
|
||||||
{
|
|
||||||
size_t excessLen = 0;
|
|
||||||
uint8_t * excessBuf = nullptr;
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
|
|
||||||
i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding);
|
|
||||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP256_KEY_LENGTH);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
|
|
||||||
i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding);
|
|
||||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::ECDSAP384_KEY_LENGTH);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
|
||||||
excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132 - 128
|
|
||||||
excessBuf = new uint8_t[excessLen];
|
|
||||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
|
||||||
excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256 - 128
|
|
||||||
excessBuf = new uint8_t[excessLen];
|
|
||||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
|
||||||
excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384 - 128
|
|
||||||
excessBuf = new uint8_t[excessLen];
|
|
||||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, 128);
|
|
||||||
excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512 - 128
|
|
||||||
excessBuf = new uint8_t[excessLen];
|
|
||||||
memcpy (excessBuf, signingKey + 128, excessLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
|
|
||||||
i2p::context.GetRandomNumberGenerator ().GenerateBlock (m_StandardIdentity.signingKey, padding);
|
|
||||||
memcpy (m_StandardIdentity.signingKey + padding, signingKey, i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
LogPrint ("Signing key type ", (int)type, " is not supported");
|
|
||||||
}
|
|
||||||
m_ExtendedLen = 4 + excessLen; // 4 bytes extra + excess length
|
|
||||||
// fill certificate
|
|
||||||
m_StandardIdentity.certificate.type = CERTIFICATE_TYPE_KEY;
|
|
||||||
m_StandardIdentity.certificate.length = htobe16 (m_ExtendedLen);
|
|
||||||
// fill extended buffer
|
|
||||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
|
||||||
htobe16buf (m_ExtendedBuffer, type);
|
|
||||||
htobe16buf (m_ExtendedBuffer + 2, CRYPTO_KEY_TYPE_ELGAMAL);
|
|
||||||
if (excessLen && excessBuf)
|
|
||||||
{
|
|
||||||
memcpy (m_ExtendedBuffer + 4, excessBuf, excessLen);
|
|
||||||
delete[] excessBuf;
|
|
||||||
}
|
|
||||||
// calculate ident hash
|
|
||||||
uint8_t * buf = new uint8_t[GetFullLen ()];
|
|
||||||
ToBuffer (buf, GetFullLen ());
|
|
||||||
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
|
|
||||||
delete[] buf;
|
|
||||||
}
|
|
||||||
else // DSA-SHA1
|
|
||||||
{
|
|
||||||
memcpy (m_StandardIdentity.signingKey, signingKey, sizeof (m_StandardIdentity.signingKey));
|
|
||||||
memset (&m_StandardIdentity.certificate, 0, sizeof (m_StandardIdentity.certificate));
|
|
||||||
m_IdentHash = m_StandardIdentity.Hash ();
|
|
||||||
m_ExtendedLen = 0;
|
|
||||||
m_ExtendedBuffer = nullptr;
|
|
||||||
}
|
|
||||||
CreateVerifier ();
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
|
|
||||||
m_Verifier (nullptr), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
|
||||||
{
|
|
||||||
FromBuffer (buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::IdentityEx (const IdentityEx& other):
|
|
||||||
m_Verifier (nullptr), m_ExtendedBuffer (nullptr)
|
|
||||||
{
|
|
||||||
*this = other;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx::~IdentityEx ()
|
|
||||||
{
|
|
||||||
delete m_Verifier;
|
|
||||||
delete[] m_ExtendedBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx& IdentityEx::operator=(const IdentityEx& other)
|
|
||||||
{
|
|
||||||
memcpy (&m_StandardIdentity, &other.m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
|
||||||
m_IdentHash = other.m_IdentHash;
|
|
||||||
|
|
||||||
delete[] m_ExtendedBuffer;
|
|
||||||
m_ExtendedLen = other.m_ExtendedLen;
|
|
||||||
if (m_ExtendedLen > 0)
|
|
||||||
{
|
|
||||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
|
||||||
memcpy (m_ExtendedBuffer, other.m_ExtendedBuffer, m_ExtendedLen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_ExtendedBuffer = nullptr;
|
|
||||||
|
|
||||||
delete m_Verifier;
|
|
||||||
m_Verifier = nullptr;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
IdentityEx& IdentityEx::operator=(const Identity& standard)
|
|
||||||
{
|
|
||||||
m_StandardIdentity = standard;
|
|
||||||
m_IdentHash = m_StandardIdentity.Hash ();
|
|
||||||
|
|
||||||
delete[] m_ExtendedBuffer;
|
|
||||||
m_ExtendedBuffer = nullptr;
|
|
||||||
m_ExtendedLen = 0;
|
|
||||||
|
|
||||||
delete m_Verifier;
|
|
||||||
m_Verifier = nullptr;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::FromBuffer (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (len < DEFAULT_IDENTITY_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Identity buffer length ", len, " is too small");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
|
|
||||||
|
|
||||||
delete[] m_ExtendedBuffer;
|
|
||||||
if (m_StandardIdentity.certificate.length)
|
|
||||||
{
|
|
||||||
m_ExtendedLen = be16toh (m_StandardIdentity.certificate.length);
|
|
||||||
if (m_ExtendedLen + DEFAULT_IDENTITY_SIZE <= len)
|
|
||||||
{
|
|
||||||
m_ExtendedBuffer = new uint8_t[m_ExtendedLen];
|
|
||||||
memcpy (m_ExtendedBuffer, buf + DEFAULT_IDENTITY_SIZE, m_ExtendedLen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Certificate length ", m_ExtendedLen, " exceeds buffer length ", len - DEFAULT_IDENTITY_SIZE);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_ExtendedLen = 0;
|
|
||||||
m_ExtendedBuffer = nullptr;
|
|
||||||
}
|
|
||||||
CryptoPP::SHA256().CalculateDigest(m_IdentHash, buf, GetFullLen ());
|
|
||||||
|
|
||||||
delete m_Verifier;
|
|
||||||
m_Verifier = nullptr;
|
|
||||||
|
|
||||||
return GetFullLen ();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::ToBuffer (uint8_t * buf, size_t len) const
|
|
||||||
{
|
|
||||||
memcpy (buf, &m_StandardIdentity, DEFAULT_IDENTITY_SIZE);
|
|
||||||
if (m_ExtendedLen > 0 && m_ExtendedBuffer)
|
|
||||||
memcpy (buf + DEFAULT_IDENTITY_SIZE, m_ExtendedBuffer, m_ExtendedLen);
|
|
||||||
return GetFullLen ();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::FromBase64(const std::string& s)
|
|
||||||
{
|
|
||||||
uint8_t buf[1024];
|
|
||||||
auto len = Base64ToByteStream (s.c_str(), s.length(), buf, 1024);
|
|
||||||
return FromBuffer (buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string IdentityEx::ToBase64 () const
|
|
||||||
{
|
|
||||||
uint8_t buf[1024];
|
|
||||||
char str[1536];
|
|
||||||
size_t l = ToBuffer (buf, 1024);
|
|
||||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, 1536);
|
|
||||||
str[l1] = 0;
|
|
||||||
return std::string (str);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::GetSigningPublicKeyLen () const
|
|
||||||
{
|
|
||||||
if (!m_Verifier) CreateVerifier ();
|
|
||||||
if (m_Verifier)
|
|
||||||
return m_Verifier->GetPublicKeyLen ();
|
|
||||||
return 128;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::GetSigningPrivateKeyLen () const
|
|
||||||
{
|
|
||||||
if (!m_Verifier) CreateVerifier ();
|
|
||||||
if (m_Verifier)
|
|
||||||
return m_Verifier->GetPrivateKeyLen ();
|
|
||||||
return GetSignatureLen ()/2;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t IdentityEx::GetSignatureLen () const
|
|
||||||
{
|
|
||||||
if (!m_Verifier) CreateVerifier ();
|
|
||||||
if (m_Verifier)
|
|
||||||
return m_Verifier->GetSignatureLen ();
|
|
||||||
return 40;
|
|
||||||
}
|
|
||||||
bool IdentityEx::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
if (!m_Verifier) CreateVerifier ();
|
|
||||||
if (m_Verifier)
|
|
||||||
return m_Verifier->Verify (buf, len, signature);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SigningKeyType IdentityEx::GetSigningKeyType () const
|
|
||||||
{
|
|
||||||
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
|
||||||
return bufbe16toh (m_ExtendedBuffer); // signing key
|
|
||||||
return SIGNING_KEY_TYPE_DSA_SHA1;
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoKeyType IdentityEx::GetCryptoKeyType () const
|
|
||||||
{
|
|
||||||
if (m_StandardIdentity.certificate.type == CERTIFICATE_TYPE_KEY && m_ExtendedBuffer)
|
|
||||||
return bufbe16toh (m_ExtendedBuffer + 2); // crypto key
|
|
||||||
return CRYPTO_KEY_TYPE_ELGAMAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentityEx::CreateVerifier () const
|
|
||||||
{
|
|
||||||
auto keyType = GetSigningKeyType ();
|
|
||||||
switch (keyType)
|
|
||||||
{
|
|
||||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
|
||||||
m_Verifier = new i2p::crypto::DSAVerifier (m_StandardIdentity.signingKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::ECDSAP256_KEY_LENGTH; // 64 = 128 - 64
|
|
||||||
m_Verifier = new i2p::crypto::ECDSAP256Verifier (m_StandardIdentity.signingKey + padding);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::ECDSAP384_KEY_LENGTH; // 32 = 128 - 96
|
|
||||||
m_Verifier = new i2p::crypto::ECDSAP384Verifier (m_StandardIdentity.signingKey + padding);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
|
||||||
{
|
|
||||||
uint8_t signingKey[i2p::crypto::ECDSAP521_KEY_LENGTH];
|
|
||||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
|
||||||
size_t excessLen = i2p::crypto::ECDSAP521_KEY_LENGTH - 128; // 4 = 132- 128
|
|
||||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
|
||||||
m_Verifier = new i2p::crypto::ECDSAP521Verifier (signingKey);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
|
||||||
{
|
|
||||||
uint8_t signingKey[i2p::crypto::RSASHA2562048_KEY_LENGTH];
|
|
||||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
|
||||||
size_t excessLen = i2p::crypto::RSASHA2562048_KEY_LENGTH - 128; // 128 = 256- 128
|
|
||||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
|
||||||
m_Verifier = new i2p::crypto:: RSASHA2562048Verifier (signingKey);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
|
||||||
{
|
|
||||||
uint8_t signingKey[i2p::crypto::RSASHA3843072_KEY_LENGTH];
|
|
||||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
|
||||||
size_t excessLen = i2p::crypto::RSASHA3843072_KEY_LENGTH - 128; // 256 = 384- 128
|
|
||||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
|
||||||
m_Verifier = new i2p::crypto:: RSASHA3843072Verifier (signingKey);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
|
||||||
{
|
|
||||||
uint8_t signingKey[i2p::crypto::RSASHA5124096_KEY_LENGTH];
|
|
||||||
memcpy (signingKey, m_StandardIdentity.signingKey, 128);
|
|
||||||
size_t excessLen = i2p::crypto::RSASHA5124096_KEY_LENGTH - 128; // 384 = 512- 128
|
|
||||||
memcpy (signingKey + 128, m_ExtendedBuffer + 4, excessLen); // right after signing and crypto key types
|
|
||||||
m_Verifier = new i2p::crypto:: RSASHA5124096Verifier (signingKey);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
|
||||||
{
|
|
||||||
size_t padding = 128 - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH; // 96 = 128 - 32
|
|
||||||
m_Verifier = new i2p::crypto::EDDSA25519Verifier (m_StandardIdentity.signingKey + padding);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
LogPrint ("Signing key type ", (int)keyType, " is not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IdentityEx::DropVerifier ()
|
|
||||||
{
|
|
||||||
auto verifier = m_Verifier;
|
|
||||||
m_Verifier = nullptr; // TODO: make this atomic
|
|
||||||
delete verifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
|
|
||||||
{
|
|
||||||
m_Public = Identity (keys);
|
|
||||||
memcpy (m_PrivateKey, keys.privateKey, 256); // 256
|
|
||||||
memcpy (m_SigningPrivateKey, keys.signingPrivateKey, m_Public.GetSigningPrivateKeyLen ());
|
|
||||||
delete m_Signer;
|
|
||||||
m_Signer = nullptr;
|
|
||||||
CreateSigner ();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateKeys& PrivateKeys::operator=(const PrivateKeys& other)
|
|
||||||
{
|
|
||||||
m_Public = other.m_Public;
|
|
||||||
memcpy (m_PrivateKey, other.m_PrivateKey, 256); // 256
|
|
||||||
memcpy (m_SigningPrivateKey, other.m_SigningPrivateKey, m_Public.GetSigningPrivateKeyLen ());
|
|
||||||
delete m_Signer;
|
|
||||||
m_Signer = nullptr;
|
|
||||||
CreateSigner ();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PrivateKeys::FromBuffer (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
size_t ret = m_Public.FromBuffer (buf, len);
|
|
||||||
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
|
|
||||||
ret += 256;
|
|
||||||
size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen ();
|
|
||||||
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
|
|
||||||
ret += signingPrivateKeySize;
|
|
||||||
delete m_Signer;
|
|
||||||
m_Signer = nullptr;
|
|
||||||
CreateSigner ();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PrivateKeys::ToBuffer (uint8_t * buf, size_t len) const
|
|
||||||
{
|
|
||||||
size_t ret = m_Public.ToBuffer (buf, len);
|
|
||||||
memcpy (buf + ret, m_PrivateKey, 256); // private key always 256
|
|
||||||
ret += 256;
|
|
||||||
size_t signingPrivateKeySize = m_Public.GetSigningPrivateKeyLen ();
|
|
||||||
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
|
|
||||||
ret += signingPrivateKeySize;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t PrivateKeys::FromBase64(const std::string& s)
|
|
||||||
{
|
|
||||||
uint8_t * buf = new uint8_t[s.length ()];
|
|
||||||
size_t l = i2p::data::Base64ToByteStream (s.c_str (), s.length (), buf, s.length ());
|
|
||||||
size_t ret = FromBuffer (buf, l);
|
|
||||||
delete[] buf;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string PrivateKeys::ToBase64 () const
|
|
||||||
{
|
|
||||||
uint8_t * buf = new uint8_t[GetFullLen ()];
|
|
||||||
char * str = new char[GetFullLen ()*2];
|
|
||||||
size_t l = ToBuffer (buf, GetFullLen ());
|
|
||||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, str, GetFullLen ()*2);
|
|
||||||
str[l1] = 0;
|
|
||||||
delete[] buf;
|
|
||||||
std::string ret(str);
|
|
||||||
delete[] str;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
|
||||||
{
|
|
||||||
if (m_Signer)
|
|
||||||
m_Signer->Sign (i2p::context.GetRandomNumberGenerator (), buf, len, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PrivateKeys::CreateSigner ()
|
|
||||||
{
|
|
||||||
switch (m_Public.GetSigningKeyType ())
|
|
||||||
{
|
|
||||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
|
||||||
m_Signer = new i2p::crypto::DSASigner (m_SigningPrivateKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
|
||||||
m_Signer = new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
|
||||||
m_Signer = new i2p::crypto::ECDSAP384Signer (m_SigningPrivateKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
|
||||||
m_Signer = new i2p::crypto::ECDSAP521Signer (m_SigningPrivateKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
|
||||||
m_Signer = new i2p::crypto::RSASHA2562048Signer (m_SigningPrivateKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
|
||||||
m_Signer = new i2p::crypto::RSASHA3843072Signer (m_SigningPrivateKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
|
||||||
m_Signer = new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
|
||||||
m_Signer = new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint ("Signing key type ", (int)m_Public.GetSigningKeyType (), " is not supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PrivateKeys PrivateKeys::CreateRandomKeys (SigningKeyType type)
|
|
||||||
{
|
|
||||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
|
||||||
{
|
|
||||||
PrivateKeys keys;
|
|
||||||
auto& rnd = i2p::context.GetRandomNumberGenerator ();
|
|
||||||
// signature
|
|
||||||
uint8_t signingPublicKey[512]; // signing public key is 512 bytes max
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
|
||||||
i2p::crypto::CreateECDSAP256RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA384_P384:
|
|
||||||
i2p::crypto::CreateECDSAP384RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_ECDSA_SHA512_P521:
|
|
||||||
i2p::crypto::CreateECDSAP521RandomKeys (rnd, keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA256_2048:
|
|
||||||
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA2562048_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA384_3072:
|
|
||||||
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA3843072_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
case SIGNING_KEY_TYPE_RSA_SHA512_4096:
|
|
||||||
i2p::crypto::CreateRSARandomKeys (rnd, i2p::crypto::RSASHA5124096_KEY_LENGTH, keys.m_SigningPrivateKey, signingPublicKey);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint ("Signing key type ", (int)type, " is not supported. Create DSA-SHA1");
|
|
||||||
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
|
|
||||||
}
|
|
||||||
// encryption
|
|
||||||
uint8_t publicKey[256];
|
|
||||||
CryptoPP::DH dh (i2p::crypto::elgp, i2p::crypto::elgg);
|
|
||||||
dh.GenerateKeyPair(rnd, keys.m_PrivateKey, publicKey);
|
|
||||||
// identity
|
|
||||||
keys.m_Public = IdentityEx (publicKey, signingPublicKey, type);
|
|
||||||
|
|
||||||
keys.CreateSigner ();
|
|
||||||
return keys;
|
|
||||||
}
|
|
||||||
return PrivateKeys (i2p::data::CreateRandomKeys ()); // DSA-SHA1
|
|
||||||
}
|
|
||||||
|
|
||||||
Keys CreateRandomKeys ()
|
|
||||||
{
|
|
||||||
Keys keys;
|
|
||||||
auto& rnd = i2p::context.GetRandomNumberGenerator ();
|
|
||||||
// encryption
|
|
||||||
i2p::crypto::GenerateElGamalKeyPair(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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
275
Identity.h
275
Identity.h
@@ -1,275 +0,0 @@
|
|||||||
#ifndef IDENTITY_H__
|
|
||||||
#define IDENTITY_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#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; };
|
|
||||||
|
|
||||||
bool IsZero () const
|
|
||||||
{
|
|
||||||
for (int i = 0; i < sz/8; i++)
|
|
||||||
if (ll[i]) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FromBase32 (const std::string& s)
|
|
||||||
{
|
|
||||||
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FromBase64 (const std::string& s)
|
|
||||||
{
|
|
||||||
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA384_P384 = 2;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_ECDSA_SHA512_P521 = 3;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA256_2048 = 4;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA384_3072 = 5;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_RSA_SHA512_4096 = 6;
|
|
||||||
const uint16_t SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519 = 7;
|
|
||||||
typedef uint16_t SigningKeyType;
|
|
||||||
typedef uint16_t CryptoKeyType;
|
|
||||||
|
|
||||||
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 FromBuffer (const uint8_t * buf, size_t len);
|
|
||||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
|
||||||
size_t FromBase64(const std::string& s);
|
|
||||||
std::string ToBase64 () 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 GetSigningPrivateKeyLen () const;
|
|
||||||
size_t GetSignatureLen () const;
|
|
||||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
|
|
||||||
SigningKeyType GetSigningKeyType () const;
|
|
||||||
CryptoKeyType GetCryptoKeyType () const;
|
|
||||||
void DropVerifier (); // to save memory
|
|
||||||
|
|
||||||
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.GetSigningPrivateKeyLen (); };
|
|
||||||
size_t FromBuffer (const uint8_t * buf, size_t len);
|
|
||||||
size_t ToBuffer (uint8_t * buf, size_t len) const;
|
|
||||||
|
|
||||||
size_t FromBase64(const std::string& s);
|
|
||||||
std::string ToBase64 () 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[1024]; // assume private key doesn't exceed 1024 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 () {};
|
|
||||||
virtual ~RoutingDestination () {};
|
|
||||||
|
|
||||||
virtual const IdentHash& GetIdentHash () const = 0;
|
|
||||||
virtual const uint8_t * GetEncryptionPublicKey () const = 0;
|
|
||||||
virtual bool IsDestination () const = 0; // for garlic
|
|
||||||
|
|
||||||
std::unique_ptr<const i2p::crypto::ElGamalEncryption>& GetElGamalEncryption () const
|
|
||||||
{
|
|
||||||
if (!m_ElGamalEncryption)
|
|
||||||
m_ElGamalEncryption.reset (new i2p::crypto::ElGamalEncryption (GetEncryptionPublicKey ()));
|
|
||||||
return m_ElGamalEncryption;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
mutable std::unique_ptr<const 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
|
|
||||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2013-2015, The PurpleI2P Project
|
Copyright (c) 2013-2023, The PurpleI2P Project
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
|
|||||||
157
LeaseSet.cpp
157
LeaseSet.cpp
@@ -1,157 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include <cryptopp/dsa.h>
|
|
||||||
#include <cryptopp/osrng.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, size_t len):
|
|
||||||
m_IsValid (true)
|
|
||||||
{
|
|
||||||
m_Buffer = new uint8_t[len];
|
|
||||||
memcpy (m_Buffer, buf, len);
|
|
||||||
m_BufferLen = len;
|
|
||||||
ReadFromBuffer ();
|
|
||||||
}
|
|
||||||
|
|
||||||
LeaseSet::LeaseSet (const i2p::tunnel::TunnelPool& pool):
|
|
||||||
m_IsValid (true)
|
|
||||||
{
|
|
||||||
// header
|
|
||||||
const i2p::data::LocalDestination * localDestination = pool.GetLocalDestination ();
|
|
||||||
if (!localDestination)
|
|
||||||
{
|
|
||||||
m_Buffer = nullptr;
|
|
||||||
m_BufferLen = 0;
|
|
||||||
m_IsValid = false;
|
|
||||||
LogPrint (eLogError, "Destination for local LeaseSet doesn't exist");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_Buffer = new uint8_t[MAX_LS_BUFFER_SIZE];
|
|
||||||
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
|
|
||||||
CryptoPP::AutoSeededRandomPool rnd;
|
|
||||||
for (auto it: tunnels)
|
|
||||||
{
|
|
||||||
memcpy (m_Buffer + m_BufferLen, it->GetNextIdentHash (), 32);
|
|
||||||
m_BufferLen += 32; // gateway id
|
|
||||||
htobe32buf (m_Buffer + m_BufferLen, it->GetNextTunnelID ());
|
|
||||||
m_BufferLen += 4; // tunnel id
|
|
||||||
uint64_t ts = it->GetCreationTime () + i2p::tunnel::TUNNEL_EXPIRATION_TIMEOUT - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD; // 1 minute before expiration
|
|
||||||
ts *= 1000; // in milliseconds
|
|
||||||
ts += rnd.GenerateWord32 (0, 5); // + random milliseconds
|
|
||||||
htobe64buf (m_Buffer + m_BufferLen, ts);
|
|
||||||
m_BufferLen += 8; // end date
|
|
||||||
}
|
|
||||||
// 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, size_t len)
|
|
||||||
{
|
|
||||||
m_Leases.clear ();
|
|
||||||
if (len > m_BufferLen)
|
|
||||||
{
|
|
||||||
auto oldBuffer = m_Buffer;
|
|
||||||
m_Buffer = new uint8_t[len];
|
|
||||||
delete[] oldBuffer;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
if (!num) m_IsValid = false;
|
|
||||||
|
|
||||||
// process leases
|
|
||||||
const uint8_t * leases = m_Buffer + size;
|
|
||||||
for (int i = 0; i < num; i++)
|
|
||||||
{
|
|
||||||
Lease lease;
|
|
||||||
lease.tunnelGateway = leases;
|
|
||||||
leases += 32; // gateway
|
|
||||||
lease.tunnelID = bufbe32toh (leases);
|
|
||||||
leases += 4; // tunnel ID
|
|
||||||
lease.endDate = bufbe64toh (leases);
|
|
||||||
leases += 8; // end date
|
|
||||||
m_Leases.push_back (lease);
|
|
||||||
|
|
||||||
// check if lease's gateway is in our netDb
|
|
||||||
if (!netdb.FindRouter (lease.tunnelGateway))
|
|
||||||
{
|
|
||||||
// if not found request it
|
|
||||||
LogPrint (eLogInfo, "Lease's tunnel gateway not found. Requested");
|
|
||||||
netdb.RequestDestination (lease.tunnelGateway);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// verify
|
|
||||||
if (!m_Identity.Verify (m_Buffer, leases - m_Buffer, leases))
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "LeaseSet verification failed");
|
|
||||||
m_IsValid = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<Lease> LeaseSet::GetNonExpiredLeases (bool withThreshold) const
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
std::vector<Lease> leases;
|
|
||||||
for (auto& it: m_Leases)
|
|
||||||
{
|
|
||||||
auto endDate = it.endDate;
|
|
||||||
if (!withThreshold)
|
|
||||||
endDate -= i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000;
|
|
||||||
if (ts < 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
74
LeaseSet.h
74
LeaseSet.h
@@ -1,74 +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
|
|
||||||
{
|
|
||||||
struct Lease
|
|
||||||
{
|
|
||||||
IdentHash tunnelGateway;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const int MAX_LS_BUFFER_SIZE = 3072;
|
|
||||||
class LeaseSet: public RoutingDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
LeaseSet (const uint8_t * buf, size_t len);
|
|
||||||
LeaseSet (const i2p::tunnel::TunnelPool& pool);
|
|
||||||
~LeaseSet () { delete[] m_Buffer; };
|
|
||||||
void Update (const uint8_t * buf, size_t len);
|
|
||||||
const IdentityEx& GetIdentity () const { return m_Identity; };
|
|
||||||
|
|
||||||
const uint8_t * GetBuffer () const { return m_Buffer; };
|
|
||||||
size_t GetBufferLen () const { return m_BufferLen; };
|
|
||||||
bool IsValid () const { return m_IsValid; };
|
|
||||||
|
|
||||||
// implements RoutingDestination
|
|
||||||
const IdentHash& GetIdentHash () const { return m_Identity.GetIdentHash (); };
|
|
||||||
const std::vector<Lease>& GetLeases () const { return m_Leases; };
|
|
||||||
const std::vector<Lease> GetNonExpiredLeases (bool withThreshold = true) const;
|
|
||||||
bool HasExpiredLeases () const;
|
|
||||||
bool HasNonExpiredLeases () const;
|
|
||||||
const uint8_t * GetEncryptionPublicKey () const { return m_EncryptionKey; };
|
|
||||||
bool IsDestination () const { return true; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void ReadFromBuffer ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_IsValid;
|
|
||||||
std::vector<Lease> m_Leases;
|
|
||||||
IdentityEx m_Identity;
|
|
||||||
uint8_t m_EncryptionKey[256];
|
|
||||||
uint8_t * m_Buffer;
|
|
||||||
size_t m_BufferLen;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,242 +0,0 @@
|
|||||||
// LittleBigEndian.h fixed for 64-bits added union
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef LITTLEBIGENDIAN_H
|
|
||||||
#define LITTLEBIGENDIAN_H
|
|
||||||
|
|
||||||
// Determine Little-Endian or Big-Endian
|
|
||||||
|
|
||||||
#define CURRENT_BYTE_ORDER (*(int *)"\x01\x02\x03\x04")
|
|
||||||
#define LITTLE_ENDIAN_BYTE_ORDER 0x04030201
|
|
||||||
#define BIG_ENDIAN_BYTE_ORDER 0x01020304
|
|
||||||
#define PDP_ENDIAN_BYTE_ORDER 0x02010403
|
|
||||||
|
|
||||||
#define IS_LITTLE_ENDIAN (CURRENT_BYTE_ORDER == LITTLE_ENDIAN_BYTE_ORDER)
|
|
||||||
#define IS_BIG_ENDIAN (CURRENT_BYTE_ORDER == BIG_ENDIAN_BYTE_ORDER)
|
|
||||||
#define IS_PDP_ENDIAN (CURRENT_BYTE_ORDER == PDP_ENDIAN_BYTE_ORDER)
|
|
||||||
|
|
||||||
// Forward declaration
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct LittleEndian;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct BigEndian;
|
|
||||||
|
|
||||||
// Little-Endian template
|
|
||||||
|
|
||||||
#pragma pack(push,1)
|
|
||||||
template<typename T>
|
|
||||||
struct LittleEndian
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
unsigned char bytes[sizeof(T)];
|
|
||||||
T raw_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
LittleEndian(T t = T())
|
|
||||||
{
|
|
||||||
operator =(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian(const LittleEndian<T> & t)
|
|
||||||
{
|
|
||||||
raw_value = t.raw_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian(const BigEndian<T> & t)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
bytes[i] = t.bytes[sizeof(T)-1-i];
|
|
||||||
}
|
|
||||||
|
|
||||||
operator const T() const
|
|
||||||
{
|
|
||||||
T t = T();
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
t |= T(bytes[i]) << (i << 3);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator = (const T t)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
bytes[sizeof(T)-1 - i] = static_cast<unsigned char>(t >> (i << 3));
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// operators
|
|
||||||
|
|
||||||
const T operator += (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this + t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator -= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this - t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator *= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this * t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator /= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this / t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator %= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this % t);
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian<T> operator ++ (int)
|
|
||||||
{
|
|
||||||
LittleEndian<T> tmp(*this);
|
|
||||||
operator ++ ();
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian<T> & operator ++ ()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
{
|
|
||||||
++bytes[i];
|
|
||||||
if (bytes[i] != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian<T> operator -- (int)
|
|
||||||
{
|
|
||||||
LittleEndian<T> tmp(*this);
|
|
||||||
operator -- ();
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
LittleEndian<T> & operator -- ()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
{
|
|
||||||
--bytes[i];
|
|
||||||
if (bytes[i] != (T)(-1))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
// Big-Endian template
|
|
||||||
|
|
||||||
#pragma pack(push,1)
|
|
||||||
template<typename T>
|
|
||||||
struct BigEndian
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
unsigned char bytes[sizeof(T)];
|
|
||||||
T raw_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
BigEndian(T t = T())
|
|
||||||
{
|
|
||||||
operator =(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian(const BigEndian<T> & t)
|
|
||||||
{
|
|
||||||
raw_value = t.raw_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian(const LittleEndian<T> & t)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
bytes[i] = t.bytes[sizeof(T)-1-i];
|
|
||||||
}
|
|
||||||
|
|
||||||
operator const T() const
|
|
||||||
{
|
|
||||||
T t = T();
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
t |= T(bytes[sizeof(T) - 1 - i]) << (i << 3);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator = (const T t)
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
bytes[sizeof(T) - 1 - i] = t >> (i << 3);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// operators
|
|
||||||
|
|
||||||
const T operator += (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this + t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator -= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this - t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator *= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this * t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator /= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this / t);
|
|
||||||
}
|
|
||||||
|
|
||||||
const T operator %= (const T t)
|
|
||||||
{
|
|
||||||
return (*this = *this % t);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian<T> operator ++ (int)
|
|
||||||
{
|
|
||||||
BigEndian<T> tmp(*this);
|
|
||||||
operator ++ ();
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian<T> & operator ++ ()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
{
|
|
||||||
++bytes[sizeof(T) - 1 - i];
|
|
||||||
if (bytes[sizeof(T) - 1 - i] != 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian<T> operator -- (int)
|
|
||||||
{
|
|
||||||
BigEndian<T> tmp(*this);
|
|
||||||
operator -- ();
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
BigEndian<T> & operator -- ()
|
|
||||||
{
|
|
||||||
for (unsigned i = 0; i < sizeof(T); i++)
|
|
||||||
{
|
|
||||||
--bytes[sizeof(T) - 1 - i];
|
|
||||||
if (bytes[sizeof(T) - 1 - i] != (T)(-1))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (*this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#pragma pack(pop)
|
|
||||||
|
|
||||||
#endif // LITTLEBIGENDIAN_H
|
|
||||||
62
Log.cpp
62
Log.cpp
@@ -1,62 +0,0 @@
|
|||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
Log * g_Log = nullptr;
|
|
||||||
|
|
||||||
static const char * g_LogLevelStr[eNumLogLevels] =
|
|
||||||
{
|
|
||||||
"error", // eLogError
|
|
||||||
"warn", // eLogWarning
|
|
||||||
"info", // eLogInfo
|
|
||||||
"debug" // eLogDebug
|
|
||||||
};
|
|
||||||
|
|
||||||
void LogMsg::Process()
|
|
||||||
{
|
|
||||||
auto& output = (log && log->GetLogStream ()) ? *log->GetLogStream () : std::cerr;
|
|
||||||
if (log)
|
|
||||||
output << log->GetTimestamp ();
|
|
||||||
else
|
|
||||||
output << boost::posix_time::second_clock::local_time().time_of_day ();
|
|
||||||
output << "/" << g_LogLevelStr[level] << " - ";
|
|
||||||
output << s.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& Log::GetTimestamp ()
|
|
||||||
{
|
|
||||||
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6)
|
|
||||||
auto ts = std::chrono::monotonic_clock::now ();
|
|
||||||
#else
|
|
||||||
auto ts = std::chrono::steady_clock::now ();
|
|
||||||
#endif
|
|
||||||
if (ts > m_LastTimestampUpdate + std::chrono::milliseconds (500)) // 0.5 second
|
|
||||||
{
|
|
||||||
m_LastTimestampUpdate = ts;
|
|
||||||
m_Timestamp = boost::posix_time::to_simple_string (boost::posix_time::second_clock::local_time().time_of_day ());
|
|
||||||
}
|
|
||||||
return m_Timestamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Log::Flush ()
|
|
||||||
{
|
|
||||||
if (m_LogStream)
|
|
||||||
m_LogStream->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Log::SetLogFile (const std::string& fullFilePath)
|
|
||||||
{
|
|
||||||
auto logFile = new std::ofstream (fullFilePath, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc);
|
|
||||||
if (logFile->is_open ())
|
|
||||||
{
|
|
||||||
SetLogStream (logFile);
|
|
||||||
LogPrint("Logging to file ", fullFilePath, " enabled.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
delete logFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Log::SetLogStream (std::ostream * logStream)
|
|
||||||
{
|
|
||||||
if (m_LogStream) delete m_LogStream;
|
|
||||||
m_LogStream = logStream;
|
|
||||||
}
|
|
||||||
129
Log.h
129
Log.h
@@ -1,129 +0,0 @@
|
|||||||
#ifndef LOG_H__
|
|
||||||
#define LOG_H__
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <functional>
|
|
||||||
#include <chrono>
|
|
||||||
#include "Queue.h"
|
|
||||||
|
|
||||||
enum LogLevel
|
|
||||||
{
|
|
||||||
eLogError = 0,
|
|
||||||
eLogWarning,
|
|
||||||
eLogInfo,
|
|
||||||
eLogDebug,
|
|
||||||
eNumLogLevels
|
|
||||||
};
|
|
||||||
|
|
||||||
class Log;
|
|
||||||
struct LogMsg
|
|
||||||
{
|
|
||||||
std::stringstream s;
|
|
||||||
Log * log;
|
|
||||||
LogLevel level;
|
|
||||||
|
|
||||||
LogMsg (Log * l = nullptr, LogLevel lv = eLogInfo): log (l), level (lv) {};
|
|
||||||
|
|
||||||
void Process();
|
|
||||||
};
|
|
||||||
|
|
||||||
class Log: public i2p::util::MsgQueue<LogMsg>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
Log (): m_LogStream (nullptr) { SetOnEmpty (std::bind (&Log::Flush, this)); };
|
|
||||||
~Log () { delete m_LogStream; };
|
|
||||||
|
|
||||||
void SetLogFile (const std::string& fullFilePath);
|
|
||||||
void SetLogStream (std::ostream * logStream);
|
|
||||||
std::ostream * GetLogStream () const { return m_LogStream; };
|
|
||||||
const std::string& GetTimestamp ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Flush ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::ostream * m_LogStream;
|
|
||||||
std::string m_Timestamp;
|
|
||||||
#if (__GNUC__ == 4) && (__GNUC_MINOR__ <= 6) // gcc 4.6
|
|
||||||
std::chrono::monotonic_clock::time_point m_LastTimestampUpdate;
|
|
||||||
#else
|
|
||||||
std::chrono::steady_clock::time_point m_LastTimestampUpdate;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Log * g_Log;
|
|
||||||
|
|
||||||
inline void StartLog (const std::string& fullFilePath)
|
|
||||||
{
|
|
||||||
if (!g_Log)
|
|
||||||
{
|
|
||||||
auto log = new Log ();
|
|
||||||
if (fullFilePath.length () > 0)
|
|
||||||
log->SetLogFile (fullFilePath);
|
|
||||||
g_Log = log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void StartLog (std::ostream * s)
|
|
||||||
{
|
|
||||||
if (!g_Log)
|
|
||||||
{
|
|
||||||
auto log = new Log ();
|
|
||||||
if (s)
|
|
||||||
log->SetLogStream (s);
|
|
||||||
g_Log = log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void StopLog ()
|
|
||||||
{
|
|
||||||
if (g_Log)
|
|
||||||
{
|
|
||||||
auto log = g_Log;
|
|
||||||
g_Log = nullptr;
|
|
||||||
log->Stop ();
|
|
||||||
delete log;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = new LogMsg (g_Log, 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
|
|
||||||
200
Makefile
200
Makefile
@@ -1,33 +1,110 @@
|
|||||||
UNAME := $(shell uname -s)
|
.DEFAULT_GOAL := all
|
||||||
SHLIB := libi2pd.so
|
|
||||||
I2PD := i2p
|
|
||||||
GREP := fgrep
|
|
||||||
DEPS := obj/make.dep
|
|
||||||
|
|
||||||
include filelist.mk
|
SYS := $(shell $(CXX) -dumpmachine)
|
||||||
|
|
||||||
USE_AESNI := yes
|
ifneq (, $(findstring darwin, $(SYS)))
|
||||||
USE_STATIC := no
|
SHARED_SUFFIX = dylib
|
||||||
|
else ifneq (, $(findstring mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||||
ifeq ($(UNAME),Darwin)
|
SHARED_SUFFIX = dll
|
||||||
DAEMON_SRC += DaemonLinux.cpp
|
else
|
||||||
include Makefile.osx
|
SHARED_SUFFIX = so
|
||||||
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
|
|
||||||
DAEMON_SRC += DaemonLinux.cpp
|
|
||||||
include Makefile.bsd
|
|
||||||
else ifeq ($(UNAME),Linux)
|
|
||||||
DAEMON_SRC += DaemonLinux.cpp
|
|
||||||
include Makefile.linux
|
|
||||||
else # win32
|
|
||||||
DAEMON_SRC += DaemonWin32.cpp
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
all: mk_build_dir $(SHLIB) $(I2PD)
|
SHLIB := libi2pd.$(SHARED_SUFFIX)
|
||||||
|
ARLIB := libi2pd.a
|
||||||
|
SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX)
|
||||||
|
ARLIB_LANG := libi2pdlang.a
|
||||||
|
SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX)
|
||||||
|
ARLIB_CLIENT := libi2pdclient.a
|
||||||
|
SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX)
|
||||||
|
ARLIB_WRAP := libi2pdwrapper.a
|
||||||
|
I2PD := i2pd
|
||||||
|
|
||||||
mk_build_dir:
|
LIB_SRC_DIR := libi2pd
|
||||||
mkdir -p obj
|
LIB_CLIENT_SRC_DIR := libi2pd_client
|
||||||
|
WRAP_SRC_DIR := libi2pd_wrapper
|
||||||
|
LANG_SRC_DIR := i18n
|
||||||
|
DAEMON_SRC_DIR := daemon
|
||||||
|
|
||||||
api: $(SHLIB)
|
# import source files lists
|
||||||
|
include filelist.mk
|
||||||
|
|
||||||
|
USE_STATIC := $(or $(USE_STATIC),no)
|
||||||
|
USE_UPNP := $(or $(USE_UPNP),no)
|
||||||
|
DEBUG := $(or $(DEBUG),yes)
|
||||||
|
|
||||||
|
# for debugging purposes only, when commit hash needed in trunk builds in i2pd version string
|
||||||
|
USE_GIT_VERSION := $(or $(USE_GIT_VERSION),no)
|
||||||
|
|
||||||
|
# for MacOS only, waiting for "1", not "yes"
|
||||||
|
HOMEBREW := $(or $(HOMEBREW),0)
|
||||||
|
|
||||||
|
ifeq ($(DEBUG),yes)
|
||||||
|
CXX_DEBUG = -g
|
||||||
|
else
|
||||||
|
CXX_DEBUG = -Os
|
||||||
|
LD_DEBUG = -s
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifneq (, $(DESTDIR))
|
||||||
|
PREFIX = $(DESTDIR)
|
||||||
|
endif
|
||||||
|
|
||||||
|
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 mingw, $(SYS))$(findstring windows-gnu, $(SYS))$(findstring cygwin, $(SYS)))
|
||||||
|
DAEMON_SRC += Win32/DaemonWin32.cpp Win32/Win32App.cpp Win32/Win32Service.cpp Win32/Win32NetState.cpp
|
||||||
|
include Makefile.mingw
|
||||||
|
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 haiku, $(SYS)))
|
||||||
|
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
|
||||||
|
include Makefile.haiku
|
||||||
|
else # not supported
|
||||||
|
$(error Not supported platform)
|
||||||
|
endif
|
||||||
|
|
||||||
|
INCFLAGS += -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR)
|
||||||
|
DEFINES += -DOPENSSL_SUPPRESS_DEPRECATED
|
||||||
|
NEEDED_CXXFLAGS += -MMD -MP
|
||||||
|
|
||||||
|
ifeq ($(USE_GIT_VERSION),yes)
|
||||||
|
GIT_VERSION := $(shell git describe --tags)
|
||||||
|
DEFINES += -DGITVER=$(GIT_VERSION)
|
||||||
|
endif
|
||||||
|
|
||||||
|
LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
||||||
|
LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC))
|
||||||
|
LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC))
|
||||||
|
DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
||||||
|
WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC))
|
||||||
|
DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d)
|
||||||
|
|
||||||
|
## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary
|
||||||
|
all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD)
|
||||||
|
|
||||||
|
mk_obj_dir:
|
||||||
|
@mkdir -p obj/$(LIB_SRC_DIR)
|
||||||
|
@mkdir -p obj/$(LIB_CLIENT_SRC_DIR)
|
||||||
|
@mkdir -p obj/$(LANG_SRC_DIR)
|
||||||
|
@mkdir -p obj/$(DAEMON_SRC_DIR)
|
||||||
|
@mkdir -p obj/$(WRAP_SRC_DIR)
|
||||||
|
@mkdir -p obj/Win32
|
||||||
|
|
||||||
|
api: $(SHLIB) $(ARLIB)
|
||||||
|
client: $(SHLIB_CLIENT) $(ARLIB_CLIENT)
|
||||||
|
lang: $(SHLIB_LANG) $(ARLIB_LANG)
|
||||||
|
api_client: api client lang
|
||||||
|
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||||
|
|
||||||
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
|
## 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.
|
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||||
@@ -36,38 +113,77 @@ api: $(SHLIB)
|
|||||||
## -std=c++11. If you want to remove this variable please do so in a way that allows setting
|
## -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.
|
## custom FLAGS to work at build-time.
|
||||||
|
|
||||||
deps:
|
obj/%.o: %.cpp | mk_obj_dir
|
||||||
@mkdir -p obj
|
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(DEFINES) $(INCFLAGS) -c -o $@ $<
|
||||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) -MM *.cpp > $(DEPS)
|
|
||||||
@sed -i -e '/\.o:/ s/^/obj\//' $(DEPS)
|
|
||||||
|
|
||||||
obj/%.o : %.cpp
|
|
||||||
@mkdir -p obj
|
|
||||||
$(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) $(CPU_FLAGS) -c -o $@ $<
|
|
||||||
|
|
||||||
# '-' is 'ignore if missing' on first run
|
# '-' is 'ignore if missing' on first run
|
||||||
-include $(DEPS)
|
-include $(DEPS)
|
||||||
|
|
||||||
$(I2PD): $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC))
|
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
|
||||||
$(CXX) -o $@ $^ $(LDLIBS) $(LDFLAGS)
|
$(CXX) $(DEFINES) $(LDFLAGS) -o $@ $^ $(LDLIBS)
|
||||||
|
|
||||||
$(SHLIB): $(patsubst %.cpp,obj/%.o,$(LIB_SRC))
|
$(SHLIB): $(LIB_OBJS)
|
||||||
ifneq ($(USE_STATIC),yes)
|
ifneq ($(USE_STATIC),yes)
|
||||||
$(CXX) $(LDFLAGS) $(LDLIBS) -shared -o $@ $^
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
clean:
|
$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) $(SHLIB) $(SHLIB_LANG)
|
||||||
rm -rf obj
|
ifneq ($(USE_STATIC),yes)
|
||||||
$(RM) $(I2PD) $(SHLIB)
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG)
|
||||||
|
endif
|
||||||
|
|
||||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 master)
|
$(SHLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||||
|
ifneq ($(USE_STATIC),yes)
|
||||||
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(SHLIB_LANG): $(LANG_OBJS)
|
||||||
|
ifneq ($(USE_STATIC),yes)
|
||||||
|
$(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
|
$(ARLIB): $(LIB_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
|
$(ARLIB_CLIENT): $(LIB_CLIENT_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
|
$(ARLIB_WRAP): $(WRAP_LIB_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
|
$(ARLIB_LANG): $(LANG_OBJS)
|
||||||
|
$(AR) -r $@ $^
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) -r obj
|
||||||
|
$(RM) -r docs/generated
|
||||||
|
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP)
|
||||||
|
|
||||||
|
strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
|
||||||
|
strip $^
|
||||||
|
|
||||||
|
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||||
|
BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
|
||||||
dist:
|
dist:
|
||||||
git archive --format=tar.gz -9 --worktree-attributes \
|
git archive --format=tar.gz -9 --worktree-attributes \
|
||||||
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
|
--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: all
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
.PHONY: deps
|
.PHONY: doxygen
|
||||||
.PHONY: dist
|
.PHONY: dist
|
||||||
|
.PHONY: last-dist
|
||||||
.PHONY: api
|
.PHONY: api
|
||||||
.PHONY: mk_build_dir
|
.PHONY: api_client
|
||||||
|
.PHONY: client
|
||||||
|
.PHONY: lang
|
||||||
|
.PHONY: mk_obj_dir
|
||||||
|
.PHONY: install
|
||||||
|
.PHONY: strip
|
||||||
|
|||||||
22
Makefile.bsd
22
Makefile.bsd
@@ -1,12 +1,22 @@
|
|||||||
CXX = g++
|
CXX = clang++
|
||||||
CXXFLAGS = -O2
|
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
|
||||||
|
DEFINES = -D_GLIBCXX_USE_NANOSLEEP=1
|
||||||
|
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
||||||
|
LDFLAGS = ${LD_DEBUG} -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||||
|
LDLIBS = -lssl -lcrypto -lz -lpthread -lboost_system -lboost_program_options
|
||||||
|
|
||||||
## NOTE: NEEDED_CXXFLAGS is here so that custom CXXFLAGS can be specified at build time
|
## 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.
|
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||||
## For example, when adding 'hardening flags' to the 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
|
## (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
|
## -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.
|
## custom FLAGS to work at build-time.
|
||||||
NEEDED_CXXFLAGS = -std=c++11
|
CXXVER := $(shell $(CXX) -dumpversion|cut -c 1-2)
|
||||||
INCFLAGS = -I/usr/include/ -I/usr/local/include/
|
ifeq (${CXXVER}, "4.") # older clang always returned 4.2.1
|
||||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
$(error Compiler too old)
|
||||||
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
else ifeq (${CXXVER}, ${filter ${CXXVER},16 17 18 19}) # clang 16 - 19
|
||||||
|
NEEDED_CXXFLAGS = -std=c++20
|
||||||
|
else
|
||||||
|
NEEDED_CXXFLAGS = -std=c++17
|
||||||
|
endif
|
||||||
|
|
||||||
|
|||||||
10
Makefile.haiku
Normal file
10
Makefile.haiku
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
CXX = g++
|
||||||
|
CXXFLAGS := -Wall -std=c++17
|
||||||
|
INCFLAGS = -I/system/develop/headers
|
||||||
|
DEFINES = -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
||||||
|
LDLIBS = -lbe -lbsd -lnetwork -lz -lssl -lcrypto -lboost_system -lboost_program_options -lpthread
|
||||||
|
|
||||||
|
ifeq ($(USE_UPNP),yes)
|
||||||
|
DEFINES += -DUSE_UPNP
|
||||||
|
LDLIBS += -lminiupnpc
|
||||||
|
endif
|
||||||
49
Makefile.homebrew
Normal file
49
Makefile.homebrew
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# root directory holding homebrew
|
||||||
|
BREWROOT = /opt/homebrew
|
||||||
|
BOOSTROOT = ${BREWROOT}/opt/boost
|
||||||
|
SSLROOT = ${BREWROOT}/opt/openssl@1.1
|
||||||
|
UPNPROOT = ${BREWROOT}/opt/miniupnpc
|
||||||
|
|
||||||
|
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wno-overloaded-virtual
|
||||||
|
NEEDED_CXXFLAGS ?= -std=c++17
|
||||||
|
INCFLAGS ?= -I${SSLROOT}/include -I${BOOSTROOT}/include
|
||||||
|
LDFLAGS ?= ${LD_DEBUG}
|
||||||
|
DEFINES += -DMAC_OSX
|
||||||
|
|
||||||
|
ifeq ($(USE_STATIC),yes)
|
||||||
|
LDLIBS = -lz ${SSLROOT}/lib/libcrypto.a ${SSLROOT}/lib/libssl.a ${BOOSTROOT}/lib/libboost_system.a ${BOOSTROOT}/lib/libboost_filesystem.a ${BOOSTROOT}/lib/libboost_program_options.a
|
||||||
|
ifeq ($(USE_UPNP),yes)
|
||||||
|
LDLIBS += ${UPNPROOT}/lib/libminiupnpc.a
|
||||||
|
endif
|
||||||
|
LDLIBS += -lpthread -ldl
|
||||||
|
else
|
||||||
|
LDFLAGS += -L${SSLROOT}/lib -L${BOOSTROOT}/lib
|
||||||
|
LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread
|
||||||
|
ifeq ($(USE_UPNP),yes)
|
||||||
|
LDFLAGS += -L${UPNPROOT}/lib
|
||||||
|
LDLIBS += -lminiupnpc
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_UPNP),yes)
|
||||||
|
DEFINES += -DUSE_UPNP
|
||||||
|
INCFLAGS += -I${UPNPROOT}/include
|
||||||
|
endif
|
||||||
|
|
||||||
|
install: all
|
||||||
|
install -d ${PREFIX}/bin
|
||||||
|
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||||
|
install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d
|
||||||
|
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
|
||||||
|
install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd
|
||||||
|
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
|
||||||
|
install -d ${PREFIX}/share/i2pd
|
||||||
|
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
|
||||||
|
install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1
|
||||||
|
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
|
||||||
|
install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
|
||||||
|
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf
|
||||||
@@ -1,57 +1,70 @@
|
|||||||
CXXFLAGS = -g -Wall
|
# set defaults instead redefine
|
||||||
INCFLAGS =
|
CXXFLAGS ?= ${CXX_DEBUG} -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-psabi
|
||||||
|
LDFLAGS ?= ${LD_DEBUG}
|
||||||
|
|
||||||
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
|
## 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.
|
## **without** overwriting the CXXFLAGS which we need in order to build.
|
||||||
## For example, when adding 'hardening flags' to the 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
|
## (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
|
## -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.
|
## custom FDLAGS to work at build-time.
|
||||||
|
|
||||||
# detect proper flag for c++11 support by compilers
|
# detect proper flag for c++17 support by compilers
|
||||||
CXXVER := $(shell $(CXX) -dumpversion)
|
CXXVER := $(shell $(CXX) -dumpversion)
|
||||||
ifeq ($(shell expr match $(CXX) 'clang'),5)
|
ifeq ($(shell expr match $(CXX) 'clang'),5)
|
||||||
NEEDED_CXXFLAGS += -std=c++11
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
else ifeq ($(shell expr match ${CXXVER} "4\.[0-9][0-9]"),4) # gcc >= 4.10
|
else ifeq ($(shell expr match ${CXXVER} "[8-9]"),1) # gcc 8 - 9
|
||||||
NEEDED_CXXFLAGS += -std=c++11
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
else ifeq ($(shell expr match ${CXXVER} "4\.[7-9]"),3) # >= 4.7
|
LDLIBS = -lboost_system -lstdc++fs
|
||||||
NEEDED_CXXFLAGS += -std=c++11
|
else ifeq ($(shell expr match ${CXXVER} "1[0-2]"),2) # gcc 10 - 12
|
||||||
else ifeq ($(shell expr match ${CXXVER} "4\.6"),3) # = 4.6
|
NEEDED_CXXFLAGS += -std=c++17
|
||||||
NEEDED_CXXFLAGS += -std=c++0x
|
else ifeq ($(shell expr match ${CXXVER} "1[3-9]"),2) # gcc 13+
|
||||||
else ifeq ($(shell expr match ${CXXVER} "5\.[0-9]"),3) # gcc >= 5.0
|
NEEDED_CXXFLAGS += -std=c++20
|
||||||
NEEDED_CXXFLAGS += -std=c++11
|
|
||||||
else # not supported
|
else # not supported
|
||||||
$(error Compiler too old)
|
$(error Compiler too old)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
NEEDED_CXXFLAGS += -fPIC
|
NEEDED_CXXFLAGS += -fPIC
|
||||||
|
|
||||||
ifeq ($(USE_STATIC),yes)
|
ifeq ($(USE_STATIC),yes)
|
||||||
LIBDIR := /usr/lib
|
# NOTE: on glibc you will get this warning:
|
||||||
LDLIBS = $(LIBDIR)/libboost_system.a
|
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
||||||
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
# the shared libraries from the glibc version used for linking
|
||||||
LDLIBS += $(LIBDIR)/libboost_filesystem.a
|
LIBDIR := /usr/lib/$(SYS)
|
||||||
LDLIBS += $(LIBDIR)/libboost_regex.a
|
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
||||||
LDLIBS += $(LIBDIR)/libboost_program_options.a
|
LDLIBS += $(LIBDIR)/libssl.a
|
||||||
LDLIBS += $(LIBDIR)/libcryptopp.a
|
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc
|
LDLIBS += $(LIBDIR)/libz.a
|
||||||
USE_AESNI := no
|
ifeq ($(USE_UPNP),yes)
|
||||||
|
LDLIBS += $(LIBDIR)/libminiupnpc.a
|
||||||
|
endif
|
||||||
|
LDLIBS += -lpthread -ldl
|
||||||
else
|
else
|
||||||
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
LDLIBS += -lssl -lcrypto -lz -lboost_program_options -lpthread -latomic
|
||||||
|
ifeq ($(USE_UPNP),yes)
|
||||||
|
LDLIBS += -lminiupnpc
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# UPNP Support (miniupnpc 1.5 or 1.6)
|
# UPNP Support (miniupnpc 1.5 and higher)
|
||||||
ifeq ($(USE_UPNP),1)
|
ifeq ($(USE_UPNP),yes)
|
||||||
LDFLAGS += -ldl
|
DEFINES += -DUSE_UPNP
|
||||||
CXXFLAGS += -DUSE_UPNP
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(GREP) -c "64")
|
install: all
|
||||||
ifeq ($(USE_AESNI),yes)
|
install -d ${PREFIX}/bin
|
||||||
ifeq ($(IS_64),1)
|
install -m 755 ${I2PD} ${PREFIX}/bin
|
||||||
#check if AES-NI is supported by CPU
|
install -d ${PREFIX}/etc ${PREFIX}/etc/i2pd ${PREFIX}/etc/i2pd/tunnels.conf.d
|
||||||
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
|
install -m 644 contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/etc/i2pd
|
||||||
CPU_FLAGS = -maes -DAESNI
|
install -d ${PREFIX}/share ${PREFIX}/share/doc ${PREFIX}/share/doc/i2pd
|
||||||
endif
|
install -m 644 ChangeLog LICENSE README.md contrib/i2pd.conf contrib/subscriptions.txt contrib/tunnels.conf ${PREFIX}/share/doc/i2pd
|
||||||
endif
|
install -d ${PREFIX}/share/i2pd
|
||||||
endif
|
@cp -R contrib/certificates ${PREFIX}/share/i2pd/
|
||||||
|
install -d ${PREFIX}/share/man ${PREFIX}/share/man/man1
|
||||||
|
@gzip -kf debian/i2pd.1 && install debian/i2pd.1.gz ${PREFIX}/share/man/man1
|
||||||
|
install -d ${PREFIX}/var ${PREFIX}/var/lib ${PREFIX}/var/lib/i2pd
|
||||||
|
@ln -sf ${PREFIX}/share/i2pd/certificates ${PREFIX}/var/lib/i2pd/certificates
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf.d ${PREFIX}/var/lib/i2pd/tunnels.d
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/i2pd.conf ${PREFIX}/var/lib/i2pd/i2pd.conf
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/subscriptions.txt ${PREFIX}/var/lib/i2pd/subscriptions.txt
|
||||||
|
@ln -sf ${PREFIX}/etc/i2pd/tunnels.conf ${PREFIX}/var/lib/i2pd/tunnels.conf
|
||||||
|
|||||||
50
Makefile.mingw
Normal file
50
Makefile.mingw
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# Build application with GUI (tray, main window)
|
||||||
|
USE_WIN32_APP := yes
|
||||||
|
|
||||||
|
WINDRES = windres
|
||||||
|
|
||||||
|
CXXFLAGS := $(CXX_DEBUG) -fPIC -msse
|
||||||
|
INCFLAGS := -I$(DAEMON_SRC_DIR) -IWin32
|
||||||
|
LDFLAGS := ${LD_DEBUG} -static -fPIC -msse
|
||||||
|
|
||||||
|
NEEDED_CXXFLAGS += -std=c++20
|
||||||
|
DEFINES += -DWIN32_LEAN_AND_MEAN
|
||||||
|
|
||||||
|
# UPNP Support
|
||||||
|
ifeq ($(USE_UPNP),yes)
|
||||||
|
DEFINES += -DUSE_UPNP -DMINIUPNP_STATICLIB
|
||||||
|
LDLIBS = -lminiupnpc
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_WINXP_FLAGS), yes)
|
||||||
|
DEFINES += -DWINVER=0x0501 -D_WIN32_WINNT=0x0501
|
||||||
|
endif
|
||||||
|
|
||||||
|
LDLIBS += \
|
||||||
|
$(MINGW_PREFIX)/lib/libboost_filesystem-mt.a \
|
||||||
|
$(MINGW_PREFIX)/lib/libboost_program_options-mt.a \
|
||||||
|
$(MINGW_PREFIX)/lib/libssl.a \
|
||||||
|
$(MINGW_PREFIX)/lib/libcrypto.a \
|
||||||
|
$(MINGW_PREFIX)/lib/libz.a \
|
||||||
|
-lwsock32 \
|
||||||
|
-lws2_32 \
|
||||||
|
-liphlpapi \
|
||||||
|
-lcrypt32 \
|
||||||
|
-lgdi32 \
|
||||||
|
-lole32 \
|
||||||
|
-luuid \
|
||||||
|
-lpthread
|
||||||
|
|
||||||
|
ifeq ($(USE_WIN32_APP), yes)
|
||||||
|
DEFINES += -DWIN32_APP
|
||||||
|
LDFLAGS += -mwindows
|
||||||
|
DAEMON_RC += Win32/Resource.rc
|
||||||
|
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(USE_ASLR),yes)
|
||||||
|
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols
|
||||||
|
endif
|
||||||
|
|
||||||
|
obj/%.o : %.rc | mk_obj_dir
|
||||||
|
$(WINDRES) $(DEFINES) $(INCFLAGS) --preprocessor-arg=-MMD --preprocessor-arg=-MP --preprocessor-arg=-MF$@.d -i $< -o $@
|
||||||
40
Makefile.osx
40
Makefile.osx
@@ -1,25 +1,29 @@
|
|||||||
CXX = clang++
|
CXX = clang++
|
||||||
CXXFLAGS = -g -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM -DMAC_OSX
|
CXXFLAGS := ${CXX_DEBUG} -Wall -std=c++17
|
||||||
#CXXFLAGS = -g -O2 -Wall -std=c++11 -DCRYPTOPP_DISABLE_ASM
|
|
||||||
INCFLAGS = -I/usr/local/include
|
INCFLAGS = -I/usr/local/include
|
||||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
DEFINES := -DMAC_OSX
|
||||||
LDLIBS = -lcryptopp -lboost_system -lboost_date_time -lboost_filesystem -lboost_regex -lboost_program_options -lpthread
|
LDFLAGS := -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||||
|
LDFLAGS += -Wl,-dead_strip
|
||||||
|
LDFLAGS += -Wl,-dead_strip_dylibs
|
||||||
|
|
||||||
ifeq ($(USE_UPNP),1)
|
ifeq ($(USE_STATIC),yes)
|
||||||
LDFLAGS += -ldl
|
LDLIBS = -lz /usr/local/lib/libssl.a /usr/local/lib/libcrypto.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
||||||
CXXFLAGS += -DUSE_UPNP
|
else
|
||||||
|
LDLIBS = -lz -lssl -lcrypto -lboost_system -lboost_filesystem -lboost_program_options -lpthread
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# OSX Notes
|
ifeq ($(USE_UPNP),yes)
|
||||||
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
|
LDFLAGS += -ldl
|
||||||
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
|
DEFINES += -DUSE_UPNP
|
||||||
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
|
ifeq ($(USE_STATIC),yes)
|
||||||
ifeq ($(USE_AESNI),yes)
|
LDLIBS += /usr/local/lib/libminiupnpc.a
|
||||||
CXXFLAGS += -maes -DAESNI
|
else
|
||||||
|
LDLIBS += -lminiupnpc
|
||||||
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Disabled, since it will be the default make rule. I think its better
|
OSARCH = $(shell uname -p)
|
||||||
# to define the default rule in Makefile and not Makefile.<ostype> - torkel
|
|
||||||
#install: all
|
ifneq ($(OSARCH),powerpc)
|
||||||
# test -d ${PREFIX} || mkdir -p ${PREFIX}/
|
CXXFLAGS += -msse
|
||||||
# cp -r i2p ${PREFIX}/
|
endif
|
||||||
|
|||||||
936
NTCPSession.cpp
936
NTCPSession.cpp
@@ -1,936 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include <cryptopp/dh.h>
|
|
||||||
#include <cryptopp/adler32.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 (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
|
|
||||||
TransportSession (in_RemoteRouter), m_Server (server), m_Socket (m_Server.GetService ()),
|
|
||||||
m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false),
|
|
||||||
m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false)
|
|
||||||
{
|
|
||||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
|
||||||
m_Establisher = new Establisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
NTCPSession::~NTCPSession ()
|
|
||||||
{
|
|
||||||
delete m_Establisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key)
|
|
||||||
{
|
|
||||||
CryptoPP::DH dh (elgp, elgg);
|
|
||||||
uint8_t sharedKey[256];
|
|
||||||
if (!dh.Agree (sharedKey, m_DHKeysPair->privateKey, pubKey))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Couldn't create shared key");
|
|
||||||
Terminate ();
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint8_t * aesKey = key;
|
|
||||||
if (sharedKey[0] & 0x80)
|
|
||||||
{
|
|
||||||
aesKey[0] = 0;
|
|
||||||
memcpy (aesKey + 1, sharedKey, 31);
|
|
||||||
}
|
|
||||||
else if (sharedKey[0])
|
|
||||||
memcpy (aesKey, sharedKey, 32);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// find first non-zero byte
|
|
||||||
uint8_t * nonZero = sharedKey + 1;
|
|
||||||
while (!*nonZero)
|
|
||||||
{
|
|
||||||
nonZero++;
|
|
||||||
if (nonZero - sharedKey > 32)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "First 32 bytes of shared key is all zeros. Ignored");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
memcpy (aesKey, nonZero, 32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::Done ()
|
|
||||||
{
|
|
||||||
m_Server.GetService ().post (std::bind (&NTCPSession::Terminate, shared_from_this ()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::Terminate ()
|
|
||||||
{
|
|
||||||
if (!m_IsTerminated)
|
|
||||||
{
|
|
||||||
m_IsTerminated = true;
|
|
||||||
m_IsEstablished = false;
|
|
||||||
m_Socket.close ();
|
|
||||||
transports.PeerDisconnected (shared_from_this ());
|
|
||||||
m_Server.RemoveNTCPSession (shared_from_this ());
|
|
||||||
m_SendQueue.clear ();
|
|
||||||
m_NextMessage = nullptr;
|
|
||||||
m_TerminationTimer.cancel ();
|
|
||||||
LogPrint (eLogInfo, "NTCP session terminated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::Connected ()
|
|
||||||
{
|
|
||||||
m_IsEstablished = true;
|
|
||||||
|
|
||||||
delete m_Establisher;
|
|
||||||
m_Establisher = nullptr;
|
|
||||||
|
|
||||||
delete m_DHKeysPair;
|
|
||||||
m_DHKeysPair = nullptr;
|
|
||||||
|
|
||||||
SendTimeSyncMessage ();
|
|
||||||
m_SendQueue.push_back (ToSharedI2NPMessage(CreateDatabaseStoreMsg ())); // we tell immediately who we are
|
|
||||||
|
|
||||||
transports.PeerConnected (shared_from_this ());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::ClientLogin ()
|
|
||||||
{
|
|
||||||
if (!m_DHKeysPair)
|
|
||||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
|
||||||
// send Phase1
|
|
||||||
const uint8_t * x = m_DHKeysPair->publicKey;
|
|
||||||
memcpy (m_Establisher->phase1.pubKey, x, 256);
|
|
||||||
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase1.HXxorHI, x, 256);
|
|
||||||
const uint8_t * ident = m_RemoteIdentity.GetIdentHash ();
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
m_Establisher->phase1.HXxorHI[i] ^= ident[i];
|
|
||||||
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandlePhase1Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
|
||||||
ScheduleTermination ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::ServerLogin ()
|
|
||||||
{
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto ep = m_Socket.remote_endpoint(ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
m_ConnectedFrom = ep.address ();
|
|
||||||
// receive Phase1
|
|
||||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
ScheduleTermination ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Couldn't send Phase 1 message: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandlePhase2Received, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Phase 1 read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// verify ident
|
|
||||||
uint8_t digest[32];
|
|
||||||
CryptoPP::SHA256().CalculateDigest(digest, m_Establisher->phase1.pubKey, 256);
|
|
||||||
const uint8_t * ident = i2p::context.GetRouterInfo ().GetIdentHash ();
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
if ((m_Establisher->phase1.HXxorHI[i] ^ ident[i]) != digest[i])
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Wrong ident");
|
|
||||||
Terminate ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SendPhase2 ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::SendPhase2 ()
|
|
||||||
{
|
|
||||||
if (!m_DHKeysPair)
|
|
||||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
|
||||||
const uint8_t * y = m_DHKeysPair->publicKey;
|
|
||||||
memcpy (m_Establisher->phase2.pubKey, y, 256);
|
|
||||||
uint8_t xy[512];
|
|
||||||
memcpy (xy, m_Establisher->phase1.pubKey, 256);
|
|
||||||
memcpy (xy + 256, y, 256);
|
|
||||||
CryptoPP::SHA256().CalculateDigest(m_Establisher->phase2.encrypted.hxy, xy, 512);
|
|
||||||
uint32_t tsB = htobe32 (i2p::util::GetSecondsSinceEpoch ());
|
|
||||||
m_Establisher->phase2.encrypted.timestamp = tsB;
|
|
||||||
// TODO: fill filler
|
|
||||||
|
|
||||||
i2p::crypto::AESKey aesKey;
|
|
||||||
CreateAESKey (m_Establisher->phase1.pubKey, aesKey);
|
|
||||||
m_Encryption.SetKey (aesKey);
|
|
||||||
m_Encryption.SetIV (y + 240);
|
|
||||||
m_Decryption.SetKey (aesKey);
|
|
||||||
m_Decryption.SetIV (m_Establisher->phase1.HXxorHI + 16);
|
|
||||||
|
|
||||||
m_Encryption.Encrypt ((uint8_t *)&m_Establisher->phase2.encrypted, sizeof(m_Establisher->phase2.encrypted), (uint8_t *)&m_Establisher->phase2.encrypted);
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (&m_Establisher->phase2, sizeof (NTCPPhase2)), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandlePhase2Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsB));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Couldn't send Phase 2 message: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, NTCP_DEFAULT_PHASE3_SIZE), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandlePhase3Received, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2, tsB));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "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
|
|
||||||
{
|
|
||||||
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];
|
|
||||||
memcpy (xy, m_DHKeysPair->publicKey, 256);
|
|
||||||
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
|
|
||||||
if (!CryptoPP::SHA256().VerifyDigest(m_Establisher->phase2.encrypted.hxy, xy, 512))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Incorrect hash");
|
|
||||||
transports.ReuseDHKeysPair (m_DHKeysPair);
|
|
||||||
m_DHKeysPair = nullptr;
|
|
||||||
Terminate ();
|
|
||||||
return ;
|
|
||||||
}
|
|
||||||
SendPhase3 ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::SendPhase3 ()
|
|
||||||
{
|
|
||||||
auto keys = i2p::context.GetPrivateKeys ();
|
|
||||||
uint8_t * buf = m_ReceiveBuffer;
|
|
||||||
htobe16buf (buf, keys.GetPublic ().GetFullLen ());
|
|
||||||
buf += 2;
|
|
||||||
buf += i2p::context.GetIdentity ().ToBuffer (buf, NTCP_BUFFER_SIZE);
|
|
||||||
uint32_t tsA = htobe32 (i2p::util::GetSecondsSinceEpoch ());
|
|
||||||
htobuf32(buf,tsA);
|
|
||||||
buf += 4;
|
|
||||||
size_t signatureLen = keys.GetPublic ().GetSignatureLen ();
|
|
||||||
size_t len = (buf - m_ReceiveBuffer) + signatureLen;
|
|
||||||
size_t paddingSize = len & 0x0F; // %16
|
|
||||||
if (paddingSize > 0)
|
|
||||||
{
|
|
||||||
paddingSize = 16 - paddingSize;
|
|
||||||
// TODO: fill padding with random data
|
|
||||||
buf += paddingSize;
|
|
||||||
len += paddingSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
SignedData s;
|
|
||||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
|
||||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
|
||||||
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident
|
|
||||||
s.Insert (tsA); // tsA
|
|
||||||
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
|
|
||||||
s.Sign (keys, buf);
|
|
||||||
|
|
||||||
m_Encryption.Encrypt(m_ReceiveBuffer, len, m_ReceiveBuffer);
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, len), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandlePhase3Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, tsA));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Couldn't send Phase 3 message: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// wait for phase4
|
|
||||||
auto signatureLen = m_RemoteIdentity.GetSignatureLen ();
|
|
||||||
size_t paddingSize = signatureLen & 0x0F; // %16
|
|
||||||
if (paddingSize > 0) signatureLen += (16 - paddingSize);
|
|
||||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandlePhase4Received, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2, tsA));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Phase 3 read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
|
|
||||||
uint8_t * buf = m_ReceiveBuffer;
|
|
||||||
uint16_t size = bufbe16toh (buf);
|
|
||||||
m_RemoteIdentity.FromBuffer (buf + 2, size);
|
|
||||||
if (m_Server.FindNTCPSession (m_RemoteIdentity.GetIdentHash ()))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "NTCP session already exists");
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity.GetSignatureLen ();
|
|
||||||
size_t paddingLen = expectedSize & 0x0F;
|
|
||||||
if (paddingLen) paddingLen = (16 - paddingLen);
|
|
||||||
if (expectedSize > NTCP_DEFAULT_PHASE3_SIZE)
|
|
||||||
{
|
|
||||||
// we need more bytes for Phase3
|
|
||||||
expectedSize += paddingLen;
|
|
||||||
boost::asio::async_read (m_Socket, boost::asio::buffer(m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, expectedSize), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandlePhase3ExtraReceived, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2, tsB, paddingLen));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
HandlePhase3 (tsB, paddingLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Phase 3 extra read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Decryption.Decrypt (m_ReceiveBuffer + NTCP_DEFAULT_PHASE3_SIZE, bytes_transferred, m_ReceiveBuffer+ NTCP_DEFAULT_PHASE3_SIZE);
|
|
||||||
HandlePhase3 (tsB, paddingLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase3 (uint32_t tsB, size_t paddingLen)
|
|
||||||
{
|
|
||||||
uint8_t * buf = m_ReceiveBuffer + m_RemoteIdentity.GetFullLen () + 2 /*size*/;
|
|
||||||
uint32_t tsA = buf32toh(buf);
|
|
||||||
buf += 4;
|
|
||||||
buf += paddingLen;
|
|
||||||
|
|
||||||
SignedData s;
|
|
||||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
|
||||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
|
||||||
s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident
|
|
||||||
s.Insert (tsA); // tsA
|
|
||||||
s.Insert (tsB); // tsB
|
|
||||||
if (!s.Verify (m_RemoteIdentity, buf))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "signature verification failed");
|
|
||||||
Terminate ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_RemoteIdentity.DropVerifier ();
|
|
||||||
|
|
||||||
SendPhase4 (tsA, tsB);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::SendPhase4 (uint32_t tsA, uint32_t tsB)
|
|
||||||
{
|
|
||||||
SignedData s;
|
|
||||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
|
||||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
|
||||||
s.Insert (m_RemoteIdentity.GetIdentHash (), 32); // ident
|
|
||||||
s.Insert (tsA); // tsA
|
|
||||||
s.Insert (tsB); // tsB
|
|
||||||
auto keys = i2p::context.GetPrivateKeys ();
|
|
||||||
auto signatureLen = keys.GetPublic ().GetSignatureLen ();
|
|
||||||
s.Sign (keys, m_ReceiveBuffer);
|
|
||||||
size_t paddingSize = signatureLen & 0x0F; // %16
|
|
||||||
if (paddingSize > 0) signatureLen += (16 - paddingSize);
|
|
||||||
m_Encryption.Encrypt (m_ReceiveBuffer, signatureLen, m_ReceiveBuffer);
|
|
||||||
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_ReceiveBuffer, signatureLen), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandlePhase4Sent, shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Couldn't send Phase 4 message: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "NTCP server session from ", m_Socket.remote_endpoint (), " connected");
|
|
||||||
m_Server.AddNTCPSession (shared_from_this ());
|
|
||||||
|
|
||||||
Connected ();
|
|
||||||
m_ReceiveBufferOffset = 0;
|
|
||||||
m_NextMessage = nullptr;
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Phase 4 read error: ", ecode.message (), ". Check your clock");
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
// this router doesn't like us
|
|
||||||
i2p::data::netdb.SetUnreachable (GetRemoteIdentity ().GetIdentHash (), true);
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Decryption.Decrypt(m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
|
|
||||||
|
|
||||||
// verify signature
|
|
||||||
SignedData s;
|
|
||||||
s.Insert (m_Establisher->phase1.pubKey, 256); // x
|
|
||||||
s.Insert (m_Establisher->phase2.pubKey, 256); // y
|
|
||||||
s.Insert (i2p::context.GetRouterInfo ().GetIdentHash (), 32); // ident
|
|
||||||
s.Insert (tsA); // tsA
|
|
||||||
s.Insert (m_Establisher->phase2.encrypted.timestamp); // tsB
|
|
||||||
|
|
||||||
if (!s.Verify (m_RemoteIdentity, m_ReceiveBuffer))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "signature verification failed");
|
|
||||||
Terminate ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_RemoteIdentity.DropVerifier ();
|
|
||||||
LogPrint (eLogInfo, "NTCP session to ", m_Socket.remote_endpoint (), " connected");
|
|
||||||
Connected ();
|
|
||||||
|
|
||||||
m_ReceiveBufferOffset = 0;
|
|
||||||
m_NextMessage = nullptr;
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::Receive ()
|
|
||||||
{
|
|
||||||
m_Socket.async_read_some (boost::asio::buffer(m_ReceiveBuffer + m_ReceiveBufferOffset, NTCP_BUFFER_SIZE - m_ReceiveBufferOffset),
|
|
||||||
std::bind(&NTCPSession::HandleReceived, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Read error: ", ecode.message ());
|
|
||||||
if (!m_NumReceivedBytes) m_Server.Ban (m_ConnectedFrom);
|
|
||||||
//if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_NumReceivedBytes += bytes_transferred;
|
|
||||||
i2p::transport::transports.UpdateReceivedBytes (bytes_transferred);
|
|
||||||
m_ReceiveBufferOffset += bytes_transferred;
|
|
||||||
|
|
||||||
if (m_ReceiveBufferOffset >= 16)
|
|
||||||
{
|
|
||||||
int numReloads = 0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
|
|
||||||
// try to read more
|
|
||||||
if (numReloads < 5)
|
|
||||||
{
|
|
||||||
boost::system::error_code ec;
|
|
||||||
size_t moreBytes = m_Socket.available(ec);
|
|
||||||
if (moreBytes)
|
|
||||||
{
|
|
||||||
if (moreBytes > NTCP_BUFFER_SIZE - m_ReceiveBufferOffset)
|
|
||||||
moreBytes = NTCP_BUFFER_SIZE - m_ReceiveBufferOffset;
|
|
||||||
moreBytes = m_Socket.read_some (boost::asio::buffer (m_ReceiveBuffer + m_ReceiveBufferOffset, moreBytes));
|
|
||||||
if (ec)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Read more bytes error: ", ec.message ());
|
|
||||||
Terminate ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_NumReceivedBytes += moreBytes;
|
|
||||||
m_ReceiveBufferOffset += moreBytes;
|
|
||||||
numReloads++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (m_ReceiveBufferOffset >= 16);
|
|
||||||
m_Handler.Flush ();
|
|
||||||
}
|
|
||||||
|
|
||||||
ScheduleTermination (); // reset termination timer
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NTCPSession::DecryptNextBlock (const uint8_t * encrypted) // 16 bytes
|
|
||||||
{
|
|
||||||
if (!m_NextMessage) // new message, header expected
|
|
||||||
{
|
|
||||||
// descrypt header and extract length
|
|
||||||
uint8_t buf[16];
|
|
||||||
m_Decryption.Decrypt (encrypted, buf);
|
|
||||||
uint16_t dataSize = bufbe16toh (buf);
|
|
||||||
if (dataSize)
|
|
||||||
{
|
|
||||||
// new message
|
|
||||||
if (dataSize > NTCP_MAX_MESSAGE_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "NTCP data size ", dataSize, " exceeds max size");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
auto msg = dataSize <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
|
|
||||||
m_NextMessage = ToSharedI2NPMessage (msg);
|
|
||||||
memcpy (m_NextMessage->buf, buf, 16);
|
|
||||||
m_NextMessageOffset = 16;
|
|
||||||
m_NextMessage->offset = 2; // size field
|
|
||||||
m_NextMessage->len = dataSize + 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// timestamp
|
|
||||||
LogPrint ("Timestamp");
|
|
||||||
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
|
|
||||||
if (CryptoPP::Adler32().VerifyDigest (m_NextMessage->buf + m_NextMessageOffset - 4, m_NextMessage->buf, m_NextMessageOffset - 4))
|
|
||||||
m_Handler.PutNextMessage (m_NextMessage);
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Incorrect adler checksum of NTCP message. Dropped");
|
|
||||||
m_NextMessage = nullptr;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::Send (std::shared_ptr<i2p::I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
m_IsSending = true;
|
|
||||||
boost::asio::async_write (m_Socket, CreateMsgBuffer (msg), boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, std::vector<std::shared_ptr<I2NPMessage> >{ msg }));
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::asio::const_buffers_1 NTCPSession::CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
uint8_t * sendBuffer;
|
|
||||||
int len;
|
|
||||||
|
|
||||||
if (msg)
|
|
||||||
{
|
|
||||||
// regular I2NP
|
|
||||||
if (msg->offset < 2)
|
|
||||||
LogPrint (eLogError, "Malformed I2NP message"); // TODO:
|
|
||||||
sendBuffer = msg->GetBuffer () - 2;
|
|
||||||
len = msg->GetLength ();
|
|
||||||
htobe16buf (sendBuffer, len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// prepare timestamp
|
|
||||||
sendBuffer = m_TimeSyncBuffer;
|
|
||||||
len = 4;
|
|
||||||
htobuf16(sendBuffer, 0);
|
|
||||||
htobe32buf (sendBuffer + 2, time (0));
|
|
||||||
}
|
|
||||||
int rem = (len + 6) & 0x0F; // %16
|
|
||||||
int padding = 0;
|
|
||||||
if (rem > 0) padding = 16 - rem;
|
|
||||||
// TODO: fill padding
|
|
||||||
CryptoPP::Adler32().CalculateDigest (sendBuffer + len + 2 + padding, sendBuffer, len + 2+ padding);
|
|
||||||
|
|
||||||
int l = len + padding + 6;
|
|
||||||
m_Encryption.Encrypt(sendBuffer, l, sendBuffer);
|
|
||||||
return boost::asio::buffer ((const uint8_t *)sendBuffer, l);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void NTCPSession::Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
|
||||||
{
|
|
||||||
m_IsSending = true;
|
|
||||||
std::vector<boost::asio::const_buffer> bufs;
|
|
||||||
for (auto it: msgs)
|
|
||||||
bufs.push_back (CreateMsgBuffer (it));
|
|
||||||
boost::asio::async_write (m_Socket, bufs, boost::asio::transfer_all (),
|
|
||||||
std::bind(&NTCPSession::HandleSent, shared_from_this (), std::placeholders::_1, std::placeholders::_2, msgs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs)
|
|
||||||
{
|
|
||||||
m_IsSending = false;
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Couldn't send msgs: ", 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;
|
|
||||||
i2p::transport::transports.UpdateSentBytes (bytes_transferred);
|
|
||||||
if (!m_SendQueue.empty())
|
|
||||||
{
|
|
||||||
Send (m_SendQueue);
|
|
||||||
m_SendQueue.clear ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ScheduleTermination (); // reset termination timer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void NTCPSession::SendTimeSyncMessage ()
|
|
||||||
{
|
|
||||||
Send (nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void NTCPSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
|
||||||
{
|
|
||||||
m_Server.GetService ().post (std::bind (&NTCPSession::PostI2NPMessages, shared_from_this (), msgs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs)
|
|
||||||
{
|
|
||||||
if (m_IsTerminated) return;
|
|
||||||
if (m_IsSending)
|
|
||||||
{
|
|
||||||
for (auto it: msgs)
|
|
||||||
m_SendQueue.push_back (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
Send (msgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::ScheduleTermination ()
|
|
||||||
{
|
|
||||||
m_TerminationTimer.cancel ();
|
|
||||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_TIMEOUT));
|
|
||||||
m_TerminationTimer.async_wait (std::bind (&NTCPSession::HandleTerminationTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPSession::HandleTerminationTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
LogPrint ("No activity fo ", NTCP_TERMINATION_TIMEOUT, " seconds");
|
|
||||||
//Terminate ();
|
|
||||||
m_Socket.close ();// invoke Terminate () from HandleReceive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------
|
|
||||||
NTCPServer::NTCPServer (int port):
|
|
||||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
|
||||||
m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
NTCPServer::~NTCPServer ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPServer::Start ()
|
|
||||||
{
|
|
||||||
if (!m_IsRunning)
|
|
||||||
{
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Thread = new std::thread (std::bind (&NTCPServer::Run, this));
|
|
||||||
// create acceptors
|
|
||||||
auto addresses = context.GetRouterInfo ().GetAddresses ();
|
|
||||||
for (auto& address : addresses)
|
|
||||||
{
|
|
||||||
if (address.transportStyle == i2p::data::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 (eLogInfo, "Start listening TCP port ", address.port);
|
|
||||||
auto conn = std::make_shared<NTCPSession>(*this);
|
|
||||||
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
|
|
||||||
conn, std::placeholders::_1));
|
|
||||||
|
|
||||||
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 (eLogInfo, "Start listening V6 TCP port ", address.port);
|
|
||||||
auto conn = std::make_shared<NTCPSession> (*this);
|
|
||||||
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6,
|
|
||||||
this, conn, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPServer::Stop ()
|
|
||||||
{
|
|
||||||
m_NTCPSessions.clear ();
|
|
||||||
|
|
||||||
if (m_IsRunning)
|
|
||||||
{
|
|
||||||
m_IsRunning = false;
|
|
||||||
delete m_NTCPAcceptor;
|
|
||||||
m_NTCPAcceptor = nullptr;
|
|
||||||
delete m_NTCPV6Acceptor;
|
|
||||||
m_NTCPV6Acceptor = nullptr;
|
|
||||||
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void NTCPServer::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint ("NTCP server: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPServer::AddNTCPSession (std::shared_ptr<NTCPSession> session)
|
|
||||||
{
|
|
||||||
if (session)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
|
|
||||||
m_NTCPSessions[session->GetRemoteIdentity ().GetIdentHash ()] = session;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPServer::RemoveNTCPSession (std::shared_ptr<NTCPSession> session)
|
|
||||||
{
|
|
||||||
if (session)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
|
|
||||||
m_NTCPSessions.erase (session->GetRemoteIdentity ().GetIdentHash ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<NTCPSession> NTCPServer::FindNTCPSession (const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_NTCPSessionsMutex);
|
|
||||||
auto it = m_NTCPSessions.find (ident);
|
|
||||||
if (it != m_NTCPSessions.end ())
|
|
||||||
return it->second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPServer::HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
|
|
||||||
{
|
|
||||||
if (!error)
|
|
||||||
{
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto ep = conn->GetSocket ().remote_endpoint(ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Connected from ", ep);
|
|
||||||
auto it = m_BanList.find (ep.address ());
|
|
||||||
if (it != m_BanList.end ())
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
if (ts < it->second)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds");
|
|
||||||
conn = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_BanList.erase (it);
|
|
||||||
}
|
|
||||||
if (conn)
|
|
||||||
conn->ServerLogin ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Connected from error ", ec.message ());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (error != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
conn = std::make_shared<NTCPSession> (*this);
|
|
||||||
m_NTCPAcceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAccept, this,
|
|
||||||
conn, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPServer::HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error)
|
|
||||||
{
|
|
||||||
if (!error)
|
|
||||||
{
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto ep = conn->GetSocket ().remote_endpoint(ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Connected from ", ep);
|
|
||||||
auto it = m_BanList.find (ep.address ());
|
|
||||||
if (it != m_BanList.end ())
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
if (ts < it->second)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, ep.address (), " is banned for ", it->second - ts, " more seconds");
|
|
||||||
conn = nullptr;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_BanList.erase (it);
|
|
||||||
}
|
|
||||||
if (conn)
|
|
||||||
conn->ServerLogin ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Connected from error ", ec.message ());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
conn = std::make_shared<NTCPSession> (*this);
|
|
||||||
m_NTCPV6Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCPServer::HandleAcceptV6, this,
|
|
||||||
conn, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPServer::Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Connecting to ", address ,":", port);
|
|
||||||
m_Service.post([conn, this]()
|
|
||||||
{
|
|
||||||
this->AddNTCPSession (conn);
|
|
||||||
});
|
|
||||||
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
|
|
||||||
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Connect error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ().GetIdentHash (), true);
|
|
||||||
conn->Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Connected to ", conn->GetSocket ().remote_endpoint ());
|
|
||||||
if (conn->GetSocket ().local_endpoint ().protocol () == boost::asio::ip::tcp::v6()) // ipv6
|
|
||||||
context.UpdateNTCPV6Address (conn->GetSocket ().local_endpoint ().address ());
|
|
||||||
conn->ClientLogin ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NTCPServer::Ban (const boost::asio::ip::address& addr)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT;
|
|
||||||
LogPrint (eLogInfo, addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
183
NTCPSession.h
183
NTCPSession.h
@@ -1,183 +0,0 @@
|
|||||||
#ifndef NTCP_SESSION_H__
|
|
||||||
#define NTCP_SESSION_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <cryptopp/modes.h>
|
|
||||||
#include <cryptopp/aes.h>
|
|
||||||
#include "aes.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "TransportSession.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
|
|
||||||
#pragma pack(1)
|
|
||||||
struct NTCPPhase1
|
|
||||||
{
|
|
||||||
uint8_t pubKey[256];
|
|
||||||
uint8_t HXxorHI[32];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NTCPPhase2
|
|
||||||
{
|
|
||||||
uint8_t pubKey[256];
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint8_t hxy[32];
|
|
||||||
uint32_t timestamp;
|
|
||||||
uint8_t filler[12];
|
|
||||||
} encrypted;
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma pack()
|
|
||||||
|
|
||||||
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
|
|
||||||
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
|
|
||||||
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
|
|
||||||
const size_t NTCP_DEFAULT_PHASE3_SIZE = 2/*size*/ + i2p::data::DEFAULT_IDENTITY_SIZE/*387*/ + 4/*ts*/ + 15/*padding*/ + 40/*signature*/; // 448
|
|
||||||
const int NTCP_BAN_EXPIRATION_TIMEOUT = 70; // in second
|
|
||||||
|
|
||||||
class NTCPServer;
|
|
||||||
class NTCPSession: public TransportSession, public std::enable_shared_from_this<NTCPSession>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter = nullptr);
|
|
||||||
~NTCPSession ();
|
|
||||||
void Terminate ();
|
|
||||||
void Done ();
|
|
||||||
|
|
||||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
|
||||||
bool IsEstablished () const { return m_IsEstablished; };
|
|
||||||
|
|
||||||
void ClientLogin ();
|
|
||||||
void ServerLogin ();
|
|
||||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
|
||||||
void Connected ();
|
|
||||||
void SendTimeSyncMessage ();
|
|
||||||
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
|
|
||||||
|
|
||||||
void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key);
|
|
||||||
|
|
||||||
// client
|
|
||||||
void SendPhase3 ();
|
|
||||||
void HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandlePhase2Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandlePhase3Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
|
|
||||||
void HandlePhase4Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsA);
|
|
||||||
|
|
||||||
//server
|
|
||||||
void SendPhase2 ();
|
|
||||||
void SendPhase4 (uint32_t tsA, uint32_t tsB);
|
|
||||||
void HandlePhase1Received (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
void HandlePhase2Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
|
|
||||||
void HandlePhase3Received (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB);
|
|
||||||
void HandlePhase3ExtraReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred, uint32_t tsB, size_t paddingLen);
|
|
||||||
void HandlePhase3 (uint32_t tsB, size_t paddingLen);
|
|
||||||
void HandlePhase4Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
|
|
||||||
// common
|
|
||||||
void Receive ();
|
|
||||||
void HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
|
||||||
bool DecryptNextBlock (const uint8_t * encrypted);
|
|
||||||
|
|
||||||
void Send (std::shared_ptr<i2p::I2NPMessage> msg);
|
|
||||||
boost::asio::const_buffers_1 CreateMsgBuffer (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void Send (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
|
||||||
void HandleSent (const boost::system::error_code& ecode, std::size_t bytes_transferred, std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
|
||||||
|
|
||||||
|
|
||||||
// timer
|
|
||||||
void ScheduleTermination ();
|
|
||||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
NTCPServer& m_Server;
|
|
||||||
boost::asio::ip::tcp::socket m_Socket;
|
|
||||||
boost::asio::deadline_timer m_TerminationTimer;
|
|
||||||
bool m_IsEstablished, m_IsTerminated;
|
|
||||||
|
|
||||||
i2p::crypto::CBCDecryption m_Decryption;
|
|
||||||
i2p::crypto::CBCEncryption m_Encryption;
|
|
||||||
|
|
||||||
struct Establisher
|
|
||||||
{
|
|
||||||
NTCPPhase1 phase1;
|
|
||||||
NTCPPhase2 phase2;
|
|
||||||
} * m_Establisher;
|
|
||||||
|
|
||||||
i2p::crypto::AESAlignedBuffer<NTCP_BUFFER_SIZE + 16> m_ReceiveBuffer;
|
|
||||||
i2p::crypto::AESAlignedBuffer<16> m_TimeSyncBuffer;
|
|
||||||
int m_ReceiveBufferOffset;
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> m_NextMessage;
|
|
||||||
size_t m_NextMessageOffset;
|
|
||||||
i2p::I2NPMessagesHandler m_Handler;
|
|
||||||
|
|
||||||
bool m_IsSending;
|
|
||||||
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
|
||||||
|
|
||||||
boost::asio::ip::address m_ConnectedFrom; // for ban
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: move to NTCP.h/.cpp
|
|
||||||
class NTCPServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
NTCPServer (int port);
|
|
||||||
~NTCPServer ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
void AddNTCPSession (std::shared_ptr<NTCPSession> session);
|
|
||||||
void RemoveNTCPSession (std::shared_ptr<NTCPSession> session);
|
|
||||||
std::shared_ptr<NTCPSession> FindNTCPSession (const i2p::data::IdentHash& ident);
|
|
||||||
void Connect (const boost::asio::ip::address& address, int port, std::shared_ptr<NTCPSession> conn);
|
|
||||||
|
|
||||||
boost::asio::io_service& GetService () { return m_Service; };
|
|
||||||
void Ban (const boost::asio::ip::address& addr);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void HandleAccept (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
|
|
||||||
void HandleAcceptV6 (std::shared_ptr<NTCPSession> conn, const boost::system::error_code& error);
|
|
||||||
|
|
||||||
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn);
|
|
||||||
|
|
||||||
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::mutex m_NTCPSessionsMutex;
|
|
||||||
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions;
|
|
||||||
std::map<boost::asio::ip::address, uint32_t> m_BanList; // IP -> ban expiration time in seconds
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP/I2PControl
|
|
||||||
const decltype(m_NTCPSessions)& GetNTCPSessions () const { return m_NTCPSessions; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
106
NetDb.h
106
NetDb.h
@@ -1,106 +0,0 @@
|
|||||||
#ifndef NETDB_H__
|
|
||||||
#define NETDB_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <set>
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include "Queue.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
#include "TunnelPool.h"
|
|
||||||
#include "Reseed.h"
|
|
||||||
#include "NetDbRequests.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
|
|
||||||
class NetDb
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
NetDb ();
|
|
||||||
~NetDb ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
void AddRouterInfo (const uint8_t * buf, int len);
|
|
||||||
void AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
|
|
||||||
void AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
std::shared_ptr<RouterInfo> FindRouter (const IdentHash& ident) const;
|
|
||||||
std::shared_ptr<LeaseSet> FindLeaseSet (const IdentHash& destination) const;
|
|
||||||
|
|
||||||
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
|
|
||||||
|
|
||||||
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
|
|
||||||
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
|
|
||||||
void HandleDatabaseLookupMsg (std::shared_ptr<const I2NPMessage> msg);
|
|
||||||
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomRouter () const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetHighBandwidthRandomRouter (std::shared_ptr<const RouterInfo> compatibleWith) const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomPeerTestRouter () const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
|
|
||||||
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
|
||||||
std::set<IdentHash>& excluded) const;
|
|
||||||
std::shared_ptr<const RouterInfo> GetClosestNonFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded) const;
|
|
||||||
void SetUnreachable (const IdentHash& ident, bool unreachable);
|
|
||||||
|
|
||||||
void PostI2NPMsg (std::shared_ptr<const I2NPMessage> msg);
|
|
||||||
|
|
||||||
void Reseed ();
|
|
||||||
|
|
||||||
// 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 ();
|
|
||||||
void SaveUpdated ();
|
|
||||||
void Run (); // exploratory thread
|
|
||||||
void Explore (int numDestinations);
|
|
||||||
void Publish ();
|
|
||||||
void ManageLeaseSets ();
|
|
||||||
void ManageRequests ();
|
|
||||||
|
|
||||||
template<typename Filter>
|
|
||||||
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
|
|
||||||
mutable std::mutex m_RouterInfosMutex;
|
|
||||||
std::map<IdentHash, std::shared_ptr<RouterInfo> > m_RouterInfos;
|
|
||||||
mutable std::mutex m_FloodfillsMutex;
|
|
||||||
std::list<std::shared_ptr<RouterInfo> > m_Floodfills;
|
|
||||||
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
i2p::util::Queue<std::shared_ptr<const I2NPMessage> > m_Queue; // of I2NPDatabaseStoreMsg
|
|
||||||
|
|
||||||
Reseeder * m_Reseeder;
|
|
||||||
|
|
||||||
friend class NetDbRequests;
|
|
||||||
NetDbRequests m_Requests;
|
|
||||||
|
|
||||||
static const char m_NetDbPath[];
|
|
||||||
};
|
|
||||||
|
|
||||||
extern NetDb netdb;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
#include "Log.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "Transports.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "NetDbRequests.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
|
|
||||||
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
|
|
||||||
{
|
|
||||||
I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
|
|
||||||
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
|
|
||||||
&m_ExcludedPeers);
|
|
||||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
|
||||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
return ToSharedI2NPMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (const IdentHash& floodfill)
|
|
||||||
{
|
|
||||||
I2NPMessage * msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
|
|
||||||
i2p::context.GetRouterInfo ().GetIdentHash () , 0, false, &m_ExcludedPeers);
|
|
||||||
m_ExcludedPeers.insert (floodfill);
|
|
||||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
return ToSharedI2NPMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RequestedDestination::ClearExcludedPeers ()
|
|
||||||
{
|
|
||||||
m_ExcludedPeers.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RequestedDestination::Success (std::shared_ptr<RouterInfo> r)
|
|
||||||
{
|
|
||||||
if (m_RequestComplete)
|
|
||||||
{
|
|
||||||
m_RequestComplete (r);
|
|
||||||
m_RequestComplete = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RequestedDestination::Fail ()
|
|
||||||
{
|
|
||||||
if (m_RequestComplete)
|
|
||||||
{
|
|
||||||
m_RequestComplete (nullptr);
|
|
||||||
m_RequestComplete = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetDbRequests::Start ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetDbRequests::Stop ()
|
|
||||||
{
|
|
||||||
m_RequestedDestinations.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<RequestedDestination> NetDbRequests::CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete)
|
|
||||||
{
|
|
||||||
// request RouterInfo directly
|
|
||||||
auto dest = std::make_shared<RequestedDestination> (destination, isExploratory);
|
|
||||||
dest->SetRequestComplete (requestComplete);
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
|
||||||
if (!m_RequestedDestinations.insert (std::make_pair (destination,
|
|
||||||
std::shared_ptr<RequestedDestination> (dest))).second) // not inserted
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetDbRequests::RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r)
|
|
||||||
{
|
|
||||||
auto it = m_RequestedDestinations.find (ident);
|
|
||||||
if (it != m_RequestedDestinations.end ())
|
|
||||||
{
|
|
||||||
if (r)
|
|
||||||
it->second->Success (r);
|
|
||||||
else
|
|
||||||
it->second->Fail ();
|
|
||||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
|
||||||
m_RequestedDestinations.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
|
|
||||||
{
|
|
||||||
auto it = m_RequestedDestinations.find (ident);
|
|
||||||
if (it != m_RequestedDestinations.end ())
|
|
||||||
return it->second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetDbRequests::ManageRequests ()
|
|
||||||
{
|
|
||||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
|
||||||
for (auto it = m_RequestedDestinations.begin (); it != m_RequestedDestinations.end ();)
|
|
||||||
{
|
|
||||||
auto& dest = it->second;
|
|
||||||
bool done = false;
|
|
||||||
if (ts < dest->GetCreationTime () + 60) // request is worthless after 1 minute
|
|
||||||
{
|
|
||||||
if (ts > dest->GetCreationTime () + 5) // no response for 5 seconds
|
|
||||||
{
|
|
||||||
auto count = dest->GetExcludedPeers ().size ();
|
|
||||||
if (!dest->IsExploratory () && count < 7)
|
|
||||||
{
|
|
||||||
auto pool = i2p::tunnel::tunnels.GetExploratoryPool ();
|
|
||||||
auto outbound = pool->GetNextOutboundTunnel ();
|
|
||||||
auto inbound = pool->GetNextInboundTunnel ();
|
|
||||||
auto nextFloodfill = netdb.GetClosestFloodfill (dest->GetDestination (), dest->GetExcludedPeers ());
|
|
||||||
if (nextFloodfill && outbound && inbound)
|
|
||||||
outbound->SendTunnelDataMsg (nextFloodfill->GetIdentHash (), 0,
|
|
||||||
dest->CreateRequestMessage (nextFloodfill, inbound));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
done = true;
|
|
||||||
if (!inbound) LogPrint (eLogWarning, "No inbound tunnels");
|
|
||||||
if (!outbound) LogPrint (eLogWarning, "No outbound tunnels");
|
|
||||||
if (!nextFloodfill) LogPrint (eLogWarning, "No more floodfills");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!dest->IsExploratory ())
|
|
||||||
LogPrint (eLogWarning, dest->GetDestination ().ToBase64 (), " not found after 7 attempts");
|
|
||||||
done = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // delete obsolete request
|
|
||||||
done = true;
|
|
||||||
|
|
||||||
if (done)
|
|
||||||
it = m_RequestedDestinations.erase (it);
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#ifndef NETDB_REQUESTS_H__
|
|
||||||
#define NETDB_REQUESTS_H__
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <map>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
class RequestedDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef std::function<void (std::shared_ptr<RouterInfo>)> RequestComplete;
|
|
||||||
|
|
||||||
RequestedDestination (const IdentHash& destination, bool isExploratory = false):
|
|
||||||
m_Destination (destination), m_IsExploratory (isExploratory), m_CreationTime (0) {};
|
|
||||||
~RequestedDestination () { if (m_RequestComplete) m_RequestComplete (nullptr); };
|
|
||||||
|
|
||||||
const IdentHash& GetDestination () const { return m_Destination; };
|
|
||||||
int GetNumExcludedPeers () const { return m_ExcludedPeers.size (); };
|
|
||||||
const std::set<IdentHash>& GetExcludedPeers () { return m_ExcludedPeers; };
|
|
||||||
void ClearExcludedPeers ();
|
|
||||||
bool IsExploratory () const { return m_IsExploratory; };
|
|
||||||
bool IsExcluded (const IdentHash& ident) const { return m_ExcludedPeers.count (ident); };
|
|
||||||
uint64_t GetCreationTime () const { return m_CreationTime; };
|
|
||||||
std::shared_ptr<I2NPMessage> CreateRequestMessage (std::shared_ptr<const RouterInfo>, std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel);
|
|
||||||
std::shared_ptr<I2NPMessage> CreateRequestMessage (const IdentHash& floodfill);
|
|
||||||
|
|
||||||
void SetRequestComplete (const RequestComplete& requestComplete) { m_RequestComplete = requestComplete; };
|
|
||||||
bool IsRequestComplete () const { return m_RequestComplete != nullptr; };
|
|
||||||
void Success (std::shared_ptr<RouterInfo> r);
|
|
||||||
void Fail ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
IdentHash m_Destination;
|
|
||||||
bool m_IsExploratory;
|
|
||||||
std::set<IdentHash> m_ExcludedPeers;
|
|
||||||
uint64_t m_CreationTime;
|
|
||||||
RequestComplete m_RequestComplete;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NetDbRequests
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
std::shared_ptr<RequestedDestination> CreateRequest (const IdentHash& destination, bool isExploratory, RequestedDestination::RequestComplete requestComplete = nullptr);
|
|
||||||
void RequestComplete (const IdentHash& ident, std::shared_ptr<RouterInfo> r);
|
|
||||||
std::shared_ptr<RequestedDestination> FindRequest (const IdentHash& ident) const;
|
|
||||||
void ManageRequests ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::mutex m_RequestedDestinationsMutex;
|
|
||||||
std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
213
Profiling.cpp
213
Profiling.cpp
@@ -1,213 +0,0 @@
|
|||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/property_tree/ptree.hpp>
|
|
||||||
#include <boost/property_tree/ini_parser.hpp>
|
|
||||||
#include "base64.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "Profiling.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
RouterProfile::RouterProfile (const IdentHash& identHash):
|
|
||||||
m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
|
|
||||||
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
|
|
||||||
m_NumTimesTaken (0), m_NumTimesRejected (0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::posix_time::ptime RouterProfile::GetTime () const
|
|
||||||
{
|
|
||||||
return boost::posix_time::second_clock::local_time();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterProfile::UpdateTime ()
|
|
||||||
{
|
|
||||||
m_LastUpdateTime = GetTime ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterProfile::Save ()
|
|
||||||
{
|
|
||||||
// fill sections
|
|
||||||
boost::property_tree::ptree participation;
|
|
||||||
participation.put (PEER_PROFILE_PARTICIPATION_AGREED, m_NumTunnelsAgreed);
|
|
||||||
participation.put (PEER_PROFILE_PARTICIPATION_DECLINED, m_NumTunnelsDeclined);
|
|
||||||
participation.put (PEER_PROFILE_PARTICIPATION_NON_REPLIED, m_NumTunnelsNonReplied);
|
|
||||||
boost::property_tree::ptree usage;
|
|
||||||
usage.put (PEER_PROFILE_USAGE_TAKEN, m_NumTimesTaken);
|
|
||||||
usage.put (PEER_PROFILE_USAGE_REJECTED, m_NumTimesRejected);
|
|
||||||
// fill property tree
|
|
||||||
boost::property_tree::ptree pt;
|
|
||||||
pt.put (PEER_PROFILE_LAST_UPDATE_TIME, boost::posix_time::to_simple_string (m_LastUpdateTime));
|
|
||||||
pt.put_child (PEER_PROFILE_SECTION_PARTICIPATION, participation);
|
|
||||||
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
|
|
||||||
|
|
||||||
// save to file
|
|
||||||
auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY;
|
|
||||||
if (!boost::filesystem::exists (path))
|
|
||||||
{
|
|
||||||
// Create directory is necessary
|
|
||||||
if (!boost::filesystem::create_directory (path))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Failed to create directory ", path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const char * chars = GetBase64SubstitutionTable (); // 64 bytes
|
|
||||||
for (int i = 0; i < 64; i++)
|
|
||||||
{
|
|
||||||
auto path1 = path / (std::string ("p") + chars[i]);
|
|
||||||
if (!boost::filesystem::create_directory (path1))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Failed to create directory ", path1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::string base64 = m_IdentHash.ToBase64 ();
|
|
||||||
path = path / (std::string ("p") + base64[0]);
|
|
||||||
auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
boost::property_tree::write_ini (filename.string (), pt);
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't write ", filename, ": ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterProfile::Load ()
|
|
||||||
{
|
|
||||||
std::string base64 = m_IdentHash.ToBase64 ();
|
|
||||||
auto path = i2p::util::filesystem::GetDefaultDataDir() / PEER_PROFILES_DIRECTORY;
|
|
||||||
path /= std::string ("p") + base64[0];
|
|
||||||
auto filename = path / (std::string (PEER_PROFILE_PREFIX) + base64 + ".txt");
|
|
||||||
if (boost::filesystem::exists (filename))
|
|
||||||
{
|
|
||||||
boost::property_tree::ptree pt;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
boost::property_tree::read_ini (filename.string (), pt);
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't read ", filename, ": ", ex.what ());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto t = pt.get (PEER_PROFILE_LAST_UPDATE_TIME, "");
|
|
||||||
if (t.length () > 0)
|
|
||||||
m_LastUpdateTime = boost::posix_time::time_from_string (t);
|
|
||||||
if ((GetTime () - m_LastUpdateTime).hours () < PEER_PROFILE_EXPIRATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// read participations
|
|
||||||
auto participations = pt.get_child (PEER_PROFILE_SECTION_PARTICIPATION);
|
|
||||||
m_NumTunnelsAgreed = participations.get (PEER_PROFILE_PARTICIPATION_AGREED, 0);
|
|
||||||
m_NumTunnelsDeclined = participations.get (PEER_PROFILE_PARTICIPATION_DECLINED, 0);
|
|
||||||
m_NumTunnelsNonReplied = participations.get (PEER_PROFILE_PARTICIPATION_NON_REPLIED, 0);
|
|
||||||
}
|
|
||||||
catch (boost::property_tree::ptree_bad_path& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_PARTICIPATION);
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// read usage
|
|
||||||
auto usage = pt.get_child (PEER_PROFILE_SECTION_USAGE);
|
|
||||||
m_NumTimesTaken = usage.get (PEER_PROFILE_USAGE_TAKEN, 0);
|
|
||||||
m_NumTimesRejected = usage.get (PEER_PROFILE_USAGE_REJECTED, 0);
|
|
||||||
}
|
|
||||||
catch (boost::property_tree::ptree_bad_path& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
*this = RouterProfile (m_IdentHash);
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't read profile ", base64, " :", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterProfile::TunnelBuildResponse (uint8_t ret)
|
|
||||||
{
|
|
||||||
UpdateTime ();
|
|
||||||
if (ret > 0)
|
|
||||||
m_NumTunnelsDeclined++;
|
|
||||||
else
|
|
||||||
m_NumTunnelsAgreed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterProfile::TunnelNonReplied ()
|
|
||||||
{
|
|
||||||
m_NumTunnelsNonReplied++;
|
|
||||||
UpdateTime ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterProfile::IsLowPartcipationRate () const
|
|
||||||
{
|
|
||||||
return 4*m_NumTunnelsAgreed < m_NumTunnelsDeclined; // < 20% rate
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterProfile::IsLowReplyRate () const
|
|
||||||
{
|
|
||||||
auto total = m_NumTunnelsAgreed + m_NumTunnelsDeclined;
|
|
||||||
return m_NumTunnelsNonReplied > 10*(total + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterProfile::IsBad ()
|
|
||||||
{
|
|
||||||
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
|
|
||||||
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
|
|
||||||
{
|
|
||||||
// reset profile
|
|
||||||
m_NumTunnelsAgreed = 0;
|
|
||||||
m_NumTunnelsDeclined = 0;
|
|
||||||
m_NumTunnelsNonReplied = 0;
|
|
||||||
isBad = false;
|
|
||||||
}
|
|
||||||
if (isBad) m_NumTimesRejected++; else m_NumTimesTaken++;
|
|
||||||
return isBad;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
|
|
||||||
{
|
|
||||||
auto profile = std::make_shared<RouterProfile> (identHash);
|
|
||||||
profile->Load (); // if possible
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeleteObsoleteProfiles ()
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
auto ts = boost::posix_time::second_clock::local_time();
|
|
||||||
boost::filesystem::path p (i2p::util::filesystem::GetDataDir()/PEER_PROFILES_DIRECTORY);
|
|
||||||
if (boost::filesystem::exists (p))
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
auto lastModified = boost::posix_time::from_time_t (boost::filesystem::last_write_time (it1->path ()));
|
|
||||||
if ((ts - lastModified).hours () >= PEER_PROFILE_EXPIRATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
boost::filesystem::remove (it1->path ());
|
|
||||||
num++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, num, " obsolete profiles deleted");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
69
Profiling.h
69
Profiling.h
@@ -1,69 +0,0 @@
|
|||||||
#ifndef PROFILING_H__
|
|
||||||
#define PROFILING_H__
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
|
||||||
#include "Identity.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
const char PEER_PROFILES_DIRECTORY[] = "peerProfiles";
|
|
||||||
const char PEER_PROFILE_PREFIX[] = "profile-";
|
|
||||||
// sections
|
|
||||||
const char PEER_PROFILE_SECTION_PARTICIPATION[] = "participation";
|
|
||||||
const char PEER_PROFILE_SECTION_USAGE[] = "usage";
|
|
||||||
// params
|
|
||||||
const char PEER_PROFILE_LAST_UPDATE_TIME[] = "lastupdatetime";
|
|
||||||
const char PEER_PROFILE_PARTICIPATION_AGREED[] = "agreed";
|
|
||||||
const char PEER_PROFILE_PARTICIPATION_DECLINED[] = "declined";
|
|
||||||
const char PEER_PROFILE_PARTICIPATION_NON_REPLIED[] = "nonreplied";
|
|
||||||
const char PEER_PROFILE_USAGE_TAKEN[] = "taken";
|
|
||||||
const char PEER_PROFILE_USAGE_REJECTED[] = "rejected";
|
|
||||||
|
|
||||||
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
|
|
||||||
|
|
||||||
class RouterProfile
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RouterProfile (const IdentHash& identHash);
|
|
||||||
RouterProfile& operator= (const RouterProfile& ) = default;
|
|
||||||
|
|
||||||
void Save ();
|
|
||||||
void Load ();
|
|
||||||
|
|
||||||
bool IsBad ();
|
|
||||||
|
|
||||||
void TunnelBuildResponse (uint8_t ret);
|
|
||||||
void TunnelNonReplied ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
boost::posix_time::ptime GetTime () const;
|
|
||||||
void UpdateTime ();
|
|
||||||
|
|
||||||
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
|
|
||||||
bool IsLowPartcipationRate () const;
|
|
||||||
bool IsLowReplyRate () const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
IdentHash m_IdentHash;
|
|
||||||
boost::posix_time::ptime m_LastUpdateTime;
|
|
||||||
// participation
|
|
||||||
uint32_t m_NumTunnelsAgreed;
|
|
||||||
uint32_t m_NumTunnelsDeclined;
|
|
||||||
uint32_t m_NumTunnelsNonReplied;
|
|
||||||
// usage
|
|
||||||
uint32_t m_NumTimesTaken;
|
|
||||||
uint32_t m_NumTimesRejected;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash);
|
|
||||||
void DeleteObsoleteProfiles ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
251
README.md
251
README.md
@@ -1,143 +1,124 @@
|
|||||||
|
[](https://github.com/PurpleI2P/i2pd/releases/latest)
|
||||||
|
[](https://snapcraft.io/i2pd)
|
||||||
|
[](https://github.com/PurpleI2P/i2pd/blob/openssl/LICENSE)
|
||||||
|
[](https://repology.org/project/i2pd/versions)
|
||||||
|
[](https://hub.docker.com/r/purplei2p/i2pd)
|
||||||
|
[](https://crowdin.com/project/i2pd)
|
||||||
|
|
||||||
|
*note: i2pd for Android can be found in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository and with Qt GUI in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository*
|
||||||
|
|
||||||
i2pd
|
i2pd
|
||||||
====
|
====
|
||||||
|
|
||||||
I2P router written in C++
|
[Русская версия](https://github.com/PurpleI2P/i2pd_docs_ru/blob/master/README.md)
|
||||||
|
|
||||||
|
i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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).
|
||||||
|
|
||||||
|
I2P allows people from all around the world to communicate and share information
|
||||||
|
without restrictions.
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
* 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 packages and binaries.
|
||||||
|
You can fetch most of them on [release](https://github.com/PurpleI2P/i2pd/releases/latest) page.
|
||||||
|
Please see [documentation](https://i2pd.readthedocs.io/en/latest/user-guide/install/) for more info.
|
||||||
|
|
||||||
|
Building
|
||||||
|
--------
|
||||||
|
See [documentation](https://i2pd.readthedocs.io/en/latest/) for how to build
|
||||||
|
i2pd from source on your OS.
|
||||||
|
|
||||||
|
note: i2pd with Qt GUI can be found in [i2pd-qt](https://github.com/PurpleI2P/i2pd-qt) repository and for android in [i2pd-android](https://github.com/PurpleI2P/i2pd-android) repository.
|
||||||
|
|
||||||
|
|
||||||
|
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 (Debian, Ubuntu, etc) - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml)
|
||||||
|
* CentOS, Fedora, Mageia - [](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/)
|
||||||
|
* Alpine, ArchLinux, openSUSE, Gentoo, etc.
|
||||||
|
* Windows - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml)
|
||||||
|
* Mac OS - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml)
|
||||||
|
* Docker image - [](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml)
|
||||||
|
* Snap - [](https://snapcraft.io/i2pd) [](https://snapcraft.io/i2pd)
|
||||||
|
* FreeBSD - [](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml)
|
||||||
|
* Android - [](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml)
|
||||||
|
* 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).
|
||||||
|
|
||||||
|
Localization
|
||||||
|
------------
|
||||||
|
|
||||||
|
You can help us with translation i2pd to your language using Crowdin platform!
|
||||||
|
Translation project can be found [here](https://crowdin.com/project/i2pd).
|
||||||
|
|
||||||
|
New languages can be requested on project's [discussion page](https://crowdin.com/project/i2pd/discussions).
|
||||||
|
|
||||||
|
Current status: [](https://crowdin.com/project/i2pd)
|
||||||
|
|
||||||
|
Donations
|
||||||
|
---------
|
||||||
|
|
||||||
|
**E-Mail**: ```i2porignal at yandex.com```
|
||||||
|
|
||||||
|
**BTC**: ```3MDoGJW9TLMTCDGrR9bLgWXfm6sjmgy86f```
|
||||||
|
|
||||||
|
**LTC**: ```LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59```
|
||||||
|
|
||||||
|
**ETH**: ```0x9e5bac70d20d1079ceaa111127f4fb3bccce379d```
|
||||||
|
|
||||||
|
**GST**: ```GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG```
|
||||||
|
|
||||||
|
**DASH**: ```Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF```
|
||||||
|
|
||||||
|
**ZEC**: ```t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ```
|
||||||
|
|
||||||
|
**ANC**: ```AQJYweYYUqM1nVfLqfoSMpUMfzxvS4Xd7z```
|
||||||
|
|
||||||
|
**XMR**: ```497pJc7X4xqKvcLBLpSUtRgWqMMyo24u4btCos3cak6gbMkpobgSU6492ztUcUBghyeHpYeczB55s38NpuHoH5WGNSPDRMH```
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
|
||||||
This project is licensed under the BSD 3-clause license, which can be found in the file
|
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.
|
LICENSE in the root of the project source code.
|
||||||
|
|
||||||
Donations
|
|
||||||
---------
|
|
||||||
|
|
||||||
BTC: 1K7Ds6KUeR8ya287UC4rYTjvC96vXyZbDY
|
|
||||||
LTC: LKQirrYrDeTuAPnpYq5y7LVKtywfkkHi59
|
|
||||||
|
|
||||||
Requirements for Linux/FreeBSD/OSX
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
GCC 4.6 or newer, Boost 1.46 or newer, crypto++. Clang can be used instead of
|
|
||||||
GCC.
|
|
||||||
|
|
||||||
Requirements for Windows
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
Downloads
|
|
||||||
------------
|
|
||||||
|
|
||||||
Official binary releases could be found at:
|
|
||||||
http://download.i2p.io/purplei2p/i2pd/releases/
|
|
||||||
|
|
||||||
|
|
||||||
Build Statuses
|
|
||||||
---------------
|
|
||||||
|
|
||||||
- Linux x64 - [](https://jenkins.nordcloud.no/job/i2pd-linux/)
|
|
||||||
- Linux ARM - To be added
|
|
||||||
- Mac OS X - Got it working, but not well tested. (Only works with clang, not GCC.)
|
|
||||||
- Microsoft VC13 - To be added
|
|
||||||
|
|
||||||
|
|
||||||
Testing
|
|
||||||
-------
|
|
||||||
|
|
||||||
First, build it.
|
|
||||||
|
|
||||||
On Ubuntu/Debian based
|
|
||||||
* sudo apt-get install libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-regex-dev libcrypto++-dev libboost-date-time-dev
|
|
||||||
* $ cd i2pd
|
|
||||||
* $ make
|
|
||||||
|
|
||||||
Then, run it:
|
|
||||||
|
|
||||||
$ ./i2p
|
|
||||||
|
|
||||||
The client should now reseed by itself.
|
|
||||||
|
|
||||||
To visit an I2P page, you need to find the b32 address of your destination.
|
|
||||||
After that, go to the webconsole and add it behind the url. (Remove http:// from the address)
|
|
||||||
|
|
||||||
This should resulting in for example:
|
|
||||||
http://localhost:7070/4oes3rlgrpbkmzv4lqcfili23h3cvpwslqcfjlk6vvguxyggspwa.b32.i2p
|
|
||||||
|
|
||||||
|
|
||||||
Cmdline options
|
|
||||||
---------------
|
|
||||||
|
|
||||||
* --host= - The external IP (deprecated).
|
|
||||||
* --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).
|
|
||||||
* --v6= - 1 if supports communication through ipv6, off by default
|
|
||||||
* --floodfill= - 1 if router is floodfill, off by default
|
|
||||||
* --bandwidth= - L if bandwidth is limited to 32Kbs/sec, O if not. Always O if floodfill, otherwise L by default.
|
|
||||||
* --httpproxyport= - The port to listen on (HTTP Proxy)
|
|
||||||
* --socksproxyport= - The port to listen on (SOCKS Proxy)
|
|
||||||
* --proxykeys= - optional keys file for proxy's local destination
|
|
||||||
* --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 tunnel's local destination
|
|
||||||
* --eepkeys= - File name containing destination keys, for example privKeys.dat.
|
|
||||||
The file will be created if it does not already exist (issue #110).
|
|
||||||
* --eephost= - Address incoming trafic forward to. 127.0.0.1 by default
|
|
||||||
* --eepport= - Port incoming trafic forward to. 80 by default
|
|
||||||
* --samport= - Port of SAM bridge. Usually 7656. SAM is off if not specified
|
|
||||||
* --bobport= - Port of BOB command channel. Usually 2827. BOB is off if not specified
|
|
||||||
* --i2pcontrolport= - Port of I2P control service. Usually 7650. I2PControl is off if not specified
|
|
||||||
* --conf= - Config file (default: ~/.i2pd/i2p.conf or /var/lib/i2pd/i2p.conf)
|
|
||||||
This parameter will be silently ignored if the specified config file does not exist.
|
|
||||||
Options specified on the command line take precedence over those in the config file.
|
|
||||||
|
|
||||||
Config files
|
|
||||||
------------
|
|
||||||
|
|
||||||
INI-like, syntax is the following : <key> = <value>.
|
|
||||||
All command-line parameters are allowed as keys, for example:
|
|
||||||
|
|
||||||
i2p.conf:
|
|
||||||
|
|
||||||
log = 1
|
|
||||||
v6 = 0
|
|
||||||
ircdest = irc.postman.i2p
|
|
||||||
|
|
||||||
tunnels.cfg (filename of this config is subject of change):
|
|
||||||
|
|
||||||
; outgoing tunnel sample, to remote service
|
|
||||||
; mandatory parameters:
|
|
||||||
; * type -- always "client"
|
|
||||||
; * port -- local port to listen to
|
|
||||||
; * destination -- i2p hostname
|
|
||||||
; optional parameters (may be omitted)
|
|
||||||
; * keys -- our identity, if unset, will be generated on every startup,
|
|
||||||
; if set and file missing, keys will be generated and placed to this file
|
|
||||||
[IRC]
|
|
||||||
type = client
|
|
||||||
port = 6668
|
|
||||||
destination = irc.echelon.i2p
|
|
||||||
keys = irc-keys.dat
|
|
||||||
|
|
||||||
; incoming tunnel sample, for local service
|
|
||||||
; mandatory parameters:
|
|
||||||
; * type -- always "server"
|
|
||||||
; * host -- ip address of our service
|
|
||||||
; * port -- port of our service
|
|
||||||
; * keys -- file with LeaseSet of address in i2p
|
|
||||||
; optional parameters (may be omitted)
|
|
||||||
; * inport -- optional, i2p service port, if unset - the same as 'port'
|
|
||||||
; * accesslist -- comma-separated list of i2p addresses, allowed to connect
|
|
||||||
; every address is b32 without '.b32.i2p' part
|
|
||||||
[LOCALSITE]
|
|
||||||
type = server
|
|
||||||
host = 127.0.0.1
|
|
||||||
port = 80
|
|
||||||
keys = site-keys.dat
|
|
||||||
inport = 81
|
|
||||||
accesslist = <b32>[,<b32>]
|
|
||||||
|
|||||||
932
Reseed.cpp
932
Reseed.cpp
@@ -1,932 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <boost/regex.hpp>
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include <cryptopp/asn.h>
|
|
||||||
#include <cryptopp/base64.h>
|
|
||||||
#include <cryptopp/crc.h>
|
|
||||||
#include <cryptopp/hmac.h>
|
|
||||||
#include <cryptopp/zinflate.h>
|
|
||||||
#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1
|
|
||||||
#include <cryptopp/arc4.h>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Reseed.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "CryptoConst.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
|
|
||||||
static std::vector<std::string> httpReseedHostList = {
|
|
||||||
"http://netdb.i2p2.no/", // only SU3 (v2) support
|
|
||||||
"http://i2p-netdb.innovatio.no/",
|
|
||||||
"http://193.150.121.66/netDb/"
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::vector<std::string> httpsReseedHostList = {
|
|
||||||
// "https://193.150.121.66/netDb/", // unstable
|
|
||||||
// "https://i2p-netdb.innovatio.no/",// Vuln to POODLE
|
|
||||||
"https://netdb.i2p2.no/", // Only SU3 (v2) support
|
|
||||||
"https://reseed.i2p-projekt.de/", // Only HTTPS
|
|
||||||
//"https://cowpuncher.drollette.com/netdb/", // returns error
|
|
||||||
"https://netdb.rows.io:444/",
|
|
||||||
"https://uk.reseed.i2p2.no:444/"
|
|
||||||
// following hosts are fine but don't support AES256
|
|
||||||
/*"https://i2p.mooo.com/netDb/",
|
|
||||||
"https://link.mx24.eu/", // Only HTTPS and SU3 (v2) support
|
|
||||||
"https://i2pseed.zarrenspry.info/", // Only HTTPS and SU3 (v2) support
|
|
||||||
"https://ieb9oopo.mooo.com/" // Only HTTPS and SU3 (v2) support*/
|
|
||||||
};
|
|
||||||
|
|
||||||
Reseeder::Reseeder()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Reseeder::~Reseeder()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Reseeder::reseedNow()
|
|
||||||
{
|
|
||||||
// This method is deprecated
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::string reseedHost = httpReseedHostList[(rand() % httpReseedHostList.size())];
|
|
||||||
LogPrint("Reseeding from ", reseedHost);
|
|
||||||
std::string content = i2p::util::http::httpRequest(reseedHost);
|
|
||||||
if (content == "")
|
|
||||||
{
|
|
||||||
LogPrint("Reseed failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
boost::regex e("<\\s*A\\s+[^>]*href\\s*=\\s*\"([^\"]*)\"", boost::regex::normal | boost::regbase::icase);
|
|
||||||
boost::sregex_token_iterator i(content.begin(), content.end(), e, 1);
|
|
||||||
boost::sregex_token_iterator j;
|
|
||||||
//TODO: Ugly code, try to clean up.
|
|
||||||
//TODO: Try to reduce N number of variables
|
|
||||||
std::string name;
|
|
||||||
std::string routerInfo;
|
|
||||||
std::string tmpUrl;
|
|
||||||
std::string filename;
|
|
||||||
std::string ignoreFileSuffix = ".su3";
|
|
||||||
boost::filesystem::path root = i2p::util::filesystem::GetDataDir();
|
|
||||||
while (i != j)
|
|
||||||
{
|
|
||||||
name = *i++;
|
|
||||||
if (name.find(ignoreFileSuffix)!=std::string::npos)
|
|
||||||
continue;
|
|
||||||
LogPrint("Downloading ", name);
|
|
||||||
tmpUrl = reseedHost;
|
|
||||||
tmpUrl.append(name);
|
|
||||||
routerInfo = i2p::util::http::httpRequest(tmpUrl);
|
|
||||||
if (routerInfo.size()==0)
|
|
||||||
continue;
|
|
||||||
filename = root.string();
|
|
||||||
#ifndef _WIN32
|
|
||||||
filename += "/netDb/r";
|
|
||||||
#else
|
|
||||||
filename += "\\netDb\\r";
|
|
||||||
#endif
|
|
||||||
filename += name.at(11); // first char in id
|
|
||||||
#ifndef _WIN32
|
|
||||||
filename.append("/");
|
|
||||||
#else
|
|
||||||
filename.append("\\");
|
|
||||||
#endif
|
|
||||||
filename.append(name.c_str());
|
|
||||||
std::ofstream outfile (filename, std::ios::binary);
|
|
||||||
outfile << routerInfo;
|
|
||||||
outfile.close();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
//TODO: error reporting
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Reseeder::ReseedNowSU3 ()
|
|
||||||
{
|
|
||||||
CryptoPP::AutoSeededRandomPool rnd;
|
|
||||||
auto ind = rnd.GenerateWord32 (0, httpReseedHostList.size() - 1 + httpsReseedHostList.size () - 1);
|
|
||||||
std::string reseedHost = (ind < httpReseedHostList.size()) ? httpReseedHostList[ind] :
|
|
||||||
httpsReseedHostList[ind - httpReseedHostList.size()];
|
|
||||||
return ReseedFromSU3 (reseedHost, ind >= httpReseedHostList.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
int Reseeder::ReseedFromSU3 (const std::string& host, bool https)
|
|
||||||
{
|
|
||||||
std::string url = host + "i2pseeds.su3";
|
|
||||||
LogPrint (eLogInfo, "Dowloading SU3 from ", host);
|
|
||||||
std::string su3 = https ? HttpsRequest (url) : i2p::util::http::httpRequest (url);
|
|
||||||
if (su3.length () > 0)
|
|
||||||
{
|
|
||||||
std::stringstream s(su3);
|
|
||||||
return ProcessSU3Stream (s);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SU3 download failed");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Reseeder::ProcessSU3File (const char * filename)
|
|
||||||
{
|
|
||||||
std::ifstream s(filename, std::ifstream::binary);
|
|
||||||
if (s.is_open ())
|
|
||||||
return ProcessSU3Stream (s);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't open file ", filename);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const char SU3_MAGIC_NUMBER[]="I2Psu3";
|
|
||||||
const uint32_t ZIP_HEADER_SIGNATURE = 0x04034B50;
|
|
||||||
const uint32_t ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE = 0x02014B50;
|
|
||||||
const uint16_t ZIP_BIT_FLAG_DATA_DESCRIPTOR = 0x0008;
|
|
||||||
int Reseeder::ProcessSU3Stream (std::istream& s)
|
|
||||||
{
|
|
||||||
char magicNumber[7];
|
|
||||||
s.read (magicNumber, 7); // magic number and zero byte 6
|
|
||||||
if (strcmp (magicNumber, SU3_MAGIC_NUMBER))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Unexpected SU3 magic number");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s.seekg (1, std::ios::cur); // su3 file format version
|
|
||||||
SigningKeyType signatureType;
|
|
||||||
s.read ((char *)&signatureType, 2); // signature type
|
|
||||||
signatureType = be16toh (signatureType);
|
|
||||||
uint16_t signatureLength;
|
|
||||||
s.read ((char *)&signatureLength, 2); // signature length
|
|
||||||
signatureLength = be16toh (signatureLength);
|
|
||||||
s.seekg (1, std::ios::cur); // unused
|
|
||||||
uint8_t versionLength;
|
|
||||||
s.read ((char *)&versionLength, 1); // version length
|
|
||||||
s.seekg (1, std::ios::cur); // unused
|
|
||||||
uint8_t signerIDLength;
|
|
||||||
s.read ((char *)&signerIDLength, 1); // signer ID length
|
|
||||||
uint64_t contentLength;
|
|
||||||
s.read ((char *)&contentLength, 8); // content length
|
|
||||||
contentLength = be64toh (contentLength);
|
|
||||||
s.seekg (1, std::ios::cur); // unused
|
|
||||||
uint8_t fileType;
|
|
||||||
s.read ((char *)&fileType, 1); // file type
|
|
||||||
if (fileType != 0x00) // zip file
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't handle file type ", (int)fileType);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s.seekg (1, std::ios::cur); // unused
|
|
||||||
uint8_t contentType;
|
|
||||||
s.read ((char *)&contentType, 1); // content type
|
|
||||||
if (contentType != 0x03) // reseed data
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Unexpected content type ", (int)contentType);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
s.seekg (12, std::ios::cur); // unused
|
|
||||||
|
|
||||||
s.seekg (versionLength, std::ios::cur); // skip version
|
|
||||||
char signerID[256];
|
|
||||||
s.read (signerID, signerIDLength); // signerID
|
|
||||||
signerID[signerIDLength] = 0;
|
|
||||||
|
|
||||||
//try to verify signature
|
|
||||||
auto it = m_SigningKeys.find (signerID);
|
|
||||||
if (it != m_SigningKeys.end ())
|
|
||||||
{
|
|
||||||
// TODO: implement all signature types
|
|
||||||
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
|
|
||||||
{
|
|
||||||
size_t pos = s.tellg ();
|
|
||||||
size_t tbsLen = pos + contentLength;
|
|
||||||
uint8_t * tbs = new uint8_t[tbsLen];
|
|
||||||
s.seekg (0, std::ios::beg);
|
|
||||||
s.read ((char *)tbs, tbsLen);
|
|
||||||
uint8_t * signature = new uint8_t[signatureLength];
|
|
||||||
s.read ((char *)signature, signatureLength);
|
|
||||||
// RSA-raw
|
|
||||||
i2p::crypto::RSASHA5124096RawVerifier verifier(it->second);
|
|
||||||
verifier.Update (tbs, tbsLen);
|
|
||||||
if (!verifier.Verify (signature))
|
|
||||||
LogPrint (eLogWarning, "SU3 signature verification failed");
|
|
||||||
delete[] signature;
|
|
||||||
delete[] tbs;
|
|
||||||
s.seekg (pos, std::ios::beg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Signature type ", signatureType, " is not supported");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Certificate for ", signerID, " not loaded");
|
|
||||||
|
|
||||||
// handle content
|
|
||||||
int numFiles = 0;
|
|
||||||
size_t contentPos = s.tellg ();
|
|
||||||
while (!s.eof ())
|
|
||||||
{
|
|
||||||
uint32_t signature;
|
|
||||||
s.read ((char *)&signature, 4);
|
|
||||||
signature = le32toh (signature);
|
|
||||||
if (signature == ZIP_HEADER_SIGNATURE)
|
|
||||||
{
|
|
||||||
// next local file
|
|
||||||
s.seekg (2, std::ios::cur); // version
|
|
||||||
uint16_t bitFlag;
|
|
||||||
s.read ((char *)&bitFlag, 2);
|
|
||||||
bitFlag = le16toh (bitFlag);
|
|
||||||
uint16_t compressionMethod;
|
|
||||||
s.read ((char *)&compressionMethod, 2);
|
|
||||||
compressionMethod = le16toh (compressionMethod);
|
|
||||||
s.seekg (4, std::ios::cur); // skip fields we don't care about
|
|
||||||
uint32_t compressedSize, uncompressedSize;
|
|
||||||
uint8_t crc32[4];
|
|
||||||
s.read ((char *)crc32, 4);
|
|
||||||
s.read ((char *)&compressedSize, 4);
|
|
||||||
compressedSize = le32toh (compressedSize);
|
|
||||||
s.read ((char *)&uncompressedSize, 4);
|
|
||||||
uncompressedSize = le32toh (uncompressedSize);
|
|
||||||
uint16_t fileNameLength, extraFieldLength;
|
|
||||||
s.read ((char *)&fileNameLength, 2);
|
|
||||||
fileNameLength = le16toh (fileNameLength);
|
|
||||||
s.read ((char *)&extraFieldLength, 2);
|
|
||||||
extraFieldLength = le16toh (extraFieldLength);
|
|
||||||
char localFileName[255];
|
|
||||||
s.read (localFileName, fileNameLength);
|
|
||||||
localFileName[fileNameLength] = 0;
|
|
||||||
s.seekg (extraFieldLength, std::ios::cur);
|
|
||||||
// take care about data desriptor if presented
|
|
||||||
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
|
|
||||||
{
|
|
||||||
size_t pos = s.tellg ();
|
|
||||||
if (!FindZipDataDescriptor (s))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SU3 archive data descriptor not found");
|
|
||||||
return numFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
s.read ((char *)crc32, 4);
|
|
||||||
s.read ((char *)&compressedSize, 4);
|
|
||||||
compressedSize = le32toh (compressedSize) + 4; // ??? we must consider signature as part of compressed data
|
|
||||||
s.read ((char *)&uncompressedSize, 4);
|
|
||||||
uncompressedSize = le32toh (uncompressedSize);
|
|
||||||
|
|
||||||
// now we know compressed and uncompressed size
|
|
||||||
s.seekg (pos, std::ios::beg); // back to compressed data
|
|
||||||
}
|
|
||||||
|
|
||||||
LogPrint (eLogDebug, "Proccessing file ", localFileName, " ", compressedSize, " bytes");
|
|
||||||
if (!compressedSize)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Unexpected size 0. Skipped");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t * compressed = new uint8_t[compressedSize];
|
|
||||||
s.read ((char *)compressed, compressedSize);
|
|
||||||
if (compressionMethod) // we assume Deflate
|
|
||||||
{
|
|
||||||
CryptoPP::Inflator decompressor;
|
|
||||||
decompressor.Put (compressed, compressedSize);
|
|
||||||
decompressor.MessageEnd();
|
|
||||||
if (decompressor.MaxRetrievable () <= uncompressedSize)
|
|
||||||
{
|
|
||||||
uint8_t * uncompressed = new uint8_t[uncompressedSize];
|
|
||||||
decompressor.Get (uncompressed, uncompressedSize);
|
|
||||||
if (CryptoPP::CRC32().VerifyDigest (crc32, uncompressed, uncompressedSize))
|
|
||||||
{
|
|
||||||
i2p::data::netdb.AddRouterInfo (uncompressed, uncompressedSize);
|
|
||||||
numFiles++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "CRC32 verification failed");
|
|
||||||
delete[] uncompressed;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Actual uncompressed size ", decompressor.MaxRetrievable (), " exceed ", uncompressedSize, " from header");
|
|
||||||
}
|
|
||||||
else // no compression
|
|
||||||
{
|
|
||||||
i2p::data::netdb.AddRouterInfo (compressed, compressedSize);
|
|
||||||
numFiles++;
|
|
||||||
}
|
|
||||||
delete[] compressed;
|
|
||||||
if (bitFlag & ZIP_BIT_FLAG_DATA_DESCRIPTOR)
|
|
||||||
s.seekg (12, std::ios::cur); // skip data descriptor section if presented (12 = 16 - 4)
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (signature != ZIP_CENTRAL_DIRECTORY_HEADER_SIGNATURE)
|
|
||||||
LogPrint (eLogWarning, "Missing zip central directory header");
|
|
||||||
break; // no more files
|
|
||||||
}
|
|
||||||
size_t end = s.tellg ();
|
|
||||||
if (end - contentPos >= contentLength)
|
|
||||||
break; // we are beyond contentLength
|
|
||||||
}
|
|
||||||
return numFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t ZIP_DATA_DESCRIPTOR_SIGNATURE[] = { 0x50, 0x4B, 0x07, 0x08 };
|
|
||||||
bool Reseeder::FindZipDataDescriptor (std::istream& s)
|
|
||||||
{
|
|
||||||
size_t nextInd = 0;
|
|
||||||
while (!s.eof ())
|
|
||||||
{
|
|
||||||
uint8_t nextByte;
|
|
||||||
s.read ((char *)&nextByte, 1);
|
|
||||||
if (nextByte == ZIP_DATA_DESCRIPTOR_SIGNATURE[nextInd])
|
|
||||||
{
|
|
||||||
nextInd++;
|
|
||||||
if (nextInd >= sizeof (ZIP_DATA_DESCRIPTOR_SIGNATURE))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
nextInd = 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char CERTIFICATE_HEADER[] = "-----BEGIN CERTIFICATE-----";
|
|
||||||
const char CERTIFICATE_FOOTER[] = "-----END CERTIFICATE-----";
|
|
||||||
void Reseeder::LoadCertificate (const std::string& filename)
|
|
||||||
{
|
|
||||||
std::ifstream s(filename, std::ifstream::binary);
|
|
||||||
if (s.is_open ())
|
|
||||||
{
|
|
||||||
s.seekg (0, std::ios::end);
|
|
||||||
size_t len = s.tellg ();
|
|
||||||
s.seekg (0, std::ios::beg);
|
|
||||||
char buf[2048];
|
|
||||||
s.read (buf, len);
|
|
||||||
std::string cert (buf, len);
|
|
||||||
// assume file in pem format
|
|
||||||
auto pos1 = cert.find (CERTIFICATE_HEADER);
|
|
||||||
auto pos2 = cert.find (CERTIFICATE_FOOTER);
|
|
||||||
if (pos1 == std::string::npos || pos2 == std::string::npos)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Malformed certificate file");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pos1 += strlen (CERTIFICATE_HEADER);
|
|
||||||
pos2 -= pos1;
|
|
||||||
std::string base64 = cert.substr (pos1, pos2);
|
|
||||||
|
|
||||||
CryptoPP::ByteQueue queue;
|
|
||||||
CryptoPP::Base64Decoder decoder; // regular base64 rather than I2P
|
|
||||||
decoder.Attach (new CryptoPP::Redirector (queue));
|
|
||||||
decoder.Put ((const uint8_t *)base64.data(), base64.length());
|
|
||||||
decoder.MessageEnd ();
|
|
||||||
|
|
||||||
LoadCertificate (queue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Can't open certificate file ", filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Reseeder::LoadCertificate (CryptoPP::ByteQueue& queue)
|
|
||||||
{
|
|
||||||
// extract X.509
|
|
||||||
CryptoPP::BERSequenceDecoder x509Cert (queue);
|
|
||||||
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
|
|
||||||
// version
|
|
||||||
uint32_t ver;
|
|
||||||
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
|
|
||||||
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
|
|
||||||
// serial
|
|
||||||
CryptoPP::Integer serial;
|
|
||||||
serial.BERDecode(tbsCert);
|
|
||||||
// signature
|
|
||||||
CryptoPP::BERSequenceDecoder signature (tbsCert);
|
|
||||||
signature.SkipAll();
|
|
||||||
|
|
||||||
// issuer
|
|
||||||
std::string name;
|
|
||||||
CryptoPP::BERSequenceDecoder issuer (tbsCert);
|
|
||||||
{
|
|
||||||
CryptoPP::BERSetDecoder c (issuer); c.SkipAll();
|
|
||||||
CryptoPP::BERSetDecoder st (issuer); st.SkipAll();
|
|
||||||
CryptoPP::BERSetDecoder l (issuer); l.SkipAll();
|
|
||||||
CryptoPP::BERSetDecoder o (issuer); o.SkipAll();
|
|
||||||
CryptoPP::BERSetDecoder ou (issuer); ou.SkipAll();
|
|
||||||
CryptoPP::BERSetDecoder cn (issuer);
|
|
||||||
{
|
|
||||||
CryptoPP::BERSequenceDecoder attributes (cn);
|
|
||||||
{
|
|
||||||
CryptoPP::BERGeneralDecoder ident(attributes, CryptoPP::OBJECT_IDENTIFIER);
|
|
||||||
ident.SkipAll ();
|
|
||||||
CryptoPP::BERDecodeTextString (attributes, name, CryptoPP::UTF8_STRING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
issuer.SkipAll();
|
|
||||||
// validity
|
|
||||||
CryptoPP::BERSequenceDecoder validity (tbsCert);
|
|
||||||
validity.SkipAll();
|
|
||||||
// subject
|
|
||||||
CryptoPP::BERSequenceDecoder subject (tbsCert);
|
|
||||||
subject.SkipAll();
|
|
||||||
// public key
|
|
||||||
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
|
|
||||||
{
|
|
||||||
CryptoPP::BERSequenceDecoder ident (publicKey);
|
|
||||||
ident.SkipAll ();
|
|
||||||
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
|
|
||||||
key.Skip (1); // FIXME: probably bug in crypto++
|
|
||||||
CryptoPP::BERSequenceDecoder keyPair (key);
|
|
||||||
CryptoPP::Integer n;
|
|
||||||
n.BERDecode (keyPair);
|
|
||||||
if (name.length () > 0)
|
|
||||||
{
|
|
||||||
PublicKey value;
|
|
||||||
n.Encode (value, 512);
|
|
||||||
m_SigningKeys[name] = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Unknown issuer. Skipped");
|
|
||||||
}
|
|
||||||
publicKey.SkipAll();
|
|
||||||
|
|
||||||
tbsCert.SkipAll();
|
|
||||||
x509Cert.SkipAll();
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reseeder::LoadCertificates ()
|
|
||||||
{
|
|
||||||
boost::filesystem::path reseedDir = i2p::util::filesystem::GetCertificatesDir() / "reseed";
|
|
||||||
|
|
||||||
if (!boost::filesystem::exists (reseedDir))
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Reseed certificates not loaded. ", reseedDir, " doesn't exist");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int numCertificates = 0;
|
|
||||||
boost::filesystem::directory_iterator end; // empty
|
|
||||||
for (boost::filesystem::directory_iterator it (reseedDir); it != end; ++it)
|
|
||||||
{
|
|
||||||
if (boost::filesystem::is_regular_file (it->status()) && it->path ().extension () == ".crt")
|
|
||||||
{
|
|
||||||
LoadCertificate (it->path ().string ());
|
|
||||||
numCertificates++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, numCertificates, " certificates loaded");
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Reseeder::HttpsRequest (const std::string& address)
|
|
||||||
{
|
|
||||||
i2p::util::http::url u(address);
|
|
||||||
if (u.port_ == 80) u.port_ = 443;
|
|
||||||
TlsSession session (u.host_, u.port_);
|
|
||||||
|
|
||||||
if (session.IsEstablished ())
|
|
||||||
{
|
|
||||||
// send request
|
|
||||||
std::stringstream ss;
|
|
||||||
ss << "GET " << u.path_ << " HTTP/1.1\r\nHost: " << u.host_
|
|
||||||
<< "\r\nAccept: */*\r\n" << "User-Agent: Wget/1.11.4\r\n" << "Connection: close\r\n\r\n";
|
|
||||||
session.Send ((uint8_t *)ss.str ().c_str (), ss.str ().length ());
|
|
||||||
|
|
||||||
// read response
|
|
||||||
std::stringstream rs;
|
|
||||||
while (session.Receive (rs))
|
|
||||||
;
|
|
||||||
return i2p::util::http::GetHttpContent (rs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
//-------------------------------------------------------------
|
|
||||||
|
|
||||||
template<class Hash>
|
|
||||||
class TlsCipherMAC: public TlsCipher
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TlsCipherMAC (const uint8_t * keys): m_Seqn (0)
|
|
||||||
{
|
|
||||||
memcpy (m_MacKey, keys, Hash::DIGESTSIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac)
|
|
||||||
{
|
|
||||||
uint8_t header[13]; // seqn (8) + type (1) + version (2) + length (2)
|
|
||||||
htobe64buf (header, m_Seqn);
|
|
||||||
header[8] = type; header[9] = 3; header[10] = 3; // 3,3 means TLS 1.2
|
|
||||||
htobe16buf (header + 11, len);
|
|
||||||
CryptoPP::HMAC<Hash> hmac (m_MacKey, Hash::DIGESTSIZE);
|
|
||||||
hmac.Update (header, 13);
|
|
||||||
hmac.Update (buf, len);
|
|
||||||
hmac.Final (mac);
|
|
||||||
m_Seqn++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
uint64_t m_Seqn;
|
|
||||||
uint8_t m_MacKey[Hash::DIGESTSIZE]; // client
|
|
||||||
};
|
|
||||||
|
|
||||||
template<class Hash>
|
|
||||||
class TlsCipher_AES_256_CBC: public TlsCipherMAC<Hash>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TlsCipher_AES_256_CBC (const uint8_t * keys): TlsCipherMAC<Hash> (keys)
|
|
||||||
{
|
|
||||||
m_Encryption.SetKey (keys + 2*Hash::DIGESTSIZE);
|
|
||||||
m_Decryption.SetKey (keys + 2*Hash::DIGESTSIZE + 32);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out)
|
|
||||||
{
|
|
||||||
size_t size = 0;
|
|
||||||
m_Rnd.GenerateBlock (out, 16); // iv
|
|
||||||
size += 16;
|
|
||||||
m_Encryption.SetIV (out);
|
|
||||||
memcpy (out + size, in, len);
|
|
||||||
size += len;
|
|
||||||
memcpy (out + size, mac, Hash::DIGESTSIZE);
|
|
||||||
size += Hash::DIGESTSIZE;
|
|
||||||
uint8_t paddingSize = size + 1;
|
|
||||||
paddingSize &= 0x0F; // %16
|
|
||||||
if (paddingSize > 0) paddingSize = 16 - paddingSize;
|
|
||||||
memset (out + size, paddingSize, paddingSize + 1); // paddind and last byte are equal to padding size
|
|
||||||
size += paddingSize + 1;
|
|
||||||
m_Encryption.Encrypt (out + 16, size - 16, out + 16);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Decrypt (uint8_t * buf, size_t len) // payload is buf + 16
|
|
||||||
{
|
|
||||||
m_Decryption.SetIV (buf);
|
|
||||||
m_Decryption.Decrypt (buf + 16, len - 16, buf + 16);
|
|
||||||
return len - 16 - Hash::DIGESTSIZE - buf[len -1] - 1; // IV(16), mac(32 or 20) and padding
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetIVSize () const { return 16; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::AutoSeededRandomPool m_Rnd;
|
|
||||||
i2p::crypto::CBCEncryption m_Encryption;
|
|
||||||
i2p::crypto::CBCDecryption m_Decryption;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class TlsCipher_RC4_SHA: public TlsCipherMAC<CryptoPP::SHA1>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TlsCipher_RC4_SHA (const uint8_t * keys): TlsCipherMAC (keys)
|
|
||||||
{
|
|
||||||
m_Encryption.SetKey (keys + 40, 16); // 20 + 20
|
|
||||||
m_Decryption.SetKey (keys + 56, 16); // 20 + 20 + 16
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out)
|
|
||||||
{
|
|
||||||
memcpy (out, in, len);
|
|
||||||
memcpy (out + len, mac, 20);
|
|
||||||
m_Encryption.ProcessData (out, out, len + 20);
|
|
||||||
return len + 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Decrypt (uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
m_Decryption.ProcessData (buf, buf, len);
|
|
||||||
return len - 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::Weak1::ARC4 m_Encryption, m_Decryption;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
TlsSession::TlsSession (const std::string& host, int port):
|
|
||||||
m_IsEstablished (false), m_Cipher (nullptr)
|
|
||||||
{
|
|
||||||
m_Site.connect(host, boost::lexical_cast<std::string>(port));
|
|
||||||
if (m_Site.good ())
|
|
||||||
Handshake ();
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Can't connect to ", host, ":", port);
|
|
||||||
}
|
|
||||||
|
|
||||||
TlsSession::~TlsSession ()
|
|
||||||
{
|
|
||||||
delete m_Cipher;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TlsSession::Handshake ()
|
|
||||||
{
|
|
||||||
static uint8_t clientHello[] =
|
|
||||||
{
|
|
||||||
0x16, // handshake
|
|
||||||
0x03, 0x03, // version (TLS 1.2)
|
|
||||||
0x00, 0x33, // length of handshake
|
|
||||||
// handshake
|
|
||||||
0x01, // handshake type (client hello)
|
|
||||||
0x00, 0x00, 0x2F, // length of handshake payload
|
|
||||||
// client hello
|
|
||||||
0x03, 0x03, // highest version supported (TLS 1.2)
|
|
||||||
0x45, 0xFA, 0x01, 0x19, 0x74, 0x55, 0x18, 0x36,
|
|
||||||
0x42, 0x05, 0xC1, 0xDD, 0x4A, 0x21, 0x80, 0x80,
|
|
||||||
0xEC, 0x37, 0x11, 0x93, 0x16, 0xF4, 0x66, 0x00,
|
|
||||||
0x12, 0x67, 0xAB, 0xBA, 0xFF, 0x29, 0x13, 0x9E, // 32 random bytes
|
|
||||||
0x00, // session id length
|
|
||||||
0x00, 0x06, // chiper suites length
|
|
||||||
0x00, 0x3D, // RSA_WITH_AES_256_CBC_SHA256
|
|
||||||
0x00, 0x35, // RSA_WITH_AES_256_CBC_SHA
|
|
||||||
0x00, 0x05, // RSA_WITH_RC4_128_SHA
|
|
||||||
0x01, // compression methods length
|
|
||||||
0x00, // no compression
|
|
||||||
0x00, 0x00 // extensions length
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint8_t changeCipherSpecs[] =
|
|
||||||
{
|
|
||||||
0x14, // change cipher specs
|
|
||||||
0x03, 0x03, // version (TLS 1.2)
|
|
||||||
0x00, 0x01, // length
|
|
||||||
0x01 // type
|
|
||||||
};
|
|
||||||
|
|
||||||
// send ClientHello
|
|
||||||
m_Site.write ((char *)clientHello, sizeof (clientHello));
|
|
||||||
m_FinishedHash.Update (clientHello + 5, sizeof (clientHello) - 5);
|
|
||||||
// read ServerHello
|
|
||||||
uint8_t type;
|
|
||||||
m_Site.read ((char *)&type, 1);
|
|
||||||
uint16_t version;
|
|
||||||
m_Site.read ((char *)&version, 2);
|
|
||||||
uint16_t length;
|
|
||||||
m_Site.read ((char *)&length, 2);
|
|
||||||
length = be16toh (length);
|
|
||||||
char * serverHello = new char[length];
|
|
||||||
m_Site.read (serverHello, length);
|
|
||||||
m_FinishedHash.Update ((uint8_t *)serverHello, length);
|
|
||||||
uint8_t serverRandom[32];
|
|
||||||
if (serverHello[0] == 0x02) // handshake type server hello
|
|
||||||
memcpy (serverRandom, serverHello + 6, 32);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHello[0]);
|
|
||||||
uint8_t sessionIDLen = serverHello[38]; // 6 + 32
|
|
||||||
char * cipherSuite = serverHello + 39 + sessionIDLen;
|
|
||||||
if (cipherSuite[1] == 0x3D || cipherSuite[1] == 0x35 || cipherSuite[1] == 0x05)
|
|
||||||
m_IsEstablished = true;
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Unsupported cipher ", (int)cipherSuite[0], ",", (int)cipherSuite[1]);
|
|
||||||
// read Certificate
|
|
||||||
m_Site.read ((char *)&type, 1);
|
|
||||||
m_Site.read ((char *)&version, 2);
|
|
||||||
m_Site.read ((char *)&length, 2);
|
|
||||||
length = be16toh (length);
|
|
||||||
char * certificate = new char[length];
|
|
||||||
m_Site.read (certificate, length);
|
|
||||||
m_FinishedHash.Update ((uint8_t *)certificate, length);
|
|
||||||
CryptoPP::RSA::PublicKey publicKey;
|
|
||||||
// 0 - handshake type
|
|
||||||
// 1 - 3 - handshake payload length
|
|
||||||
// 4 - 6 - length of array of certificates
|
|
||||||
// 7 - 9 - length of certificate
|
|
||||||
if (certificate[0] == 0x0B) // handshake type certificate
|
|
||||||
publicKey = ExtractPublicKey ((uint8_t *)certificate + 10, length - 10);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Unexpected handshake type ", (int)certificate[0]);
|
|
||||||
// read ServerHelloDone
|
|
||||||
m_Site.read ((char *)&type, 1);
|
|
||||||
m_Site.read ((char *)&version, 2);
|
|
||||||
m_Site.read ((char *)&length, 2);
|
|
||||||
length = be16toh (length);
|
|
||||||
char * serverHelloDone = new char[length];
|
|
||||||
m_Site.read (serverHelloDone, length);
|
|
||||||
m_FinishedHash.Update ((uint8_t *)serverHelloDone, length);
|
|
||||||
if (serverHelloDone[0] != 0x0E) // handshake type hello done
|
|
||||||
LogPrint (eLogError, "Unexpected handshake type ", (int)serverHelloDone[0]);
|
|
||||||
// our turn now
|
|
||||||
// generate secret key
|
|
||||||
uint8_t secret[48];
|
|
||||||
secret[0] = 3; secret[1] = 3; // version
|
|
||||||
CryptoPP::AutoSeededRandomPool rnd;
|
|
||||||
rnd.GenerateBlock (secret + 2, 46); // 46 random bytes
|
|
||||||
// encrypt RSA
|
|
||||||
CryptoPP::RSAES_PKCS1v15_Encryptor encryptor(publicKey);
|
|
||||||
size_t encryptedLen = encryptor.CiphertextLength (48); // number of bytes for encrypted 48 bytes, usually 256 (2048 bits key)
|
|
||||||
uint8_t * encrypted = new uint8_t[encryptedLen + 2]; // + 2 bytes for length
|
|
||||||
htobe16buf (encrypted, encryptedLen); // first two bytes means length
|
|
||||||
encryptor.Encrypt (rnd, secret, 48, encrypted + 2);
|
|
||||||
// send ClientKeyExchange
|
|
||||||
// 0x10 - handshake type "client key exchange"
|
|
||||||
SendHandshakeMsg (0x10, encrypted, encryptedLen + 2);
|
|
||||||
delete[] encrypted;
|
|
||||||
// send ChangeCipherSpecs
|
|
||||||
m_Site.write ((char *)changeCipherSpecs, sizeof (changeCipherSpecs));
|
|
||||||
// calculate master secret
|
|
||||||
uint8_t random[64];
|
|
||||||
memcpy (random, clientHello + 11, 32);
|
|
||||||
memcpy (random + 32, serverRandom, 32);
|
|
||||||
PRF (secret, "master secret", random, 64, 48, m_MasterSecret);
|
|
||||||
// create keys
|
|
||||||
memcpy (random, serverRandom, 32);
|
|
||||||
memcpy (random + 32, clientHello + 11, 32);
|
|
||||||
uint8_t keys[128]; // clientMACKey(32 or 20), serverMACKey(32 or 20), clientKey(32), serverKey(32)
|
|
||||||
PRF (m_MasterSecret, "key expansion", random, 64, 128, keys);
|
|
||||||
// create cipher
|
|
||||||
if (cipherSuite[1] == 0x3D)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Chiper suite is RSA_WITH_AES_256_CBC_SHA256");
|
|
||||||
m_Cipher = new TlsCipher_AES_256_CBC<CryptoPP::SHA256> (keys);
|
|
||||||
}
|
|
||||||
else if (cipherSuite[1] == 0x35)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Chiper suite is RSA_WITH_AES_256_CBC_SHA");
|
|
||||||
m_Cipher = new TlsCipher_AES_256_CBC<CryptoPP::SHA1> (keys);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO:
|
|
||||||
if (cipherSuite[1] == 0x05)
|
|
||||||
LogPrint (eLogInfo, "Chiper suite is RSA_WITH_RC4_128_SHA");
|
|
||||||
m_Cipher = new TlsCipher_RC4_SHA (keys);
|
|
||||||
}
|
|
||||||
// send finished
|
|
||||||
SendFinishedMsg ();
|
|
||||||
// read ChangeCipherSpecs
|
|
||||||
uint8_t changeCipherSpecs1[6];
|
|
||||||
m_Site.read ((char *)changeCipherSpecs1, 6);
|
|
||||||
// read finished
|
|
||||||
m_Site.read ((char *)&type, 1);
|
|
||||||
m_Site.read ((char *)&version, 2);
|
|
||||||
m_Site.read ((char *)&length, 2);
|
|
||||||
length = be16toh (length);
|
|
||||||
char * finished1 = new char[length];
|
|
||||||
m_Site.read (finished1, length);
|
|
||||||
m_Cipher->Decrypt ((uint8_t *)finished1, length); // for streaming ciphers
|
|
||||||
delete[] finished1;
|
|
||||||
|
|
||||||
delete[] serverHello;
|
|
||||||
delete[] certificate;
|
|
||||||
delete[] serverHelloDone;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TlsSession::SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len)
|
|
||||||
{
|
|
||||||
uint8_t handshakeHeader[9];
|
|
||||||
handshakeHeader[0] = 0x16; // handshake
|
|
||||||
handshakeHeader[1] = 0x03; handshakeHeader[2] = 0x03; // version is always TLS 1.2 (3,3)
|
|
||||||
htobe16buf (handshakeHeader + 3, len + 4); // length of payload
|
|
||||||
//payload starts
|
|
||||||
handshakeHeader[5] = handshakeType; // handshake type
|
|
||||||
handshakeHeader[6] = 0; // highest byte of payload length is always zero
|
|
||||||
htobe16buf (handshakeHeader + 7, len); // length of data
|
|
||||||
m_Site.write ((char *)handshakeHeader, 9);
|
|
||||||
m_FinishedHash.Update (handshakeHeader + 5, 4); // only payload counts
|
|
||||||
m_Site.write ((char *)data, len);
|
|
||||||
m_FinishedHash.Update (data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TlsSession::SendFinishedMsg ()
|
|
||||||
{
|
|
||||||
// 0x16 handshake
|
|
||||||
// 0x03, 0x03 version (TLS 1.2)
|
|
||||||
// 2 bytes length of handshake (80 or 64 bytes)
|
|
||||||
// handshake (encrypted)
|
|
||||||
// unencrypted context
|
|
||||||
// 0x14 handshake type (finished)
|
|
||||||
// 0x00, 0x00, 0x0C length of handshake payload
|
|
||||||
// 12 bytes of verified data
|
|
||||||
|
|
||||||
uint8_t finishedHashDigest[32], finishedPayload[40], encryptedPayload[80];
|
|
||||||
finishedPayload[0] = 0x14; // handshake type (finished)
|
|
||||||
finishedPayload[1] = 0; finishedPayload[2] = 0; finishedPayload[3] = 0x0C; // 12 bytes
|
|
||||||
m_FinishedHash.Final (finishedHashDigest);
|
|
||||||
PRF (m_MasterSecret, "client finished", finishedHashDigest, 32, 12, finishedPayload + 4);
|
|
||||||
uint8_t mac[32];
|
|
||||||
m_Cipher->CalculateMAC (0x16, finishedPayload, 16, mac);
|
|
||||||
size_t encryptedPayloadSize = m_Cipher->Encrypt (finishedPayload, 16, mac, encryptedPayload);
|
|
||||||
uint8_t finished[5];
|
|
||||||
finished[0] = 0x16; // handshake
|
|
||||||
finished[1] = 0x03; finished[2] = 0x03; // version is always TLS 1.2 (3,3)
|
|
||||||
htobe16buf (finished + 3, encryptedPayloadSize); // length of payload
|
|
||||||
m_Site.write ((char *)finished, sizeof (finished));
|
|
||||||
m_Site.write ((char *)encryptedPayload, encryptedPayloadSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TlsSession::PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
|
|
||||||
size_t len, uint8_t * buf)
|
|
||||||
{
|
|
||||||
// secret is assumed 48 bytes
|
|
||||||
// random is not more than 64 bytes
|
|
||||||
CryptoPP::HMAC<CryptoPP::SHA256> hmac (secret, 48);
|
|
||||||
uint8_t seed[96]; size_t seedLen;
|
|
||||||
seedLen = strlen (label);
|
|
||||||
memcpy (seed, label, seedLen);
|
|
||||||
memcpy (seed + seedLen, random, randomLen);
|
|
||||||
seedLen += randomLen;
|
|
||||||
|
|
||||||
size_t offset = 0;
|
|
||||||
uint8_t a[128];
|
|
||||||
hmac.CalculateDigest (a, seed, seedLen);
|
|
||||||
while (offset < len)
|
|
||||||
{
|
|
||||||
memcpy (a + 32, seed, seedLen);
|
|
||||||
hmac.CalculateDigest (buf + offset, a, seedLen + 32);
|
|
||||||
offset += 32;
|
|
||||||
hmac.CalculateDigest (a, a, 32);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoPP::RSA::PublicKey TlsSession::ExtractPublicKey (const uint8_t * certificate, size_t len)
|
|
||||||
{
|
|
||||||
CryptoPP::ByteQueue queue;
|
|
||||||
queue.Put (certificate, len);
|
|
||||||
queue.MessageEnd ();
|
|
||||||
// extract X.509
|
|
||||||
CryptoPP::BERSequenceDecoder x509Cert (queue);
|
|
||||||
CryptoPP::BERSequenceDecoder tbsCert (x509Cert);
|
|
||||||
// version
|
|
||||||
uint32_t ver;
|
|
||||||
CryptoPP::BERGeneralDecoder context (tbsCert, CryptoPP::CONTEXT_SPECIFIC | CryptoPP::CONSTRUCTED);
|
|
||||||
CryptoPP::BERDecodeUnsigned<uint32_t>(context, ver, CryptoPP::INTEGER);
|
|
||||||
// serial
|
|
||||||
CryptoPP::Integer serial;
|
|
||||||
serial.BERDecode(tbsCert);
|
|
||||||
// signature
|
|
||||||
CryptoPP::BERSequenceDecoder signature (tbsCert);
|
|
||||||
signature.SkipAll();
|
|
||||||
// issuer
|
|
||||||
CryptoPP::BERSequenceDecoder issuer (tbsCert);
|
|
||||||
issuer.SkipAll();
|
|
||||||
// validity
|
|
||||||
CryptoPP::BERSequenceDecoder validity (tbsCert);
|
|
||||||
validity.SkipAll();
|
|
||||||
// subject
|
|
||||||
CryptoPP::BERSequenceDecoder subject (tbsCert);
|
|
||||||
subject.SkipAll();
|
|
||||||
// public key
|
|
||||||
CryptoPP::BERSequenceDecoder publicKey (tbsCert);
|
|
||||||
CryptoPP::BERSequenceDecoder ident (publicKey);
|
|
||||||
ident.SkipAll ();
|
|
||||||
CryptoPP::BERGeneralDecoder key (publicKey, CryptoPP::BIT_STRING);
|
|
||||||
key.Skip (1); // FIXME: probably bug in crypto++
|
|
||||||
CryptoPP::BERSequenceDecoder keyPair (key);
|
|
||||||
CryptoPP::Integer n, e;
|
|
||||||
n.BERDecode (keyPair);
|
|
||||||
e.BERDecode (keyPair);
|
|
||||||
|
|
||||||
CryptoPP::RSA::PublicKey ret;
|
|
||||||
ret.Initialize (n, e);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TlsSession::Send (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
uint8_t * out = new uint8_t[len + 64 + 5]; // 64 = 32 mac + 16 iv + upto 16 padding, 5 = header
|
|
||||||
out[0] = 0x17; // application data
|
|
||||||
out[1] = 0x03; out[2] = 0x03; // version
|
|
||||||
uint8_t mac[32];
|
|
||||||
m_Cipher->CalculateMAC (0x17, buf, len, mac);
|
|
||||||
size_t encryptedLen = m_Cipher->Encrypt (buf, len, mac, out + 5);
|
|
||||||
htobe16buf (out + 3, encryptedLen);
|
|
||||||
m_Site.write ((char *)out, encryptedLen + 5);
|
|
||||||
delete[] out;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TlsSession::Receive (std::ostream& rs)
|
|
||||||
{
|
|
||||||
if (m_Site.eof ()) return false;
|
|
||||||
uint8_t type; uint16_t version, length;
|
|
||||||
m_Site.read ((char *)&type, 1);
|
|
||||||
m_Site.read ((char *)&version, 2);
|
|
||||||
m_Site.read ((char *)&length, 2);
|
|
||||||
length = be16toh (length);
|
|
||||||
uint8_t * buf = new uint8_t[length];
|
|
||||||
m_Site.read ((char *)buf, length);
|
|
||||||
size_t decryptedLen = m_Cipher->Decrypt (buf, length);
|
|
||||||
rs.write ((char *)buf + m_Cipher->GetIVSize (), decryptedLen);
|
|
||||||
delete[] buf;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
95
Reseed.h
95
Reseed.h
@@ -1,95 +0,0 @@
|
|||||||
#ifndef RESEED_H
|
|
||||||
#define RESEED_H
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <cryptopp/osrng.h>
|
|
||||||
#include <cryptopp/rsa.h>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "aes.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
|
|
||||||
class Reseeder
|
|
||||||
{
|
|
||||||
typedef Tag<512> PublicKey;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
Reseeder();
|
|
||||||
~Reseeder();
|
|
||||||
bool reseedNow(); // depreacted
|
|
||||||
int ReseedNowSU3 ();
|
|
||||||
|
|
||||||
void LoadCertificates ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void LoadCertificate (const std::string& filename);
|
|
||||||
std::string LoadCertificate (CryptoPP::ByteQueue& queue); // returns issuer's name
|
|
||||||
|
|
||||||
int ReseedFromSU3 (const std::string& host, bool https = false);
|
|
||||||
int ProcessSU3File (const char * filename);
|
|
||||||
int ProcessSU3Stream (std::istream& s);
|
|
||||||
|
|
||||||
bool FindZipDataDescriptor (std::istream& s);
|
|
||||||
|
|
||||||
std::string HttpsRequest (const std::string& address);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::map<std::string, PublicKey> m_SigningKeys;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class TlsCipher
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~TlsCipher () {};
|
|
||||||
|
|
||||||
virtual void CalculateMAC (uint8_t type, const uint8_t * buf, size_t len, uint8_t * mac) = 0;
|
|
||||||
virtual size_t Encrypt (const uint8_t * in, size_t len, const uint8_t * mac, uint8_t * out) = 0;
|
|
||||||
virtual size_t Decrypt (uint8_t * buf, size_t len) = 0;
|
|
||||||
virtual size_t GetIVSize () const { return 0; }; // override for AES
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class TlsSession
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TlsSession (const std::string& host, int port);
|
|
||||||
~TlsSession ();
|
|
||||||
void Send (const uint8_t * buf, size_t len);
|
|
||||||
bool Receive (std::ostream& rs);
|
|
||||||
bool IsEstablished () const { return m_IsEstablished; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Handshake ();
|
|
||||||
void SendHandshakeMsg (uint8_t handshakeType, uint8_t * data, size_t len);
|
|
||||||
void SendFinishedMsg ();
|
|
||||||
CryptoPP::RSA::PublicKey ExtractPublicKey (const uint8_t * certificate, size_t len);
|
|
||||||
|
|
||||||
void PRF (const uint8_t * secret, const char * label, const uint8_t * random, size_t randomLen,
|
|
||||||
size_t len, uint8_t * buf);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_IsEstablished;
|
|
||||||
boost::asio::ip::tcp::iostream m_Site;
|
|
||||||
CryptoPP::SHA256 m_FinishedHash;
|
|
||||||
uint8_t m_MasterSecret[64]; // actual size is 48, but must be multiple of 32
|
|
||||||
TlsCipher * m_Cipher;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
#include <fstream>
|
|
||||||
#include <cryptopp/dh.h>
|
|
||||||
#include <cryptopp/dsa.h>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include "CryptoConst.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "util.h"
|
|
||||||
#include "version.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
RouterContext context;
|
|
||||||
|
|
||||||
RouterContext::RouterContext ():
|
|
||||||
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
|
|
||||||
m_StartupTime (0), m_Status (eRouterStatusOK )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::Init ()
|
|
||||||
{
|
|
||||||
m_StartupTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
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::SetFloodfill (bool floodfill)
|
|
||||||
{
|
|
||||||
m_IsFloodfill = floodfill;
|
|
||||||
if (floodfill)
|
|
||||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eFloodfill);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eFloodfill);
|
|
||||||
// we don't publish number of routers and leaseset for non-floodfill
|
|
||||||
m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_LEASESETS);
|
|
||||||
m_RouterInfo.DeleteProperty (ROUTER_INFO_PROPERTY_ROUTERS);
|
|
||||||
}
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetHighBandwidth ()
|
|
||||||
{
|
|
||||||
if (!m_RouterInfo.IsHighBandwidth ())
|
|
||||||
{
|
|
||||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () | i2p::data::RouterInfo::eHighBandwidth);
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetLowBandwidth ()
|
|
||||||
{
|
|
||||||
if (m_RouterInfo.IsHighBandwidth ())
|
|
||||||
{
|
|
||||||
m_RouterInfo.SetCaps (m_RouterInfo.GetCaps () & ~i2p::data::RouterInfo::eHighBandwidth);
|
|
||||||
UpdateRouterInfo ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterContext::IsUnreachable () const
|
|
||||||
{
|
|
||||||
return m_RouterInfo.GetCaps () & i2p::data::RouterInfo::eUnreachable;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::SetUnreachable ()
|
|
||||||
{
|
|
||||||
// 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::SetReachable ()
|
|
||||||
{
|
|
||||||
// update caps
|
|
||||||
uint8_t caps = m_RouterInfo.GetCaps ();
|
|
||||||
caps &= ~i2p::data::RouterInfo::eUnreachable;
|
|
||||||
caps |= i2p::data::RouterInfo::eReachable;
|
|
||||||
caps |= i2p::data::RouterInfo::eSSUIntroducer;
|
|
||||||
if (m_IsFloodfill)
|
|
||||||
caps |= i2p::data::RouterInfo::eFloodfill;
|
|
||||||
m_RouterInfo.SetCaps (caps);
|
|
||||||
|
|
||||||
// insert NTCP back
|
|
||||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
|
||||||
for (size_t i = 0; i < addresses.size (); i++)
|
|
||||||
{
|
|
||||||
if (addresses[i].transportStyle == i2p::data::RouterInfo::eTransportSSU)
|
|
||||||
{
|
|
||||||
// insert NTCP address with host/port form SSU
|
|
||||||
m_RouterInfo.AddNTCPAddress (addresses[i].host.to_string ().c_str (), addresses[i].port);
|
|
||||||
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 ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::UpdateStats ()
|
|
||||||
{
|
|
||||||
if (m_IsFloodfill)
|
|
||||||
{
|
|
||||||
// update routers and leasesets
|
|
||||||
m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_LEASESETS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumLeaseSets ()));
|
|
||||||
m_RouterInfo.SetProperty (ROUTER_INFO_PROPERTY_ROUTERS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumRouters ()));
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (IsUnreachable ())
|
|
||||||
SetReachable (); // we assume reachable until we discover firewall through peer tests
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> RouterContext::GetTunnelPool () const
|
|
||||||
{
|
|
||||||
return i2p::tunnel::tunnels.GetExploratoryPool ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from)
|
|
||||||
{
|
|
||||||
i2p::HandleI2NPMessage (CreateI2NPMessage (buf, GetI2NPMessageLength (buf), from));
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
|
||||||
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterContext::ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_GarlicMutex);
|
|
||||||
i2p::garlic::GarlicDestination::ProcessDeliveryStatusMessage (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t RouterContext::GetUptime () const
|
|
||||||
{
|
|
||||||
return i2p::util::GetSecondsSinceEpoch () - m_StartupTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
107
RouterContext.h
107
RouterContext.h
@@ -1,107 +0,0 @@
|
|||||||
#ifndef ROUTER_CONTEXT_H__
|
|
||||||
#define ROUTER_CONTEXT_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#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
|
|
||||||
|
|
||||||
const char ROUTER_INFO_PROPERTY_LEASESETS[] = "netdb.knownLeaseSets";
|
|
||||||
const char ROUTER_INFO_PROPERTY_ROUTERS[] = "netdb.knownRouters";
|
|
||||||
|
|
||||||
enum RouterStatus
|
|
||||||
{
|
|
||||||
eRouterStatusOK = 0,
|
|
||||||
eRouterStatusTesting = 1,
|
|
||||||
eRouterStatusFirewalled = 2
|
|
||||||
};
|
|
||||||
|
|
||||||
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; };
|
|
||||||
uint32_t GetUptime () const;
|
|
||||||
uint32_t GetStartupTime () const { return m_StartupTime; };
|
|
||||||
uint64_t GetLastUpdateTime () const { return m_LastUpdateTime; };
|
|
||||||
RouterStatus GetStatus () const { return m_Status; };
|
|
||||||
void SetStatus (RouterStatus status) { m_Status = status; };
|
|
||||||
|
|
||||||
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;
|
|
||||||
void SetUnreachable ();
|
|
||||||
void SetReachable ();
|
|
||||||
bool IsFloodfill () const { return m_IsFloodfill; };
|
|
||||||
void SetFloodfill (bool floodfill);
|
|
||||||
void SetHighBandwidth ();
|
|
||||||
void SetLowBandwidth ();
|
|
||||||
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
|
|
||||||
void UpdateStats ();
|
|
||||||
|
|
||||||
// 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
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> GetLeaseSet () { return nullptr; };
|
|
||||||
std::shared_ptr<i2p::tunnel::TunnelPool> GetTunnelPool () const;
|
|
||||||
void HandleI2NPMessage (const uint8_t * buf, size_t len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
|
||||||
|
|
||||||
// override GarlicDestination
|
|
||||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void ProcessDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
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_AcceptsTunnels, m_IsFloodfill;
|
|
||||||
uint64_t m_StartupTime; // in seconds since epoch
|
|
||||||
RouterStatus m_Status;
|
|
||||||
std::mutex m_GarlicMutex;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern RouterContext context;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
666
RouterInfo.cpp
666
RouterInfo.cpp
@@ -1,666 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include <fstream>
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include <cryptopp/sha.h>
|
|
||||||
#include <cryptopp/dsa.h>
|
|
||||||
#include "CryptoConst.h"
|
|
||||||
#include "base64.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace data
|
|
||||||
{
|
|
||||||
RouterInfo::RouterInfo (const std::string& fullPath):
|
|
||||||
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
|
|
||||||
m_SupportedTransports (0), m_Caps (0)
|
|
||||||
{
|
|
||||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
|
||||||
ReadFromFile ();
|
|
||||||
}
|
|
||||||
|
|
||||||
RouterInfo::RouterInfo (const uint8_t * buf, int len):
|
|
||||||
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
|
|
||||||
{
|
|
||||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
|
||||||
memcpy (m_Buffer, buf, len);
|
|
||||||
m_BufferLen = len;
|
|
||||||
ReadFromBuffer (true);
|
|
||||||
}
|
|
||||||
|
|
||||||
RouterInfo::~RouterInfo ()
|
|
||||||
{
|
|
||||||
delete[] m_Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::Update (const uint8_t * buf, int len)
|
|
||||||
{
|
|
||||||
if (!m_Buffer)
|
|
||||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
|
||||||
m_IsUpdated = true;
|
|
||||||
m_IsUnreachable = false;
|
|
||||||
m_SupportedTransports = 0;
|
|
||||||
m_Caps = 0;
|
|
||||||
m_Addresses.clear ();
|
|
||||||
m_Properties.clear ();
|
|
||||||
memcpy (m_Buffer, buf, len);
|
|
||||||
m_BufferLen = len;
|
|
||||||
ReadFromBuffer (true);
|
|
||||||
// don't delete buffer until save to file
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::SetRouterIdentity (const IdentityEx& identity)
|
|
||||||
{
|
|
||||||
m_RouterIdentity = identity;
|
|
||||||
m_Timestamp = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RouterInfo::LoadFile ()
|
|
||||||
{
|
|
||||||
std::ifstream s(m_FullPath.c_str (), std::ifstream::binary);
|
|
||||||
if (s.is_open ())
|
|
||||||
{
|
|
||||||
s.seekg (0,std::ios::end);
|
|
||||||
m_BufferLen = s.tellg ();
|
|
||||||
if (m_BufferLen < 40)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError, "File", m_FullPath, " is malformed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
s.seekg(0, std::ios::beg);
|
|
||||||
if (!m_Buffer)
|
|
||||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
|
||||||
s.read((char *)m_Buffer, m_BufferLen);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't open file ", m_FullPath);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::ReadFromFile ()
|
|
||||||
{
|
|
||||||
if (LoadFile ())
|
|
||||||
ReadFromBuffer (false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::ReadFromBuffer (bool verifySignature)
|
|
||||||
{
|
|
||||||
size_t identityLen = m_RouterIdentity.FromBuffer (m_Buffer, m_BufferLen);
|
|
||||||
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
|
|
||||||
ReadFromStream (str);
|
|
||||||
if (verifySignature)
|
|
||||||
{
|
|
||||||
// verify signature
|
|
||||||
int l = m_BufferLen - m_RouterIdentity.GetSignatureLen ();
|
|
||||||
if (!m_RouterIdentity.Verify ((uint8_t *)m_Buffer, l, (uint8_t *)m_Buffer + l))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "signature verification failed");
|
|
||||||
m_IsUnreachable = true;
|
|
||||||
}
|
|
||||||
m_RouterIdentity.DropVerifier ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::ReadFromStream (std::istream& s)
|
|
||||||
{
|
|
||||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
|
||||||
m_Timestamp = be64toh (m_Timestamp);
|
|
||||||
// read addresses
|
|
||||||
uint8_t numAddresses;
|
|
||||||
s.read ((char *)&numAddresses, sizeof (numAddresses));
|
|
||||||
bool introducers = false;
|
|
||||||
for (int i = 0; i < numAddresses; i++)
|
|
||||||
{
|
|
||||||
bool isValidAddress = true;
|
|
||||||
Address address;
|
|
||||||
s.read ((char *)&address.cost, sizeof (address.cost));
|
|
||||||
s.read ((char *)&address.date, sizeof (address.date));
|
|
||||||
char transportStyle[5];
|
|
||||||
ReadString (transportStyle, s);
|
|
||||||
if (!strcmp (transportStyle, "NTCP"))
|
|
||||||
address.transportStyle = eTransportNTCP;
|
|
||||||
else if (!strcmp (transportStyle, "SSU"))
|
|
||||||
address.transportStyle = eTransportSSU;
|
|
||||||
else
|
|
||||||
address.transportStyle = eTransportUnknown;
|
|
||||||
address.port = 0;
|
|
||||||
address.mtu = 0;
|
|
||||||
uint16_t size, r = 0;
|
|
||||||
s.read ((char *)&size, sizeof (size));
|
|
||||||
size = be16toh (size);
|
|
||||||
while (r < size)
|
|
||||||
{
|
|
||||||
char key[500], value[500];
|
|
||||||
r += ReadString (key, s);
|
|
||||||
s.seekg (1, std::ios_base::cur); r++; // =
|
|
||||||
r += ReadString (value, s);
|
|
||||||
s.seekg (1, std::ios_base::cur); r++; // ;
|
|
||||||
if (!strcmp (key, "host"))
|
|
||||||
{
|
|
||||||
boost::system::error_code ecode;
|
|
||||||
address.host = boost::asio::ip::address::from_string (value, ecode);
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
if (address.transportStyle == eTransportNTCP)
|
|
||||||
{
|
|
||||||
m_SupportedTransports |= eNTCPV4; // TODO:
|
|
||||||
address.addressString = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO: resolve address for SSU
|
|
||||||
LogPrint (eLogWarning, "Unexpected SSU 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;
|
|
||||||
if (m_Caps & eFloodfill)
|
|
||||||
{
|
|
||||||
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // highest bandwidth
|
|
||||||
caps += CAPS_FLAG_FLOODFILL; // floodfill
|
|
||||||
}
|
|
||||||
else
|
|
||||||
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 : CAPS_FLAG_LOW_BANDWIDTH2; // bandwidth
|
|
||||||
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);
|
|
||||||
if (f.is_open ())
|
|
||||||
f.write ((char *)m_Buffer, m_BufferLen);
|
|
||||||
else
|
|
||||||
LogPrint(eLogError, "Can't save RouterInfo to ", fullPath);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Can't save RouterInfo m_Buffer==NULL");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 std::string& key, const std::string& value)
|
|
||||||
{
|
|
||||||
m_Properties[key] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RouterInfo::DeleteProperty (const std::string& key)
|
|
||||||
{
|
|
||||||
m_Properties.erase (key);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<RouterProfile> RouterInfo::GetProfile () const
|
|
||||||
{
|
|
||||||
if (!m_Profile)
|
|
||||||
m_Profile = GetRouterProfile (GetIdentHash ());
|
|
||||||
return m_Profile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
182
RouterInfo.h
182
RouterInfo.h
@@ -1,182 +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"
|
|
||||||
#include "Profiling.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;
|
|
||||||
std::string addressString;
|
|
||||||
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 std::string& key, const std::string& value); // called from RouterContext only
|
|
||||||
void DeleteProperty (const std::string& key); // called from RouterContext only
|
|
||||||
void ClearProperties () { m_Properties.clear (); };
|
|
||||||
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; };
|
|
||||||
bool IsHighBandwidth () const { return m_Caps & RouterInfo::eHighBandwidth; };
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
std::shared_ptr<RouterProfile> GetProfile () const;
|
|
||||||
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
|
|
||||||
|
|
||||||
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;
|
|
||||||
mutable std::shared_ptr<RouterProfile> m_Profile;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
871
SAM.cpp
871
SAM.cpp
@@ -1,871 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#include <stdlib.h>
|
|
||||||
#endif
|
|
||||||
#include <boost/lexical_cast.hpp>
|
|
||||||
#include "base64.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Log.h"
|
|
||||||
#include "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_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false),
|
|
||||||
m_Stream (nullptr), m_Session (nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMSocket::~SAMSocket ()
|
|
||||||
{
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::CloseStream ()
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
m_Stream->Close ();
|
|
||||||
m_Stream.reset ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::Terminate ()
|
|
||||||
{
|
|
||||||
CloseStream ();
|
|
||||||
|
|
||||||
switch (m_SocketType)
|
|
||||||
{
|
|
||||||
case eSAMSocketTypeSession:
|
|
||||||
m_Owner.CloseSession (m_ID);
|
|
||||||
break;
|
|
||||||
case eSAMSocketTypeStream:
|
|
||||||
{
|
|
||||||
if (m_Session)
|
|
||||||
m_Session->sockets.remove (shared_from_this ());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eSAMSocketTypeAcceptor:
|
|
||||||
{
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
m_Session->sockets.remove (shared_from_this ());
|
|
||||||
m_Session->localDestination->StopAcceptingStreams ();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
m_SocketType = eSAMSocketTypeTerminated;
|
|
||||||
m_Socket.close ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ReceiveHandshake ()
|
|
||||||
{
|
|
||||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
|
||||||
std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleHandshakeReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM handshake read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Buffer[bytes_transferred] = 0;
|
|
||||||
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
|
|
||||||
if (eol)
|
|
||||||
*eol = 0;
|
|
||||||
LogPrint ("SAM handshake ", m_Buffer);
|
|
||||||
char * separator = strchr (m_Buffer, ' ');
|
|
||||||
if (separator)
|
|
||||||
{
|
|
||||||
separator = strchr (separator + 1, ' ');
|
|
||||||
if (separator)
|
|
||||||
*separator = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!strcmp (m_Buffer, SAM_HANDSHAKE))
|
|
||||||
{
|
|
||||||
std::string version("3.0");
|
|
||||||
// try to find MIN and MAX, 3.0 if not found
|
|
||||||
if (separator)
|
|
||||||
{
|
|
||||||
separator++;
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (separator, params);
|
|
||||||
auto it = params.find (SAM_PARAM_MAX);
|
|
||||||
// TODO: check MIN as well
|
|
||||||
if (it != params.end ())
|
|
||||||
version = it->second;
|
|
||||||
}
|
|
||||||
if (version[0] == '3') // we support v3 (3.0 and 3.1) only
|
|
||||||
{
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
|
|
||||||
#else
|
|
||||||
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_HANDSHAKE_REPLY, version.c_str ());
|
|
||||||
#endif
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_Buffer, l), boost::asio::transfer_all (),
|
|
||||||
std::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("SAM handshake mismatch");
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleHandshakeReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM handshake reply send error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
|
||||||
std::bind(&SAMSocket::HandleMessage, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::SendMessageReply (const char * msg, size_t len, bool close)
|
|
||||||
{
|
|
||||||
if (!m_IsSilent)
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (msg, len), boost::asio::transfer_all (),
|
|
||||||
std::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2, close));
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (close)
|
|
||||||
Terminate ();
|
|
||||||
else
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM reply send error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (close)
|
|
||||||
Terminate ();
|
|
||||||
else
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bytes_transferred += m_BufferOffset;
|
|
||||||
m_BufferOffset = 0;
|
|
||||||
m_Buffer[bytes_transferred] = 0;
|
|
||||||
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
|
|
||||||
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 if (!strcmp (m_Buffer, SAM_DATAGRAM_SEND))
|
|
||||||
{
|
|
||||||
size_t len = bytes_transferred - (separator - m_Buffer) - 1;
|
|
||||||
size_t processed = ProcessDatagramSend (separator + 1, len, eol + 1);
|
|
||||||
if (processed < len)
|
|
||||||
{
|
|
||||||
m_BufferOffset = len - processed;
|
|
||||||
if (processed > 0)
|
|
||||||
memmove (m_Buffer, separator + 1 + processed, m_BufferOffset);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// restore string back
|
|
||||||
*separator = ' ';
|
|
||||||
*eol = '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// since it's SAM v1 reply is not expected
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM unexpected message ", m_Buffer);
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SAM malformed message ", m_Buffer);
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SAM incomplete message ", bytes_transferred);
|
|
||||||
m_BufferOffset = bytes_transferred;
|
|
||||||
// try to receive remaining message
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessSessionCreate (char * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM session create: ", buf);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
std::string& style = params[SAM_PARAM_STYLE];
|
|
||||||
std::string& id = params[SAM_PARAM_ID];
|
|
||||||
std::string& destination = params[SAM_PARAM_DESTINATION];
|
|
||||||
m_ID = id;
|
|
||||||
if (m_Owner.FindSession (id))
|
|
||||||
{
|
|
||||||
// session exists
|
|
||||||
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_ID, strlen(SAM_SESSION_CREATE_DUPLICATED_ID), true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create destination
|
|
||||||
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms);
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
m_SocketType = eSAMSocketTypeSession;
|
|
||||||
if (style == SAM_VALUE_DATAGRAM)
|
|
||||||
{
|
|
||||||
auto dest = m_Session->localDestination->CreateDatagramDestination ();
|
|
||||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_Session->localDestination->IsReady ())
|
|
||||||
SendSessionCreateReplyOk ();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
|
|
||||||
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_SESSION_CREATE_DUPLICATED_DEST, strlen(SAM_SESSION_CREATE_DUPLICATED_DEST), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
if (m_Session->localDestination->IsReady ())
|
|
||||||
SendSessionCreateReplyOk ();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
|
|
||||||
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::SendSessionCreateReplyOk ()
|
|
||||||
{
|
|
||||||
uint8_t buf[1024];
|
|
||||||
char priv[1024];
|
|
||||||
size_t l = m_Session->localDestination->GetPrivateKeys ().ToBuffer (buf, 1024);
|
|
||||||
size_t l1 = i2p::data::ByteStreamToBase64 (buf, l, priv, 1024);
|
|
||||||
priv[l1] = 0;
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
|
||||||
#else
|
|
||||||
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, l2, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessStreamConnect (char * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM stream connect: ", buf);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
std::string& id = params[SAM_PARAM_ID];
|
|
||||||
std::string& destination = params[SAM_PARAM_DESTINATION];
|
|
||||||
std::string& silent = params[SAM_PARAM_SILENT];
|
|
||||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
|
||||||
m_ID = id;
|
|
||||||
m_Session = m_Owner.FindSession (id);
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
i2p::data::IdentityEx dest;
|
|
||||||
size_t len = dest.FromBase64(destination);
|
|
||||||
if (len > 0)
|
|
||||||
{
|
|
||||||
context.GetAddressBook().InsertAddress(dest);
|
|
||||||
auto leaseSet = m_Session->localDestination->FindLeaseSet(dest.GetIdentHash());
|
|
||||||
if (leaseSet)
|
|
||||||
Connect(leaseSet);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_Session->localDestination->RequestDestination(dest.GetIdentHash(),
|
|
||||||
std::bind(&SAMSocket::HandleConnectLeaseSetRequestComplete,
|
|
||||||
shared_from_this(), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply(SAM_SESSION_STATUS_INVALID_KEY, strlen(SAM_SESSION_STATUS_INVALID_KEY), true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote)
|
|
||||||
{
|
|
||||||
m_SocketType = eSAMSocketTypeStream;
|
|
||||||
m_Session->sockets.push_back (shared_from_this ());
|
|
||||||
m_Stream = m_Session->localDestination->CreateStream (remote);
|
|
||||||
m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect
|
|
||||||
I2PReceive ();
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
|
||||||
{
|
|
||||||
if (leaseSet)
|
|
||||||
Connect (leaseSet);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("SAM destination to connect not found");
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_CANT_REACH_PEER, strlen(SAM_STREAM_STATUS_CANT_REACH_PEER), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessStreamAccept (char * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM stream accept: ", buf);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
std::string& id = params[SAM_PARAM_ID];
|
|
||||||
std::string& silent = params[SAM_PARAM_SILENT];
|
|
||||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
|
||||||
m_ID = id;
|
|
||||||
m_Session = m_Owner.FindSession (id);
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
if (!m_Session->localDestination->IsAcceptingStreams ())
|
|
||||||
{
|
|
||||||
m_SocketType = eSAMSocketTypeAcceptor;
|
|
||||||
m_Session->sockets.push_back (shared_from_this ());
|
|
||||||
m_Session->localDestination->AcceptStreams (std::bind (&SAMSocket::HandleI2PAccept, shared_from_this (), std::placeholders::_1));
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_I2P_ERROR, strlen(SAM_STREAM_STATUS_I2P_ERROR), true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t SAMSocket::ProcessDatagramSend (char * buf, size_t len, const char * data)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM datagram send: ", buf, " ", len);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
size_t size = boost::lexical_cast<int>(params[SAM_PARAM_SIZE]), offset = data - buf;
|
|
||||||
if (offset + size <= len)
|
|
||||||
{
|
|
||||||
if (m_Session)
|
|
||||||
{
|
|
||||||
auto d = m_Session->localDestination->GetDatagramDestination ();
|
|
||||||
if (d)
|
|
||||||
{
|
|
||||||
i2p::data::IdentityEx dest;
|
|
||||||
dest.FromBase64 (params[SAM_PARAM_DESTINATION]);
|
|
||||||
d->SendDatagramTo ((const uint8_t *)data, size, dest.GetIdentHash ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM missing datagram destination");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM session is not created from DATAGRAM SEND");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SAM sent datagram size ", size, " exceeds buffer ", len - offset);
|
|
||||||
return 0; // try to receive more
|
|
||||||
}
|
|
||||||
return offset + size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessDestGenerate ()
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM dest generate");
|
|
||||||
auto keys = i2p::data::PrivateKeys::CreateRandomKeys ();
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
|
|
||||||
keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_DEST_REPLY,
|
|
||||||
keys.GetPublic ().ToBase64 ().c_str (), keys.ToBase64 ().c_str ());
|
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, len, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ProcessNamingLookup (char * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM naming lookup: ", buf);
|
|
||||||
std::map<std::string, std::string> params;
|
|
||||||
ExtractParams (buf, params);
|
|
||||||
std::string& name = params[SAM_PARAM_NAME];
|
|
||||||
i2p::data::IdentityEx identity;
|
|
||||||
i2p::data::IdentHash ident;
|
|
||||||
if (name == "ME")
|
|
||||||
SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
|
|
||||||
else if (context.GetAddressBook ().GetAddress (name, identity))
|
|
||||||
SendNamingLookupReply (identity);
|
|
||||||
else if (m_Session && m_Session->localDestination &&
|
|
||||||
context.GetAddressBook ().GetIdentHash (name, ident))
|
|
||||||
{
|
|
||||||
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
|
|
||||||
if (leaseSet)
|
|
||||||
SendNamingLookupReply (leaseSet->GetIdentity ());
|
|
||||||
else
|
|
||||||
m_Session->localDestination->RequestDestination (ident,
|
|
||||||
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
|
|
||||||
shared_from_this (), std::placeholders::_1, ident));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("SAM naming failed. Unknown address ", name);
|
|
||||||
#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::HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident)
|
|
||||||
{
|
|
||||||
if (leaseSet)
|
|
||||||
{
|
|
||||||
context.GetAddressBook ().InsertAddress (leaseSet->GetIdentity ());
|
|
||||||
SendNamingLookupReply (leaseSet->GetIdentity ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "SAM naming lookup failed. LeaseSet for ", ident.ToBase32 (), " not found");
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
|
|
||||||
context.GetAddressBook ().ToAddress (ident).c_str());
|
|
||||||
#else
|
|
||||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
|
|
||||||
context.GetAddressBook ().ToAddress (ident).c_str());
|
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, len, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::SendNamingLookupReply (const i2p::data::IdentityEx& identity)
|
|
||||||
{
|
|
||||||
auto base64 = identity.ToBase64 ();
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
|
|
||||||
#else
|
|
||||||
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ());
|
|
||||||
#endif
|
|
||||||
SendMessageReply (m_Buffer, l, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::ExtractParams (char * buf, 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 ()
|
|
||||||
{
|
|
||||||
if (m_BufferOffset >= SAM_SOCKET_BUFFER_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Buffer is full. Terminate");
|
|
||||||
Terminate ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset),
|
|
||||||
std::bind((m_SocketType == eSAMSocketTypeStream) ? &SAMSocket::HandleReceived : &SAMSocket::HandleMessage,
|
|
||||||
shared_from_this (), std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
{
|
|
||||||
auto s = shared_from_this ();
|
|
||||||
m_Stream->AsyncSend ((uint8_t *)m_Buffer, bytes_transferred,
|
|
||||||
[s](const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
s->Receive ();
|
|
||||||
else
|
|
||||||
s->Terminate ();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::I2PReceive ()
|
|
||||||
{
|
|
||||||
if (m_Stream)
|
|
||||||
m_Stream->AsyncReceive (boost::asio::buffer (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE),
|
|
||||||
std::bind (&SAMSocket::HandleI2PReceive, shared_from_this (),
|
|
||||||
std::placeholders::_1, std::placeholders::_2),
|
|
||||||
SAM_SOCKET_CONNECTION_MAX_IDLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM stream read error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
|
|
||||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleWriteI2PData (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM socket write error: ", ecode.message ());
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Terminate ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
I2PReceive ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleI2PAccept (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM incoming I2P connection for session ", m_ID);
|
|
||||||
m_Stream = stream;
|
|
||||||
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
|
|
||||||
auto session = m_Owner.FindSession (m_ID);
|
|
||||||
if (session)
|
|
||||||
session->localDestination->StopAcceptingStreams ();
|
|
||||||
m_SocketType = eSAMSocketTypeStream;
|
|
||||||
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, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
|
|
||||||
m_StreamBuffer[l1] = '\n';
|
|
||||||
HandleI2PReceive (boost::system::error_code (), l1 +1); // we send identity like it has been received from stream
|
|
||||||
}
|
|
||||||
else
|
|
||||||
I2PReceive ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogInfo, "SAM I2P acceptor has been reset");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSocket::HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "SAM datagram received ", len);
|
|
||||||
auto base64 = from.ToBase64 ();
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
|
|
||||||
#else
|
|
||||||
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), len);
|
|
||||||
#endif
|
|
||||||
if (len < SAM_SOCKET_BUFFER_SIZE - l)
|
|
||||||
{
|
|
||||||
memcpy (m_StreamBuffer + l, buf, len);
|
|
||||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len + l),
|
|
||||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "SAM received datagram size ", len," exceeds buffer");
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMSession::SAMSession (std::shared_ptr<ClientDestination> dest):
|
|
||||||
localDestination (dest)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMSession::~SAMSession ()
|
|
||||||
{
|
|
||||||
for (auto it: sockets)
|
|
||||||
it->SetSocketType (eSAMSocketTypeTerminated);
|
|
||||||
i2p::client::context.DeleteLocalDestination (localDestination);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMSession::CloseStreams ()
|
|
||||||
{
|
|
||||||
for (auto it: sockets)
|
|
||||||
{
|
|
||||||
it->CloseStream ();
|
|
||||||
it->SetSocketType (eSAMSocketTypeTerminated);
|
|
||||||
}
|
|
||||||
sockets.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMBridge::SAMBridge (int port):
|
|
||||||
m_IsRunning (false), m_Thread (nullptr),
|
|
||||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
|
|
||||||
m_DatagramEndpoint (boost::asio::ip::udp::v4 (), port-1), m_DatagramSocket (m_Service, m_DatagramEndpoint)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMBridge::~SAMBridge ()
|
|
||||||
{
|
|
||||||
if (m_IsRunning)
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::Start ()
|
|
||||||
{
|
|
||||||
Accept ();
|
|
||||||
ReceiveDatagram ();
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_Thread = new std::thread (std::bind (&SAMBridge::Run, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::Stop ()
|
|
||||||
{
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_Acceptor.cancel ();
|
|
||||||
for (auto it: m_Sessions)
|
|
||||||
delete it.second;
|
|
||||||
m_Sessions.clear ();
|
|
||||||
m_Service.stop ();
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint ("SAM: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::Accept ()
|
|
||||||
{
|
|
||||||
auto newSocket = std::make_shared<SAMSocket> (*this);
|
|
||||||
m_Acceptor.async_accept (newSocket->GetSocket (), std::bind (&SAMBridge::HandleAccept, this,
|
|
||||||
std::placeholders::_1, newSocket));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
boost::system::error_code ec;
|
|
||||||
auto ep = socket->GetSocket ().remote_endpoint (ec);
|
|
||||||
if (!ec)
|
|
||||||
{
|
|
||||||
LogPrint ("New SAM connection from ", ep);
|
|
||||||
socket->ReceiveHandshake ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SAM connection from error ", ec.message ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("SAM accept error: ", ecode.message ());
|
|
||||||
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
Accept ();
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMSession * SAMBridge::CreateSession (const std::string& id, const std::string& destination,
|
|
||||||
const std::map<std::string, std::string> * params)
|
|
||||||
{
|
|
||||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
|
||||||
if (destination != "")
|
|
||||||
{
|
|
||||||
i2p::data::PrivateKeys keys;
|
|
||||||
keys.FromBase64 (destination);
|
|
||||||
localDestination = i2p::client::context.CreateNewLocalDestination (keys, true, params);
|
|
||||||
}
|
|
||||||
else // transient
|
|
||||||
{
|
|
||||||
// extract signature type
|
|
||||||
i2p::data::SigningKeyType signatureType = i2p::data::SIGNING_KEY_TYPE_DSA_SHA1;
|
|
||||||
if (params)
|
|
||||||
{
|
|
||||||
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
|
|
||||||
if (it != params->end ())
|
|
||||||
// TODO: extract string values
|
|
||||||
signatureType = boost::lexical_cast<int> (it->second);
|
|
||||||
}
|
|
||||||
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
|
|
||||||
}
|
|
||||||
if (localDestination)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
auto ret = m_Sessions.insert (std::pair<std::string, SAMSession *>(id, new SAMSession (localDestination)));
|
|
||||||
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 ())
|
|
||||||
{
|
|
||||||
auto session = it->second;
|
|
||||||
session->localDestination->StopAcceptingStreams ();
|
|
||||||
session->CloseStreams ();
|
|
||||||
m_Sessions.erase (it);
|
|
||||||
delete session;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SAMSession * SAMBridge::FindSession (const std::string& id) const
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
auto it = m_Sessions.find (id);
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
return it->second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::ReceiveDatagram ()
|
|
||||||
{
|
|
||||||
m_DatagramSocket.async_receive_from (
|
|
||||||
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
|
|
||||||
m_SenderEndpoint,
|
|
||||||
std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SAMBridge::HandleReceivedDatagram (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
m_DatagramReceiveBuffer[bytes_transferred] = 0;
|
|
||||||
char * eol = strchr ((char *)m_DatagramReceiveBuffer, '\n');
|
|
||||||
*eol = 0; eol++;
|
|
||||||
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
|
|
||||||
LogPrint ("SAM datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
|
|
||||||
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
|
|
||||||
if (sessionID)
|
|
||||||
{
|
|
||||||
sessionID++;
|
|
||||||
char * destination = strchr (sessionID, ' ');
|
|
||||||
if (destination)
|
|
||||||
{
|
|
||||||
*destination = 0; destination++;
|
|
||||||
auto session = FindSession (sessionID);
|
|
||||||
if (session)
|
|
||||||
{
|
|
||||||
i2p::data::IdentityEx dest;
|
|
||||||
dest.FromBase64 (destination);
|
|
||||||
session->localDestination->GetDatagramDestination ()->
|
|
||||||
SendDatagramTo ((uint8_t *)eol, payloadLen, dest.GetIdentHash ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("Session ", sessionID, " not found");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("Missing destination key");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("Missing sessionID");
|
|
||||||
ReceiveDatagram ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint ("SAM datagram receive error: ", ecode.message ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
192
SAM.h
192
SAM.h
@@ -1,192 +0,0 @@
|
|||||||
#ifndef SAM_H__
|
|
||||||
#define SAM_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#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 = 8192;
|
|
||||||
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // 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=%s\n";
|
|
||||||
const char SAM_HANDSHAKE_I2P_ERROR[] = "HELLO REPLY RESULT=I2P_ERROR\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_SESSION_STATUS_INVALID_KEY[] = "SESSION STATUS RESULT=INVALID_KEY\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_DATAGRAM_SEND[] = "DATAGRAM SEND";
|
|
||||||
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=%lu\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_MIN[] = "MIN";
|
|
||||||
const char SAM_PARAM_MAX[] = "MAX";
|
|
||||||
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_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
|
|
||||||
const char SAM_PARAM_SIZE[] = "SIZE";
|
|
||||||
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,
|
|
||||||
eSAMSocketTypeTerminated
|
|
||||||
};
|
|
||||||
|
|
||||||
class SAMBridge;
|
|
||||||
struct SAMSession;
|
|
||||||
class SAMSocket: public std::enable_shared_from_this<SAMSocket>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SAMSocket (SAMBridge& owner);
|
|
||||||
~SAMSocket ();
|
|
||||||
void CloseStream (); // TODO: implement it better
|
|
||||||
|
|
||||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
|
||||||
void ReceiveHandshake ();
|
|
||||||
void SetSocketType (SAMSocketType socketType) { m_SocketType = socketType; };
|
|
||||||
SAMSocketType GetSocketType () const { return m_SocketType; };
|
|
||||||
|
|
||||||
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 (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
void HandleWriteI2PData (const boost::system::error_code& ecode);
|
|
||||||
void HandleI2PDatagramReceive (const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, 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);
|
|
||||||
size_t ProcessDatagramSend (char * buf, size_t len, const char * data); // from SAM 1.0
|
|
||||||
void ExtractParams (char * buf, std::map<std::string, std::string>& params);
|
|
||||||
|
|
||||||
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote);
|
|
||||||
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
|
|
||||||
void SendNamingLookupReply (const i2p::data::IdentityEx& identity);
|
|
||||||
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, i2p::data::IdentHash ident);
|
|
||||||
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];
|
|
||||||
size_t m_BufferOffset;
|
|
||||||
uint8_t m_StreamBuffer[SAM_SOCKET_BUFFER_SIZE];
|
|
||||||
SAMSocketType m_SocketType;
|
|
||||||
std::string m_ID; // nickname
|
|
||||||
bool m_IsSilent;
|
|
||||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
|
||||||
SAMSession * m_Session;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SAMSession
|
|
||||||
{
|
|
||||||
std::shared_ptr<ClientDestination> localDestination;
|
|
||||||
std::list<std::shared_ptr<SAMSocket> > sockets;
|
|
||||||
|
|
||||||
SAMSession (std::shared_ptr<ClientDestination> dest);
|
|
||||||
~SAMSession ();
|
|
||||||
|
|
||||||
void CloseStreams ();
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
const std::map<std::string, std::string> * params);
|
|
||||||
void CloseSession (const std::string& id);
|
|
||||||
SAMSession * FindSession (const std::string& id) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
|
|
||||||
void Accept ();
|
|
||||||
void HandleAccept(const boost::system::error_code& ecode, std::shared_ptr<SAMSocket> socket);
|
|
||||||
|
|
||||||
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;
|
|
||||||
mutable std::mutex m_SessionsMutex;
|
|
||||||
std::map<std::string, SAMSession *> m_Sessions;
|
|
||||||
uint8_t m_DatagramReceiveBuffer[i2p::datagram::MAX_DATAGRAM_SIZE+1];
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP
|
|
||||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
574
SOCKS.cpp
574
SOCKS.cpp
@@ -1,574 +0,0 @@
|
|||||||
#include <cstring>
|
|
||||||
#include <cassert>
|
|
||||||
#include <string>
|
|
||||||
#include <atomic>
|
|
||||||
#include "SOCKS.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "Streaming.h"
|
|
||||||
#include "Destination.h"
|
|
||||||
#include "ClientContext.h"
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "I2PTunnel.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace proxy
|
|
||||||
{
|
|
||||||
static const size_t socks_buffer_size = 8192;
|
|
||||||
static const size_t max_socks_hostname_size = 255; // Limit for socks5 and bad idea to traverse
|
|
||||||
|
|
||||||
struct SOCKSDnsAddress
|
|
||||||
{
|
|
||||||
uint8_t size;
|
|
||||||
char value[max_socks_hostname_size];
|
|
||||||
void FromString (std::string str)
|
|
||||||
{
|
|
||||||
size = str.length();
|
|
||||||
if (str.length() > max_socks_hostname_size) size = max_socks_hostname_size;
|
|
||||||
memcpy(value,str.c_str(),size);
|
|
||||||
}
|
|
||||||
std::string ToString() { return std::string(value, size); }
|
|
||||||
void push_back (char c) { value[size++] = c; }
|
|
||||||
};
|
|
||||||
|
|
||||||
class SOCKSServer;
|
|
||||||
class SOCKSHandler: public i2p::client::I2PServiceHandler, public std::enable_shared_from_this<SOCKSHandler>
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
enum state
|
|
||||||
{
|
|
||||||
GET_SOCKSV,
|
|
||||||
GET_COMMAND,
|
|
||||||
GET_PORT,
|
|
||||||
GET_IPV4,
|
|
||||||
GET4_IDENT,
|
|
||||||
GET4A_HOST,
|
|
||||||
GET5_AUTHNUM,
|
|
||||||
GET5_AUTH,
|
|
||||||
GET5_REQUESTV,
|
|
||||||
GET5_GETRSV,
|
|
||||||
GET5_GETADDRTYPE,
|
|
||||||
GET5_IPV6,
|
|
||||||
GET5_HOST_SIZE,
|
|
||||||
GET5_HOST,
|
|
||||||
DONE
|
|
||||||
};
|
|
||||||
enum authMethods
|
|
||||||
{
|
|
||||||
AUTH_NONE = 0, //No authentication, skip to next step
|
|
||||||
AUTH_GSSAPI = 1, //GSSAPI authentication
|
|
||||||
AUTH_USERPASSWD = 2, //Username and password
|
|
||||||
AUTH_UNACCEPTABLE = 0xff //No acceptable method found
|
|
||||||
};
|
|
||||||
enum addrTypes
|
|
||||||
{
|
|
||||||
ADDR_IPV4 = 1, //IPv4 address (4 octets)
|
|
||||||
ADDR_DNS = 3, // DNS name (up to 255 octets)
|
|
||||||
ADDR_IPV6 = 4 //IPV6 address (16 octets)
|
|
||||||
};
|
|
||||||
enum errTypes
|
|
||||||
{
|
|
||||||
SOCKS5_OK = 0, // No error for SOCKS5
|
|
||||||
SOCKS5_GEN_FAIL = 1, // General server failure
|
|
||||||
SOCKS5_RULE_DENIED = 2, // Connection disallowed by ruleset
|
|
||||||
SOCKS5_NET_UNREACH = 3, // Network unreachable
|
|
||||||
SOCKS5_HOST_UNREACH = 4, // Host unreachable
|
|
||||||
SOCKS5_CONN_REFUSED = 5, // Connection refused by the peer
|
|
||||||
SOCKS5_TTL_EXPIRED = 6, // TTL Expired
|
|
||||||
SOCKS5_CMD_UNSUP = 7, // Command unsuported
|
|
||||||
SOCKS5_ADDR_UNSUP = 8, // Address type unsuported
|
|
||||||
SOCKS4_OK = 90, // No error for SOCKS4
|
|
||||||
SOCKS4_FAIL = 91, // Failed establishing connecting or not allowed
|
|
||||||
SOCKS4_IDENTD_MISSING = 92, // Couldn't connect to the identd server
|
|
||||||
SOCKS4_IDENTD_DIFFER = 93 // The ID reported by the application and by identd differ
|
|
||||||
};
|
|
||||||
enum cmdTypes
|
|
||||||
{
|
|
||||||
CMD_CONNECT = 1, // TCP Connect
|
|
||||||
CMD_BIND = 2, // TCP Bind
|
|
||||||
CMD_UDP = 3 // UDP associate
|
|
||||||
};
|
|
||||||
enum socksVersions
|
|
||||||
{
|
|
||||||
SOCKS4 = 4, // SOCKS4
|
|
||||||
SOCKS5 = 5 // SOCKS5
|
|
||||||
};
|
|
||||||
union address
|
|
||||||
{
|
|
||||||
uint32_t ip;
|
|
||||||
SOCKSDnsAddress dns;
|
|
||||||
uint8_t ipv6[16];
|
|
||||||
};
|
|
||||||
|
|
||||||
void EnterState(state nstate, uint8_t parseleft = 1);
|
|
||||||
bool HandleData(uint8_t *sock_buff, std::size_t len);
|
|
||||||
bool ValidateSOCKSRequest();
|
|
||||||
void HandleSockRecv(const boost::system::error_code & ecode, std::size_t bytes_transfered);
|
|
||||||
void Terminate();
|
|
||||||
void AsyncSockRead();
|
|
||||||
boost::asio::const_buffers_1 GenerateSOCKS5SelectAuth(authMethods method);
|
|
||||||
boost::asio::const_buffers_1 GenerateSOCKS4Response(errTypes error, uint32_t ip, uint16_t port);
|
|
||||||
boost::asio::const_buffers_1 GenerateSOCKS5Response(errTypes error, addrTypes type, const address &addr, uint16_t port);
|
|
||||||
bool Socks5ChooseAuth();
|
|
||||||
void SocksRequestFailed(errTypes error);
|
|
||||||
void SocksRequestSuccess();
|
|
||||||
void SentSocksFailed(const boost::system::error_code & ecode);
|
|
||||||
void SentSocksDone(const boost::system::error_code & ecode);
|
|
||||||
void SentSocksResponse(const boost::system::error_code & ecode);
|
|
||||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
|
||||||
|
|
||||||
uint8_t m_sock_buff[socks_buffer_size];
|
|
||||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
|
|
||||||
std::shared_ptr<i2p::stream::Stream> m_stream;
|
|
||||||
uint8_t *m_remaining_data; //Data left to be sent
|
|
||||||
uint8_t m_response[7+max_socks_hostname_size];
|
|
||||||
address m_address; //Address
|
|
||||||
std::size_t m_remaining_data_len; //Size of the data left to be sent
|
|
||||||
uint32_t m_4aip; //Used in 4a requests
|
|
||||||
uint16_t m_port;
|
|
||||||
uint8_t m_command;
|
|
||||||
uint8_t m_parseleft; //Octets left to parse
|
|
||||||
authMethods m_authchosen; //Authentication chosen
|
|
||||||
addrTypes m_addrtype; //Address type chosen
|
|
||||||
socksVersions m_socksv; //Socks version
|
|
||||||
cmdTypes m_cmd; // Command requested
|
|
||||||
state m_state;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SOCKSHandler(SOCKSServer * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
|
|
||||||
I2PServiceHandler(parent), m_sock(sock), m_stream(nullptr),
|
|
||||||
m_authchosen(AUTH_UNACCEPTABLE), m_addrtype(ADDR_IPV4)
|
|
||||||
{ m_address.ip = 0; EnterState(GET_SOCKSV); }
|
|
||||||
~SOCKSHandler() { Terminate(); }
|
|
||||||
void Handle() { AsyncSockRead(); }
|
|
||||||
};
|
|
||||||
|
|
||||||
void SOCKSHandler::AsyncSockRead()
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- SOCKS async sock read");
|
|
||||||
if(m_sock)
|
|
||||||
m_sock->async_receive(boost::asio::buffer(m_sock_buff, socks_buffer_size),
|
|
||||||
std::bind(&SOCKSHandler::HandleSockRecv, shared_from_this(),
|
|
||||||
std::placeholders::_1, std::placeholders::_2));
|
|
||||||
else
|
|
||||||
LogPrint(eLogError,"--- SOCKS no socket for read");
|
|
||||||
}
|
|
||||||
|
|
||||||
void SOCKSHandler::Terminate()
|
|
||||||
{
|
|
||||||
if (Kill()) return;
|
|
||||||
if (m_sock)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- SOCKS close sock");
|
|
||||||
m_sock->close();
|
|
||||||
m_sock = nullptr;
|
|
||||||
}
|
|
||||||
if (m_stream)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- SOCKS close stream");
|
|
||||||
m_stream.reset ();
|
|
||||||
}
|
|
||||||
Done(shared_from_this());
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS4Response(SOCKSHandler::errTypes error, uint32_t ip, uint16_t port)
|
|
||||||
{
|
|
||||||
assert(error >= SOCKS4_OK);
|
|
||||||
m_response[0] = '\x00'; //Version
|
|
||||||
m_response[1] = error; //Response code
|
|
||||||
htobe16buf(m_response+2,port); //Port
|
|
||||||
htobe32buf(m_response+4,ip); //IP
|
|
||||||
return boost::asio::const_buffers_1(m_response,8);
|
|
||||||
}
|
|
||||||
|
|
||||||
boost::asio::const_buffers_1 SOCKSHandler::GenerateSOCKS5Response(SOCKSHandler::errTypes error, SOCKSHandler::addrTypes type, const SOCKSHandler::address &addr, uint16_t port)
|
|
||||||
{
|
|
||||||
size_t size = 6;
|
|
||||||
assert(error <= SOCKS5_ADDR_UNSUP);
|
|
||||||
m_response[0] = '\x05'; //Version
|
|
||||||
m_response[1] = error; //Response code
|
|
||||||
m_response[2] = '\x00'; //RSV
|
|
||||||
m_response[3] = type; //Address type
|
|
||||||
switch (type)
|
|
||||||
{
|
|
||||||
case ADDR_IPV4:
|
|
||||||
size = 10;
|
|
||||||
htobe32buf(m_response+4,addr.ip);
|
|
||||||
break;
|
|
||||||
case ADDR_IPV6:
|
|
||||||
size = 22;
|
|
||||||
memcpy(m_response+4,addr.ipv6, 16);
|
|
||||||
break;
|
|
||||||
case ADDR_DNS:
|
|
||||||
size = 7+addr.dns.size;
|
|
||||||
m_response[4] = addr.dns.size;
|
|
||||||
memcpy(m_response+5,addr.dns.value, addr.dns.size);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
htobe16buf(m_response+size-2,port); //Port
|
|
||||||
return boost::asio::const_buffers_1(m_response,size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SOCKSHandler::Socks5ChooseAuth()
|
|
||||||
{
|
|
||||||
m_response[0] = '\x05'; //Version
|
|
||||||
m_response[1] = m_authchosen; //Response code
|
|
||||||
boost::asio::const_buffers_1 response(m_response,2);
|
|
||||||
if (m_authchosen == AUTH_UNACCEPTABLE)
|
|
||||||
{
|
|
||||||
LogPrint(eLogWarning,"--- SOCKS5 authentication negotiation failed");
|
|
||||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
|
|
||||||
shared_from_this(), std::placeholders::_1));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- SOCKS5 choosing authentication method: ", m_authchosen);
|
|
||||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksResponse,
|
|
||||||
shared_from_this(), std::placeholders::_1));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* All hope is lost beyond this point */
|
|
||||||
void SOCKSHandler::SocksRequestFailed(SOCKSHandler::errTypes error)
|
|
||||||
{
|
|
||||||
boost::asio::const_buffers_1 response(nullptr,0);
|
|
||||||
assert(error != SOCKS4_OK && error != SOCKS5_OK);
|
|
||||||
switch (m_socksv)
|
|
||||||
{
|
|
||||||
case SOCKS4:
|
|
||||||
LogPrint(eLogWarning,"--- SOCKS4 failed: ", error);
|
|
||||||
if (error < SOCKS4_OK) error = SOCKS4_FAIL; //Transparently map SOCKS5 errors
|
|
||||||
response = GenerateSOCKS4Response(error, m_4aip, m_port);
|
|
||||||
break;
|
|
||||||
case SOCKS5:
|
|
||||||
LogPrint(eLogWarning,"--- SOCKS5 failed: ", error);
|
|
||||||
response = GenerateSOCKS5Response(error, m_addrtype, m_address, m_port);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksFailed,
|
|
||||||
shared_from_this(), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SOCKSHandler::SocksRequestSuccess()
|
|
||||||
{
|
|
||||||
boost::asio::const_buffers_1 response(nullptr,0);
|
|
||||||
//TODO: this should depend on things like the command type and callbacks may change
|
|
||||||
switch (m_socksv)
|
|
||||||
{
|
|
||||||
case SOCKS4:
|
|
||||||
LogPrint(eLogInfo,"--- SOCKS4 connection success");
|
|
||||||
response = GenerateSOCKS4Response(SOCKS4_OK, m_4aip, m_port);
|
|
||||||
break;
|
|
||||||
case SOCKS5:
|
|
||||||
LogPrint(eLogInfo,"--- SOCKS5 connection success");
|
|
||||||
auto s = i2p::client::context.GetAddressBook().ToAddress(GetOwner()->GetLocalDestination()->GetIdentHash());
|
|
||||||
address ad; ad.dns.FromString(s);
|
|
||||||
//HACK only 16 bits passed in port as SOCKS5 doesn't allow for more
|
|
||||||
response = GenerateSOCKS5Response(SOCKS5_OK, ADDR_DNS, ad, m_stream->GetRecvStreamID());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
boost::asio::async_write(*m_sock, response, std::bind(&SOCKSHandler::SentSocksDone,
|
|
||||||
shared_from_this(), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SOCKSHandler::EnterState(SOCKSHandler::state nstate, uint8_t parseleft) {
|
|
||||||
switch (nstate)
|
|
||||||
{
|
|
||||||
case GET_PORT: parseleft = 2; break;
|
|
||||||
case GET_IPV4: m_addrtype = ADDR_IPV4; m_address.ip = 0; parseleft = 4; break;
|
|
||||||
case GET4_IDENT: m_4aip = m_address.ip; break;
|
|
||||||
case GET4A_HOST:
|
|
||||||
case GET5_HOST: m_addrtype = ADDR_DNS; m_address.dns.size = 0; break;
|
|
||||||
case GET5_IPV6: m_addrtype = ADDR_IPV6; parseleft = 16; break;
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
m_parseleft = parseleft;
|
|
||||||
m_state = nstate;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SOCKSHandler::ValidateSOCKSRequest()
|
|
||||||
{
|
|
||||||
if ( m_cmd != CMD_CONNECT )
|
|
||||||
{
|
|
||||||
//TODO: we need to support binds and other shit!
|
|
||||||
LogPrint(eLogError,"--- SOCKS unsupported command: ", m_cmd);
|
|
||||||
SocksRequestFailed(SOCKS5_CMD_UNSUP);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//TODO: we may want to support other address types!
|
|
||||||
if ( m_addrtype != ADDR_DNS )
|
|
||||||
{
|
|
||||||
switch (m_socksv)
|
|
||||||
{
|
|
||||||
case SOCKS5:
|
|
||||||
LogPrint(eLogError,"--- SOCKS5 unsupported address type: ", m_addrtype);
|
|
||||||
break;
|
|
||||||
case SOCKS4:
|
|
||||||
LogPrint(eLogError,"--- SOCKS4a rejected because it's actually SOCKS4");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
//TODO: we may want to support other domains
|
|
||||||
if(m_addrtype == ADDR_DNS && m_address.dns.ToString().find(".i2p") == std::string::npos)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError,"--- SOCKS invalid hostname: ", m_address.dns.ToString());
|
|
||||||
SocksRequestFailed(SOCKS5_ADDR_UNSUP);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SOCKSHandler::HandleData(uint8_t *sock_buff, std::size_t len)
|
|
||||||
{
|
|
||||||
assert(len); // This should always be called with a least a byte left to parse
|
|
||||||
while (len > 0)
|
|
||||||
{
|
|
||||||
switch (m_state)
|
|
||||||
{
|
|
||||||
case GET_SOCKSV:
|
|
||||||
m_socksv = (SOCKSHandler::socksVersions) *sock_buff;
|
|
||||||
switch (*sock_buff)
|
|
||||||
{
|
|
||||||
case SOCKS4:
|
|
||||||
EnterState(GET_COMMAND); //Initialize the parser at the right position
|
|
||||||
break;
|
|
||||||
case SOCKS5:
|
|
||||||
EnterState(GET5_AUTHNUM); //Initialize the parser at the right position
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint(eLogError,"--- SOCKS rejected invalid version: ", ((int)*sock_buff));
|
|
||||||
Terminate();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET5_AUTHNUM:
|
|
||||||
EnterState(GET5_AUTH, *sock_buff);
|
|
||||||
break;
|
|
||||||
case GET5_AUTH:
|
|
||||||
m_parseleft --;
|
|
||||||
if (*sock_buff == AUTH_NONE)
|
|
||||||
m_authchosen = AUTH_NONE;
|
|
||||||
if ( m_parseleft == 0 )
|
|
||||||
{
|
|
||||||
if (!Socks5ChooseAuth()) return false;
|
|
||||||
EnterState(GET5_REQUESTV);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET_COMMAND:
|
|
||||||
switch (*sock_buff)
|
|
||||||
{
|
|
||||||
case CMD_CONNECT:
|
|
||||||
case CMD_BIND:
|
|
||||||
break;
|
|
||||||
case CMD_UDP:
|
|
||||||
if (m_socksv == SOCKS5) break;
|
|
||||||
default:
|
|
||||||
LogPrint(eLogError,"--- SOCKS invalid command: ", ((int)*sock_buff));
|
|
||||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_cmd = (SOCKSHandler::cmdTypes)*sock_buff;
|
|
||||||
switch (m_socksv)
|
|
||||||
{
|
|
||||||
case SOCKS5: EnterState(GET5_GETRSV); break;
|
|
||||||
case SOCKS4: EnterState(GET_PORT); break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET_PORT:
|
|
||||||
m_port = (m_port << 8)|((uint16_t)*sock_buff);
|
|
||||||
m_parseleft--;
|
|
||||||
if (m_parseleft == 0)
|
|
||||||
{
|
|
||||||
switch (m_socksv)
|
|
||||||
{
|
|
||||||
case SOCKS5: EnterState(DONE); break;
|
|
||||||
case SOCKS4: EnterState(GET_IPV4); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET_IPV4:
|
|
||||||
m_address.ip = (m_address.ip << 8)|((uint32_t)*sock_buff);
|
|
||||||
m_parseleft--;
|
|
||||||
if (m_parseleft == 0)
|
|
||||||
{
|
|
||||||
switch (m_socksv)
|
|
||||||
{
|
|
||||||
case SOCKS5: EnterState(GET_PORT); break;
|
|
||||||
case SOCKS4: EnterState(GET4_IDENT); m_4aip = m_address.ip; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET4_IDENT:
|
|
||||||
if (!*sock_buff)
|
|
||||||
{
|
|
||||||
if( m_4aip == 0 || m_4aip > 255 )
|
|
||||||
EnterState(DONE);
|
|
||||||
else
|
|
||||||
EnterState(GET4A_HOST);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET4A_HOST:
|
|
||||||
if (!*sock_buff)
|
|
||||||
{
|
|
||||||
EnterState(DONE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (m_address.dns.size >= max_socks_hostname_size)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError,"--- SOCKS4a destination is too large");
|
|
||||||
SocksRequestFailed(SOCKS4_FAIL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
m_address.dns.push_back(*sock_buff);
|
|
||||||
break;
|
|
||||||
case GET5_REQUESTV:
|
|
||||||
if (*sock_buff != SOCKS5)
|
|
||||||
{
|
|
||||||
LogPrint(eLogError,"--- SOCKS5 rejected unknown request version: ", ((int)*sock_buff));
|
|
||||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
EnterState(GET_COMMAND);
|
|
||||||
break;
|
|
||||||
case GET5_GETRSV:
|
|
||||||
if ( *sock_buff != 0 )
|
|
||||||
{
|
|
||||||
LogPrint(eLogError,"--- SOCKS5 unknown reserved field: ", ((int)*sock_buff));
|
|
||||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
EnterState(GET5_GETADDRTYPE);
|
|
||||||
break;
|
|
||||||
case GET5_GETADDRTYPE:
|
|
||||||
switch (*sock_buff)
|
|
||||||
{
|
|
||||||
case ADDR_IPV4: EnterState(GET_IPV4); break;
|
|
||||||
case ADDR_IPV6: EnterState(GET5_IPV6); break;
|
|
||||||
case ADDR_DNS : EnterState(GET5_HOST_SIZE); break;
|
|
||||||
default:
|
|
||||||
LogPrint(eLogError,"--- SOCKS5 unknown address type: ", ((int)*sock_buff));
|
|
||||||
SocksRequestFailed(SOCKS5_GEN_FAIL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GET5_IPV6:
|
|
||||||
m_address.ipv6[16-m_parseleft] = *sock_buff;
|
|
||||||
m_parseleft--;
|
|
||||||
if (m_parseleft == 0) EnterState(GET_PORT);
|
|
||||||
break;
|
|
||||||
case GET5_HOST_SIZE:
|
|
||||||
EnterState(GET5_HOST, *sock_buff);
|
|
||||||
break;
|
|
||||||
case GET5_HOST:
|
|
||||||
m_address.dns.push_back(*sock_buff);
|
|
||||||
m_parseleft--;
|
|
||||||
if (m_parseleft == 0) EnterState(GET_PORT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint(eLogError,"--- SOCKS parse state?? ", m_state);
|
|
||||||
Terminate();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
sock_buff++;
|
|
||||||
len--;
|
|
||||||
if (m_state == DONE)
|
|
||||||
{
|
|
||||||
m_remaining_data_len = len;
|
|
||||||
m_remaining_data = sock_buff;
|
|
||||||
return ValidateSOCKSRequest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SOCKSHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
|
|
||||||
{
|
|
||||||
LogPrint(eLogDebug,"--- SOCKS sock recv: ", len);
|
|
||||||
if(ecode)
|
|
||||||
{
|
|
||||||
LogPrint(eLogWarning," --- SOCKS sock recv got error: ", ecode);
|
|
||||||
Terminate();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HandleData(m_sock_buff, len))
|
|
||||||
{
|
|
||||||
if (m_state == DONE)
|
|
||||||
{
|
|
||||||
LogPrint(eLogInfo,"--- SOCKS requested ", m_address.dns.ToString(), ":" , m_port);
|
|
||||||
GetOwner()->CreateStream ( std::bind (&SOCKSHandler::HandleStreamRequestComplete,
|
|
||||||
shared_from_this(), std::placeholders::_1), m_address.dns.ToString(), m_port);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
AsyncSockRead();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SOCKSHandler::SentSocksFailed(const boost::system::error_code & ecode)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
Terminate();
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError,"--- SOCKS Closing socket after sending failure because: ", ecode.message ());
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SOCKSHandler::SentSocksDone(const boost::system::error_code & ecode)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
if (Kill()) return;
|
|
||||||
LogPrint (eLogInfo,"--- SOCKS New I2PTunnel connection");
|
|
||||||
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, m_stream);
|
|
||||||
GetOwner()->AddHandler (connection);
|
|
||||||
connection->I2PConnect (m_remaining_data,m_remaining_data_len);
|
|
||||||
Done(shared_from_this());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError,"--- SOCKS Closing socket after completion reply because: ", ecode.message ());
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SOCKSHandler::SentSocksResponse(const boost::system::error_code & ecode)
|
|
||||||
{
|
|
||||||
if (ecode)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError,"--- SOCKS Closing socket after sending reply because: ", ecode.message ());
|
|
||||||
Terminate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SOCKSHandler::HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
m_stream = stream;
|
|
||||||
SocksRequestSuccess();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError,"--- SOCKS Issue when creating the stream, check the previous warnings for more info.");
|
|
||||||
SocksRequestFailed(SOCKS5_HOST_UNREACH);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SOCKSServer::SOCKSServer(int port, std::shared_ptr<i2p::client::ClientDestination> localDestination) :
|
|
||||||
TCPIPAcceptor (port, localDestination ? localDestination : i2p::client::context.GetSharedLocalDestination ())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<i2p::client::I2PServiceHandler> SOCKSServer::CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
|
||||||
{
|
|
||||||
return std::make_shared<SOCKSHandler> (this, socket);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
32
SOCKS.h
32
SOCKS.h
@@ -1,32 +0,0 @@
|
|||||||
#ifndef SOCKS_H__
|
|
||||||
#define SOCKS_H__
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include <mutex>
|
|
||||||
#include "I2PService.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace proxy
|
|
||||||
{
|
|
||||||
class SOCKSServer: public i2p::client::TCPIPAcceptor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SOCKSServer(int port, std::shared_ptr<i2p::client::ClientDestination> localDestination = nullptr);
|
|
||||||
~SOCKSServer() {};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Implements TCPIPAcceptor
|
|
||||||
std::shared_ptr<i2p::client::I2PServiceHandler> CreateHandler(std::shared_ptr<boost::asio::ip::tcp::socket> socket);
|
|
||||||
const char* GetName() { return "SOCKS"; }
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef SOCKSServer SOCKSProxy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
556
SSU.cpp
556
SSU.cpp
@@ -1,556 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "NetDb.h"
|
|
||||||
#include "SSU.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
SSUServer::SSUServer (int port): m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
|
|
||||||
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
|
|
||||||
m_Endpoint (boost::asio::ip::udp::v4 (), port), m_EndpointV6 (boost::asio::ip::udp::v6 (), port),
|
|
||||||
m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService),
|
|
||||||
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service)
|
|
||||||
{
|
|
||||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
|
||||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
|
||||||
if (context.SupportsV6 ())
|
|
||||||
{
|
|
||||||
m_SocketV6.open (boost::asio::ip::udp::v6());
|
|
||||||
m_SocketV6.set_option (boost::asio::ip::v6_only (true));
|
|
||||||
m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
|
||||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
|
||||||
m_SocketV6.bind (m_EndpointV6);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SSUServer::~SSUServer ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Start ()
|
|
||||||
{
|
|
||||||
m_IsRunning = true;
|
|
||||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
|
||||||
m_Thread = new std::thread (std::bind (&SSUServer::Run, this));
|
|
||||||
m_ReceiversService.post (std::bind (&SSUServer::Receive, this));
|
|
||||||
if (context.SupportsV6 ())
|
|
||||||
{
|
|
||||||
m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this));
|
|
||||||
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
|
|
||||||
}
|
|
||||||
SchedulePeerTestsCleanupTimer ();
|
|
||||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Stop ()
|
|
||||||
{
|
|
||||||
DeleteAllSessions ();
|
|
||||||
m_IsRunning = false;
|
|
||||||
m_Service.stop ();
|
|
||||||
m_Socket.close ();
|
|
||||||
m_ServiceV6.stop ();
|
|
||||||
m_SocketV6.close ();
|
|
||||||
m_ReceiversService.stop ();
|
|
||||||
if (m_ReceiversThread)
|
|
||||||
{
|
|
||||||
m_ReceiversThread->join ();
|
|
||||||
delete m_ReceiversThread;
|
|
||||||
m_ReceiversThread = nullptr;
|
|
||||||
}
|
|
||||||
if (m_Thread)
|
|
||||||
{
|
|
||||||
m_Thread->join ();
|
|
||||||
delete m_Thread;
|
|
||||||
m_Thread = nullptr;
|
|
||||||
}
|
|
||||||
if (m_ThreadV6)
|
|
||||||
{
|
|
||||||
m_ThreadV6->join ();
|
|
||||||
delete m_ThreadV6;
|
|
||||||
m_ThreadV6 = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Run ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Service.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU server: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::RunV6 ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_ServiceV6.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU V6 server: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::RunReceivers ()
|
|
||||||
{
|
|
||||||
while (m_IsRunning)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_ReceiversService.run ();
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU receivers: ", ex.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay)
|
|
||||||
{
|
|
||||||
m_Relays[tag] = relay;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::FindRelaySession (uint32_t tag)
|
|
||||||
{
|
|
||||||
auto it = m_Relays.find (tag);
|
|
||||||
if (it != m_Relays.end ())
|
|
||||||
return FindSession (it->second);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to)
|
|
||||||
{
|
|
||||||
if (to.protocol () == boost::asio::ip::udp::v4())
|
|
||||||
m_Socket.send_to (boost::asio::buffer (buf, len), to);
|
|
||||||
else
|
|
||||||
m_SocketV6.send_to (boost::asio::buffer (buf, len), to);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::Receive ()
|
|
||||||
{
|
|
||||||
SSUPacket * packet = new SSUPacket ();
|
|
||||||
m_Socket.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from,
|
|
||||||
std::bind (&SSUServer::HandleReceivedFrom, this, std::placeholders::_1, std::placeholders::_2, packet));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::ReceiveV6 ()
|
|
||||||
{
|
|
||||||
SSUPacket * packet = new SSUPacket ();
|
|
||||||
m_SocketV6.async_receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from,
|
|
||||||
std::bind (&SSUServer::HandleReceivedFromV6, this, std::placeholders::_1, std::placeholders::_2, packet));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
packet->len = bytes_transferred;
|
|
||||||
std::vector<SSUPacket *> packets;
|
|
||||||
packets.push_back (packet);
|
|
||||||
|
|
||||||
boost::system::error_code ec;
|
|
||||||
size_t moreBytes = m_Socket.available(ec);
|
|
||||||
while (moreBytes && packets.size () < 25)
|
|
||||||
{
|
|
||||||
packet = new SSUPacket ();
|
|
||||||
packet->len = m_Socket.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V4), packet->from);
|
|
||||||
packets.push_back (packet);
|
|
||||||
moreBytes = m_Socket.available();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets));
|
|
||||||
Receive ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("SSU receive error: ", ecode.message ());
|
|
||||||
delete packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet)
|
|
||||||
{
|
|
||||||
if (!ecode)
|
|
||||||
{
|
|
||||||
packet->len = bytes_transferred;
|
|
||||||
std::vector<SSUPacket *> packets;
|
|
||||||
packets.push_back (packet);
|
|
||||||
|
|
||||||
size_t moreBytes = m_SocketV6.available ();
|
|
||||||
while (moreBytes && packets.size () < 25)
|
|
||||||
{
|
|
||||||
packet = new SSUPacket ();
|
|
||||||
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from);
|
|
||||||
packets.push_back (packet);
|
|
||||||
moreBytes = m_SocketV6.available();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets));
|
|
||||||
ReceiveV6 ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("SSU V6 receive error: ", ecode.message ());
|
|
||||||
delete packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleReceivedPackets (std::vector<SSUPacket *> packets)
|
|
||||||
{
|
|
||||||
std::shared_ptr<SSUSession> session;
|
|
||||||
for (auto it1: packets)
|
|
||||||
{
|
|
||||||
auto packet = it1;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!session || session->GetRemoteEndpoint () != packet->from) // we received packet for other session than previous
|
|
||||||
{
|
|
||||||
if (session) session->FlushData ();
|
|
||||||
auto it = m_Sessions.find (packet->from);
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
session = it->second;
|
|
||||||
if (!session)
|
|
||||||
{
|
|
||||||
session = std::make_shared<SSUSession> (*this, packet->from);
|
|
||||||
session->WaitForConnect ();
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
m_Sessions[packet->from] = session;
|
|
||||||
}
|
|
||||||
LogPrint (eLogInfo, "New SSU session from ", packet->from.address ().to_string (), ":", packet->from.port (), " created");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
session->ProcessNextMessage (packet->buf, packet->len, packet->from);
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU: HandleReceivedPackets ", ex.what ());
|
|
||||||
if (session) session->FlushData ();
|
|
||||||
session = nullptr;
|
|
||||||
}
|
|
||||||
delete packet;
|
|
||||||
}
|
|
||||||
if (session) session->FlushData ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const
|
|
||||||
{
|
|
||||||
if (!router) return nullptr;
|
|
||||||
auto address = router->GetSSUAddress (true); // v4 only
|
|
||||||
if (!address) return nullptr;
|
|
||||||
auto session = FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
|
||||||
if (session || !context.SupportsV6 ())
|
|
||||||
return session;
|
|
||||||
// try v6
|
|
||||||
address = router->GetSSUV6Address ();
|
|
||||||
if (!address) return nullptr;
|
|
||||||
return FindSession (boost::asio::ip::udp::endpoint (address->host, address->port));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::FindSession (const boost::asio::ip::udp::endpoint& e) const
|
|
||||||
{
|
|
||||||
auto it = m_Sessions.find (e);
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
return it->second;
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
|
|
||||||
{
|
|
||||||
std::shared_ptr<SSUSession> session;
|
|
||||||
if (router)
|
|
||||||
{
|
|
||||||
auto address = router->GetSSUAddress (!context.SupportsV6 ());
|
|
||||||
if (address)
|
|
||||||
{
|
|
||||||
boost::asio::ip::udp::endpoint remoteEndpoint (address->host, address->port);
|
|
||||||
auto it = m_Sessions.find (remoteEndpoint);
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
session = it->second;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// otherwise create new session
|
|
||||||
session = std::make_shared<SSUSession> (*this, remoteEndpoint, router, peerTest);
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
m_Sessions[remoteEndpoint] = session;
|
|
||||||
}
|
|
||||||
if (!router->UsesIntroducer ())
|
|
||||||
{
|
|
||||||
// connect directly
|
|
||||||
LogPrint ("Creating new SSU session to [", router->GetIdentHashAbbreviation (), "] ",
|
|
||||||
remoteEndpoint.address ().to_string (), ":", remoteEndpoint.port ());
|
|
||||||
session->Connect ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// connect through introducer
|
|
||||||
int numIntroducers = address->introducers.size ();
|
|
||||||
if (numIntroducers > 0)
|
|
||||||
{
|
|
||||||
std::shared_ptr<SSUSession> introducerSession;
|
|
||||||
const i2p::data::RouterInfo::Introducer * introducer = nullptr;
|
|
||||||
// we might have a session to introducer already
|
|
||||||
for (int i = 0; i < numIntroducers; i++)
|
|
||||||
{
|
|
||||||
introducer = &(address->introducers[i]);
|
|
||||||
it = m_Sessions.find (boost::asio::ip::udp::endpoint (introducer->iHost, introducer->iPort));
|
|
||||||
if (it != m_Sessions.end ())
|
|
||||||
{
|
|
||||||
introducerSession = it->second;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (introducerSession) // session found
|
|
||||||
LogPrint ("Session to introducer already exists");
|
|
||||||
else // create new
|
|
||||||
{
|
|
||||||
LogPrint ("Creating new session to introducer");
|
|
||||||
introducer = &(address->introducers[0]); // TODO:
|
|
||||||
boost::asio::ip::udp::endpoint introducerEndpoint (introducer->iHost, introducer->iPort);
|
|
||||||
introducerSession = std::make_shared<SSUSession> (*this, introducerEndpoint, router);
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
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
|
|
||||||
{
|
|
||||||
uint8_t buf[1];
|
|
||||||
Send (buf, 0, remoteEndpoint); // send HolePunch
|
|
||||||
}
|
|
||||||
introducerSession->Introduce (introducer->iTag, introducer->iKey);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Can't connect to unreachable router. No introducers presented");
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
m_Sessions.erase (remoteEndpoint);
|
|
||||||
session.reset ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Router ", router->GetIdentHashAbbreviation (), " doesn't have SSU address");
|
|
||||||
}
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::DeleteSession (std::shared_ptr<SSUSession> session)
|
|
||||||
{
|
|
||||||
if (session)
|
|
||||||
{
|
|
||||||
session->Close ();
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
m_Sessions.erase (session->GetRemoteEndpoint ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::DeleteAllSessions ()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
|
||||||
for (auto it: m_Sessions)
|
|
||||||
it.second->Close ();
|
|
||||||
m_Sessions.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Filter>
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::GetRandomSession (Filter filter)
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<SSUSession> > filteredSessions;
|
|
||||||
for (auto s :m_Sessions)
|
|
||||||
if (filter (s.second)) filteredSessions.push_back (s.second);
|
|
||||||
if (filteredSessions.size () > 0)
|
|
||||||
{
|
|
||||||
auto ind = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, filteredSessions.size ()-1);
|
|
||||||
return filteredSessions[ind];
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded)
|
|
||||||
{
|
|
||||||
return GetRandomSession (
|
|
||||||
[excluded](std::shared_ptr<SSUSession> session)->bool
|
|
||||||
{
|
|
||||||
return session->GetState () == eSessionStateEstablished && !session->IsV6 () &&
|
|
||||||
session != excluded;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<SSUSession *> SSUServer::FindIntroducers (int maxNumIntroducers)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
std::set<SSUSession *> ret;
|
|
||||||
for (int i = 0; i < maxNumIntroducers; i++)
|
|
||||||
{
|
|
||||||
auto session = GetRandomSession (
|
|
||||||
[&ret, ts](std::shared_ptr<SSUSession> session)->bool
|
|
||||||
{
|
|
||||||
return session->GetRelayTag () && !ret.count (session.get ()) &&
|
|
||||||
session->GetState () == eSessionStateEstablished &&
|
|
||||||
ts < session->GetCreationTime () + SSU_TO_INTRODUCER_SESSION_DURATION;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
if (session)
|
|
||||||
{
|
|
||||||
ret.insert (session.get ());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::ScheduleIntroducersUpdateTimer ()
|
|
||||||
{
|
|
||||||
m_IntroducersUpdateTimer.expires_from_now (boost::posix_time::seconds(SSU_KEEP_ALIVE_INTERVAL));
|
|
||||||
m_IntroducersUpdateTimer.async_wait (std::bind (&SSUServer::HandleIntroducersUpdateTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandleIntroducersUpdateTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
// timeout expired
|
|
||||||
if (i2p::context.GetStatus () == eRouterStatusTesting)
|
|
||||||
{
|
|
||||||
// we still don't know if we need introducers
|
|
||||||
ScheduleIntroducersUpdateTimer ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (i2p::context.GetStatus () == eRouterStatusOK) return; // we don't need introducers anymore
|
|
||||||
// we are firewalled
|
|
||||||
if (!i2p::context.IsUnreachable ()) i2p::context.SetUnreachable ();
|
|
||||||
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;
|
|
||||||
if (m_Introducers.empty ())
|
|
||||||
{
|
|
||||||
auto introducer = i2p::data::netdb.GetRandomIntroducer ();
|
|
||||||
if (introducer)
|
|
||||||
GetSession (introducer);
|
|
||||||
}
|
|
||||||
ScheduleIntroducersUpdateTimer ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session)
|
|
||||||
{
|
|
||||||
m_PeerTests[nonce] = { i2p::util::GetMillisecondsSinceEpoch (), role, session };
|
|
||||||
}
|
|
||||||
|
|
||||||
PeerTestParticipant SSUServer::GetPeerTestParticipant (uint32_t nonce)
|
|
||||||
{
|
|
||||||
auto it = m_PeerTests.find (nonce);
|
|
||||||
if (it != m_PeerTests.end ())
|
|
||||||
return it->second.role;
|
|
||||||
else
|
|
||||||
return ePeerTestParticipantUnknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<SSUSession> SSUServer::GetPeerTestSession (uint32_t nonce)
|
|
||||||
{
|
|
||||||
auto it = m_PeerTests.find (nonce);
|
|
||||||
if (it != m_PeerTests.end ())
|
|
||||||
return it->second.session;
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::UpdatePeerTest (uint32_t nonce, PeerTestParticipant role)
|
|
||||||
{
|
|
||||||
auto it = m_PeerTests.find (nonce);
|
|
||||||
if (it != m_PeerTests.end ())
|
|
||||||
it->second.role = role;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::RemovePeerTest (uint32_t nonce)
|
|
||||||
{
|
|
||||||
m_PeerTests.erase (nonce);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::SchedulePeerTestsCleanupTimer ()
|
|
||||||
{
|
|
||||||
m_PeerTestsCleanupTimer.expires_from_now (boost::posix_time::seconds(SSU_PEER_TEST_TIMEOUT));
|
|
||||||
m_PeerTestsCleanupTimer.async_wait (std::bind (&SSUServer::HandlePeerTestsCleanupTimer,
|
|
||||||
this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUServer::HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
int numDeleted = 0;
|
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
for (auto it = m_PeerTests.begin (); it != m_PeerTests.end ();)
|
|
||||||
{
|
|
||||||
if (ts > it->second.creationTime + SSU_PEER_TEST_TIMEOUT*1000LL)
|
|
||||||
{
|
|
||||||
numDeleted++;
|
|
||||||
it = m_PeerTests.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
if (numDeleted > 0)
|
|
||||||
LogPrint (eLogInfo, numDeleted, " peer tests have been expired");
|
|
||||||
SchedulePeerTestsCleanupTimer ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
114
SSU.h
114
SSU.h
@@ -1,114 +0,0 @@
|
|||||||
#ifndef SSU_H__
|
|
||||||
#define SSU_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <set>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#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_PEER_TEST_TIMEOUT = 60; // 60 seconds
|
|
||||||
const int SSU_TO_INTRODUCER_SESSION_DURATION = 3600; // 1 hour
|
|
||||||
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
|
|
||||||
|
|
||||||
struct SSUPacket
|
|
||||||
{
|
|
||||||
i2p::crypto::AESAlignedBuffer<1500> buf;
|
|
||||||
boost::asio::ip::udp::endpoint from;
|
|
||||||
size_t len;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SSUServer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SSUServer (int port);
|
|
||||||
~SSUServer ();
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
std::shared_ptr<SSUSession> GetSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
|
|
||||||
std::shared_ptr<SSUSession> FindSession (std::shared_ptr<const i2p::data::RouterInfo> router) const;
|
|
||||||
std::shared_ptr<SSUSession> FindSession (const boost::asio::ip::udp::endpoint& e) const;
|
|
||||||
std::shared_ptr<SSUSession> GetRandomEstablishedSession (std::shared_ptr<const SSUSession> excluded);
|
|
||||||
void DeleteSession (std::shared_ptr<SSUSession> session);
|
|
||||||
void DeleteAllSessions ();
|
|
||||||
|
|
||||||
boost::asio::io_service& GetService () { return m_Service; };
|
|
||||||
boost::asio::io_service& GetServiceV6 () { return m_ServiceV6; };
|
|
||||||
const boost::asio::ip::udp::endpoint& GetEndpoint () const { return m_Endpoint; };
|
|
||||||
void Send (const uint8_t * buf, size_t len, const boost::asio::ip::udp::endpoint& to);
|
|
||||||
void AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay);
|
|
||||||
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
|
|
||||||
|
|
||||||
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
|
|
||||||
PeerTestParticipant GetPeerTestParticipant (uint32_t nonce);
|
|
||||||
std::shared_ptr<SSUSession> GetPeerTestSession (uint32_t nonce);
|
|
||||||
void UpdatePeerTest (uint32_t nonce, PeerTestParticipant role);
|
|
||||||
void RemovePeerTest (uint32_t nonce);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void RunV6 ();
|
|
||||||
void RunReceivers ();
|
|
||||||
void Receive ();
|
|
||||||
void ReceiveV6 ();
|
|
||||||
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
|
||||||
void HandleReceivedFromV6 (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
|
||||||
void HandleReceivedPackets (std::vector<SSUPacket *> packets);
|
|
||||||
|
|
||||||
template<typename Filter>
|
|
||||||
std::shared_ptr<SSUSession> GetRandomSession (Filter filter);
|
|
||||||
|
|
||||||
std::set<SSUSession *> FindIntroducers (int maxNumIntroducers);
|
|
||||||
void ScheduleIntroducersUpdateTimer ();
|
|
||||||
void HandleIntroducersUpdateTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
void SchedulePeerTestsCleanupTimer ();
|
|
||||||
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct PeerTest
|
|
||||||
{
|
|
||||||
uint64_t creationTime;
|
|
||||||
PeerTestParticipant role;
|
|
||||||
std::shared_ptr<SSUSession> session; // for Bob to Alice
|
|
||||||
};
|
|
||||||
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread;
|
|
||||||
boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService;
|
|
||||||
boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork;
|
|
||||||
boost::asio::ip::udp::endpoint m_Endpoint, m_EndpointV6;
|
|
||||||
boost::asio::ip::udp::socket m_Socket, m_SocketV6;
|
|
||||||
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer;
|
|
||||||
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
|
|
||||||
mutable std::mutex m_SessionsMutex;
|
|
||||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions;
|
|
||||||
std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer
|
|
||||||
std::map<uint32_t, PeerTest> m_PeerTests; // nonce -> creation time in milliseconds
|
|
||||||
|
|
||||||
public:
|
|
||||||
// for HTTP only
|
|
||||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
507
SSUData.cpp
507
SSUData.cpp
@@ -1,507 +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
|
|
||||||
{
|
|
||||||
void IncompleteMessage::AttachNextFragment (const uint8_t * fragment, size_t fragmentSize)
|
|
||||||
{
|
|
||||||
if (msg->len + fragmentSize > msg->maxLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "SSU I2NP message size ", msg->maxLen, " is not enough");
|
|
||||||
auto newMsg = ToSharedI2NPMessage(NewI2NPMessage ());
|
|
||||||
*newMsg = *msg;
|
|
||||||
msg = newMsg;
|
|
||||||
}
|
|
||||||
memcpy (msg->buf + msg->len, fragment, fragmentSize);
|
|
||||||
msg->len += fragmentSize;
|
|
||||||
nextFragmentNum++;
|
|
||||||
}
|
|
||||||
|
|
||||||
SSUData::SSUData (SSUSession& session):
|
|
||||||
m_Session (session), m_ResendTimer (session.GetService ()), m_DecayTimer (session.GetService ()),
|
|
||||||
m_IncompleteMessagesCleanupTimer (session.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 ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::Start ()
|
|
||||||
{
|
|
||||||
ScheduleIncompleteMessagesCleanup ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::Stop ()
|
|
||||||
{
|
|
||||||
m_ResendTimer.cancel ();
|
|
||||||
m_DecayTimer.cancel ();
|
|
||||||
m_IncompleteMessagesCleanupTimer.cancel ();
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ())
|
|
||||||
{
|
|
||||||
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 (bufbe32toh (buf+i*4));
|
|
||||||
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 = bufbe32toh (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)
|
|
||||||
it->second->fragments[fragment].reset (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 = bufbe32toh (buf); // message ID
|
|
||||||
buf += 4;
|
|
||||||
uint8_t frag[4];
|
|
||||||
frag[0] = 0;
|
|
||||||
memcpy (frag + 1, buf, 3);
|
|
||||||
buf += 3;
|
|
||||||
uint32_t fragmentInfo = bufbe32toh (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
|
|
||||||
if (fragmentSize >= SSU_V4_MAX_PACKET_SIZE)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Fragment size ", fragmentSize, "exceeds max SSU packet size");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find message with msgID
|
|
||||||
auto it = m_IncompleteMessages.find (msgID);
|
|
||||||
if (it == m_IncompleteMessages.end ())
|
|
||||||
{
|
|
||||||
// create new message
|
|
||||||
auto msg = ToSharedI2NPMessage (NewI2NPShortMessage ());
|
|
||||||
msg->len -= I2NP_SHORT_HEADER_SIZE;
|
|
||||||
it = m_IncompleteMessages.insert (std::make_pair (msgID,
|
|
||||||
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first;
|
|
||||||
}
|
|
||||||
std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second;
|
|
||||||
|
|
||||||
// handle current fragment
|
|
||||||
if (fragmentNum == incompleteMessage->nextFragmentNum)
|
|
||||||
{
|
|
||||||
// expected fragment
|
|
||||||
incompleteMessage->AttachNextFragment (buf, fragmentSize);
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
incompleteMessage->AttachNextFragment (savedFragment->buf, savedFragment->len);
|
|
||||||
isLast = savedFragment->isLast;
|
|
||||||
incompleteMessage->savedFragments.erase (it1++);
|
|
||||||
}
|
|
||||||
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 (std::unique_ptr<Fragment>(savedFragment)).second)
|
|
||||||
incompleteMessage->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Fragment ", (int)fragmentNum, " of message ", msgID, " already saved");
|
|
||||||
}
|
|
||||||
isLast = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLast)
|
|
||||||
{
|
|
||||||
// delete incomplete message
|
|
||||||
auto msg = incompleteMessage->msg;
|
|
||||||
incompleteMessage->msg = nullptr;
|
|
||||||
m_IncompleteMessages.erase (msgID);
|
|
||||||
// process message
|
|
||||||
SendMsgAck (msgID);
|
|
||||||
msg->FromSSU (msgID);
|
|
||||||
if (m_Session.GetState () == eSessionStateEstablished)
|
|
||||||
{
|
|
||||||
if (!m_ReceivedMessages.count (msgID))
|
|
||||||
{
|
|
||||||
if (m_ReceivedMessages.size () > MAX_NUM_RECEIVED_MESSAGES)
|
|
||||||
m_ReceivedMessages.clear ();
|
|
||||||
else
|
|
||||||
ScheduleDecay ();
|
|
||||||
m_ReceivedMessages.insert (msgID);
|
|
||||||
m_Handler.PutNextMessage (msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "SSU message ", msgID, " already received");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// we expect DeliveryStatus
|
|
||||||
if (msg->GetTypeID () == eI2NPDeliveryStatus)
|
|
||||||
{
|
|
||||||
LogPrint ("SSU session established");
|
|
||||||
m_Session.Established ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "SSU unexpected message ", (int)msg->GetTypeID ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SendFragmentAck (msgID, fragmentNum);
|
|
||||||
buf += fragmentSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::FlushReceivedMessage ()
|
|
||||||
{
|
|
||||||
m_Handler.Flush ();
|
|
||||||
}
|
|
||||||
|
|
||||||
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, " len=", len);
|
|
||||||
// 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 (std::shared_ptr<i2p::I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
uint32_t msgID = msg->ToSSU ();
|
|
||||||
if (m_SentMessages.count (msgID) > 0)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SSU message ", msgID, " already sent");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_SentMessages.empty ()) // schedule resend at first message only
|
|
||||||
ScheduleResend ();
|
|
||||||
|
|
||||||
auto ret = m_SentMessages.insert (std::make_pair (msgID, std::unique_ptr<SentMessage>(new SentMessage)));
|
|
||||||
std::unique_ptr<SentMessage>& sentMessage = ret.first->second;
|
|
||||||
if (ret.second)
|
|
||||||
{
|
|
||||||
sentMessage->nextResendTime = i2p::util::GetSecondsSinceEpoch () + RESEND_INTERVAL;
|
|
||||||
sentMessage->numResends = 0;
|
|
||||||
}
|
|
||||||
auto& fragments = sentMessage->fragments;
|
|
||||||
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;
|
|
||||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
|
||||||
*payload = DATA_FLAG_WANT_REPLY; // for compatibility
|
|
||||||
payload++;
|
|
||||||
*payload = 1; // always 1 message fragment per message
|
|
||||||
payload++;
|
|
||||||
htobe32buf (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;
|
|
||||||
fragments.push_back (std::unique_ptr<Fragment> (fragment));
|
|
||||||
|
|
||||||
// encrypt message with session key
|
|
||||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, size);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Session.Send (buf, size);
|
|
||||||
}
|
|
||||||
catch (boost::system::system_error& ec)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't send SSU fragment ", ec.what ());
|
|
||||||
}
|
|
||||||
if (!isLast)
|
|
||||||
{
|
|
||||||
len -= payloadSize;
|
|
||||||
msgBuf += payloadSize;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
len = 0;
|
|
||||||
fragmentNum++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::SendMsgAck (uint32_t msgID)
|
|
||||||
{
|
|
||||||
uint8_t buf[48 + 18]; // actual length is 44 = 37 + 7 but pad it to multiple of 16
|
|
||||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
|
||||||
*payload = DATA_FLAG_EXPLICIT_ACKS_INCLUDED; // flag
|
|
||||||
payload++;
|
|
||||||
*payload = 1; // number of ACKs
|
|
||||||
payload++;
|
|
||||||
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
|
||||||
payload += 4;
|
|
||||||
*payload = 0; // number of fragments
|
|
||||||
|
|
||||||
// encrypt message with session key
|
|
||||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
|
|
||||||
m_Session.Send (buf, 48);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum)
|
|
||||||
{
|
|
||||||
if (fragmentNum > 64)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Fragment number ", fragmentNum, " exceeds 64");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
uint8_t buf[64 + 18];
|
|
||||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
|
||||||
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
|
|
||||||
payload++;
|
|
||||||
*payload = 1; // number of ACK bitfields
|
|
||||||
payload++;
|
|
||||||
// one ack
|
|
||||||
*(uint32_t *)(payload) = htobe32 (msgID); // msgID
|
|
||||||
payload += 4;
|
|
||||||
div_t d = div (fragmentNum, 7);
|
|
||||||
memset (payload, 0x80, d.quot); // 0x80 means non-last
|
|
||||||
payload += d.quot;
|
|
||||||
*payload = 0x01 << d.rem; // set corresponding bit
|
|
||||||
payload++;
|
|
||||||
*payload = 0; // number of fragments
|
|
||||||
|
|
||||||
size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1)
|
|
||||||
// encrypt message with session key
|
|
||||||
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
|
|
||||||
m_Session.Send (buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::ScheduleResend()
|
|
||||||
{
|
|
||||||
m_ResendTimer.cancel ();
|
|
||||||
m_ResendTimer.expires_from_now (boost::posix_time::seconds(RESEND_INTERVAL));
|
|
||||||
auto s = m_Session.shared_from_this();
|
|
||||||
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
|
||||||
{ s->m_Data.HandleResendTimer (ecode); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::HandleResendTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto it = m_SentMessages.begin (); it != m_SentMessages.end ();)
|
|
||||||
{
|
|
||||||
if (ts >= it->second->nextResendTime)
|
|
||||||
{
|
|
||||||
if (it->second->numResends < MAX_NUM_RESENDS)
|
|
||||||
{
|
|
||||||
for (auto& f: it->second->fragments)
|
|
||||||
if (f)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_Session.Send (f->buf, f->len); // resend
|
|
||||||
}
|
|
||||||
catch (boost::system::system_error& ec)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Can't resend SSU fragment ", ec.what ());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
it->second->numResends++;
|
|
||||||
it->second->nextResendTime += it->second->numResends*RESEND_INTERVAL;
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU message has not been ACKed after ", MAX_NUM_RESENDS, " attempts. Deleted");
|
|
||||||
it = m_SentMessages.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
ScheduleResend ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::ScheduleDecay ()
|
|
||||||
{
|
|
||||||
m_DecayTimer.cancel ();
|
|
||||||
m_DecayTimer.expires_from_now (boost::posix_time::seconds(DECAY_INTERVAL));
|
|
||||||
auto s = m_Session.shared_from_this();
|
|
||||||
m_ResendTimer.async_wait ([s](const boost::system::error_code& ecode)
|
|
||||||
{ s->m_Data.HandleDecayTimer (ecode); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::HandleDecayTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
m_ReceivedMessages.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::ScheduleIncompleteMessagesCleanup ()
|
|
||||||
{
|
|
||||||
m_IncompleteMessagesCleanupTimer.cancel ();
|
|
||||||
m_IncompleteMessagesCleanupTimer.expires_from_now (boost::posix_time::seconds(INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT));
|
|
||||||
auto s = m_Session.shared_from_this();
|
|
||||||
m_IncompleteMessagesCleanupTimer.async_wait ([s](const boost::system::error_code& ecode)
|
|
||||||
{ s->m_Data.HandleIncompleteMessagesCleanupTimer (ecode); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void SSUData::HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
|
|
||||||
{
|
|
||||||
if (ts > it->second->lastFragmentInsertTime + INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "SSU message ", it->first, " was not completed in ", INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT, " seconds. Deleted");
|
|
||||||
it = m_IncompleteMessages.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
ScheduleIncompleteMessagesCleanup ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
128
SSUData.h
128
SSUData.h
@@ -1,128 +0,0 @@
|
|||||||
#ifndef SSU_DATA_H__
|
|
||||||
#define SSU_DATA_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <set>
|
|
||||||
#include <memory>
|
|
||||||
#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;
|
|
||||||
const int DECAY_INTERVAL = 20; // in seconds
|
|
||||||
const int MAX_NUM_RECEIVED_MESSAGES = 1000; // how many msgID we store for duplicates check
|
|
||||||
const int INCOMPLETE_MESSAGES_CLEANUP_TIMEOUT = 30; // in seconds
|
|
||||||
// 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 std::unique_ptr<Fragment>& f1, const std::unique_ptr<Fragment>& f2) const
|
|
||||||
{
|
|
||||||
return f1->fragmentNum < f2->fragmentNum;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct IncompleteMessage
|
|
||||||
{
|
|
||||||
std::shared_ptr<I2NPMessage> msg;
|
|
||||||
int nextFragmentNum;
|
|
||||||
uint32_t lastFragmentInsertTime; // in seconds
|
|
||||||
std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments;
|
|
||||||
|
|
||||||
IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {};
|
|
||||||
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SentMessage
|
|
||||||
{
|
|
||||||
std::vector<std::unique_ptr<Fragment> > fragments;
|
|
||||||
uint32_t nextResendTime; // in seconds
|
|
||||||
int numResends;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SSUSession;
|
|
||||||
class SSUData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SSUData (SSUSession& session);
|
|
||||||
~SSUData ();
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
void ProcessMessage (uint8_t * buf, size_t len);
|
|
||||||
void FlushReceivedMessage ();
|
|
||||||
void Send (std::shared_ptr<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 ScheduleDecay ();
|
|
||||||
void HandleDecayTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
void ScheduleIncompleteMessagesCleanup ();
|
|
||||||
void HandleIncompleteMessagesCleanupTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
void AdjustPacketSize (const i2p::data::RouterInfo& remoteRouter);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
SSUSession& m_Session;
|
|
||||||
std::map<uint32_t, std::unique_ptr<IncompleteMessage> > m_IncompleteMessages;
|
|
||||||
std::map<uint32_t, std::unique_ptr<SentMessage> > m_SentMessages;
|
|
||||||
std::set<uint32_t> m_ReceivedMessages;
|
|
||||||
boost::asio::deadline_timer m_ResendTimer, m_DecayTimer, m_IncompleteMessagesCleanupTimer;
|
|
||||||
int m_MaxPacketSize, m_PacketSize;
|
|
||||||
i2p::I2NPMessagesHandler m_Handler;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
1092
SSUSession.cpp
1092
SSUSession.cpp
File diff suppressed because it is too large
Load Diff
157
SSUSession.h
157
SSUSession.h
@@ -1,157 +0,0 @@
|
|||||||
#ifndef SSU_SESSION_H__
|
|
||||||
#define SSU_SESSION_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <set>
|
|
||||||
#include <memory>
|
|
||||||
#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,
|
|
||||||
eSessionStateClosed,
|
|
||||||
eSessionStateFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
enum PeerTestParticipant
|
|
||||||
{
|
|
||||||
ePeerTestParticipantUnknown = 0,
|
|
||||||
ePeerTestParticipantAlice1,
|
|
||||||
ePeerTestParticipantAlice2,
|
|
||||||
ePeerTestParticipantBob,
|
|
||||||
ePeerTestParticipantCharlie
|
|
||||||
};
|
|
||||||
|
|
||||||
class SSUServer;
|
|
||||||
class SSUSession: public TransportSession, public std::enable_shared_from_this<SSUSession>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
|
|
||||||
std::shared_ptr<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 WaitForConnect ();
|
|
||||||
void Introduce (uint32_t iTag, const uint8_t * iKey);
|
|
||||||
void WaitForIntroduction ();
|
|
||||||
void Close ();
|
|
||||||
void Done ();
|
|
||||||
boost::asio::ip::udp::endpoint& GetRemoteEndpoint () { return m_RemoteEndpoint; };
|
|
||||||
bool IsV6 () const { return m_RemoteEndpoint.address ().is_v6 (); };
|
|
||||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
|
||||||
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; };
|
|
||||||
|
|
||||||
void FlushData ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
boost::asio::io_service& GetService ();
|
|
||||||
void CreateAESandMacKey (const uint8_t * pubKey);
|
|
||||||
|
|
||||||
void PostI2NPMessages (std::vector<std::shared_ptr<I2NPMessage> > msgs);
|
|
||||||
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 (const 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, bool sendAddress = 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;
|
|
||||||
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
|
|
||||||
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
|
|
||||||
i2p::crypto::AESKey m_SessionKey;
|
|
||||||
i2p::crypto::MACKey m_MacKey;
|
|
||||||
uint32_t m_CreationTime; // seconds since epoch
|
|
||||||
SSUData m_Data;
|
|
||||||
bool m_IsDataReceived;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
121
Signature.cpp
121
Signature.cpp
@@ -1,121 +0,0 @@
|
|||||||
#include <memory>
|
|
||||||
#include <cryptopp/integer.h>
|
|
||||||
#include <cryptopp/eccrypto.h>
|
|
||||||
#include "Log.h"
|
|
||||||
#include "Signature.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
class Ed25519
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
Ed25519 ()
|
|
||||||
{
|
|
||||||
q = CryptoPP::Integer::Power2 (255) - CryptoPP::Integer (19); // 2^255-19
|
|
||||||
l = CryptoPP::Integer::Power2 (252) + CryptoPP::Integer ("27742317777372353535851937790883648493");
|
|
||||||
// 2^252 + 27742317777372353535851937790883648493
|
|
||||||
d = CryptoPP::Integer (-121665) * CryptoPP::Integer (121666).InverseMod (q); // -121665/121666
|
|
||||||
I = a_exp_b_mod_c (CryptoPP::Integer::Two (), (q - CryptoPP::Integer::One ()).DividedBy (4), q);
|
|
||||||
B = DecodePoint (CryptoPP::Integer (4)*CryptoPP::Integer (5).InverseMod (q));
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoPP::ECP::Point DecodePublicKey (const uint8_t * key) const
|
|
||||||
{
|
|
||||||
return DecodePoint (CryptoPP::Integer (key, 32));
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoPP::ECP::Point GeneratePublicKey (const uint8_t * privateKey) const
|
|
||||||
{
|
|
||||||
return Mul (B, CryptoPP::Integer (privateKey, 32));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::ECP::Point Sum (const CryptoPP::ECP::Point& p1, const CryptoPP::ECP::Point& p2) const
|
|
||||||
{
|
|
||||||
CryptoPP::Integer m = d*p1.x*p2.x*p1.y*p2.y,
|
|
||||||
x = a_times_b_mod_c (p1.x*p2.y + p2.x*p1.y, (CryptoPP::Integer::One() + m).InverseMod (q), q),
|
|
||||||
y = a_times_b_mod_c (p1.y*p2.y + p1.x*p2.x, (CryptoPP::Integer::One() - m).InverseMod (q), q);
|
|
||||||
return CryptoPP::ECP::Point {x, y};
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoPP::ECP::Point Mul (const CryptoPP::ECP::Point& p, const CryptoPP::Integer& e) const
|
|
||||||
{
|
|
||||||
CryptoPP::ECP::Point res {0, 1};
|
|
||||||
if (!e.IsZero ())
|
|
||||||
{
|
|
||||||
auto bitCount = e.BitCount ();
|
|
||||||
for (int i = bitCount - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
res = Sum (res, res);
|
|
||||||
if (e.GetBit (i)) res = Sum (res, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsOnCurve (const CryptoPP::ECP::Point& p) const
|
|
||||||
{
|
|
||||||
auto x2 = p.x.Squared(), y2 = p.y.Squared ();
|
|
||||||
return (y2 - x2 - CryptoPP::Integer::One() - d*x2*y2).Modulo (q).IsZero ();
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoPP::Integer RecoverX (const CryptoPP::Integer& y) const
|
|
||||||
{
|
|
||||||
auto y2 = y.Squared ();
|
|
||||||
auto xx = (y2 - CryptoPP::Integer::One())*(d*y2 + CryptoPP::Integer::One()).InverseMod (q);
|
|
||||||
auto x = a_exp_b_mod_c (xx, (q + CryptoPP::Integer (3)).DividedBy (8), q);
|
|
||||||
if (!(x.Squared () - xx).Modulo (q).IsZero ())
|
|
||||||
x = a_times_b_mod_c (x, I, q);
|
|
||||||
if (x.IsOdd ()) x = q - x;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
CryptoPP::ECP::Point DecodePoint (const CryptoPP::Integer& y) const
|
|
||||||
{
|
|
||||||
auto x = RecoverX (y);
|
|
||||||
CryptoPP::ECP::Point p {x, y};
|
|
||||||
if (!IsOnCurve (p))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Decoded point is not on 25519");
|
|
||||||
return CryptoPP::ECP::Point {0, 1};
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::Integer q, l, d, I;
|
|
||||||
CryptoPP::ECP::Point B; // base point
|
|
||||||
};
|
|
||||||
|
|
||||||
static std::unique_ptr<Ed25519> g_Ed25519;
|
|
||||||
std::unique_ptr<Ed25519>& GetEd25519 ()
|
|
||||||
{
|
|
||||||
if (!g_Ed25519)
|
|
||||||
g_Ed25519.reset (new Ed25519 ());
|
|
||||||
return g_Ed25519;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
EDDSA25519Verifier::EDDSA25519Verifier (const uint8_t * signingKey):
|
|
||||||
m_PublicKey (GetEd25519 ()->DecodePublicKey (signingKey))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EDDSA25519Verifier::Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
return true; // TODO:
|
|
||||||
}
|
|
||||||
|
|
||||||
void EDDSA25519Signer::Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
|
||||||
{
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
445
Signature.h
445
Signature.h
@@ -1,445 +0,0 @@
|
|||||||
#ifndef SIGNATURE_H__
|
|
||||||
#define SIGNATURE_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <cryptopp/dsa.h>
|
|
||||||
#include <cryptopp/rsa.h>
|
|
||||||
#include <cryptopp/asn.h>
|
|
||||||
#include <cryptopp/oids.h>
|
|
||||||
#include <cryptopp/osrng.h>
|
|
||||||
#include <cryptopp/eccrypto.h>
|
|
||||||
#include "CryptoConst.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace crypto
|
|
||||||
{
|
|
||||||
class Verifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~Verifier () {};
|
|
||||||
virtual bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const = 0;
|
|
||||||
virtual size_t GetPublicKeyLen () const = 0;
|
|
||||||
virtual size_t GetSignatureLen () const = 0;
|
|
||||||
virtual size_t GetPrivateKeyLen () const { return GetSignatureLen ()/2; };
|
|
||||||
};
|
|
||||||
|
|
||||||
class Signer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~Signer () {};
|
|
||||||
virtual void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t DSA_PUBLIC_KEY_LENGTH = 128;
|
|
||||||
const size_t DSA_SIGNATURE_LENGTH = 40;
|
|
||||||
const size_t DSA_PRIVATE_KEY_LENGTH = DSA_SIGNATURE_LENGTH/2;
|
|
||||||
class DSAVerifier: public Verifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
DSAVerifier (const uint8_t * signingKey)
|
|
||||||
{
|
|
||||||
m_PublicKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingKey, DSA_PUBLIC_KEY_LENGTH));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
CryptoPP::DSA::Verifier verifier (m_PublicKey);
|
|
||||||
return verifier.VerifyMessage (buf, len, signature, DSA_SIGNATURE_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetPublicKeyLen () const { return DSA_PUBLIC_KEY_LENGTH; };
|
|
||||||
size_t GetSignatureLen () const { return DSA_SIGNATURE_LENGTH; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::DSA::PublicKey m_PublicKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DSASigner: public Signer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
DSASigner (const uint8_t * signingPrivateKey)
|
|
||||||
{
|
|
||||||
m_PrivateKey.Initialize (dsap, dsaq, dsag, CryptoPP::Integer (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
|
||||||
{
|
|
||||||
CryptoPP::DSA::Signer signer (m_PrivateKey);
|
|
||||||
signer.SignMessage (rnd, buf, len, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::DSA::PrivateKey m_PrivateKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void CreateDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
|
||||||
{
|
|
||||||
CryptoPP::DSA::PrivateKey privateKey;
|
|
||||||
CryptoPP::DSA::PublicKey publicKey;
|
|
||||||
privateKey.Initialize (rnd, dsap, dsaq, dsag);
|
|
||||||
privateKey.MakePublicKey (publicKey);
|
|
||||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
|
||||||
publicKey.GetPublicElement ().Encode (signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Hash, size_t keyLen>
|
|
||||||
class ECDSAVerifier: public Verifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
template<typename Curve>
|
|
||||||
ECDSAVerifier (Curve curve, const uint8_t * signingKey)
|
|
||||||
{
|
|
||||||
m_PublicKey.Initialize (curve,
|
|
||||||
CryptoPP::ECP::Point (CryptoPP::Integer (signingKey, keyLen/2),
|
|
||||||
CryptoPP::Integer (signingKey + keyLen/2, keyLen/2)));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Verifier verifier (m_PublicKey);
|
|
||||||
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t GetPublicKeyLen () const { return keyLen; };
|
|
||||||
size_t GetSignatureLen () const { return keyLen; }; // signature length = key length
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey m_PublicKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Hash>
|
|
||||||
class ECDSASigner: public Signer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
template<typename Curve>
|
|
||||||
ECDSASigner (Curve curve, const uint8_t * signingPrivateKey, size_t keyLen)
|
|
||||||
{
|
|
||||||
m_PrivateKey.Initialize (curve, CryptoPP::Integer (signingPrivateKey, keyLen/2)); // private key length
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
|
||||||
{
|
|
||||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::Signer signer (m_PrivateKey);
|
|
||||||
signer.SignMessage (rnd, buf, len, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey m_PrivateKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Hash, typename Curve>
|
|
||||||
inline void CreateECDSARandomKeys (CryptoPP::RandomNumberGenerator& rnd, Curve curve,
|
|
||||||
size_t keyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
|
||||||
{
|
|
||||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PrivateKey privateKey;
|
|
||||||
typename CryptoPP::ECDSA<CryptoPP::ECP, Hash>::PublicKey publicKey;
|
|
||||||
privateKey.Initialize (rnd, curve);
|
|
||||||
privateKey.MakePublicKey (publicKey);
|
|
||||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey, keyLen/2);
|
|
||||||
auto q = publicKey.GetPublicElement ();
|
|
||||||
q.x.Encode (signingPublicKey, keyLen/2);
|
|
||||||
q.y.Encode (signingPublicKey + keyLen/2, keyLen/2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ECDSA_SHA256_P256
|
|
||||||
const size_t ECDSAP256_KEY_LENGTH = 64;
|
|
||||||
class ECDSAP256Verifier: public ECDSAVerifier<CryptoPP::SHA256, ECDSAP256_KEY_LENGTH>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ECDSAP256Verifier (const uint8_t * signingKey):
|
|
||||||
ECDSAVerifier (CryptoPP::ASN1::secp256r1(), signingKey)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ECDSAP256Signer: public ECDSASigner<CryptoPP::SHA256>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ECDSAP256Signer (const uint8_t * signingPrivateKey):
|
|
||||||
ECDSASigner (CryptoPP::ASN1::secp256r1(), signingPrivateKey, ECDSAP256_KEY_LENGTH)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void CreateECDSAP256RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
|
||||||
{
|
|
||||||
CreateECDSARandomKeys<CryptoPP::SHA256> (rnd, CryptoPP::ASN1::secp256r1(), ECDSAP256_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ECDSA_SHA384_P384
|
|
||||||
const size_t ECDSAP384_KEY_LENGTH = 96;
|
|
||||||
class ECDSAP384Verifier: public ECDSAVerifier<CryptoPP::SHA384, ECDSAP384_KEY_LENGTH>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ECDSAP384Verifier (const uint8_t * signingKey):
|
|
||||||
ECDSAVerifier (CryptoPP::ASN1::secp384r1(), signingKey)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ECDSAP384Signer: public ECDSASigner<CryptoPP::SHA384>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ECDSAP384Signer (const uint8_t * signingPrivateKey):
|
|
||||||
ECDSASigner (CryptoPP::ASN1::secp384r1(), signingPrivateKey, ECDSAP384_KEY_LENGTH)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void CreateECDSAP384RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
|
||||||
{
|
|
||||||
CreateECDSARandomKeys<CryptoPP::SHA384> (rnd, CryptoPP::ASN1::secp384r1(), ECDSAP384_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ECDSA_SHA512_P521
|
|
||||||
const size_t ECDSAP521_KEY_LENGTH = 132;
|
|
||||||
class ECDSAP521Verifier: public ECDSAVerifier<CryptoPP::SHA512, ECDSAP521_KEY_LENGTH>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ECDSAP521Verifier (const uint8_t * signingKey):
|
|
||||||
ECDSAVerifier (CryptoPP::ASN1::secp521r1(), signingKey)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ECDSAP521Signer: public ECDSASigner<CryptoPP::SHA512>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
ECDSAP521Signer (const uint8_t * signingPrivateKey):
|
|
||||||
ECDSASigner (CryptoPP::ASN1::secp521r1(), signingPrivateKey, ECDSAP521_KEY_LENGTH)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void CreateECDSAP521RandomKeys (CryptoPP::RandomNumberGenerator& rnd, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
|
||||||
{
|
|
||||||
CreateECDSARandomKeys<CryptoPP::SHA512> (rnd, CryptoPP::ASN1::secp521r1(), ECDSAP521_KEY_LENGTH, signingPrivateKey, signingPublicKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
// RSA
|
|
||||||
template<typename Hash, size_t keyLen>
|
|
||||||
class RSAVerifier: public Verifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSAVerifier (const uint8_t * signingKey)
|
|
||||||
{
|
|
||||||
m_PublicKey.Initialize (CryptoPP::Integer (signingKey, keyLen), CryptoPP::Integer (rsae));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Verifier verifier (m_PublicKey);
|
|
||||||
return verifier.VerifyMessage (buf, len, signature, keyLen); // signature length
|
|
||||||
}
|
|
||||||
size_t GetPublicKeyLen () const { return keyLen; }
|
|
||||||
size_t GetSignatureLen () const { return keyLen; }
|
|
||||||
size_t GetPrivateKeyLen () const { return GetSignatureLen ()*2; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::RSA::PublicKey m_PublicKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template<typename Hash>
|
|
||||||
class RSASigner: public Signer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSASigner (const uint8_t * signingPrivateKey, size_t keyLen)
|
|
||||||
{
|
|
||||||
m_PrivateKey.Initialize (CryptoPP::Integer (signingPrivateKey, keyLen/2),
|
|
||||||
rsae,
|
|
||||||
CryptoPP::Integer (signingPrivateKey + keyLen/2, keyLen/2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const
|
|
||||||
{
|
|
||||||
typename CryptoPP::RSASS<CryptoPP::PKCS1v15, Hash>::Signer signer (m_PrivateKey);
|
|
||||||
signer.SignMessage (rnd, buf, len, signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::RSA::PrivateKey m_PrivateKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline void CreateRSARandomKeys (CryptoPP::RandomNumberGenerator& rnd,
|
|
||||||
size_t publicKeyLen, uint8_t * signingPrivateKey, uint8_t * signingPublicKey)
|
|
||||||
{
|
|
||||||
CryptoPP::RSA::PrivateKey privateKey;
|
|
||||||
privateKey.Initialize (rnd, publicKeyLen*8, rsae);
|
|
||||||
privateKey.GetModulus ().Encode (signingPrivateKey, publicKeyLen);
|
|
||||||
privateKey.GetPrivateExponent ().Encode (signingPrivateKey + publicKeyLen, publicKeyLen);
|
|
||||||
privateKey.GetModulus ().Encode (signingPublicKey, publicKeyLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// RSA_SHA256_2048
|
|
||||||
const size_t RSASHA2562048_KEY_LENGTH = 256;
|
|
||||||
class RSASHA2562048Verifier: public RSAVerifier<CryptoPP::SHA256, RSASHA2562048_KEY_LENGTH>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSASHA2562048Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class RSASHA2562048Signer: public RSASigner<CryptoPP::SHA256>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSASHA2562048Signer (const uint8_t * signingPrivateKey):
|
|
||||||
RSASigner (signingPrivateKey, RSASHA2562048_KEY_LENGTH*2)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// RSA_SHA384_3072
|
|
||||||
const size_t RSASHA3843072_KEY_LENGTH = 384;
|
|
||||||
class RSASHA3843072Verifier: public RSAVerifier<CryptoPP::SHA384, RSASHA3843072_KEY_LENGTH>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSASHA3843072Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class RSASHA3843072Signer: public RSASigner<CryptoPP::SHA384>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSASHA3843072Signer (const uint8_t * signingPrivateKey):
|
|
||||||
RSASigner (signingPrivateKey, RSASHA3843072_KEY_LENGTH*2)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// RSA_SHA512_4096
|
|
||||||
const size_t RSASHA5124096_KEY_LENGTH = 512;
|
|
||||||
class RSASHA5124096Verifier: public RSAVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSASHA5124096Verifier (const uint8_t * signingKey): RSAVerifier (signingKey)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class RSASHA5124096Signer: public RSASigner<CryptoPP::SHA512>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSASHA5124096Signer (const uint8_t * signingPrivateKey):
|
|
||||||
RSASigner (signingPrivateKey, RSASHA5124096_KEY_LENGTH*2)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Raw verifiers
|
|
||||||
class RawVerifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
virtual ~RawVerifier () {};
|
|
||||||
virtual void Update (const uint8_t * buf, size_t len) = 0;
|
|
||||||
virtual bool Verify (const uint8_t * signature) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Hash, size_t keyLen>
|
|
||||||
class RSARawVerifier: public RawVerifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSARawVerifier (const uint8_t * signingKey):
|
|
||||||
n (signingKey, keyLen)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
m_Hash.Update (buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Verify (const uint8_t * signature)
|
|
||||||
{
|
|
||||||
// RSA encryption first
|
|
||||||
CryptoPP::Integer enSig (a_exp_b_mod_c (CryptoPP::Integer (signature, keyLen),
|
|
||||||
CryptoPP::Integer (i2p::crypto::rsae), n)); // s^e mod n
|
|
||||||
uint8_t enSigBuf[keyLen];
|
|
||||||
enSig.Encode (enSigBuf, keyLen);
|
|
||||||
|
|
||||||
uint8_t digest[Hash::DIGESTSIZE];
|
|
||||||
m_Hash.Final (digest);
|
|
||||||
if ((int)keyLen < Hash::DIGESTSIZE) return false; // can't verify digest longer than key
|
|
||||||
// we assume digest is right aligned, at least for PKCS#1 v1.5 padding
|
|
||||||
return !memcmp (enSigBuf + (keyLen - Hash::DIGESTSIZE), digest, Hash::DIGESTSIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::Integer n; // RSA modulus
|
|
||||||
Hash m_Hash;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RSASHA5124096RawVerifier: public RSARawVerifier<CryptoPP::SHA512, RSASHA5124096_KEY_LENGTH>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
RSASHA5124096RawVerifier (const uint8_t * signingKey): RSARawVerifier (signingKey)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// EdDSA
|
|
||||||
const size_t EDDSA25519_PUBLIC_KEY_LENGTH = 32;
|
|
||||||
const size_t EDDSA25519_SIGNATURE_LENGTH = 64;
|
|
||||||
const size_t EDDSA25519_PRIVATE_KEY_LENGTH = 32;
|
|
||||||
class EDDSA25519Verifier: public Verifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
EDDSA25519Verifier (const uint8_t * signingKey);
|
|
||||||
bool Verify (const uint8_t * buf, size_t len, const uint8_t * signature) const;
|
|
||||||
|
|
||||||
size_t GetPublicKeyLen () const { return EDDSA25519_PUBLIC_KEY_LENGTH; };
|
|
||||||
size_t GetSignatureLen () const { return EDDSA25519_SIGNATURE_LENGTH; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
CryptoPP::ECP::Point m_PublicKey;
|
|
||||||
};
|
|
||||||
|
|
||||||
class EDDSA25519Signer: public Signer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
EDDSA25519Signer (const uint8_t * signingPrivateKey) {};
|
|
||||||
|
|
||||||
void Sign (CryptoPP::RandomNumberGenerator& rnd, const uint8_t * buf, int len, uint8_t * signature) const;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
894
Streaming.cpp
894
Streaming.cpp
@@ -1,894 +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,
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> remote, int port): m_Service (service),
|
|
||||||
m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1),
|
|
||||||
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local),
|
|
||||||
m_RemoteLeaseSet (remote), m_ReceiveTimer (m_Service), m_ResendTimer (m_Service),
|
|
||||||
m_AckSendTimer (m_Service), m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (port),
|
|
||||||
m_WindowSize (MIN_WINDOW_SIZE), m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO),
|
|
||||||
m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0)
|
|
||||||
{
|
|
||||||
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
|
||||||
m_RemoteIdentity = remote->GetIdentity ();
|
|
||||||
m_CurrentRemoteLease.endDate = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream::Stream (boost::asio::io_service& service, StreamingDestination& local):
|
|
||||||
m_Service (service), m_SendStreamID (0), m_SequenceNumber (0), m_LastReceivedSequenceNumber (-1),
|
|
||||||
m_Status (eStreamStatusNew), m_IsAckSendScheduled (false), m_LocalDestination (local),
|
|
||||||
m_ReceiveTimer (m_Service), m_ResendTimer (m_Service), m_AckSendTimer (m_Service),
|
|
||||||
m_NumSentBytes (0), m_NumReceivedBytes (0), m_Port (0), m_WindowSize (MIN_WINDOW_SIZE),
|
|
||||||
m_RTT (INITIAL_RTT), m_RTO (INITIAL_RTO), m_LastWindowSizeIncreaseTime (0), m_NumResendAttempts (0)
|
|
||||||
{
|
|
||||||
m_RecvStreamID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream::~Stream ()
|
|
||||||
{
|
|
||||||
Terminate ();
|
|
||||||
while (!m_ReceiveQueue.empty ())
|
|
||||||
{
|
|
||||||
auto packet = m_ReceiveQueue.front ();
|
|
||||||
m_ReceiveQueue.pop ();
|
|
||||||
delete packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it: m_SentPackets)
|
|
||||||
delete it;
|
|
||||||
m_SentPackets.clear ();
|
|
||||||
|
|
||||||
for (auto it: m_SavedPackets)
|
|
||||||
delete it;
|
|
||||||
m_SavedPackets.clear ();
|
|
||||||
|
|
||||||
LogPrint (eLogDebug, "Stream deleted");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::Terminate ()
|
|
||||||
{
|
|
||||||
m_AckSendTimer.cancel ();
|
|
||||||
m_ReceiveTimer.cancel ();
|
|
||||||
m_ResendTimer.cancel ();
|
|
||||||
if (m_SendHandler)
|
|
||||||
{
|
|
||||||
auto handler = m_SendHandler;
|
|
||||||
m_SendHandler = nullptr;
|
|
||||||
handler (boost::asio::error::make_error_code (boost::asio::error::operation_aborted));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::HandleNextPacket (Packet * packet)
|
|
||||||
{
|
|
||||||
m_NumReceivedBytes += packet->GetLength ();
|
|
||||||
if (!m_SendStreamID)
|
|
||||||
m_SendStreamID = packet->GetReceiveStreamID ();
|
|
||||||
|
|
||||||
if (!packet->IsNoAck ()) // ack received
|
|
||||||
ProcessAck (packet);
|
|
||||||
|
|
||||||
int32_t receivedSeqn = packet->GetSeqn ();
|
|
||||||
bool isSyn = packet->IsSYN ();
|
|
||||||
if (!receivedSeqn && !isSyn)
|
|
||||||
{
|
|
||||||
// plain ack
|
|
||||||
LogPrint (eLogDebug, "Plain ACK received");
|
|
||||||
delete packet;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogPrint (eLogDebug, "Received seqn=", receivedSeqn);
|
|
||||||
if (isSyn || receivedSeqn == m_LastReceivedSequenceNumber + 1)
|
|
||||||
{
|
|
||||||
// we have received next in sequence message
|
|
||||||
ProcessPacket (packet);
|
|
||||||
|
|
||||||
// we should also try stored messages if any
|
|
||||||
for (auto it = m_SavedPackets.begin (); it != m_SavedPackets.end ();)
|
|
||||||
{
|
|
||||||
if ((*it)->GetSeqn () == (uint32_t)(m_LastReceivedSequenceNumber + 1))
|
|
||||||
{
|
|
||||||
Packet * savedPacket = *it;
|
|
||||||
m_SavedPackets.erase (it++);
|
|
||||||
|
|
||||||
ProcessPacket (savedPacket);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// schedule ack for last message
|
|
||||||
if (m_Status == eStreamStatusOpen)
|
|
||||||
{
|
|
||||||
if (!m_IsAckSendScheduled)
|
|
||||||
{
|
|
||||||
m_IsAckSendScheduled = true;
|
|
||||||
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT));
|
|
||||||
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isSyn)
|
|
||||||
// we have to send SYN back to incoming connection
|
|
||||||
SendBuffer (); // also sets m_IsOpen
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (receivedSeqn <= m_LastReceivedSequenceNumber)
|
|
||||||
{
|
|
||||||
// we have received duplicate
|
|
||||||
LogPrint (eLogWarning, "Duplicate message ", receivedSeqn, " received");
|
|
||||||
SendQuickAck (); // resend ack for previous message again
|
|
||||||
delete packet; // packet dropped
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Missing messages from ", m_LastReceivedSequenceNumber + 1, " to ", receivedSeqn - 1);
|
|
||||||
// save message and wait for missing message again
|
|
||||||
SavePacket (packet);
|
|
||||||
if (m_LastReceivedSequenceNumber >= 0)
|
|
||||||
{
|
|
||||||
// send NACKs for missing messages ASAP
|
|
||||||
if (m_IsAckSendScheduled)
|
|
||||||
{
|
|
||||||
m_IsAckSendScheduled = false;
|
|
||||||
m_AckSendTimer.cancel ();
|
|
||||||
}
|
|
||||||
SendQuickAck ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// wait for SYN
|
|
||||||
m_IsAckSendScheduled = true;
|
|
||||||
m_AckSendTimer.expires_from_now (boost::posix_time::milliseconds(ACK_SEND_TIMEOUT));
|
|
||||||
m_AckSendTimer.async_wait (std::bind (&Stream::HandleAckSendTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::SavePacket (Packet * packet)
|
|
||||||
{
|
|
||||||
m_SavedPackets.insert (packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::ProcessPacket (Packet * packet)
|
|
||||||
{
|
|
||||||
// process flags
|
|
||||||
uint32_t receivedSeqn = packet->GetSeqn ();
|
|
||||||
uint16_t flags = packet->GetFlags ();
|
|
||||||
LogPrint (eLogDebug, "Process seqn=", receivedSeqn, ", flags=", flags);
|
|
||||||
|
|
||||||
const uint8_t * optionData = packet->GetOptionData ();
|
|
||||||
if (flags & PACKET_FLAG_SYNCHRONIZE)
|
|
||||||
LogPrint (eLogDebug, "Synchronize");
|
|
||||||
|
|
||||||
if (flags & PACKET_FLAG_DELAY_REQUESTED)
|
|
||||||
{
|
|
||||||
optionData += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & PACKET_FLAG_FROM_INCLUDED)
|
|
||||||
{
|
|
||||||
optionData += m_RemoteIdentity.FromBuffer (optionData, packet->GetOptionSize ());
|
|
||||||
LogPrint (eLogInfo, "From identity ", m_RemoteIdentity.GetIdentHash ().ToBase64 ());
|
|
||||||
if (!m_RemoteLeaseSet)
|
|
||||||
LogPrint (eLogDebug, "Incoming stream from ", m_RemoteIdentity.GetIdentHash ().ToBase64 ());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED)
|
|
||||||
{
|
|
||||||
uint16_t maxPacketSize = bufbe16toh (optionData);
|
|
||||||
LogPrint (eLogDebug, "Max packet size ", maxPacketSize);
|
|
||||||
optionData += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flags & PACKET_FLAG_SIGNATURE_INCLUDED)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Signature");
|
|
||||||
uint8_t signature[256];
|
|
||||||
auto signatureLen = m_RemoteIdentity.GetSignatureLen ();
|
|
||||||
memcpy (signature, optionData, signatureLen);
|
|
||||||
memset (const_cast<uint8_t *>(optionData), 0, signatureLen);
|
|
||||||
if (!m_RemoteIdentity.Verify (packet->GetBuffer (), packet->GetLength (), signature))
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Signature verification failed");
|
|
||||||
Close ();
|
|
||||||
flags |= PACKET_FLAG_CLOSE;
|
|
||||||
}
|
|
||||||
memcpy (const_cast<uint8_t *>(optionData), signature, signatureLen);
|
|
||||||
optionData += signatureLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
packet->offset = packet->GetPayload () - packet->buf;
|
|
||||||
if (packet->GetLength () > 0)
|
|
||||||
{
|
|
||||||
m_ReceiveQueue.push (packet);
|
|
||||||
m_ReceiveTimer.cancel ();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
delete packet;
|
|
||||||
|
|
||||||
m_LastReceivedSequenceNumber = receivedSeqn;
|
|
||||||
|
|
||||||
if (flags & (PACKET_FLAG_CLOSE | PACKET_FLAG_RESET))
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, (flags & PACKET_FLAG_RESET) ? "Reset" : "Closed");
|
|
||||||
m_Status = eStreamStatusReset;
|
|
||||||
Close ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::ProcessAck (Packet * packet)
|
|
||||||
{
|
|
||||||
bool acknowledged = false;
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
uint32_t ackThrough = packet->GetAckThrough ();
|
|
||||||
int nackCount = packet->GetNACKCount ();
|
|
||||||
for (auto it = m_SentPackets.begin (); it != m_SentPackets.end ();)
|
|
||||||
{
|
|
||||||
auto seqn = (*it)->GetSeqn ();
|
|
||||||
if (seqn <= ackThrough)
|
|
||||||
{
|
|
||||||
if (nackCount > 0)
|
|
||||||
{
|
|
||||||
bool nacked = false;
|
|
||||||
for (int i = 0; i < nackCount; i++)
|
|
||||||
if (seqn == packet->GetNACK (i))
|
|
||||||
{
|
|
||||||
nacked = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (nacked)
|
|
||||||
{
|
|
||||||
LogPrint (eLogDebug, "Packet ", seqn, " NACK");
|
|
||||||
it++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto sentPacket = *it;
|
|
||||||
uint64_t rtt = ts - sentPacket->sendTime;
|
|
||||||
m_RTT = (m_RTT*seqn + rtt)/(seqn + 1);
|
|
||||||
m_RTO = m_RTT*1.5; // TODO: implement it better
|
|
||||||
LogPrint (eLogDebug, "Packet ", seqn, " acknowledged rtt=", rtt);
|
|
||||||
m_SentPackets.erase (it++);
|
|
||||||
delete sentPacket;
|
|
||||||
acknowledged = true;
|
|
||||||
if (m_WindowSize < WINDOW_SIZE)
|
|
||||||
m_WindowSize++; // slow start
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// linear growth
|
|
||||||
if (ts > m_LastWindowSizeIncreaseTime + m_RTT)
|
|
||||||
{
|
|
||||||
m_WindowSize++;
|
|
||||||
if (m_WindowSize > MAX_WINDOW_SIZE) m_WindowSize = MAX_WINDOW_SIZE;
|
|
||||||
m_LastWindowSizeIncreaseTime = ts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (m_SentPackets.empty ())
|
|
||||||
m_ResendTimer.cancel ();
|
|
||||||
if (acknowledged)
|
|
||||||
{
|
|
||||||
m_NumResendAttempts = 0;
|
|
||||||
SendBuffer ();
|
|
||||||
}
|
|
||||||
if (m_Status == eStreamStatusClosing)
|
|
||||||
Close (); // all outgoing messages have been sent
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t Stream::Send (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
if (len > 0 && buf)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SendBufferMutex);
|
|
||||||
m_SendBuffer.clear ();
|
|
||||||
m_SendBuffer.write ((const char *)buf, len);
|
|
||||||
}
|
|
||||||
m_Service.post (std::bind (&Stream::SendBuffer, shared_from_this ()));
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::AsyncSend (const uint8_t * buf, size_t len, SendHandler handler)
|
|
||||||
{
|
|
||||||
if (m_SendHandler)
|
|
||||||
handler (boost::asio::error::make_error_code (boost::asio::error::in_progress));
|
|
||||||
else
|
|
||||||
m_SendHandler = handler;
|
|
||||||
Send (buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::SendBuffer ()
|
|
||||||
{
|
|
||||||
int numMsgs = m_WindowSize - m_SentPackets.size ();
|
|
||||||
if (numMsgs <= 0) return; // window is full
|
|
||||||
|
|
||||||
bool isNoAck = m_LastReceivedSequenceNumber < 0; // first packet
|
|
||||||
std::vector<Packet *> packets;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SendBufferMutex);
|
|
||||||
while ((m_Status == eStreamStatusNew) || (IsEstablished () && !m_SendBuffer.eof () && numMsgs > 0))
|
|
||||||
{
|
|
||||||
Packet * p = new Packet ();
|
|
||||||
uint8_t * packet = p->GetBuffer ();
|
|
||||||
// TODO: implement setters
|
|
||||||
size_t size = 0;
|
|
||||||
htobe32buf (packet + size, m_SendStreamID);
|
|
||||||
size += 4; // sendStreamID
|
|
||||||
htobe32buf (packet + size, m_RecvStreamID);
|
|
||||||
size += 4; // receiveStreamID
|
|
||||||
htobe32buf (packet + size, m_SequenceNumber++);
|
|
||||||
size += 4; // sequenceNum
|
|
||||||
if (isNoAck)
|
|
||||||
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
|
|
||||||
else
|
|
||||||
htobuf32 (packet + size, 0);
|
|
||||||
size += 4; // ack Through
|
|
||||||
packet[size] = 0;
|
|
||||||
size++; // NACK count
|
|
||||||
packet[size] = m_RTO/1000;
|
|
||||||
size++; // resend delay
|
|
||||||
if (m_Status == eStreamStatusNew)
|
|
||||||
{
|
|
||||||
// initial packet
|
|
||||||
m_Status = eStreamStatusOpen;
|
|
||||||
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;
|
|
||||||
htobe16buf (packet + size, flags);
|
|
||||||
size += 2; // flags
|
|
||||||
size_t identityLen = m_LocalDestination.GetOwner ().GetIdentity ().GetFullLen ();
|
|
||||||
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
|
|
||||||
htobe16buf (packet + size, identityLen + signatureLen + 2); // identity + signature + packet size
|
|
||||||
size += 2; // options size
|
|
||||||
m_LocalDestination.GetOwner ().GetIdentity ().ToBuffer (packet + size, identityLen);
|
|
||||||
size += identityLen; // from
|
|
||||||
htobe16buf (packet + size, 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
|
|
||||||
m_SendBuffer.read ((char *)(packet + size), STREAMING_MTU - size);
|
|
||||||
size += m_SendBuffer.gcount (); // payload
|
|
||||||
m_LocalDestination.GetOwner ().Sign (packet, size, signature);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// follow on packet
|
|
||||||
htobuf16 (packet + size, 0);
|
|
||||||
size += 2; // flags
|
|
||||||
htobuf16 (packet + size, 0); // no options
|
|
||||||
size += 2; // options size
|
|
||||||
m_SendBuffer.read((char *)(packet + size), STREAMING_MTU - size);
|
|
||||||
size += m_SendBuffer.gcount (); // payload
|
|
||||||
}
|
|
||||||
p->len = size;
|
|
||||||
packets.push_back (p);
|
|
||||||
numMsgs--;
|
|
||||||
}
|
|
||||||
if (m_SendBuffer.eof () && m_SendHandler)
|
|
||||||
{
|
|
||||||
m_SendHandler (boost::system::error_code ());
|
|
||||||
m_SendHandler = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (packets.size () > 0)
|
|
||||||
{
|
|
||||||
m_IsAckSendScheduled = false;
|
|
||||||
m_AckSendTimer.cancel ();
|
|
||||||
bool isEmpty = m_SentPackets.empty ();
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
for (auto it: packets)
|
|
||||||
{
|
|
||||||
it->sendTime = ts;
|
|
||||||
m_SentPackets.insert (it);
|
|
||||||
}
|
|
||||||
SendPackets (packets);
|
|
||||||
if (m_Status == eStreamStatusClosing && m_SendBuffer.eof ())
|
|
||||||
SendClose ();
|
|
||||||
if (isEmpty)
|
|
||||||
ScheduleResend ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::SendQuickAck ()
|
|
||||||
{
|
|
||||||
int32_t lastReceivedSeqn = m_LastReceivedSequenceNumber;
|
|
||||||
if (!m_SavedPackets.empty ())
|
|
||||||
{
|
|
||||||
int32_t seqn = (*m_SavedPackets.rbegin ())->GetSeqn ();
|
|
||||||
if (seqn > lastReceivedSeqn) lastReceivedSeqn = seqn;
|
|
||||||
}
|
|
||||||
if (lastReceivedSeqn < 0)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "No packets have been received yet");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Packet p;
|
|
||||||
uint8_t * packet = p.GetBuffer ();
|
|
||||||
size_t size = 0;
|
|
||||||
htobe32buf (packet + size, m_SendStreamID);
|
|
||||||
size += 4; // sendStreamID
|
|
||||||
htobe32buf (packet + size, m_RecvStreamID);
|
|
||||||
size += 4; // receiveStreamID
|
|
||||||
htobuf32 (packet + size, 0); // this is plain Ack message
|
|
||||||
size += 4; // sequenceNum
|
|
||||||
htobe32buf (packet + size, lastReceivedSeqn);
|
|
||||||
size += 4; // ack Through
|
|
||||||
uint8_t numNacks = 0;
|
|
||||||
if (lastReceivedSeqn > m_LastReceivedSequenceNumber)
|
|
||||||
{
|
|
||||||
// fill NACKs
|
|
||||||
uint8_t * nacks = packet + size + 1;
|
|
||||||
auto nextSeqn = m_LastReceivedSequenceNumber + 1;
|
|
||||||
for (auto it: m_SavedPackets)
|
|
||||||
{
|
|
||||||
auto seqn = it->GetSeqn ();
|
|
||||||
if (numNacks + (seqn - nextSeqn) >= 256)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Number of NACKs exceeds 256. seqn=", seqn, " nextSeqn=", nextSeqn);
|
|
||||||
htobe32buf (packet + 12, nextSeqn); // change ack Through
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (uint32_t i = nextSeqn; i < seqn; i++)
|
|
||||||
{
|
|
||||||
htobe32buf (nacks, i);
|
|
||||||
nacks += 4;
|
|
||||||
numNacks++;
|
|
||||||
}
|
|
||||||
nextSeqn = seqn + 1;
|
|
||||||
}
|
|
||||||
packet[size] = numNacks;
|
|
||||||
size++; // NACK count
|
|
||||||
size += numNacks*4; // NACKs
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// No NACKs
|
|
||||||
packet[size] = 0;
|
|
||||||
size++; // NACK count
|
|
||||||
}
|
|
||||||
size++; // resend delay
|
|
||||||
htobuf16 (packet + size, 0); // nof flags set
|
|
||||||
size += 2; // flags
|
|
||||||
htobuf16 (packet + size, 0); // no options
|
|
||||||
size += 2; // options size
|
|
||||||
p.len = size;
|
|
||||||
|
|
||||||
SendPackets (std::vector<Packet *> { &p });
|
|
||||||
LogPrint ("Quick Ack sent. ", (int)numNacks, " NACKs");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::Close ()
|
|
||||||
{
|
|
||||||
switch (m_Status)
|
|
||||||
{
|
|
||||||
case eStreamStatusOpen:
|
|
||||||
m_Status = eStreamStatusClosing;
|
|
||||||
Close (); // recursion
|
|
||||||
if (m_Status == eStreamStatusClosing) //still closing
|
|
||||||
LogPrint (eLogInfo, "Trying to send stream data before closing");
|
|
||||||
break;
|
|
||||||
case eStreamStatusReset:
|
|
||||||
SendClose ();
|
|
||||||
Terminate ();
|
|
||||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
|
||||||
break;
|
|
||||||
case eStreamStatusClosing:
|
|
||||||
if (m_SentPackets.empty () && m_SendBuffer.eof ()) // nothing to send
|
|
||||||
{
|
|
||||||
m_Status = eStreamStatusClosed;
|
|
||||||
SendClose ();
|
|
||||||
Terminate ();
|
|
||||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case eStreamStatusClosed:
|
|
||||||
// already closed
|
|
||||||
Terminate ();
|
|
||||||
m_LocalDestination.DeleteStream (shared_from_this ());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint (eLogWarning, "Unexpected stream status ", (int)m_Status);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::SendClose ()
|
|
||||||
{
|
|
||||||
Packet * p = new Packet ();
|
|
||||||
uint8_t * packet = p->GetBuffer ();
|
|
||||||
size_t size = 0;
|
|
||||||
htobe32buf (packet + size, m_SendStreamID);
|
|
||||||
size += 4; // sendStreamID
|
|
||||||
htobe32buf (packet + size, m_RecvStreamID);
|
|
||||||
size += 4; // receiveStreamID
|
|
||||||
htobe32buf (packet + size, m_SequenceNumber++);
|
|
||||||
size += 4; // sequenceNum
|
|
||||||
htobe32buf (packet + size, m_LastReceivedSequenceNumber);
|
|
||||||
size += 4; // ack Through
|
|
||||||
packet[size] = 0;
|
|
||||||
size++; // NACK count
|
|
||||||
size++; // resend delay
|
|
||||||
htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
|
|
||||||
size += 2; // flags
|
|
||||||
size_t signatureLen = m_LocalDestination.GetOwner ().GetIdentity ().GetSignatureLen ();
|
|
||||||
htobe16buf (packet + size, signatureLen); // signature only
|
|
||||||
size += 2; // options size
|
|
||||||
uint8_t * signature = packet + size;
|
|
||||||
memset (packet + size, 0, signatureLen);
|
|
||||||
size += signatureLen; // signature
|
|
||||||
m_LocalDestination.GetOwner ().Sign (packet, size, signature);
|
|
||||||
|
|
||||||
p->len = size;
|
|
||||||
m_Service.post (std::bind (&Stream::SendPacket, shared_from_this (), p));
|
|
||||||
LogPrint ("FIN sent");
|
|
||||||
}
|
|
||||||
|
|
||||||
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_Status == eStreamStatusOpen)
|
|
||||||
{
|
|
||||||
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 (eLogError, "Can't send packets. Missing remote LeaseSet");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!m_CurrentOutboundTunnel || !m_CurrentOutboundTunnel->IsEstablished ())
|
|
||||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNewOutboundTunnel (m_CurrentOutboundTunnel);
|
|
||||||
if (!m_CurrentOutboundTunnel)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "No outbound tunnels in the pool");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
if (ts >= m_CurrentRemoteLease.endDate - i2p::tunnel::TUNNEL_EXPIRATION_THRESHOLD*1000)
|
|
||||||
UpdateCurrentRemoteLease (true);
|
|
||||||
if (ts < m_CurrentRemoteLease.endDate)
|
|
||||||
{
|
|
||||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
|
||||||
for (auto it: packets)
|
|
||||||
{
|
|
||||||
auto msg = m_RoutingSession->WrapSingleMessage (CreateDataMessage (it->GetBuffer (), it->GetLength ()));
|
|
||||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
|
||||||
{
|
|
||||||
i2p::tunnel::eDeliveryTypeTunnel,
|
|
||||||
m_CurrentRemoteLease.tunnelGateway, m_CurrentRemoteLease.tunnelID,
|
|
||||||
msg
|
|
||||||
});
|
|
||||||
m_NumSentBytes += it->GetLength ();
|
|
||||||
}
|
|
||||||
m_CurrentOutboundTunnel->SendTunnelDataMsg (msgs);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "All leases are expired");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Stream::ScheduleResend ()
|
|
||||||
{
|
|
||||||
m_ResendTimer.cancel ();
|
|
||||||
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO));
|
|
||||||
m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer,
|
|
||||||
shared_from_this (), std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::HandleResendTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
// check for resend attempts
|
|
||||||
if (m_NumResendAttempts >= MAX_NUM_RESEND_ATTEMPTS)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "Stream packet was not ACKed after ", MAX_NUM_RESEND_ATTEMPTS, " attempts. Terminate");
|
|
||||||
m_Status = eStreamStatusReset;
|
|
||||||
Close ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// collect packets to resend
|
|
||||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
std::vector<Packet *> packets;
|
|
||||||
for (auto it : m_SentPackets)
|
|
||||||
{
|
|
||||||
if (ts >= it->sendTime + m_RTO)
|
|
||||||
{
|
|
||||||
it->sendTime = ts;
|
|
||||||
packets.push_back (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// select tunnels if necessary and send
|
|
||||||
if (packets.size () > 0)
|
|
||||||
{
|
|
||||||
m_NumResendAttempts++;
|
|
||||||
m_RTO *= 2;
|
|
||||||
switch (m_NumResendAttempts)
|
|
||||||
{
|
|
||||||
case 1: // congesion avoidance
|
|
||||||
m_WindowSize /= 2;
|
|
||||||
if (m_WindowSize < MIN_WINDOW_SIZE) m_WindowSize = MIN_WINDOW_SIZE;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
m_RTO = INITIAL_RTO; // drop RTO to initial upon tunnels pair change first time
|
|
||||||
// no break here
|
|
||||||
case 4:
|
|
||||||
UpdateCurrentRemoteLease (); // pick another lease
|
|
||||||
LogPrint (eLogWarning, "Another remote lease has been selected for stream");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
// pick another outbound tunnel
|
|
||||||
m_CurrentOutboundTunnel = m_LocalDestination.GetOwner ().GetTunnelPool ()->GetNextOutboundTunnel (m_CurrentOutboundTunnel);
|
|
||||||
LogPrint (eLogWarning, "Another outbound tunnel has been selected for stream");
|
|
||||||
break;
|
|
||||||
default: ;
|
|
||||||
}
|
|
||||||
SendPackets (packets);
|
|
||||||
}
|
|
||||||
ScheduleResend ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::HandleAckSendTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (m_IsAckSendScheduled)
|
|
||||||
{
|
|
||||||
if (m_LastReceivedSequenceNumber < 0)
|
|
||||||
{
|
|
||||||
LogPrint (eLogWarning, "SYN has not been recived after ", ACK_SEND_TIMEOUT, " milliseconds after follow on. Terminate");
|
|
||||||
m_Status = eStreamStatusReset;
|
|
||||||
Close ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (m_Status == eStreamStatusOpen)
|
|
||||||
SendQuickAck ();
|
|
||||||
m_IsAckSendScheduled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Stream::UpdateCurrentRemoteLease (bool expired)
|
|
||||||
{
|
|
||||||
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 (false); // try without threshold first
|
|
||||||
if (leases.empty ())
|
|
||||||
{
|
|
||||||
expired = false;
|
|
||||||
m_LocalDestination.GetOwner ().RequestDestination (m_RemoteIdentity.GetIdentHash ()); // time to re-request
|
|
||||||
leases = m_RemoteLeaseSet->GetNonExpiredLeases (true); // then with threshold
|
|
||||||
}
|
|
||||||
if (!leases.empty ())
|
|
||||||
{
|
|
||||||
bool updated = false;
|
|
||||||
if (expired)
|
|
||||||
{
|
|
||||||
for (auto it: leases)
|
|
||||||
if ((it.tunnelGateway == m_CurrentRemoteLease.tunnelGateway) && (it.tunnelID != m_CurrentRemoteLease.tunnelID))
|
|
||||||
{
|
|
||||||
m_CurrentRemoteLease = it;
|
|
||||||
updated = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!updated)
|
|
||||||
{
|
|
||||||
uint32_t i = i2p::context.GetRandomNumberGenerator ().GenerateWord32 (0, leases.size () - 1);
|
|
||||||
if (m_CurrentRemoteLease.endDate && leases[i].tunnelID == m_CurrentRemoteLease.tunnelID)
|
|
||||||
// make sure we don't select previous
|
|
||||||
i = (i + 1) % leases.size (); // if so, pick next
|
|
||||||
m_CurrentRemoteLease = leases[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_RemoteLeaseSet = nullptr;
|
|
||||||
m_CurrentRemoteLease.endDate = 0;
|
|
||||||
// re-request expired
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_CurrentRemoteLease.endDate = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<I2NPMessage> Stream::CreateDataMessage (const uint8_t * payload, size_t len)
|
|
||||||
{
|
|
||||||
auto msg = ToSharedI2NPMessage (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 ();
|
|
||||||
htobe32buf (buf, size); // length
|
|
||||||
buf += 4;
|
|
||||||
compressor.Get (buf, size);
|
|
||||||
htobe16buf (buf + 4, m_LocalDestination.GetLocalPort ()); // source port
|
|
||||||
htobe16buf (buf + 6, m_Port); // destination port
|
|
||||||
buf[9] = i2p::client::PROTOCOL_TYPE_STREAMING; // streaming protocol
|
|
||||||
msg->len += size + 4;
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPData);
|
|
||||||
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingDestination::Start ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingDestination::Stop ()
|
|
||||||
{
|
|
||||||
ResetAcceptor ();
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
|
||||||
m_Streams.clear ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingDestination::HandleNextPacket (Packet * packet)
|
|
||||||
{
|
|
||||||
uint32_t sendStreamID = packet->GetSendStreamID ();
|
|
||||||
if (sendStreamID)
|
|
||||||
{
|
|
||||||
auto it = m_Streams.find (sendStreamID);
|
|
||||||
if (it != m_Streams.end ())
|
|
||||||
it->second->HandleNextPacket (packet);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("Unknown stream sendStreamID=", sendStreamID);
|
|
||||||
delete packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (packet->IsSYN () && !packet->GetSeqn ()) // 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // follow on packet without SYN
|
|
||||||
{
|
|
||||||
uint32_t receiveStreamID = packet->GetReceiveStreamID ();
|
|
||||||
for (auto it: m_Streams)
|
|
||||||
if (it.second->GetSendStreamID () == receiveStreamID)
|
|
||||||
{
|
|
||||||
// found
|
|
||||||
it.second->HandleNextPacket (packet);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// TODO: should queue it up
|
|
||||||
LogPrint ("Unknown stream receiveStreamID=", receiveStreamID);
|
|
||||||
delete packet;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Stream> StreamingDestination::CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port)
|
|
||||||
{
|
|
||||||
auto s = std::make_shared<Stream> (m_Owner.GetService (), *this, remote, port);
|
|
||||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
|
||||||
m_Streams[s->GetRecvStreamID ()] = s;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Stream> StreamingDestination::CreateNewIncomingStream ()
|
|
||||||
{
|
|
||||||
auto s = std::make_shared<Stream> (m_Owner.GetService (), *this);
|
|
||||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
|
||||||
m_Streams[s->GetRecvStreamID ()] = s;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingDestination::DeleteStream (std::shared_ptr<Stream> stream)
|
|
||||||
{
|
|
||||||
if (stream)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_StreamsMutex);
|
|
||||||
auto it = m_Streams.find (stream->GetRecvStreamID ());
|
|
||||||
if (it != m_Streams.end ())
|
|
||||||
m_Streams.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StreamingDestination::HandleDataMessagePayload (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
// unzip it
|
|
||||||
CryptoPP::Gunzip decompressor;
|
|
||||||
decompressor.Put (buf, len);
|
|
||||||
decompressor.MessageEnd();
|
|
||||||
Packet * uncompressed = new Packet;
|
|
||||||
uncompressed->offset = 0;
|
|
||||||
uncompressed->len = decompressor.MaxRetrievable ();
|
|
||||||
if (uncompressed->len <= MAX_PACKET_SIZE)
|
|
||||||
{
|
|
||||||
decompressor.Get (uncompressed->buf, uncompressed->len);
|
|
||||||
HandleNextPacket (uncompressed);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("Received packet size ", uncompressed->len, " exceeds max packet size. Skipped");
|
|
||||||
delete uncompressed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
271
Streaming.h
271
Streaming.h
@@ -1,271 +0,0 @@
|
|||||||
#ifndef STREAMING_H__
|
|
||||||
#define STREAMING_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <string>
|
|
||||||
#include <sstream>
|
|
||||||
#include <map>
|
|
||||||
#include <set>
|
|
||||||
#include <queue>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <boost/asio.hpp>
|
|
||||||
#include "I2PEndian.h"
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "LeaseSet.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "Garlic.h"
|
|
||||||
#include "Tunnel.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace client
|
|
||||||
{
|
|
||||||
class ClientDestination;
|
|
||||||
}
|
|
||||||
namespace stream
|
|
||||||
{
|
|
||||||
const uint16_t PACKET_FLAG_SYNCHRONIZE = 0x0001;
|
|
||||||
const uint16_t PACKET_FLAG_CLOSE = 0x0002;
|
|
||||||
const uint16_t PACKET_FLAG_RESET = 0x0004;
|
|
||||||
const uint16_t PACKET_FLAG_SIGNATURE_INCLUDED = 0x0008;
|
|
||||||
const uint16_t PACKET_FLAG_SIGNATURE_REQUESTED = 0x0010;
|
|
||||||
const uint16_t PACKET_FLAG_FROM_INCLUDED = 0x0020;
|
|
||||||
const uint16_t PACKET_FLAG_DELAY_REQUESTED = 0x0040;
|
|
||||||
const uint16_t PACKET_FLAG_MAX_PACKET_SIZE_INCLUDED = 0x0080;
|
|
||||||
const uint16_t PACKET_FLAG_PROFILE_INTERACTIVE = 0x0100;
|
|
||||||
const uint16_t PACKET_FLAG_ECHO = 0x0200;
|
|
||||||
const uint16_t PACKET_FLAG_NO_ACK = 0x0400;
|
|
||||||
|
|
||||||
const size_t STREAMING_MTU = 1730;
|
|
||||||
const size_t MAX_PACKET_SIZE = 4096;
|
|
||||||
const size_t COMPRESSION_THRESHOLD_SIZE = 66;
|
|
||||||
const int ACK_SEND_TIMEOUT = 200; // in milliseconds
|
|
||||||
const int MAX_NUM_RESEND_ATTEMPTS = 6;
|
|
||||||
const int WINDOW_SIZE = 6; // in messages
|
|
||||||
const int MIN_WINDOW_SIZE = 1;
|
|
||||||
const int MAX_WINDOW_SIZE = 128;
|
|
||||||
const int INITIAL_RTT = 8000; // in milliseconds
|
|
||||||
const int INITIAL_RTO = 9000; // in milliseconds
|
|
||||||
|
|
||||||
struct Packet
|
|
||||||
{
|
|
||||||
size_t len, offset;
|
|
||||||
uint8_t buf[MAX_PACKET_SIZE];
|
|
||||||
uint64_t sendTime;
|
|
||||||
|
|
||||||
Packet (): len (0), offset (0), sendTime (0) {};
|
|
||||||
uint8_t * GetBuffer () { return buf + offset; };
|
|
||||||
size_t GetLength () const { return len - offset; };
|
|
||||||
|
|
||||||
uint32_t GetSendStreamID () const { return bufbe32toh (buf); };
|
|
||||||
uint32_t GetReceiveStreamID () const { return bufbe32toh (buf + 4); };
|
|
||||||
uint32_t GetSeqn () const { return bufbe32toh (buf + 8); };
|
|
||||||
uint32_t GetAckThrough () const { return bufbe32toh (buf + 12); };
|
|
||||||
uint8_t GetNACKCount () const { return buf[16]; };
|
|
||||||
uint32_t GetNACK (int i) const { return bufbe32toh (buf + 17 + 4 * i); };
|
|
||||||
const uint8_t * GetOption () const { return buf + 17 + GetNACKCount ()*4 + 3; }; // 3 = resendDelay + flags
|
|
||||||
uint16_t GetFlags () const { return bufbe16toh (GetOption () - 2); };
|
|
||||||
uint16_t GetOptionSize () const { return bufbe16toh (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 ();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
enum StreamStatus
|
|
||||||
{
|
|
||||||
eStreamStatusNew = 0,
|
|
||||||
eStreamStatusOpen,
|
|
||||||
eStreamStatusReset,
|
|
||||||
eStreamStatusClosing,
|
|
||||||
eStreamStatusClosed
|
|
||||||
};
|
|
||||||
|
|
||||||
class StreamingDestination;
|
|
||||||
class Stream: public std::enable_shared_from_this<Stream>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef std::function<void (const boost::system::error_code& ecode)> SendHandler;
|
|
||||||
|
|
||||||
Stream (boost::asio::io_service& service, StreamingDestination& local,
|
|
||||||
std::shared_ptr<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; };
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> GetRemoteLeaseSet () const { return m_RemoteLeaseSet; };
|
|
||||||
const i2p::data::IdentityEx& GetRemoteIdentity () const { return m_RemoteIdentity; };
|
|
||||||
bool IsOpen () const { return m_Status == eStreamStatusOpen; };
|
|
||||||
bool IsEstablished () const { return m_SendStreamID; };
|
|
||||||
StreamStatus GetStatus () const { return m_Status; };
|
|
||||||
StreamingDestination& GetLocalDestination () { return m_LocalDestination; };
|
|
||||||
|
|
||||||
void HandleNextPacket (Packet * packet);
|
|
||||||
size_t Send (const uint8_t * buf, size_t len);
|
|
||||||
void AsyncSend (const uint8_t * buf, size_t len, SendHandler handler);
|
|
||||||
|
|
||||||
template<typename Buffer, typename ReceiveHandler>
|
|
||||||
void AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout = 0);
|
|
||||||
size_t ReadSome (uint8_t * buf, size_t len) { return ConcatenatePackets (buf, len); };
|
|
||||||
|
|
||||||
void Close ();
|
|
||||||
void Cancel () { m_ReceiveTimer.cancel (); };
|
|
||||||
|
|
||||||
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 (); };
|
|
||||||
size_t GetSendBufferSize () const { return m_SendBuffer.rdbuf ()->in_avail (); };
|
|
||||||
int GetWindowSize () const { return m_WindowSize; };
|
|
||||||
int GetRTT () const { return m_RTT; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Terminate ();
|
|
||||||
|
|
||||||
void SendBuffer ();
|
|
||||||
void SendQuickAck ();
|
|
||||||
void SendClose ();
|
|
||||||
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 (bool expired = false);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
std::shared_ptr<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;
|
|
||||||
StreamStatus m_Status;
|
|
||||||
bool m_IsAckSendScheduled;
|
|
||||||
StreamingDestination& m_LocalDestination;
|
|
||||||
i2p::data::IdentityEx m_RemoteIdentity;
|
|
||||||
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
|
|
||||||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
|
||||||
i2p::data::Lease m_CurrentRemoteLease;
|
|
||||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
|
|
||||||
std::queue<Packet *> m_ReceiveQueue;
|
|
||||||
std::set<Packet *, PacketCmp> m_SavedPackets;
|
|
||||||
std::set<Packet *, PacketCmp> m_SentPackets;
|
|
||||||
boost::asio::deadline_timer m_ReceiveTimer, m_ResendTimer, m_AckSendTimer;
|
|
||||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
|
||||||
uint16_t m_Port;
|
|
||||||
|
|
||||||
std::mutex m_SendBufferMutex;
|
|
||||||
std::stringstream m_SendBuffer;
|
|
||||||
int m_WindowSize, m_RTT, m_RTO;
|
|
||||||
uint64_t m_LastWindowSizeIncreaseTime;
|
|
||||||
int m_NumResendAttempts;
|
|
||||||
SendHandler m_SendHandler;
|
|
||||||
};
|
|
||||||
|
|
||||||
class StreamingDestination
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef std::function<void (std::shared_ptr<Stream>)> Acceptor;
|
|
||||||
|
|
||||||
StreamingDestination (i2p::client::ClientDestination& owner, uint16_t localPort = 0):
|
|
||||||
m_Owner (owner), m_LocalPort (localPort) {};
|
|
||||||
~StreamingDestination () {};
|
|
||||||
|
|
||||||
void Start ();
|
|
||||||
void Stop ();
|
|
||||||
|
|
||||||
std::shared_ptr<Stream> CreateNewOutgoingStream (std::shared_ptr<const i2p::data::LeaseSet> remote, int port = 0);
|
|
||||||
void DeleteStream (std::shared_ptr<Stream> stream);
|
|
||||||
void SetAcceptor (const Acceptor& acceptor) { m_Acceptor = acceptor; };
|
|
||||||
void ResetAcceptor () { if (m_Acceptor) m_Acceptor (nullptr); m_Acceptor = nullptr; };
|
|
||||||
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
|
|
||||||
i2p::client::ClientDestination& GetOwner () { return m_Owner; };
|
|
||||||
uint16_t GetLocalPort () const { return m_LocalPort; };
|
|
||||||
|
|
||||||
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleNextPacket (Packet * packet);
|
|
||||||
std::shared_ptr<Stream> CreateNewIncomingStream ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
i2p::client::ClientDestination& m_Owner;
|
|
||||||
uint16_t m_LocalPort;
|
|
||||||
std::mutex m_StreamsMutex;
|
|
||||||
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams;
|
|
||||||
Acceptor m_Acceptor;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP only
|
|
||||||
const decltype(m_Streams)& GetStreams () const { return m_Streams; };
|
|
||||||
};
|
|
||||||
|
|
||||||
//-------------------------------------------------
|
|
||||||
|
|
||||||
template<typename Buffer, typename ReceiveHandler>
|
|
||||||
void Stream::AsyncReceive (const Buffer& buffer, ReceiveHandler handler, int timeout)
|
|
||||||
{
|
|
||||||
auto s = shared_from_this();
|
|
||||||
m_Service.post ([=](void)
|
|
||||||
{
|
|
||||||
if (!m_ReceiveQueue.empty () || m_Status == eStreamStatusReset)
|
|
||||||
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
|
|
||||||
s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
|
|
||||||
{ s->HandleReceiveTimer (ecode, buffer, handler); });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Buffer, typename ReceiveHandler>
|
|
||||||
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
|
|
||||||
{
|
|
||||||
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
|
|
||||||
if (received > 0)
|
|
||||||
handler (boost::system::error_code (), received);
|
|
||||||
else if (ecode == boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
// timeout not expired
|
|
||||||
if (m_Status == eStreamStatusReset)
|
|
||||||
handler (boost::asio::error::make_error_code (boost::asio::error::connection_reset), 0);
|
|
||||||
else
|
|
||||||
handler (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
|
|
||||||
32
Timestamp.h
32
Timestamp.h
@@ -1,32 +0,0 @@
|
|||||||
#ifndef TIMESTAMP_H__
|
|
||||||
#define TIMESTAMP_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace util
|
|
||||||
{
|
|
||||||
inline uint64_t GetMillisecondsSinceEpoch ()
|
|
||||||
{
|
|
||||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count ();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint32_t GetHoursSinceEpoch ()
|
|
||||||
{
|
|
||||||
return std::chrono::duration_cast<std::chrono::hours>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count ();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint64_t GetSecondsSinceEpoch ()
|
|
||||||
{
|
|
||||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@@ -1,111 +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_Encryption.SetKeys (layerKey, ivKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitTunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
|
|
||||||
{
|
|
||||||
m_Encryption.Encrypt (in->GetPayload () + 4, out->GetPayload () + 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
TransitTunnelParticipant::~TransitTunnelParticipant ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
|
||||||
{
|
|
||||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
|
||||||
EncryptTunnelMsg (tunnelMsg, newMsg);
|
|
||||||
|
|
||||||
m_NumTransmittedBytes += tunnelMsg->GetLength ();
|
|
||||||
htobe32buf (newMsg->GetPayload (), GetNextTunnelID ());
|
|
||||||
newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
|
|
||||||
m_TunnelDataMsgs.push_back (newMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitTunnelParticipant::FlushTunnelDataMsgs ()
|
|
||||||
{
|
|
||||||
if (!m_TunnelDataMsgs.empty ())
|
|
||||||
{
|
|
||||||
auto num = m_TunnelDataMsgs.size ();
|
|
||||||
if (num > 1)
|
|
||||||
LogPrint (eLogDebug, "TransitTunnel: ",GetTunnelID (),"->", GetNextTunnelID (), " ", num);
|
|
||||||
i2p::transport::transports.SendMessages (GetNextIdentHash (), m_TunnelDataMsgs);
|
|
||||||
m_TunnelDataMsgs.clear ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitTunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "We are not a gateway for transit tunnel ", m_TunnelID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Incoming tunnel message is not supported ", m_TunnelID);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitTunnelGateway::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
TunnelMessageBlock block;
|
|
||||||
block.deliveryType = eDeliveryTypeLocal;
|
|
||||||
block.data = msg;
|
|
||||||
std::unique_lock<std::mutex> l(m_SendMutex);
|
|
||||||
m_Gateway.PutTunnelDataMsg (block);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitTunnelGateway::FlushTunnelDataMsgs ()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_SendMutex);
|
|
||||||
m_Gateway.SendBuffer ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
|
||||||
{
|
|
||||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
|
||||||
EncryptTunnelMsg (tunnelMsg, newMsg);
|
|
||||||
|
|
||||||
LogPrint (eLogDebug, "TransitTunnel endpoint for ", GetTunnelID ());
|
|
||||||
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (eLogInfo, "TransitTunnel endpoint: ", receiveTunnelID, " created");
|
|
||||||
return new TransitTunnelEndpoint (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
|
||||||
}
|
|
||||||
else if (isGateway)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "TransitTunnel gateway: ", receiveTunnelID, " created");
|
|
||||||
return new TransitTunnelGateway (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "TransitTunnel: ", receiveTunnelID, "->", nextTunnelID, " created");
|
|
||||||
return new TransitTunnelParticipant (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
111
TransitTunnel.h
111
TransitTunnel.h
@@ -1,111 +0,0 @@
|
|||||||
#ifndef TRANSIT_TUNNEL_H__
|
|
||||||
#define TRANSIT_TUNNEL_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#include "aes.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "TunnelEndpoint.h"
|
|
||||||
#include "TunnelGateway.h"
|
|
||||||
#include "TunnelBase.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
class TransitTunnel: public TunnelBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TransitTunnel (uint32_t receiveTunnelID,
|
|
||||||
const uint8_t * nextIdent, uint32_t nextTunnelID,
|
|
||||||
const uint8_t * layerKey,const uint8_t * ivKey);
|
|
||||||
|
|
||||||
virtual size_t GetNumTransmittedBytes () const { return 0; };
|
|
||||||
|
|
||||||
uint32_t GetTunnelID () const { return m_TunnelID; };
|
|
||||||
|
|
||||||
// implements TunnelBase
|
|
||||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
|
||||||
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
|
|
||||||
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
|
|
||||||
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;
|
|
||||||
|
|
||||||
i2p::crypto::TunnelEncryption m_Encryption;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TransitTunnelParticipant: public TransitTunnel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TransitTunnelParticipant (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_NumTransmittedBytes (0) {};
|
|
||||||
~TransitTunnelParticipant ();
|
|
||||||
|
|
||||||
size_t GetNumTransmittedBytes () const { return m_NumTransmittedBytes; };
|
|
||||||
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
|
|
||||||
void FlushTunnelDataMsgs ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
size_t m_NumTransmittedBytes;
|
|
||||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > m_TunnelDataMsgs;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (std::shared_ptr<i2p::I2NPMessage> msg);
|
|
||||||
void FlushTunnelDataMsgs ();
|
|
||||||
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 (std::shared_ptr<const 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,86 +0,0 @@
|
|||||||
#ifndef TRANSPORT_SESSION_H__
|
|
||||||
#define TRANSPORT_SESSION_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <vector>
|
|
||||||
#include "Identity.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace transport
|
|
||||||
{
|
|
||||||
struct DHKeysPair // transient keys for transport sessions
|
|
||||||
{
|
|
||||||
uint8_t publicKey[256];
|
|
||||||
uint8_t privateKey[256];
|
|
||||||
};
|
|
||||||
|
|
||||||
class SignedData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
SignedData () {};
|
|
||||||
void Insert (const uint8_t * buf, size_t len)
|
|
||||||
{
|
|
||||||
m_Stream.write ((char *)buf, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void Insert (T t)
|
|
||||||
{
|
|
||||||
m_Stream.write ((char *)&t, sizeof (T));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Verify (const i2p::data::IdentityEx& ident, const uint8_t * signature) const
|
|
||||||
{
|
|
||||||
return ident.Verify ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sign (const i2p::data::PrivateKeys& keys, uint8_t * signature) const
|
|
||||||
{
|
|
||||||
keys.Sign ((const uint8_t *)m_Stream.str ().c_str (), m_Stream.str ().size (), signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::stringstream m_Stream;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TransportSession
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
|
|
||||||
m_RemoteRouter (in_RemoteRouter), m_DHKeysPair (nullptr),
|
|
||||||
m_NumSentBytes (0), m_NumReceivedBytes (0)
|
|
||||||
{
|
|
||||||
if (m_RemoteRouter)
|
|
||||||
m_RemoteIdentity = m_RemoteRouter->GetRouterIdentity ();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~TransportSession () { delete m_DHKeysPair; };
|
|
||||||
virtual void Done () = 0;
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> GetRemoteRouter () { return m_RemoteRouter; };
|
|
||||||
const i2p::data::IdentityEx& GetRemoteIdentity () { return m_RemoteIdentity; };
|
|
||||||
|
|
||||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
|
||||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
|
||||||
|
|
||||||
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> m_RemoteRouter;
|
|
||||||
i2p::data::IdentityEx m_RemoteIdentity;
|
|
||||||
DHKeysPair * m_DHKeysPair; // X - for client and Y - for server
|
|
||||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
501
Transports.cpp
501
Transports.cpp
@@ -1,501 +0,0 @@
|
|||||||
#include <cryptopp/dh.h>
|
|
||||||
#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_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
|
|
||||||
m_NTCPServer (nullptr), m_SSUServer (nullptr), m_DHKeysPairSupplier (5), // 5 pre-generated keys
|
|
||||||
m_TotalSentBytes(0), m_TotalReceivedBytes(0), m_InBandwidth (0), m_OutBandwidth (0),
|
|
||||||
m_LastInBandwidthUpdateBytes (0), m_LastOutBandwidthUpdateBytes (0), m_LastBandwidthUpdateTime (0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Transports::~Transports ()
|
|
||||||
{
|
|
||||||
Stop ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::Start ()
|
|
||||||
{
|
|
||||||
#ifdef USE_UPNP
|
|
||||||
m_UPnP.Start ();
|
|
||||||
LogPrint(eLogInfo, "UPnP started");
|
|
||||||
#endif
|
|
||||||
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 (!m_NTCPServer)
|
|
||||||
{
|
|
||||||
m_NTCPServer = new NTCPServer (address.port);
|
|
||||||
m_NTCPServer->Start ();
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
|
||||||
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::Stop ()
|
|
||||||
{
|
|
||||||
#ifdef USE_UPNP
|
|
||||||
m_UPnP.Stop ();
|
|
||||||
LogPrint(eLogInfo, "UPnP stopped");
|
|
||||||
#endif
|
|
||||||
m_PeerCleanupTimer.cancel ();
|
|
||||||
m_Peers.clear ();
|
|
||||||
if (m_SSUServer)
|
|
||||||
{
|
|
||||||
m_SSUServer->Stop ();
|
|
||||||
delete m_SSUServer;
|
|
||||||
m_SSUServer = nullptr;
|
|
||||||
}
|
|
||||||
if (m_NTCPServer)
|
|
||||||
{
|
|
||||||
m_NTCPServer->Stop ();
|
|
||||||
delete m_NTCPServer;
|
|
||||||
m_NTCPServer = 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::UpdateBandwidth ()
|
|
||||||
{
|
|
||||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
|
||||||
if (m_LastBandwidthUpdateTime > 0)
|
|
||||||
{
|
|
||||||
auto delta = ts - m_LastBandwidthUpdateTime;
|
|
||||||
if (delta > 0)
|
|
||||||
{
|
|
||||||
m_InBandwidth = (m_TotalReceivedBytes - m_LastInBandwidthUpdateBytes)*1000/delta; // per second
|
|
||||||
m_OutBandwidth = (m_TotalSentBytes - m_LastOutBandwidthUpdateBytes)*1000/delta; // per second
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_LastBandwidthUpdateTime = ts;
|
|
||||||
m_LastInBandwidthUpdateBytes = m_TotalReceivedBytes;
|
|
||||||
m_LastOutBandwidthUpdateBytes = m_TotalSentBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Transports::IsBandwidthExceeded () const
|
|
||||||
{
|
|
||||||
if (i2p::context.GetRouterInfo ().IsHighBandwidth ()) return false;
|
|
||||||
return std::max (m_InBandwidth, m_OutBandwidth) > LOW_BANDWIDTH_LIMIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
SendMessages (ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > {msg });
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs)
|
|
||||||
{
|
|
||||||
m_Service.post (std::bind (&Transports::PostMessages, this, ident, msgs));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs)
|
|
||||||
{
|
|
||||||
if (ident == i2p::context.GetRouterInfo ().GetIdentHash ())
|
|
||||||
{
|
|
||||||
// we send it to ourself
|
|
||||||
for (auto it: msgs)
|
|
||||||
i2p::HandleI2NPMessage (it);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto it = m_Peers.find (ident);
|
|
||||||
if (it == m_Peers.end ())
|
|
||||||
{
|
|
||||||
bool connected = false;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
auto r = netdb.FindRouter (ident);
|
|
||||||
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},
|
|
||||||
i2p::util::GetSecondsSinceEpoch () })).first;
|
|
||||||
connected = ConnectToPeer (ident, it->second);
|
|
||||||
}
|
|
||||||
catch (std::exception& ex)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Transports::PostMessages ", ex.what ());
|
|
||||||
}
|
|
||||||
if (!connected) return;
|
|
||||||
}
|
|
||||||
if (!it->second.sessions.empty ())
|
|
||||||
it->second.sessions.front ()->SendI2NPMessages (msgs);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (auto it1: msgs)
|
|
||||||
it->second.delayedMessages.push_back (it1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Transports::ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer)
|
|
||||||
{
|
|
||||||
if (peer.router) // we have RI already
|
|
||||||
{
|
|
||||||
if (!peer.numAttempts) // NTCP
|
|
||||||
{
|
|
||||||
peer.numAttempts++;
|
|
||||||
auto address = peer.router->GetNTCPAddress (!context.SupportsV6 ());
|
|
||||||
if (address)
|
|
||||||
{
|
|
||||||
#if BOOST_VERSION >= 104900
|
|
||||||
if (!address->host.is_unspecified ()) // we have address now
|
|
||||||
#else
|
|
||||||
boost::system::error_code ecode;
|
|
||||||
address->host.to_string (ecode);
|
|
||||||
if (!ecode)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
if (!peer.router->UsesIntroducer () && !peer.router->IsUnreachable ())
|
|
||||||
{
|
|
||||||
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
|
|
||||||
m_NTCPServer->Connect (address->host, address->port, s);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // we don't have address
|
|
||||||
{
|
|
||||||
if (address->addressString.length () > 0) // trying to resolve
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Resolving ", address->addressString);
|
|
||||||
NTCPResolve (address->addressString, ident);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (peer.numAttempts == 1)// SSU
|
|
||||||
{
|
|
||||||
peer.numAttempts++;
|
|
||||||
if (m_SSUServer)
|
|
||||||
{
|
|
||||||
if (m_SSUServer->GetSession (peer.router))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogError, "No NTCP and SSU addresses available");
|
|
||||||
peer.Done ();
|
|
||||||
m_Peers.erase (ident);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else // otherwise request RI
|
|
||||||
{
|
|
||||||
LogPrint ("Router not found. Requested");
|
|
||||||
i2p::data::netdb.RequestDestination (ident, std::bind (
|
|
||||||
&Transports::RequestComplete, this, std::placeholders::_1, ident));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
m_Service.post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
auto it = m_Peers.find (ident);
|
|
||||||
if (it != m_Peers.end ())
|
|
||||||
{
|
|
||||||
if (r)
|
|
||||||
{
|
|
||||||
LogPrint ("Router found. Trying to connect");
|
|
||||||
it->second.router = r;
|
|
||||||
ConnectToPeer (ident, it->second);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint ("Router not found. Failed to send messages");
|
|
||||||
m_Peers.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident)
|
|
||||||
{
|
|
||||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
|
|
||||||
resolver->async_resolve (boost::asio::ip::tcp::resolver::query (addr, ""),
|
|
||||||
std::bind (&Transports::HandleNTCPResolve, this,
|
|
||||||
std::placeholders::_1, std::placeholders::_2, ident, resolver));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver)
|
|
||||||
{
|
|
||||||
auto it1 = m_Peers.find (ident);
|
|
||||||
if (it1 != m_Peers.end ())
|
|
||||||
{
|
|
||||||
auto& peer = it1->second;
|
|
||||||
if (!ecode && peer.router)
|
|
||||||
{
|
|
||||||
auto address = (*it).endpoint ().address ();
|
|
||||||
LogPrint (eLogInfo, (*it).host_name (), " has been resolved to ", address);
|
|
||||||
auto addr = peer.router->GetNTCPAddress ();
|
|
||||||
if (addr)
|
|
||||||
{
|
|
||||||
auto s = std::make_shared<NTCPSession> (*m_NTCPServer, peer.router);
|
|
||||||
m_NTCPServer->Connect (address, addr->port, s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LogPrint (eLogError, "Unable to resolve NTCP address: ", ecode.message ());
|
|
||||||
m_Peers.erase (it1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
|
||||||
{
|
|
||||||
if (!router) return;
|
|
||||||
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
|
||||||
{
|
|
||||||
auto ssuSession = m_SSUServer ? m_SSUServer->FindSession (router) : nullptr;
|
|
||||||
if (ssuSession) // try SSU first
|
|
||||||
{
|
|
||||||
m_SSUServer->DeleteSession (ssuSession);
|
|
||||||
LogPrint ("SSU session closed");
|
|
||||||
}
|
|
||||||
// TODO: delete NTCP
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::DetectExternalIP ()
|
|
||||||
{
|
|
||||||
if (m_SSUServer)
|
|
||||||
{
|
|
||||||
i2p::context.SetStatus (eRouterStatusTesting);
|
|
||||||
for (int i = 0; i < 5; i++)
|
|
||||||
{
|
|
||||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
|
|
||||||
if (router && router->IsSSU ())
|
|
||||||
m_SSUServer->GetSession (router, true); // peer test
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if not peer test capable routers found pick any
|
|
||||||
router = i2p::data::netdb.GetRandomRouter ();
|
|
||||||
if (router && router->IsSSU ())
|
|
||||||
m_SSUServer->GetSession (router); // no peer test
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Can't detect external IP. SSU is not available");
|
|
||||||
}
|
|
||||||
|
|
||||||
DHKeysPair * Transports::GetNextDHKeysPair ()
|
|
||||||
{
|
|
||||||
return m_DHKeysPairSupplier.Acquire ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::ReuseDHKeysPair (DHKeysPair * pair)
|
|
||||||
{
|
|
||||||
m_DHKeysPairSupplier.Return (pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
|
|
||||||
{
|
|
||||||
m_Service.post([session, this]()
|
|
||||||
{
|
|
||||||
auto ident = session->GetRemoteIdentity ().GetIdentHash ();
|
|
||||||
auto it = m_Peers.find (ident);
|
|
||||||
if (it != m_Peers.end ())
|
|
||||||
{
|
|
||||||
it->second.sessions.push_back (session);
|
|
||||||
session->SendI2NPMessages (it->second.delayedMessages);
|
|
||||||
it->second.delayedMessages.clear ();
|
|
||||||
}
|
|
||||||
else // incoming connection
|
|
||||||
m_Peers.insert (std::make_pair (ident, Peer{ 0, nullptr, { session }, i2p::util::GetSecondsSinceEpoch () }));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::PeerDisconnected (std::shared_ptr<TransportSession> session)
|
|
||||||
{
|
|
||||||
m_Service.post([session, this]()
|
|
||||||
{
|
|
||||||
auto ident = session->GetRemoteIdentity ().GetIdentHash ();
|
|
||||||
auto it = m_Peers.find (ident);
|
|
||||||
if (it != m_Peers.end ())
|
|
||||||
{
|
|
||||||
it->second.sessions.remove (session);
|
|
||||||
if (it->second.sessions.empty ()) // TODO: why?
|
|
||||||
{
|
|
||||||
if (it->second.delayedMessages.size () > 0)
|
|
||||||
ConnectToPeer (ident, it->second);
|
|
||||||
else
|
|
||||||
m_Peers.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Transports::IsConnected (const i2p::data::IdentHash& ident) const
|
|
||||||
{
|
|
||||||
auto it = m_Peers.find (ident);
|
|
||||||
return it != m_Peers.end ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transports::HandlePeerCleanupTimer (const boost::system::error_code& ecode)
|
|
||||||
{
|
|
||||||
if (ecode != boost::asio::error::operation_aborted)
|
|
||||||
{
|
|
||||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto it = m_Peers.begin (); it != m_Peers.end (); )
|
|
||||||
{
|
|
||||||
if (it->second.sessions.empty () && ts > it->second.creationTime + SESSION_CREATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Session to peer ", it->first.ToBase64 (), " has not been created in ", SESSION_CREATION_TIMEOUT, " seconds");
|
|
||||||
it = m_Peers.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
}
|
|
||||||
UpdateBandwidth (); // TODO: use separate timer(s) for it
|
|
||||||
if (i2p::context.GetStatus () == eRouterStatusTesting) // if still testing, repeat peer test
|
|
||||||
DetectExternalIP ();
|
|
||||||
m_PeerCleanupTimer.expires_from_now (boost::posix_time::seconds(5*SESSION_CREATION_TIMEOUT));
|
|
||||||
m_PeerCleanupTimer.async_wait (std::bind (&Transports::HandlePeerCleanupTimer, this, std::placeholders::_1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
|
|
||||||
{
|
|
||||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
|
||||||
auto it = m_Peers.begin ();
|
|
||||||
std::advance (it, rnd.GenerateWord32 (0, m_Peers.size () - 1));
|
|
||||||
return it != m_Peers.end () ? it->second.router : nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
160
Transports.h
160
Transports.h
@@ -1,160 +0,0 @@
|
|||||||
#ifndef TRANSPORTS_H__
|
|
||||||
#define TRANSPORTS_H__
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <functional>
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <queue>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
#include <atomic>
|
|
||||||
#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"
|
|
||||||
|
|
||||||
#ifdef USE_UPNP
|
|
||||||
#include "UPnP.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Peer
|
|
||||||
{
|
|
||||||
int numAttempts;
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> router;
|
|
||||||
std::list<std::shared_ptr<TransportSession> > sessions;
|
|
||||||
uint64_t creationTime;
|
|
||||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > delayedMessages;
|
|
||||||
|
|
||||||
void Done ()
|
|
||||||
{
|
|
||||||
for (auto it: sessions)
|
|
||||||
it->Done ();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
|
|
||||||
const uint32_t LOW_BANDWIDTH_LIMIT = 32*1024; // 32KBs
|
|
||||||
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 SendMessage (const i2p::data::IdentHash& ident, std::shared_ptr<i2p::I2NPMessage> msg);
|
|
||||||
void SendMessages (const i2p::data::IdentHash& ident, const std::vector<std::shared_ptr<i2p::I2NPMessage> >& msgs);
|
|
||||||
void CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
|
||||||
|
|
||||||
void PeerConnected (std::shared_ptr<TransportSession> session);
|
|
||||||
void PeerDisconnected (std::shared_ptr<TransportSession> session);
|
|
||||||
bool IsConnected (const i2p::data::IdentHash& ident) const;
|
|
||||||
|
|
||||||
void UpdateSentBytes (uint64_t numBytes) { m_TotalSentBytes += numBytes; };
|
|
||||||
void UpdateReceivedBytes (uint64_t numBytes) { m_TotalReceivedBytes += numBytes; };
|
|
||||||
uint64_t GetTotalSentBytes () const { return m_TotalSentBytes; };
|
|
||||||
uint64_t GetTotalReceivedBytes () const { return m_TotalReceivedBytes; };
|
|
||||||
uint32_t GetInBandwidth () const { return m_InBandwidth; }; // bytes per second
|
|
||||||
uint32_t GetOutBandwidth () const { return m_OutBandwidth; }; // bytes per second
|
|
||||||
bool IsBandwidthExceeded () const;
|
|
||||||
size_t GetNumPeers () const { return m_Peers.size (); };
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> GetRandomPeer () const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void RequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
|
|
||||||
void HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, const i2p::data::IdentHash& ident);
|
|
||||||
void PostMessages (i2p::data::IdentHash ident, std::vector<std::shared_ptr<i2p::I2NPMessage> > msgs);
|
|
||||||
void PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router);
|
|
||||||
bool ConnectToPeer (const i2p::data::IdentHash& ident, Peer& peer);
|
|
||||||
void HandlePeerCleanupTimer (const boost::system::error_code& ecode);
|
|
||||||
|
|
||||||
void NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident);
|
|
||||||
void HandleNTCPResolve (const boost::system::error_code& ecode, boost::asio::ip::tcp::resolver::iterator it,
|
|
||||||
i2p::data::IdentHash ident, std::shared_ptr<boost::asio::ip::tcp::resolver> resolver);
|
|
||||||
|
|
||||||
void UpdateBandwidth ();
|
|
||||||
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::deadline_timer m_PeerCleanupTimer;
|
|
||||||
|
|
||||||
NTCPServer * m_NTCPServer;
|
|
||||||
SSUServer * m_SSUServer;
|
|
||||||
std::map<i2p::data::IdentHash, Peer> m_Peers;
|
|
||||||
|
|
||||||
DHKeysPairSupplier m_DHKeysPairSupplier;
|
|
||||||
|
|
||||||
std::atomic<uint64_t> m_TotalSentBytes, m_TotalReceivedBytes;
|
|
||||||
uint32_t m_InBandwidth, m_OutBandwidth;
|
|
||||||
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
|
|
||||||
uint64_t m_LastBandwidthUpdateTime;
|
|
||||||
|
|
||||||
#ifdef USE_UPNP
|
|
||||||
UPnP m_UPnP;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// for HTTP only
|
|
||||||
const NTCPServer * GetNTCPServer () const { return m_NTCPServer; };
|
|
||||||
const SSUServer * GetSSUServer () const { return m_SSUServer; };
|
|
||||||
const decltype(m_Peers)& GetPeers () const { return m_Peers; };
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Transports transports;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
732
Tunnel.cpp
732
Tunnel.cpp
@@ -1,732 +0,0 @@
|
|||||||
#include <string.h>
|
|
||||||
#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 (std::shared_ptr<const TunnelConfig> config):
|
|
||||||
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Tunnel::~Tunnel ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
|
|
||||||
{
|
|
||||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
|
||||||
auto numHops = m_Config->GetNumHops ();
|
|
||||||
int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : numHops;
|
|
||||||
auto msg = NewI2NPShortMessage ();
|
|
||||||
*msg->GetPayload () = numRecords;
|
|
||||||
msg->len += numRecords*TUNNEL_BUILD_RECORD_SIZE + 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
|
|
||||||
uint8_t * records = msg->GetPayload () + 1;
|
|
||||||
TunnelHopConfig * hop = m_Config->GetFirstHop ();
|
|
||||||
int i = 0;
|
|
||||||
while (hop)
|
|
||||||
{
|
|
||||||
int idx = recordIndicies[i];
|
|
||||||
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE,
|
|
||||||
hop->next ? rnd.GenerateWord32 () : replyMsgID); // we set replyMsgID for last hop only
|
|
||||||
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 (records + idx*TUNNEL_BUILD_RECORD_SIZE, TUNNEL_BUILD_RECORD_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
uint8_t * record = records + hop1->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
|
|
||||||
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, record);
|
|
||||||
hop1 = hop1->next;
|
|
||||||
}
|
|
||||||
hop = hop->prev;
|
|
||||||
}
|
|
||||||
msg->FillI2NPMessageHeader (eI2NPVariableTunnelBuild);
|
|
||||||
|
|
||||||
// send message
|
|
||||||
if (outboundTunnel)
|
|
||||||
outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, ToSharedI2NPMessage (msg));
|
|
||||||
else
|
|
||||||
i2p::transport::transports.SendMessage (GetNextIdentHash (), ToSharedI2NPMessage (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*TUNNEL_BUILD_RECORD_SIZE;
|
|
||||||
decryption.SetIV (hop->replyIV);
|
|
||||||
decryption.Decrypt(record, TUNNEL_BUILD_RECORD_SIZE, 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)
|
|
||||||
{
|
|
||||||
const uint8_t * record = msg + 1 + hop->recordIndex*TUNNEL_BUILD_RECORD_SIZE;
|
|
||||||
uint8_t ret = record[BUILD_RESPONSE_RECORD_RET_OFFSET];
|
|
||||||
LogPrint ("Ret code=", (int)ret);
|
|
||||||
hop->router->GetProfile ()->TunnelBuildResponse (ret);
|
|
||||||
if (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 (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
|
|
||||||
{
|
|
||||||
const uint8_t * inPayload = in->GetPayload () + 4;
|
|
||||||
uint8_t * outPayload = out->GetPayload () + 4;
|
|
||||||
TunnelHopConfig * hop = m_Config->GetLastHop ();
|
|
||||||
while (hop)
|
|
||||||
{
|
|
||||||
hop->decryption.Decrypt (inPayload, outPayload);
|
|
||||||
hop = hop->prev;
|
|
||||||
inPayload = outPayload;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnel::SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Can't send I2NP messages without delivery instructions");
|
|
||||||
}
|
|
||||||
|
|
||||||
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
|
|
||||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
|
||||||
EncryptTunnelMsg (msg, newMsg);
|
|
||||||
newMsg->from = shared_from_this ();
|
|
||||||
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<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 ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Incoming message for outbound tunnel ", GetTunnelID ());
|
|
||||||
}
|
|
||||||
|
|
||||||
Tunnels tunnels;
|
|
||||||
|
|
||||||
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
|
|
||||||
m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Tunnels::~Tunnels ()
|
|
||||||
{
|
|
||||||
for (auto& it : m_TransitTunnels)
|
|
||||||
delete it.second;
|
|
||||||
m_TransitTunnels.clear ();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<InboundTunnel> Tunnels::GetPendingInboundTunnel (uint32_t replyMsgID)
|
|
||||||
{
|
|
||||||
return GetPendingTunnel (replyMsgID, m_PendingInboundTunnels);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<OutboundTunnel> Tunnels::GetPendingOutboundTunnel (uint32_t replyMsgID)
|
|
||||||
{
|
|
||||||
return GetPendingTunnel (replyMsgID, m_PendingOutboundTunnels);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class TTunnel>
|
|
||||||
std::shared_ptr<TTunnel> Tunnels::GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels)
|
|
||||||
{
|
|
||||||
auto it = pendingTunnels.find(replyMsgID);
|
|
||||||
if (it != pendingTunnels.end () && it->second->GetState () == eTunnelStatePending)
|
|
||||||
{
|
|
||||||
it->second->SetState (eTunnelStateBuildReplyReceived);
|
|
||||||
return it->second;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<InboundTunnel> Tunnels::GetNextInboundTunnel ()
|
|
||||||
{
|
|
||||||
std::shared_ptr<InboundTunnel> tunnel;
|
|
||||||
size_t minReceived = 0;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<OutboundTunnel> Tunnels::GetNextOutboundTunnel ()
|
|
||||||
{
|
|
||||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
|
||||||
uint32_t ind = rnd.GenerateWord32 (0, m_OutboundTunnels.size () - 1), i = 0;
|
|
||||||
std::shared_ptr<OutboundTunnel> tunnel;
|
|
||||||
for (auto it: m_OutboundTunnels)
|
|
||||||
{
|
|
||||||
if (it->IsEstablished ())
|
|
||||||
{
|
|
||||||
tunnel = it;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if (i > ind && tunnel) break;
|
|
||||||
}
|
|
||||||
return tunnel;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TunnelPool> Tunnels::CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels)
|
|
||||||
{
|
|
||||||
auto pool = std::make_shared<TunnelPool> (localDestination, numInboundHops, numOutboundHops, numInboundTunnels, numOutboundTunnels);
|
|
||||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
|
||||||
m_Pools.push_back (pool);
|
|
||||||
return pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::DeleteTunnelPool (std::shared_ptr<TunnelPool> pool)
|
|
||||||
{
|
|
||||||
if (pool)
|
|
||||||
{
|
|
||||||
StopTunnelPool (pool);
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_PoolsMutex);
|
|
||||||
m_Pools.remove (pool);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::StopTunnelPool (std::shared_ptr<TunnelPool> pool)
|
|
||||||
{
|
|
||||||
if (pool)
|
|
||||||
{
|
|
||||||
pool->SetActive (false);
|
|
||||||
pool->DetachTunnels ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::AddTransitTunnel (TransitTunnel * tunnel)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
|
|
||||||
if (!m_TransitTunnels.insert (std::make_pair (tunnel->GetTunnelID (), tunnel)).second)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Transit tunnel ", tunnel->GetTunnelID (), " already exists");
|
|
||||||
delete 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
|
|
||||||
{
|
|
||||||
auto msg = m_Queue.GetNextWithTimeout (1000); // 1 sec
|
|
||||||
if (msg)
|
|
||||||
{
|
|
||||||
uint32_t prevTunnelID = 0, tunnelID = 0;
|
|
||||||
TunnelBase * prevTunnel = nullptr;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
TunnelBase * tunnel = nullptr;
|
|
||||||
uint8_t typeID = msg->GetTypeID ();
|
|
||||||
switch (typeID)
|
|
||||||
{
|
|
||||||
case eI2NPTunnelData:
|
|
||||||
case eI2NPTunnelGateway:
|
|
||||||
{
|
|
||||||
tunnelID = bufbe32toh (msg->GetPayload ());
|
|
||||||
if (tunnelID == prevTunnelID)
|
|
||||||
tunnel = prevTunnel;
|
|
||||||
else if (prevTunnel)
|
|
||||||
prevTunnel->FlushTunnelDataMsgs ();
|
|
||||||
|
|
||||||
if (!tunnel && typeID == eI2NPTunnelData)
|
|
||||||
tunnel = GetInboundTunnel (tunnelID).get ();
|
|
||||||
if (!tunnel)
|
|
||||||
tunnel = GetTransitTunnel (tunnelID);
|
|
||||||
if (tunnel)
|
|
||||||
{
|
|
||||||
if (typeID == eI2NPTunnelData)
|
|
||||||
tunnel->HandleTunnelDataMsg (msg);
|
|
||||||
else // tunnel gateway assumed
|
|
||||||
HandleTunnelGatewayMsg (tunnel, msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogWarning, "Tunnel ", tunnelID, " not found");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case eI2NPVariableTunnelBuild:
|
|
||||||
case eI2NPVariableTunnelBuildReply:
|
|
||||||
case eI2NPTunnelBuild:
|
|
||||||
case eI2NPTunnelBuildReply:
|
|
||||||
HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint (eLogError, "Unexpected messsage type ", (int)typeID);
|
|
||||||
}
|
|
||||||
|
|
||||||
msg = m_Queue.Get ();
|
|
||||||
if (msg)
|
|
||||||
{
|
|
||||||
prevTunnelID = tunnelID;
|
|
||||||
prevTunnel = tunnel;
|
|
||||||
}
|
|
||||||
else if (tunnel)
|
|
||||||
tunnel->FlushTunnelDataMsgs ();
|
|
||||||
}
|
|
||||||
while (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
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::HandleTunnelGatewayMsg (TunnelBase * tunnel, std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (!tunnel)
|
|
||||||
{
|
|
||||||
LogPrint (eLogError, "Missing tunnel for TunnelGateway");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const uint8_t * payload = msg->GetPayload ();
|
|
||||||
uint16_t len = bufbe16toh(payload + TUNNEL_GATEWAY_HEADER_LENGTH_OFFSET);
|
|
||||||
// we make payload as new I2NP message to send
|
|
||||||
msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE;
|
|
||||||
msg->len = msg->offset + len;
|
|
||||||
auto typeID = msg->GetTypeID ();
|
|
||||||
LogPrint (eLogDebug, "TunnelGateway of ", (int)len, " bytes for tunnel ", tunnel->GetTunnelID (), ". Msg type ", (int)typeID);
|
|
||||||
|
|
||||||
if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply)
|
|
||||||
// transit DatabaseStore my contain new/updated RI
|
|
||||||
// or DatabaseSearchReply with new routers
|
|
||||||
i2p::data::netdb.PostI2NPMsg (msg);
|
|
||||||
tunnel->SendTunnelDataMsg (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::ManageTunnels ()
|
|
||||||
{
|
|
||||||
ManagePendingTunnels ();
|
|
||||||
ManageInboundTunnels ();
|
|
||||||
ManageOutboundTunnels ();
|
|
||||||
ManageTransitTunnels ();
|
|
||||||
ManageTunnelPools ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::ManagePendingTunnels ()
|
|
||||||
{
|
|
||||||
ManagePendingTunnels (m_PendingInboundTunnels);
|
|
||||||
ManagePendingTunnels (m_PendingOutboundTunnels);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class PendingTunnels>
|
|
||||||
void Tunnels::ManagePendingTunnels (PendingTunnels& pendingTunnels)
|
|
||||||
{
|
|
||||||
// check pending tunnel. delete failed or timeout
|
|
||||||
uint64_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
for (auto it = pendingTunnels.begin (); it != 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");
|
|
||||||
// update stats
|
|
||||||
auto config = tunnel->GetTunnelConfig ();
|
|
||||||
if (config)
|
|
||||||
{
|
|
||||||
auto hop = config->GetFirstHop ();
|
|
||||||
while (hop)
|
|
||||||
{
|
|
||||||
if (hop->router)
|
|
||||||
hop->router->GetProfile ()->TunnelNonReplied ();
|
|
||||||
hop = hop->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// delete
|
|
||||||
it = pendingTunnels.erase (it);
|
|
||||||
m_NumFailedTunnelCreations++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
it++;
|
|
||||||
break;
|
|
||||||
case eTunnelStateBuildFailed:
|
|
||||||
LogPrint ("Pending tunnel build request ", it->first, " failed. Deleted");
|
|
||||||
it = pendingTunnels.erase (it);
|
|
||||||
m_NumFailedTunnelCreations++;
|
|
||||||
break;
|
|
||||||
case eTunnelStateBuildReplyReceived:
|
|
||||||
// intermediate state, will be either established of build failed
|
|
||||||
it++;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// success
|
|
||||||
it = pendingTunnels.erase (it);
|
|
||||||
m_NumSuccesiveTunnelCreations++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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");
|
|
||||||
auto pool = tunnel->GetTunnelPool ();
|
|
||||||
if (pool)
|
|
||||||
pool->TunnelExpired (tunnel);
|
|
||||||
it = m_OutboundTunnels.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (tunnel->IsEstablished ())
|
|
||||||
{
|
|
||||||
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
tunnel->SetIsRecreated ();
|
|
||||||
auto pool = tunnel->GetTunnelPool ();
|
|
||||||
if (pool)
|
|
||||||
pool->RecreateOutboundTunnel (tunnel);
|
|
||||||
}
|
|
||||||
if (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
|
|
||||||
auto inboundTunnel = GetNextInboundTunnel ();
|
|
||||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
|
||||||
if (!inboundTunnel || !router) return;
|
|
||||||
LogPrint ("Creating one hop outbound tunnel...");
|
|
||||||
CreateTunnel<OutboundTunnel> (
|
|
||||||
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > { router },
|
|
||||||
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");
|
|
||||||
auto pool = tunnel->GetTunnelPool ();
|
|
||||||
if (pool)
|
|
||||||
pool->TunnelExpired (tunnel);
|
|
||||||
it = m_InboundTunnels.erase (it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (tunnel->IsEstablished ())
|
|
||||||
{
|
|
||||||
if (!tunnel->IsRecreated () && ts + TUNNEL_RECREATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
|
||||||
{
|
|
||||||
tunnel->SetIsRecreated ();
|
|
||||||
auto pool = tunnel->GetTunnelPool ();
|
|
||||||
if (pool)
|
|
||||||
pool->RecreateInboundTunnel (tunnel);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (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, 5, 5); // 2-hop exploratory, 5 tunnels
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5)
|
|
||||||
{
|
|
||||||
// trying to create one more inbound tunnel
|
|
||||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
|
||||||
LogPrint ("Creating one hop inbound tunnel...");
|
|
||||||
CreateTunnel<InboundTunnel> (
|
|
||||||
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > { router })
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
auto tmp = it->second;
|
|
||||||
LogPrint ("Transit tunnel ", tmp->GetTunnelID (), " expired");
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
auto pool = it;
|
|
||||||
if (pool && pool->IsActive ())
|
|
||||||
{
|
|
||||||
pool->CreateTunnels ();
|
|
||||||
pool->TestTunnels ();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::PostTunnelData (std::shared_ptr<I2NPMessage> msg)
|
|
||||||
{
|
|
||||||
if (msg) m_Queue.Put (msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
|
||||||
{
|
|
||||||
m_Queue.Put (msgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class TTunnel>
|
|
||||||
std::shared_ptr<TTunnel> Tunnels::CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
|
|
||||||
{
|
|
||||||
auto newTunnel = std::make_shared<TTunnel> (config);
|
|
||||||
uint32_t replyMsgID = i2p::context.GetRandomNumberGenerator ().GenerateWord32 ();
|
|
||||||
AddPendingTunnel (replyMsgID, newTunnel);
|
|
||||||
newTunnel->Build (replyMsgID, outboundTunnel);
|
|
||||||
return newTunnel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel)
|
|
||||||
{
|
|
||||||
m_PendingInboundTunnels[replyMsgID] = tunnel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel)
|
|
||||||
{
|
|
||||||
m_PendingOutboundTunnels[replyMsgID] = tunnel;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel)
|
|
||||||
{
|
|
||||||
m_OutboundTunnels.push_back (newTunnel);
|
|
||||||
auto pool = newTunnel->GetTunnelPool ();
|
|
||||||
if (pool && pool->IsActive ())
|
|
||||||
pool->TunnelCreated (newTunnel);
|
|
||||||
else
|
|
||||||
newTunnel->SetTunnelPool (nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Tunnels::AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel)
|
|
||||||
{
|
|
||||||
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> (
|
|
||||||
std::make_shared<TunnelConfig> (std::vector<std::shared_ptr<const i2p::data::RouterInfo> >
|
|
||||||
{
|
|
||||||
i2p::context.GetSharedRouterInfo ()
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
int Tunnels::GetTransitTunnelsExpirationTimeout ()
|
|
||||||
{
|
|
||||||
int timeout = 0;
|
|
||||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
|
||||||
std::unique_lock<std::mutex> l(m_TransitTunnelsMutex);
|
|
||||||
for (auto it: m_TransitTunnels)
|
|
||||||
{
|
|
||||||
int t = it.second->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT - ts;
|
|
||||||
if (t > timeout) timeout = t;
|
|
||||||
}
|
|
||||||
return timeout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
203
Tunnel.h
203
Tunnel.h
@@ -1,203 +0,0 @@
|
|||||||
#ifndef TUNNEL_H__
|
|
||||||
#define TUNNEL_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <map>
|
|
||||||
#include <list>
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
|
||||||
#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_RECREATION_THRESHOLD = 90; // 1.5 minutes
|
|
||||||
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 (std::shared_ptr<const TunnelConfig> config);
|
|
||||||
~Tunnel ();
|
|
||||||
|
|
||||||
void Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
|
|
||||||
|
|
||||||
std::shared_ptr<const 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; };
|
|
||||||
bool IsRecreated () const { return m_IsRecreated; };
|
|
||||||
void SetIsRecreated () { m_IsRecreated = true; };
|
|
||||||
|
|
||||||
std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; };
|
|
||||||
void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };
|
|
||||||
|
|
||||||
bool HandleTunnelBuildResponse (uint8_t * msg, size_t len);
|
|
||||||
|
|
||||||
// implements TunnelBase
|
|
||||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
|
||||||
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
|
|
||||||
uint32_t GetNextTunnelID () const { return m_Config->GetFirstHop ()->tunnelID; };
|
|
||||||
const i2p::data::IdentHash& GetNextIdentHash () const { return m_Config->GetFirstHop ()->router->GetIdentHash (); };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::shared_ptr<const TunnelConfig> m_Config;
|
|
||||||
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
|
|
||||||
TunnelState m_State;
|
|
||||||
bool m_IsRecreated;
|
|
||||||
};
|
|
||||||
|
|
||||||
class OutboundTunnel: public Tunnel
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
OutboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Gateway (this) {};
|
|
||||||
|
|
||||||
void SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr<i2p::I2NPMessage> msg);
|
|
||||||
void SendTunnelDataMsg (const std::vector<TunnelMessageBlock>& msgs); // multiple messages
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> GetEndpointRouter () const
|
|
||||||
{ return GetTunnelConfig ()->GetLastHop ()->router; };
|
|
||||||
size_t GetNumSentBytes () const { return m_Gateway.GetNumSentBytes (); };
|
|
||||||
|
|
||||||
// implements TunnelBase
|
|
||||||
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
|
|
||||||
uint32_t GetTunnelID () const { return GetNextTunnelID (); };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::mutex m_SendMutex;
|
|
||||||
TunnelGateway m_Gateway;
|
|
||||||
};
|
|
||||||
|
|
||||||
class InboundTunnel: public Tunnel, public std::enable_shared_from_this<InboundTunnel>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
InboundTunnel (std::shared_ptr<const TunnelConfig> config): Tunnel (config), m_Endpoint (true) {};
|
|
||||||
void HandleTunnelDataMsg (std::shared_ptr<const 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 ();
|
|
||||||
|
|
||||||
std::shared_ptr<InboundTunnel> GetInboundTunnel (uint32_t tunnelID);
|
|
||||||
std::shared_ptr<InboundTunnel> GetPendingInboundTunnel (uint32_t replyMsgID);
|
|
||||||
std::shared_ptr<OutboundTunnel> GetPendingOutboundTunnel (uint32_t replyMsgID);
|
|
||||||
std::shared_ptr<InboundTunnel> GetNextInboundTunnel ();
|
|
||||||
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel ();
|
|
||||||
std::shared_ptr<TunnelPool> GetExploratoryPool () const { return m_ExploratoryPool; };
|
|
||||||
TransitTunnel * GetTransitTunnel (uint32_t tunnelID);
|
|
||||||
int GetTransitTunnelsExpirationTimeout ();
|
|
||||||
void AddTransitTunnel (TransitTunnel * tunnel);
|
|
||||||
void AddOutboundTunnel (std::shared_ptr<OutboundTunnel> newTunnel);
|
|
||||||
void AddInboundTunnel (std::shared_ptr<InboundTunnel> newTunnel);
|
|
||||||
void PostTunnelData (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
void PostTunnelData (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
|
||||||
template<class TTunnel>
|
|
||||||
std::shared_ptr<TTunnel> CreateTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel = nullptr);
|
|
||||||
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<InboundTunnel> tunnel);
|
|
||||||
void AddPendingTunnel (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> tunnel);
|
|
||||||
std::shared_ptr<TunnelPool> CreateTunnelPool (i2p::garlic::GarlicDestination * localDestination, int numInboundHops, int numOuboundHops, int numInboundTunnels, int numOutboundTunnels);
|
|
||||||
void DeleteTunnelPool (std::shared_ptr<TunnelPool> pool);
|
|
||||||
void StopTunnelPool (std::shared_ptr<TunnelPool> pool);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
template<class TTunnel>
|
|
||||||
std::shared_ptr<TTunnel> GetPendingTunnel (uint32_t replyMsgID, const std::map<uint32_t, std::shared_ptr<TTunnel> >& pendingTunnels);
|
|
||||||
|
|
||||||
void HandleTunnelGatewayMsg (TunnelBase * tunnel, std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
void Run ();
|
|
||||||
void ManageTunnels ();
|
|
||||||
void ManageOutboundTunnels ();
|
|
||||||
void ManageInboundTunnels ();
|
|
||||||
void ManageTransitTunnels ();
|
|
||||||
void ManagePendingTunnels ();
|
|
||||||
template<class PendingTunnels>
|
|
||||||
void ManagePendingTunnels (PendingTunnels& pendingTunnels);
|
|
||||||
void ManageTunnelPools ();
|
|
||||||
|
|
||||||
void CreateZeroHopsInboundTunnel ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool m_IsRunning;
|
|
||||||
std::thread * m_Thread;
|
|
||||||
std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_PendingInboundTunnels; // by replyMsgID
|
|
||||||
std::map<uint32_t, std::shared_ptr<OutboundTunnel> > m_PendingOutboundTunnels; // by replyMsgID
|
|
||||||
std::map<uint32_t, std::shared_ptr<InboundTunnel> > m_InboundTunnels;
|
|
||||||
std::list<std::shared_ptr<OutboundTunnel> > m_OutboundTunnels;
|
|
||||||
std::mutex m_TransitTunnelsMutex;
|
|
||||||
std::map<uint32_t, TransitTunnel *> m_TransitTunnels;
|
|
||||||
std::mutex m_PoolsMutex;
|
|
||||||
std::list<std::shared_ptr<TunnelPool>> m_Pools;
|
|
||||||
std::shared_ptr<TunnelPool> m_ExploratoryPool;
|
|
||||||
i2p::util::Queue<std::shared_ptr<I2NPMessage> > m_Queue;
|
|
||||||
|
|
||||||
// some stats
|
|
||||||
int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations;
|
|
||||||
|
|
||||||
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; };
|
|
||||||
int GetQueueSize () { return m_Queue.GetSize (); };
|
|
||||||
int GetTunnelCreationSuccessRate () const // in percents
|
|
||||||
{
|
|
||||||
int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations;
|
|
||||||
return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Tunnels tunnels;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
69
TunnelBase.h
69
TunnelBase.h
@@ -1,69 +0,0 @@
|
|||||||
#ifndef TUNNEL_BASE_H__
|
|
||||||
#define TUNNEL_BASE_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <memory>
|
|
||||||
#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;
|
|
||||||
std::shared_ptr<I2NPMessage> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TunnelBase
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
//WARNING!!! GetSecondsSinceEpoch() return uint64_t
|
|
||||||
TunnelBase (): m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
|
|
||||||
virtual ~TunnelBase () {};
|
|
||||||
|
|
||||||
virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0;
|
|
||||||
virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0;
|
|
||||||
virtual void FlushTunnelDataMsgs () {};
|
|
||||||
virtual void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out) = 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() (std::shared_ptr<const TunnelBase> t1, std::shared_ptr<const TunnelBase> t2) const
|
|
||||||
{
|
|
||||||
if (t1->GetCreationTime () != t2->GetCreationTime ())
|
|
||||||
return t1->GetCreationTime () > t2->GetCreationTime ();
|
|
||||||
else
|
|
||||||
return t1 < t2;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
233
TunnelConfig.h
233
TunnelConfig.h
@@ -1,233 +0,0 @@
|
|||||||
#ifndef TUNNEL_CONFIG_H__
|
|
||||||
#define TUNNEL_CONFIG_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
#include "aes.h"
|
|
||||||
#include "RouterInfo.h"
|
|
||||||
#include "RouterContext.h"
|
|
||||||
#include "Timestamp.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
struct TunnelHopConfig
|
|
||||||
{
|
|
||||||
std::shared_ptr<const i2p::data::RouterInfo> router, nextRouter;
|
|
||||||
uint32_t tunnelID, nextTunnelID;
|
|
||||||
uint8_t layerKey[32];
|
|
||||||
uint8_t ivKey[32];
|
|
||||||
uint8_t replyKey[32];
|
|
||||||
uint8_t replyIV[16];
|
|
||||||
bool isGateway, isEndpoint;
|
|
||||||
|
|
||||||
TunnelHopConfig * next, * prev;
|
|
||||||
i2p::crypto::TunnelDecryption decryption;
|
|
||||||
int recordIndex; // record # in tunnel build message
|
|
||||||
|
|
||||||
TunnelHopConfig (std::shared_ptr<const i2p::data::RouterInfo> r)
|
|
||||||
{
|
|
||||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
|
||||||
rnd.GenerateBlock (layerKey, 32);
|
|
||||||
rnd.GenerateBlock (ivKey, 32);
|
|
||||||
rnd.GenerateBlock (replyIV, 16);
|
|
||||||
tunnelID = rnd.GenerateWord32 ();
|
|
||||||
isGateway = true;
|
|
||||||
isEndpoint = true;
|
|
||||||
router = r;
|
|
||||||
//nextRouter = nullptr;
|
|
||||||
nextTunnelID = 0;
|
|
||||||
|
|
||||||
next = nullptr;
|
|
||||||
prev = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetNextRouter (std::shared_ptr<const i2p::data::RouterInfo> r)
|
|
||||||
{
|
|
||||||
nextRouter = r;
|
|
||||||
isEndpoint = false;
|
|
||||||
CryptoPP::RandomNumberGenerator& rnd = i2p::context.GetRandomNumberGenerator ();
|
|
||||||
nextTunnelID = rnd.GenerateWord32 ();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetReplyHop (const TunnelHopConfig * replyFirstHop)
|
|
||||||
{
|
|
||||||
nextRouter = replyFirstHop->router;
|
|
||||||
nextTunnelID = replyFirstHop->tunnelID;
|
|
||||||
isEndpoint = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetNext (TunnelHopConfig * n)
|
|
||||||
{
|
|
||||||
next = n;
|
|
||||||
if (next)
|
|
||||||
{
|
|
||||||
next->prev = this;
|
|
||||||
next->isGateway = false;
|
|
||||||
isEndpoint = false;
|
|
||||||
nextRouter = next->router;
|
|
||||||
nextTunnelID = next->tunnelID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetPrev (TunnelHopConfig * p)
|
|
||||||
{
|
|
||||||
prev = p;
|
|
||||||
if (prev)
|
|
||||||
{
|
|
||||||
prev->next = this;
|
|
||||||
prev->isEndpoint = false;
|
|
||||||
isGateway = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID) const
|
|
||||||
{
|
|
||||||
uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE];
|
|
||||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET, tunnelID);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET, router->GetIdentHash (), 32);
|
|
||||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, nextTunnelID);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, nextRouter->GetIdentHash (), 32);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, layerKey, 32);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, ivKey, 32);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_KEY_OFFSET, replyKey, 32);
|
|
||||||
memcpy (clearText + BUILD_REQUEST_RECORD_REPLY_IV_OFFSET, replyIV, 16);
|
|
||||||
uint8_t flag = 0;
|
|
||||||
if (isGateway) flag |= 0x80;
|
|
||||||
if (isEndpoint) flag |= 0x40;
|
|
||||||
clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] = flag;
|
|
||||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
|
|
||||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
|
|
||||||
// TODO: fill padding
|
|
||||||
router->GetElGamalEncryption ()->Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
|
|
||||||
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)router->GetIdentHash (), 16);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class TunnelConfig: public std::enable_shared_from_this<TunnelConfig>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
|
|
||||||
TunnelConfig (std::vector<std::shared_ptr<const i2p::data::RouterInfo> > peers,
|
|
||||||
std::shared_ptr<const TunnelConfig> replyTunnelConfig = nullptr) // replyTunnelConfig=nullptr means inbound
|
|
||||||
{
|
|
||||||
TunnelHopConfig * prev = nullptr;
|
|
||||||
for (auto it: peers)
|
|
||||||
{
|
|
||||||
auto hop = new TunnelHopConfig (it);
|
|
||||||
if (prev)
|
|
||||||
prev->SetNext (hop);
|
|
||||||
else
|
|
||||||
m_FirstHop = hop;
|
|
||||||
prev = hop;
|
|
||||||
}
|
|
||||||
m_LastHop = prev;
|
|
||||||
|
|
||||||
if (replyTunnelConfig) // outbound
|
|
||||||
{
|
|
||||||
m_FirstHop->isGateway = false;
|
|
||||||
m_LastHop->SetReplyHop (replyTunnelConfig->GetFirstHop ());
|
|
||||||
}
|
|
||||||
else // inbound
|
|
||||||
m_LastHop->SetNextRouter (i2p::context.GetSharedRouterInfo ());
|
|
||||||
}
|
|
||||||
|
|
||||||
~TunnelConfig ()
|
|
||||||
{
|
|
||||||
TunnelHopConfig * hop = m_FirstHop;
|
|
||||||
|
|
||||||
while (hop)
|
|
||||||
{
|
|
||||||
auto tmp = hop;
|
|
||||||
hop = hop->next;
|
|
||||||
delete tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelHopConfig * GetFirstHop () const
|
|
||||||
{
|
|
||||||
return m_FirstHop;
|
|
||||||
}
|
|
||||||
|
|
||||||
TunnelHopConfig * GetLastHop () const
|
|
||||||
{
|
|
||||||
return m_LastHop;
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetNumHops () const
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
TunnelHopConfig * hop = m_FirstHop;
|
|
||||||
while (hop)
|
|
||||||
{
|
|
||||||
num++;
|
|
||||||
hop = hop->next;
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsInbound () const { return m_FirstHop->isGateway; }
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<const i2p::data::RouterInfo> > GetPeers () const
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<const i2p::data::RouterInfo> > peers;
|
|
||||||
TunnelHopConfig * hop = m_FirstHop;
|
|
||||||
while (hop)
|
|
||||||
{
|
|
||||||
peers.push_back (hop->router);
|
|
||||||
hop = hop->next;
|
|
||||||
}
|
|
||||||
return peers;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Print (std::stringstream& s) const
|
|
||||||
{
|
|
||||||
TunnelHopConfig * hop = m_FirstHop;
|
|
||||||
if (!IsInbound ()) // outbound
|
|
||||||
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";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TunnelConfig> Invert () const
|
|
||||||
{
|
|
||||||
auto peers = GetPeers ();
|
|
||||||
std::reverse (peers.begin (), peers.end ());
|
|
||||||
// we use ourself as reply tunnel for outbound tunnel
|
|
||||||
return IsInbound () ? std::make_shared<TunnelConfig>(peers, shared_from_this ()) : std::make_shared<TunnelConfig>(peers);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TunnelConfig> Clone (std::shared_ptr<const TunnelConfig> replyTunnelConfig = nullptr) const
|
|
||||||
{
|
|
||||||
return std::make_shared<TunnelConfig> (GetPeers (), 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,259 +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 ()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelEndpoint::HandleDecryptedTunnelDataMsg (std::shared_ptr<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)
|
|
||||||
{
|
|
||||||
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 (eLogError, "TunnelMessage: checksum verification failed");
|
|
||||||
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
|
|
||||||
break;
|
|
||||||
case eDeliveryTypeTunnel: // 1
|
|
||||||
m.tunnelID = bufbe32toh (fragment);
|
|
||||||
fragment += 4; // tunnelID
|
|
||||||
m.hash = i2p::data::IdentHash (fragment);
|
|
||||||
fragment += 32; // hash
|
|
||||||
break;
|
|
||||||
case eDeliveryTypeRouter: // 2
|
|
||||||
m.hash = i2p::data::IdentHash (fragment);
|
|
||||||
fragment += 32; // to hash
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isFragmented = flag & 0x08;
|
|
||||||
if (isFragmented)
|
|
||||||
{
|
|
||||||
// Message ID
|
|
||||||
msgID = bufbe32toh (fragment);
|
|
||||||
fragment += 4;
|
|
||||||
isLastFragment = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// follow on
|
|
||||||
msgID = bufbe32toh (fragment); // MessageID
|
|
||||||
fragment += 4;
|
|
||||||
fragmentNum = (flag >> 1) & 0x3F; // 6 bits
|
|
||||||
isLastFragment = flag & 0x01;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t size = bufbe16toh (fragment);
|
|
||||||
fragment += 2;
|
|
||||||
|
|
||||||
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 = ToSharedI2NPMessage (NewI2NPShortMessage ());
|
|
||||||
m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
|
|
||||||
m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
|
|
||||||
*(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 ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
|
|
||||||
if (ret.second)
|
|
||||||
HandleOutOfSequenceFragment (msgID, ret.first->second);
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Incomplete message ", msgID, "already exists");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m.nextFragmentNum = fragmentNum;
|
|
||||||
HandleFollowOnFragment (msgID, isLastFragment, m);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "Message is fragmented, but msgID is not presented");
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment += size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
LogPrint (eLogError, "TunnelMessage: zero not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
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 message is not too long
|
|
||||||
{
|
|
||||||
if (msg.data->len + size > msg.data->maxLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
|
|
||||||
auto newMsg = ToSharedI2NPMessage (NewI2NPMessage ());
|
|
||||||
*newMsg = *(msg.data);
|
|
||||||
msg.data = newMsg;
|
|
||||||
}
|
|
||||||
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 (eLogError, "Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size. Message dropped");
|
|
||||||
m_IncompleteMessages.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ". Saved");
|
|
||||||
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "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, std::shared_ptr<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}));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (eLogInfo, "Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
|
|
||||||
auto size = it->second.data->GetLength ();
|
|
||||||
if (msg.data->len + size > msg.data->maxLen)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
|
|
||||||
auto newMsg = ToSharedI2NPMessage (NewI2NPMessage ());
|
|
||||||
*newMsg = *(msg.data);
|
|
||||||
msg.data = newMsg;
|
|
||||||
}
|
|
||||||
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++;
|
|
||||||
m_OutOfSequenceFragments.erase (it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
|
|
||||||
{
|
|
||||||
LogPrint (eLogInfo, "TunnelMessage: handle fragment of ", msg.data->GetLength ()," bytes. Msg type ", (int)msg.data->GetTypeID ());
|
|
||||||
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
|
|
||||||
{
|
|
||||||
/* auto typeID = msg.data->GetTypeID ();
|
|
||||||
if (typeID == eI2NPDatabaseStore || typeID == eI2NPDatabaseSearchReply )
|
|
||||||
// catch RI or reply with new list of routers
|
|
||||||
i2p::data::netdb.PostI2NPMsg (msg.data);*/
|
|
||||||
i2p::transport::transports.SendMessage (msg.hash, msg.data);
|
|
||||||
}
|
|
||||||
else // we shouldn't send this message. possible leakage
|
|
||||||
LogPrint (eLogError, "Message to another router arrived from an inbound tunnel. Dropped");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
#ifndef TUNNEL_ENDPOINT_H__
|
|
||||||
#define TUNNEL_ENDPOINT_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "TunnelBase.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
class TunnelEndpoint
|
|
||||||
{
|
|
||||||
struct TunnelMessageBlockEx: public TunnelMessageBlock
|
|
||||||
{
|
|
||||||
uint8_t nextFragmentNum;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Fragment
|
|
||||||
{
|
|
||||||
uint8_t fragmentNum;
|
|
||||||
bool isLastFragment;
|
|
||||||
std::shared_ptr<I2NPMessage> data;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
|
|
||||||
~TunnelEndpoint ();
|
|
||||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
|
||||||
|
|
||||||
void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m);
|
|
||||||
void HandleNextMessage (const TunnelMessageBlock& msg);
|
|
||||||
|
|
||||||
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data);
|
|
||||||
void HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
|
|
||||||
std::map<uint32_t, Fragment> m_OutOfSequenceFragments;
|
|
||||||
bool m_IsInbound;
|
|
||||||
size_t m_NumReceivedBytes;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
#ifndef TUNNEL_GATEWAY_H__
|
|
||||||
#define TUNNEL_GATEWAY_H__
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
#include "I2NPProtocol.h"
|
|
||||||
#include "TunnelBase.h"
|
|
||||||
|
|
||||||
namespace i2p
|
|
||||||
{
|
|
||||||
namespace tunnel
|
|
||||||
{
|
|
||||||
class TunnelGatewayBuffer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TunnelGatewayBuffer (uint32_t tunnelID);
|
|
||||||
~TunnelGatewayBuffer ();
|
|
||||||
void PutI2NPMsg (const TunnelMessageBlock& block);
|
|
||||||
const std::vector<std::shared_ptr<I2NPMessage> >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
|
|
||||||
void ClearTunnelDataMsgs ();
|
|
||||||
void CompleteCurrentTunnelDataMessage ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void CreateCurrentTunnelDataMessage ();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
uint32_t m_TunnelID;
|
|
||||||
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelDataMsgs;
|
|
||||||
std::shared_ptr<I2NPMessage> m_CurrentTunnelDataMsg;
|
|
||||||
size_t m_RemainingSize;
|
|
||||||
uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
class TunnelGateway
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
TunnelGateway (TunnelBase * tunnel):
|
|
||||||
m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {};
|
|
||||||
void SendTunnelDataMsg (const TunnelMessageBlock& block);
|
|
||||||
void PutTunnelDataMsg (const TunnelMessageBlock& block);
|
|
||||||
void SendBuffer ();
|
|
||||||
size_t GetNumSentBytes () const { return m_NumSentBytes; };
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
TunnelBase * m_Tunnel;
|
|
||||||
TunnelGatewayBuffer m_Buffer;
|
|
||||||
size_t m_NumSentBytes;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user