mirror of
https://github.com/PurpleI2P/i2pd.git
synced 2025-03-07 06:09:42 +00:00
Compare commits
785 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ae40d89c1 | ||
|
|
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 | ||
|
|
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 | ||
|
|
298c5f0de2 | ||
|
|
a6c2b25f6f | ||
|
|
3a8c90c0d4 | ||
|
|
a25ce2296a | ||
|
|
280407a553 | ||
|
|
32c98e2161 | ||
|
|
2cbdb0bc17 | ||
|
|
4317694c64 | ||
|
|
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 | ||
|
|
11231abe8a | ||
|
|
c577706415 | ||
|
|
f1eea6a0bf | ||
|
|
8ce55f90d3 | ||
|
|
723f35ec5a | ||
|
|
025d9d3276 | ||
|
|
4f0c1d11eb | ||
|
|
1aae921ce7 | ||
|
|
2e1c508bc4 | ||
|
|
cea6ea4344 | ||
|
|
57310fdbd6 | ||
|
|
62ca6212ce | ||
|
|
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 | ||
|
|
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 | ||
|
|
f1acd122bc | ||
|
|
739b6645f8 | ||
|
|
7a7ae4cc83 | ||
|
|
db83cbe58f | ||
|
|
87228429d6 | ||
|
|
2651723b50 | ||
|
|
b8a01d2ff1 | ||
|
|
5c20751937 | ||
|
|
06b0a50462 | ||
|
|
0d589895f6 | ||
|
|
230c2aaf26 | ||
|
|
1d8807a6ba | ||
|
|
81978b214c | ||
|
|
8704234669 | ||
|
|
5699b7bae5 | ||
|
|
e726d216bb | ||
|
|
3480824290 | ||
|
|
c8b935151a | ||
|
|
5e5aefa290 | ||
|
|
0e14b54b6d | ||
|
|
c6ddae2d8e | ||
|
|
bc0aed186e | ||
|
|
d092b21da7 | ||
|
|
a8061003dd | ||
|
|
50f0099645 | ||
|
|
c270687223 | ||
|
|
a92652f4ad | ||
|
|
9ba961fa72 | ||
|
|
006e4526e8 | ||
|
|
55dbbb3546 | ||
|
|
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 | ||
|
|
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 | ||
|
|
b83e7e6c5c | ||
|
|
5f463d5f6b | ||
|
|
2e301c2919 | ||
|
|
9526d42ec5 | ||
|
|
a566479ddb | ||
|
|
1bba0f6bb2 | ||
|
|
232d42881b | ||
|
|
abeaf76fe9 | ||
|
|
03d4584562 | ||
|
|
f2f5226ebb | ||
|
|
660860b92d | ||
|
|
c0a1a8b47c | ||
|
|
bd82e81e26 | ||
|
|
0a94df592c | ||
|
|
66506ea1ce | ||
|
|
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 | ||
|
|
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 | ||
|
|
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 | ||
|
|
dd15472da7 | ||
|
|
b03712a30e | ||
|
|
d025ba2793 | ||
|
|
e5e09c9b51 | ||
|
|
5b8d1df349 | ||
|
|
fa092c0162 | ||
|
|
08c1359a27 | ||
|
|
dba355eccd | ||
|
|
2ad927b677 | ||
|
|
315f672254 | ||
|
|
7a51407f6d | ||
|
|
783c2b6b03 | ||
|
|
a64e1b2aa6 | ||
|
|
440516e95f | ||
|
|
dc6108575c | ||
|
|
6c7316408b | ||
|
|
9aecc69461 | ||
|
|
949be436a6 | ||
|
|
cb91891f22 | ||
|
|
8795f0c8c4 | ||
|
|
fbb5bb2f05 | ||
|
|
ba309fe6e5 | ||
|
|
fee5f959fd | ||
|
|
f9a5f4955c | ||
|
|
325b362727 | ||
|
|
75065f29f7 | ||
|
|
ed874fe3ea | ||
|
|
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 | ||
|
|
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 | ||
|
|
c09212de81 | ||
|
|
03927b0a68 | ||
|
|
c8f5fb4d03 | ||
|
|
070a21a9eb | ||
|
|
cf8ff2cf86 | ||
|
|
ee9dc789af | ||
|
|
570598f556 | ||
|
|
e8c3546433 | ||
|
|
584379b502 | ||
|
|
87e1c45c05 | ||
|
|
9447afe49c | ||
|
|
fc5b1ae3e2 | ||
|
|
13735d0475 | ||
|
|
6b3a783ce9 | ||
|
|
3b66bba92e | ||
|
|
a2e01f8a53 | ||
|
|
34da9a9655 | ||
|
|
df8d73ae43 | ||
|
|
aa3a93b6a0 | ||
|
|
17bfa35f77 | ||
|
|
59797a5c9a | ||
|
|
61fe2923e4 | ||
|
|
50b9eca34c | ||
|
|
f5684eba90 | ||
|
|
f32510e10a | ||
|
|
4fb0eeda37 | ||
|
|
eeeae12639 | ||
|
|
0d854c6ea6 | ||
|
|
1e1c4d159b | ||
|
|
4dc9f6948d | ||
|
|
c06e739c9b | ||
|
|
c90c008f65 | ||
|
|
6d54401d7c | ||
|
|
c9a0897208 | ||
|
|
5995ab3f4c | ||
|
|
289cae4213 | ||
|
|
eaac21cda1 | ||
|
|
c908beade2 | ||
|
|
0e31da5e51 | ||
|
|
8cc6756815 | ||
|
|
c79363ef63 | ||
|
|
03d7330af5 | ||
|
|
f08ea4a9a3 | ||
|
|
fb213a1efd | ||
|
|
f995595202 | ||
|
|
d7a06dc7a9 | ||
|
|
ccc24337be |
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++))))
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -8,13 +8,14 @@ netDb
|
||||
/i2pd
|
||||
/libi2pd.a
|
||||
/libi2pdclient.a
|
||||
i2pd.exe
|
||||
|
||||
|
||||
# Autotools
|
||||
autom4te.cache
|
||||
.deps
|
||||
stamp-h1
|
||||
Makefile
|
||||
#Makefile
|
||||
config.h
|
||||
config.h.in~
|
||||
config.log
|
||||
@@ -238,3 +239,11 @@ pip-log.txt
|
||||
# Sphinx
|
||||
docs/_build
|
||||
/androidIdea/
|
||||
|
||||
|
||||
# emacs files
|
||||
*~
|
||||
*\#*
|
||||
|
||||
# gdb files
|
||||
.gdb_history
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <openssl/rand.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "Base.h"
|
||||
#include "util.h"
|
||||
#include "Identity.h"
|
||||
@@ -15,6 +16,7 @@
|
||||
#include "NetDb.h"
|
||||
#include "ClientContext.h"
|
||||
#include "AddressBook.h"
|
||||
#include "Config.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -404,9 +406,21 @@ namespace client
|
||||
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, s));
|
||||
}
|
||||
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
|
||||
LogPrint (eLogWarning, "Addressbook: subscriptions.txt usage is deprecated, use config file instead");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Addressbook: subscriptions.txt not found in datadir");
|
||||
else if (!i2p::config::IsDefault("addressbook.subscriptions"))
|
||||
{
|
||||
// using config file items
|
||||
std::string subscriptionURLs; i2p::config::GetOption("addressbook.subscriptions", subscriptionURLs);
|
||||
std::vector<std::string> subsList;
|
||||
boost::split(subsList, subscriptionURLs, boost::is_any_of(","), boost::token_compress_on);
|
||||
|
||||
for (size_t i = 0; i < subsList.size (); i++)
|
||||
{
|
||||
m_Subscriptions.push_back (std::make_shared<AddressBookSubscription> (*this, subsList[i]));
|
||||
}
|
||||
LogPrint (eLogInfo, "Addressbook: ", m_Subscriptions.size (), " subscriptions urls loaded");
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Addressbook: subscriptions already loaded");
|
||||
@@ -511,10 +525,11 @@ namespace client
|
||||
{
|
||||
if (!m_IsLoaded)
|
||||
{
|
||||
// download it from http://i2p-projekt.i2p/hosts.txt
|
||||
// download it from default subscription
|
||||
LogPrint (eLogInfo, "Addressbook: trying to download it from default subscription.");
|
||||
std::string defaultSubURL; i2p::config::GetOption("addressbook.defaulturl", defaultSubURL);
|
||||
if (!m_DefaultSubscription)
|
||||
m_DefaultSubscription = std::make_shared<AddressBookSubscription>(*this, DEFAULT_SUBSCRIPTION_ADDRESS);
|
||||
m_DefaultSubscription = std::make_shared<AddressBookSubscription>(*this, defaultSubURL);
|
||||
m_IsDownloading = true;
|
||||
std::thread load_hosts(std::bind (&AddressBookSubscription::CheckUpdates, m_DefaultSubscription));
|
||||
load_hosts.detach(); // TODO: use join
|
||||
@@ -546,8 +561,8 @@ namespace client
|
||||
auto datagram = dest->GetDatagramDestination ();
|
||||
if (!datagram)
|
||||
datagram = dest->CreateDatagramDestination ();
|
||||
datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this,
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
|
||||
datagram->SetReceiver (std::bind (&AddressBook::HandleLookupResponse, this,
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
|
||||
ADDRESS_RESPONSE_DATAGRAM_PORT);
|
||||
}
|
||||
}
|
||||
@@ -558,8 +573,7 @@ namespace client
|
||||
if (dest)
|
||||
{
|
||||
auto datagram = dest->GetDatagramDestination ();
|
||||
if (datagram)
|
||||
datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT);
|
||||
if (datagram) datagram->ResetReceiver (ADDRESS_RESPONSE_DATAGRAM_PORT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,7 +636,11 @@ namespace client
|
||||
if (address.length () > 0)
|
||||
{
|
||||
// TODO: verify from
|
||||
m_Addresses[address] = buf + 8;
|
||||
i2p::data::IdentHash hash(buf + 8);
|
||||
if (!hash.IsZero ())
|
||||
m_Addresses[address] = hash;
|
||||
else
|
||||
LogPrint (eLogInfo, "AddressBook: Lookup response: ", address, " not found");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,13 +703,14 @@ namespace client
|
||||
int dest_port = url.port ? url.port : 80;
|
||||
/* create http request & send it */
|
||||
i2p::http::HTTPReq req;
|
||||
req.add_header("Host", dest_host);
|
||||
req.add_header("User-Agent", "Wget/1.11.4");
|
||||
req.add_header("Connection", "close");
|
||||
req.AddHeader("Host", dest_host);
|
||||
req.AddHeader("User-Agent", "Wget/1.11.4");
|
||||
req.AddHeader("X-Accept-Encoding", "x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
|
||||
req.AddHeader("Connection", "close");
|
||||
if (!m_Etag.empty())
|
||||
req.add_header("If-None-Match", m_Etag);
|
||||
req.AddHeader("If-None-Match", m_Etag);
|
||||
if (!m_LastModified.empty())
|
||||
req.add_header("If-Modified-Since", m_LastModified);
|
||||
req.AddHeader("If-Modified-Since", m_LastModified);
|
||||
/* convert url to relative */
|
||||
url.schema = "";
|
||||
url.host = "";
|
||||
@@ -703,7 +722,9 @@ namespace client
|
||||
std::string response;
|
||||
uint8_t recv_buf[4096];
|
||||
bool end = false;
|
||||
while (!end) {
|
||||
int numAttempts = 5;
|
||||
while (!end)
|
||||
{
|
||||
stream->AsyncReceive (boost::asio::buffer (recv_buf, 4096),
|
||||
[&](const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
@@ -716,60 +737,69 @@ namespace client
|
||||
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, "Addressbook: subscriptions request timeout expired");
|
||||
numAttempts++;
|
||||
if (numAttempts > 5) end = true;
|
||||
}
|
||||
}
|
||||
// process remaining buffer
|
||||
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf))) {
|
||||
while (size_t len = stream->ReadSome (recv_buf, sizeof(recv_buf)))
|
||||
response.append ((char *)recv_buf, len);
|
||||
}
|
||||
/* parse response */
|
||||
i2p::http::HTTPRes res;
|
||||
int res_head_len = res.parse(response);
|
||||
if (res_head_len < 0) {
|
||||
if (res_head_len < 0)
|
||||
{
|
||||
LogPrint(eLogError, "Addressbook: can't parse http response from ", dest_host);
|
||||
return false;
|
||||
}
|
||||
if (res_head_len == 0) {
|
||||
if (res_head_len == 0)
|
||||
{
|
||||
LogPrint(eLogError, "Addressbook: incomplete http response from ", dest_host, ", interrupted by timeout");
|
||||
return false;
|
||||
}
|
||||
/* assert: res_head_len > 0 */
|
||||
response.erase(0, res_head_len);
|
||||
if (res.code == 304) {
|
||||
if (res.code == 304)
|
||||
{
|
||||
LogPrint (eLogInfo, "Addressbook: no updates from ", dest_host, ", code 304");
|
||||
return false;
|
||||
}
|
||||
if (res.code != 200) {
|
||||
if (res.code != 200)
|
||||
{
|
||||
LogPrint (eLogWarning, "Adressbook: can't get updates from ", dest_host, ", response code ", res.code);
|
||||
return false;
|
||||
}
|
||||
int len = res.content_length();
|
||||
if (response.empty()) {
|
||||
if (response.empty())
|
||||
{
|
||||
LogPrint(eLogError, "Addressbook: empty response from ", dest_host, ", expected ", len, " bytes");
|
||||
return false;
|
||||
}
|
||||
if (len > 0 && len != (int) response.length()) {
|
||||
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", response.length(), ", got: ", len, "bytes");
|
||||
if (!res.is_gzipped () && len > 0 && len != (int) response.length())
|
||||
{
|
||||
LogPrint(eLogError, "Addressbook: response size mismatch, expected: ", len, ", got: ", response.length(), "bytes");
|
||||
return false;
|
||||
}
|
||||
/* assert: res.code == 200 */
|
||||
auto it = res.headers.find("ETag");
|
||||
if (it != res.headers.end()) {
|
||||
m_Etag = it->second;
|
||||
}
|
||||
if (it != res.headers.end()) m_Etag = it->second;
|
||||
it = res.headers.find("If-Modified-Since");
|
||||
if (it != res.headers.end()) {
|
||||
m_LastModified = it->second;
|
||||
}
|
||||
if (res.is_chunked()) {
|
||||
if (it != res.headers.end()) m_LastModified = it->second;
|
||||
if (res.is_chunked())
|
||||
{
|
||||
std::stringstream in(response), out;
|
||||
i2p::http::MergeChunkedResponse (in, out);
|
||||
response = out.str();
|
||||
} else if (res.is_gzipped()) {
|
||||
}
|
||||
else if (res.is_gzipped())
|
||||
{
|
||||
std::stringstream out;
|
||||
i2p::data::GzipInflator inflator;
|
||||
inflator.Inflate ((const uint8_t *) response.data(), response.length(), out);
|
||||
if (out.fail()) {
|
||||
if (out.fail())
|
||||
{
|
||||
LogPrint(eLogError, "Addressbook: can't gunzip http response");
|
||||
return false;
|
||||
}
|
||||
@@ -828,7 +858,7 @@ namespace client
|
||||
else
|
||||
memset (response + 8, 0, 32); // not found
|
||||
memset (response + 40, 0, 4); // set expiration time to zero
|
||||
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash (), toPort, fromPort);
|
||||
m_LocalDestination->GetDatagramDestination ()->SendDatagramTo (response, 44, from.GetIdentHash(), toPort, fromPort);
|
||||
}
|
||||
|
||||
void AddressResolver::AddAddress (const std::string& name, const i2p::data::IdentHash& ident)
|
||||
|
||||
@@ -18,11 +18,6 @@ namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
#ifdef MESHNET
|
||||
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://i42ofzetmgicvui5sshinfckpijix2udewbam4sjo6x5fbukltia.b32.i2p/hosts.txt";
|
||||
#else
|
||||
const char DEFAULT_SUBSCRIPTION_ADDRESS[] = "http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt";
|
||||
#endif
|
||||
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)
|
||||
|
||||
28
BOB.cpp
28
BOB.cpp
@@ -1,7 +1,7 @@
|
||||
#include <string.h>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Log.h"
|
||||
#include "ClientContext.h"
|
||||
#include "util.h"
|
||||
#include "BOB.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -437,8 +437,11 @@ namespace client
|
||||
void BOBCommandSession::GetkeysCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: getkeys");
|
||||
SendReplyOK (m_Keys.ToBase64 ().c_str ());
|
||||
}
|
||||
if (m_Keys.GetPublic ()) // keys are set ?
|
||||
SendReplyOK (m_Keys.ToBase64 ().c_str ());
|
||||
else
|
||||
SendReplyError ("keys are not set");
|
||||
}
|
||||
|
||||
void BOBCommandSession::GetdestCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
@@ -456,7 +459,7 @@ namespace client
|
||||
void BOBCommandSession::OutportCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: outport ", operand);
|
||||
m_OutPort = boost::lexical_cast<int>(operand);
|
||||
m_OutPort = std::stoi(operand);
|
||||
if (m_OutPort >= 0)
|
||||
SendReplyOK ("outbound port set");
|
||||
else
|
||||
@@ -473,7 +476,7 @@ namespace client
|
||||
void BOBCommandSession::InportCommandHandler (const char * operand, size_t len)
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: inport ", operand);
|
||||
m_InPort = boost::lexical_cast<int>(operand);
|
||||
m_InPort = std::stoi(operand);
|
||||
if (m_InPort >= 0)
|
||||
SendReplyOK ("inbound port set");
|
||||
else
|
||||
@@ -501,12 +504,12 @@ namespace client
|
||||
{
|
||||
LogPrint (eLogDebug, "BOB: lookup ", operand);
|
||||
i2p::data::IdentHash ident;
|
||||
if (!context.GetAddressBook ().GetIdentHash (operand, ident) || !m_CurrentDestination)
|
||||
if (!context.GetAddressBook ().GetIdentHash (operand, ident))
|
||||
{
|
||||
SendReplyError ("Address Not found");
|
||||
return;
|
||||
}
|
||||
auto localDestination = m_CurrentDestination->GetLocalDestination ();
|
||||
auto localDestination = m_CurrentDestination ? m_CurrentDestination->GetLocalDestination () : i2p::client::context.GetSharedLocalDestination ();
|
||||
auto leaseSet = localDestination->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
SendReplyOK (leaseSet->GetIdentity ()->ToBase64 ().c_str ());
|
||||
@@ -568,10 +571,15 @@ namespace client
|
||||
{
|
||||
std::stringstream s;
|
||||
s << "DATA"; s << " NICKNAME: "; s << m_Nickname;
|
||||
if (m_CurrentDestination->GetLocalDestination ()->IsReady ())
|
||||
s << " STARTING: false RUNNING: true STOPPING: false";
|
||||
if (m_CurrentDestination)
|
||||
{
|
||||
if (m_CurrentDestination->GetLocalDestination ()->IsReady ())
|
||||
s << " STARTING: false RUNNING: true STOPPING: false";
|
||||
else
|
||||
s << " STARTING: true RUNNING: false STOPPING: false";
|
||||
}
|
||||
else
|
||||
s << " STARTING: true RUNNING: false STOPPING: false";
|
||||
s << " STARTING: false RUNNING: false STOPPING: false";
|
||||
s << " KEYS: true"; s << " QUIET: "; s << (m_IsQuiet ? "true":"false");
|
||||
if (m_InPort)
|
||||
{
|
||||
|
||||
69
BloomFilter.cpp
Normal file
69
BloomFilter.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#include "BloomFilter.h"
|
||||
#include "I2PEndian.h"
|
||||
#include <array>
|
||||
#include <openssl/sha.h>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
/** @brief decaying bloom filter implementation */
|
||||
class DecayingBloomFilter : public IBloomFilter
|
||||
{
|
||||
public:
|
||||
|
||||
DecayingBloomFilter(const std::size_t size)
|
||||
{
|
||||
m_Size = size;
|
||||
m_Data = new uint8_t[size];
|
||||
}
|
||||
|
||||
/** @brief implements IBloomFilter::~IBloomFilter */
|
||||
~DecayingBloomFilter()
|
||||
{
|
||||
delete [] m_Data;
|
||||
}
|
||||
|
||||
/** @brief implements IBloomFilter::Add */
|
||||
bool Add(const uint8_t * data, std::size_t len)
|
||||
{
|
||||
std::size_t idx;
|
||||
uint8_t mask;
|
||||
Get(data, len, idx, mask);
|
||||
if(m_Data[idx] & mask) return false; // filter hit
|
||||
m_Data[idx] |= mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @brief implements IBloomFilter::Decay */
|
||||
void Decay()
|
||||
{
|
||||
// reset bloom filter buffer
|
||||
memset(m_Data, 0, m_Size);
|
||||
}
|
||||
|
||||
private:
|
||||
/** @brief get bit index for for data */
|
||||
void Get(const uint8_t * data, std::size_t len, std::size_t & idx, uint8_t & bm)
|
||||
{
|
||||
bm = 1;
|
||||
uint8_t digest[32];
|
||||
// TODO: use blake2 because it's faster
|
||||
SHA256(data, len, digest);
|
||||
uint64_t i = buf64toh(digest);
|
||||
idx = i % m_Size;
|
||||
bm <<= (i % 8);
|
||||
}
|
||||
|
||||
uint8_t * m_Data;
|
||||
std::size_t m_Size;
|
||||
};
|
||||
|
||||
|
||||
BloomFilterPtr BloomFilter(std::size_t capacity)
|
||||
{
|
||||
return std::make_shared<DecayingBloomFilter>(capacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
BloomFilter.h
Normal file
31
BloomFilter.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef BLOOM_FILTER_H_
|
||||
#define BLOOM_FILTER_H_
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
/** @brief interface for bloom filter */
|
||||
struct IBloomFilter
|
||||
{
|
||||
|
||||
/** @brief destructor */
|
||||
virtual ~IBloomFilter() {};
|
||||
/** @brief add entry to bloom filter, return false if filter hit otherwise return true */
|
||||
virtual bool Add(const uint8_t * data, std::size_t len) = 0;
|
||||
/** @brief optionally decay old entries */
|
||||
virtual void Decay() = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<IBloomFilter> BloomFilterPtr;
|
||||
|
||||
/** @brief create bloom filter */
|
||||
BloomFilterPtr BloomFilter(std::size_t capacity = 1024 * 8);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
66
ChangeLog
66
ChangeLog
@@ -1,9 +1,73 @@
|
||||
# for this file format description,
|
||||
# see https://github.com/olivierlacan/keep-a-changelog
|
||||
|
||||
## [2.9.0] - UNRELEASED
|
||||
## [2.12.0] - 2017-02-14
|
||||
### Added
|
||||
- Additional HTTP and SOCKS proxy tunnels
|
||||
- Reseed from ZIP archive
|
||||
- Some stats in a main window for Windows version
|
||||
### Changed
|
||||
- Reseed servers list
|
||||
- MTU of 1488 for ipv6
|
||||
- Android and Mac OS X versions use OpenSSL 1.1
|
||||
- New logo for Android
|
||||
### Fixed
|
||||
- Multiple memory leaks
|
||||
- Incomptibility of some EdDSA private keys with Java
|
||||
- Clock skew for Windows XP
|
||||
- Occasional crashes with I2PSnark
|
||||
|
||||
## [2.11.0] - 2016-12-18
|
||||
### Added
|
||||
- Websockets support
|
||||
- Reseed through a floodfill
|
||||
- Tunnel configuration for HTTP and SOCKS proxy
|
||||
- Zero-hops tunnels for destinations
|
||||
- Multiple acceptors for SAM
|
||||
### Changed
|
||||
- Reseed servers list
|
||||
- DHT uses AVX if applicable
|
||||
- New logo
|
||||
- LeaseSet lookups
|
||||
### Fixed
|
||||
- HTTP Proxy connection reset for Windows
|
||||
- Crash upon SAM session termination
|
||||
- Can't connect to a destination for a longer time after restart
|
||||
- Mass packet loss for UDP tunnels
|
||||
|
||||
## [2.10.2] - 2016-12-04
|
||||
### Fixed
|
||||
- Fixes UPnP discovery bug, producing excessive CPU usage
|
||||
- Fixes sudden SSU thread stop for Windows.
|
||||
|
||||
## [2.10.1] - 2016-11-07
|
||||
### Fixed
|
||||
- Fixed some performance issues for Windows and Android
|
||||
|
||||
## [2.10.0] - 2016-10-17
|
||||
### Added
|
||||
- Datagram i2p tunnels
|
||||
- Unique local addresses for server tunnels
|
||||
- Configurable list of reseed servers and initial addressbook
|
||||
- Configurable netid
|
||||
- Initial iOS support
|
||||
|
||||
### Changed
|
||||
- Reduced file descriptiors usage
|
||||
- Strict reseed checks enabled by default
|
||||
|
||||
## Fixed
|
||||
- Multiple fixes in I2CP and BOB implementations
|
||||
|
||||
## [2.9.0] - 2016-08-12
|
||||
### Changed
|
||||
- Proxy refactoring & speedup
|
||||
- Transmission-I2P support
|
||||
- Graceful shutdown for Windows
|
||||
- Android without QT
|
||||
- Reduced number of timers in SSU
|
||||
- ipv6 peer test support
|
||||
- Reseed from SU3 file
|
||||
|
||||
## [2.8.0] - 2016-06-20
|
||||
### Added
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "Identity.h"
|
||||
#include "util.h"
|
||||
#include "ClientContext.h"
|
||||
#include "SOCKS.h"
|
||||
#include "WebSocks.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -39,20 +41,28 @@ namespace client
|
||||
m_SharedLocalDestination->Start ();
|
||||
}
|
||||
|
||||
|
||||
m_AddressBook.Start ();
|
||||
|
||||
|
||||
std::shared_ptr<ClientDestination> localDestination;
|
||||
bool httproxy; i2p::config::GetOption("httpproxy.enabled", httproxy);
|
||||
if (httproxy) {
|
||||
std::string httpProxyKeys; i2p::config::GetOption("httpproxy.keys", httpProxyKeys);
|
||||
std::string httpProxyAddr; i2p::config::GetOption("httpproxy.address", httpProxyAddr);
|
||||
uint16_t httpProxyPort; i2p::config::GetOption("httpproxy.port", httpProxyPort);
|
||||
i2p::data::SigningKeyType sigType; i2p::config::GetOption("httpproxy.signaturetype", sigType);
|
||||
LogPrint(eLogInfo, "Clients: starting HTTP Proxy at ", httpProxyAddr, ":", httpProxyPort);
|
||||
if (httpProxyKeys.length () > 0)
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
LoadPrivateKeys (keys, httpProxyKeys);
|
||||
localDestination = CreateNewLocalDestination (keys, false);
|
||||
if(LoadPrivateKeys (keys, httpProxyKeys, sigType))
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
ReadI2CPOptionsFromConfig ("httpproxy.", params);
|
||||
localDestination = CreateNewLocalDestination (keys, false, ¶ms);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Clients: failed to load HTTP Proxy key");
|
||||
}
|
||||
try {
|
||||
m_HttpProxy = new i2p::proxy::HTTPProxy(httpProxyAddr, httpProxyPort, localDestination);
|
||||
@@ -61,20 +71,29 @@ namespace client
|
||||
LogPrint(eLogError, "Clients: Exception in HTTP Proxy: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
localDestination = nullptr;
|
||||
bool socksproxy; i2p::config::GetOption("socksproxy.enabled", socksproxy);
|
||||
if (socksproxy) {
|
||||
if (socksproxy)
|
||||
{
|
||||
std::string socksProxyKeys; i2p::config::GetOption("socksproxy.keys", socksProxyKeys);
|
||||
std::string socksProxyAddr; i2p::config::GetOption("socksproxy.address", socksProxyAddr);
|
||||
uint16_t socksProxyPort; i2p::config::GetOption("socksproxy.port", socksProxyPort);
|
||||
std::string socksOutProxyAddr; i2p::config::GetOption("socksproxy.outproxy", socksOutProxyAddr);
|
||||
uint16_t socksOutProxyPort; i2p::config::GetOption("socksproxy.outproxyport", socksOutProxyPort);
|
||||
i2p::data::SigningKeyType sigType; i2p::config::GetOption("socksproxy.signaturetype", sigType);
|
||||
LogPrint(eLogInfo, "Clients: starting SOCKS Proxy at ", socksProxyAddr, ":", socksProxyPort);
|
||||
if (socksProxyKeys.length () > 0)
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
LoadPrivateKeys (keys, socksProxyKeys);
|
||||
localDestination = CreateNewLocalDestination (keys, false);
|
||||
if (LoadPrivateKeys (keys, socksProxyKeys, sigType))
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
ReadI2CPOptionsFromConfig ("socksproxy.", params);
|
||||
localDestination = CreateNewLocalDestination (keys, false, ¶ms);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Clients: failed to load SOCKS Proxy key");
|
||||
}
|
||||
try {
|
||||
m_SocksProxy = new i2p::proxy::SOCKSProxy(socksProxyAddr, socksProxyPort, socksOutProxyAddr, socksOutProxyPort, localDestination);
|
||||
@@ -83,10 +102,10 @@ namespace client
|
||||
LogPrint(eLogError, "Clients: Exception in SOCKS Proxy: ", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
// I2P tunnels
|
||||
ReadTunnels ();
|
||||
|
||||
// I2P tunnels
|
||||
ReadTunnels ();
|
||||
|
||||
// SAM
|
||||
bool sam; i2p::config::GetOption("sam.enabled", sam);
|
||||
if (sam) {
|
||||
@@ -134,6 +153,13 @@ namespace client
|
||||
}
|
||||
|
||||
m_AddressBook.StartResolvers ();
|
||||
|
||||
// start UDP cleanup
|
||||
if (!m_ServerForwards.empty ())
|
||||
{
|
||||
m_CleanupUDPTimer.reset (new boost::asio::deadline_timer(m_SharedLocalDestination->GetService ()));
|
||||
ScheduleCleanupUDP();
|
||||
}
|
||||
}
|
||||
|
||||
void ClientContext::Stop ()
|
||||
@@ -193,20 +219,37 @@ namespace client
|
||||
}
|
||||
|
||||
LogPrint(eLogInfo, "Clients: stopping AddressBook");
|
||||
m_AddressBook.Stop ();
|
||||
m_AddressBook.Stop ();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
||||
m_ServerForwards.clear();
|
||||
m_ClientForwards.clear();
|
||||
}
|
||||
|
||||
if (m_CleanupUDPTimer)
|
||||
{
|
||||
m_CleanupUDPTimer->cancel ();
|
||||
m_CleanupUDPTimer = nullptr;
|
||||
}
|
||||
|
||||
for (auto& it: m_Destinations)
|
||||
it.second->Stop ();
|
||||
m_Destinations.clear ();
|
||||
m_SharedLocalDestination = nullptr;
|
||||
m_SharedLocalDestination = nullptr;
|
||||
}
|
||||
|
||||
void ClientContext::ReloadConfig ()
|
||||
{
|
||||
ReadTunnels (); // TODO: it reads new tunnels only, should be implemented better
|
||||
std::string config; i2p::config::GetOption("conf", config);
|
||||
i2p::config::ParseConfig(config);
|
||||
Stop();
|
||||
Start();
|
||||
}
|
||||
|
||||
void ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
|
||||
bool ClientContext::LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType)
|
||||
{
|
||||
bool success = true;
|
||||
std::string fullPath = i2p::fs::DataDirPath (filename);
|
||||
std::ifstream s(fullPath, std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
@@ -216,9 +259,14 @@ namespace client
|
||||
s.seekg (0, std::ios::beg);
|
||||
uint8_t * buf = new uint8_t[len];
|
||||
s.read ((char *)buf, len);
|
||||
keys.FromBuffer (buf, len);
|
||||
if(!keys.FromBuffer (buf, len))
|
||||
{
|
||||
LogPrint (eLogError, "Clients: failed to load keyfile ", filename);
|
||||
success = false;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
|
||||
delete[] buf;
|
||||
LogPrint (eLogInfo, "Clients: Local address ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " loaded");
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -232,9 +280,33 @@ namespace client
|
||||
delete[] buf;
|
||||
|
||||
LogPrint (eLogInfo, "Clients: New private keys file ", fullPath, " for ", m_AddressBook.ToAddress(keys.GetPublic ()->GetIdentHash ()), " created");
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > ClientContext::GetForwardInfosFor(const i2p::data::IdentHash & destination)
|
||||
{
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
|
||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
||||
for(const auto & c : m_ClientForwards)
|
||||
{
|
||||
if (c.second->IsLocalDestination(destination))
|
||||
{
|
||||
for (auto & i : c.second->GetSessions()) infos.push_back(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(const auto & s : m_ServerForwards)
|
||||
{
|
||||
if(std::get<0>(s.first) == destination)
|
||||
{
|
||||
for( auto & i : s.second->GetSessions()) infos.push_back(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return infos;
|
||||
}
|
||||
|
||||
std::shared_ptr<ClientDestination> ClientContext::CreateNewLocalDestination (bool isPublic, i2p::data::SigningKeyType sigType,
|
||||
const std::map<std::string, std::string> * params)
|
||||
{
|
||||
@@ -304,6 +376,25 @@ namespace client
|
||||
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, DEFAULT_INBOUND_TUNNELS_QUANTITY);
|
||||
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = GetI2CPOption (section, I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, DEFAULT_OUTBOUND_TUNNELS_QUANTITY);
|
||||
options[I2CP_PARAM_TAGS_TO_SEND] = GetI2CPOption (section, I2CP_PARAM_TAGS_TO_SEND, DEFAULT_TAGS_TO_SEND);
|
||||
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MIN_TUNNEL_LATENCY, DEFAULT_MIN_TUNNEL_LATENCY);
|
||||
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = GetI2CPOption(section, I2CP_PARAM_MAX_TUNNEL_LATENCY, DEFAULT_MAX_TUNNEL_LATENCY);
|
||||
}
|
||||
|
||||
void ClientContext::ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const
|
||||
{
|
||||
std::string value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNEL_LENGTH, value))
|
||||
options[I2CP_PARAM_INBOUND_TUNNEL_LENGTH] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_INBOUND_TUNNELS_QUANTITY, value))
|
||||
options[I2CP_PARAM_INBOUND_TUNNELS_QUANTITY] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH, value))
|
||||
options[I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY, value))
|
||||
options[I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_MIN_TUNNEL_LATENCY, value))
|
||||
options[I2CP_PARAM_MIN_TUNNEL_LATENCY] = value;
|
||||
if (i2p::config::GetOption(prefix + I2CP_PARAM_MAX_TUNNEL_LATENCY, value))
|
||||
options[I2CP_PARAM_MAX_TUNNEL_LATENCY] = value;
|
||||
}
|
||||
|
||||
void ClientContext::ReadTunnels ()
|
||||
@@ -337,10 +428,16 @@ namespace client
|
||||
try
|
||||
{
|
||||
std::string type = section.second.get<std::string> (I2P_TUNNELS_SECTION_TYPE);
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT)
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT
|
||||
|| type == I2P_TUNNELS_SECTION_TYPE_SOCKS
|
||||
|| type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS
|
||||
|| type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY
|
||||
|| type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT)
|
||||
{
|
||||
// mandatory params
|
||||
std::string dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
|
||||
std::string dest;
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT)
|
||||
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, "");
|
||||
@@ -355,22 +452,69 @@ namespace client
|
||||
if (keys.length () > 0)
|
||||
{
|
||||
i2p::data::PrivateKeys k;
|
||||
LoadPrivateKeys (k, keys, sigType);
|
||||
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
|
||||
if(LoadPrivateKeys (k, keys, sigType))
|
||||
{
|
||||
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
|
||||
if (!localDestination)
|
||||
localDestination = CreateNewLocalDestination (k, type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT, &options);
|
||||
}
|
||||
}
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
|
||||
// udp client
|
||||
// TODO: hostnames
|
||||
boost::asio::ip::udp::endpoint end(boost::asio::ip::address::from_string(address), port);
|
||||
if (!localDestination)
|
||||
localDestination = CreateNewLocalDestination (k, false, &options);
|
||||
{
|
||||
localDestination = m_SharedLocalDestination;
|
||||
}
|
||||
auto clientTunnel = new I2PUDPClientTunnel(name, dest, end, localDestination, destinationPort);
|
||||
if(m_ClientForwards.insert(std::make_pair(end, std::unique_ptr<I2PUDPClientTunnel>(clientTunnel))).second)
|
||||
{
|
||||
clientTunnel->Start();
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Clients: I2P Client forward for endpoint ", end, " already exists");
|
||||
|
||||
} else {
|
||||
boost::asio::ip::tcp::endpoint clientEndpoint;
|
||||
I2PService * clientTunnel = nullptr;
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_SOCKS)
|
||||
{
|
||||
// socks proxy
|
||||
clientTunnel = new i2p::proxy::SOCKSProxy(address, port, "", destinationPort, localDestination);
|
||||
clientEndpoint = ((i2p::proxy::SOCKSProxy*)clientTunnel)->GetAcceptor().local_endpoint();
|
||||
}
|
||||
else if (type == I2P_TUNNELS_SECTION_TYPE_HTTPPROXY)
|
||||
{
|
||||
// http proxy
|
||||
clientTunnel = new i2p::proxy::HTTPProxy(address, port, localDestination);
|
||||
clientEndpoint = ((i2p::proxy::HTTPProxy*)clientTunnel)->GetAcceptor().local_endpoint();
|
||||
}
|
||||
else if (type == I2P_TUNNELS_SECTION_TYPE_WEBSOCKS)
|
||||
{
|
||||
// websocks proxy
|
||||
clientTunnel = new WebSocks(address, port, localDestination);;
|
||||
clientEndpoint = ((WebSocks*)clientTunnel)->GetLocalEndpoint();
|
||||
}
|
||||
else
|
||||
{
|
||||
// tcp client
|
||||
clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
|
||||
clientEndpoint = ((I2PClientTunnel*)clientTunnel)->GetAcceptor().local_endpoint();
|
||||
}
|
||||
if (m_ClientTunnels.insert (std::make_pair (clientEndpoint, std::unique_ptr<I2PService>(clientTunnel))).second)
|
||||
{
|
||||
clientTunnel->Start ();
|
||||
numClientTunnels++;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientEndpoint, "already exists");
|
||||
}
|
||||
auto clientTunnel = new I2PClientTunnel (name, dest, address, port, localDestination, destinationPort);
|
||||
if (m_ClientTunnels.insert (std::make_pair (clientTunnel->GetAcceptor ().local_endpoint (),
|
||||
std::unique_ptr<I2PClientTunnel>(clientTunnel))).second)
|
||||
{
|
||||
clientTunnel->Start ();
|
||||
numClientTunnels++;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogError, "Clients: I2P client tunnel for endpoint ", clientTunnel->GetAcceptor ().local_endpoint (), " already exists");
|
||||
}
|
||||
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER || type == I2P_TUNNELS_SECTION_TYPE_HTTP || type == I2P_TUNNELS_SECTION_TYPE_IRC)
|
||||
else if (type == I2P_TUNNELS_SECTION_TYPE_SERVER
|
||||
|| type == I2P_TUNNELS_SECTION_TYPE_HTTP
|
||||
|| type == I2P_TUNNELS_SECTION_TYPE_IRC
|
||||
|| type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
|
||||
{
|
||||
// mandatory params
|
||||
std::string host = section.second.get<std::string> (I2P_SERVER_TUNNEL_HOST);
|
||||
@@ -383,17 +527,49 @@ namespace client
|
||||
std::string webircpass = section.second.get<std::string> (I2P_SERVER_TUNNEL_WEBIRC_PASSWORD, "");
|
||||
bool gzip = section.second.get (I2P_SERVER_TUNNEL_GZIP, true);
|
||||
i2p::data::SigningKeyType sigType = section.second.get (I2P_SERVER_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
||||
uint32_t maxConns = section.second.get(i2p::stream::I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN, i2p::stream::DEFAULT_MAX_CONNS_PER_MIN);
|
||||
std::string address = section.second.get<std::string> (I2P_SERVER_TUNNEL_ADDRESS, "127.0.0.1");
|
||||
bool isUniqueLocal = section.second.get(I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL, true);
|
||||
|
||||
// I2CP
|
||||
std::map<std::string, std::string> options;
|
||||
ReadI2CPOptions (section, options);
|
||||
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
i2p::data::PrivateKeys k;
|
||||
LoadPrivateKeys (k, keys, sigType);
|
||||
if(!LoadPrivateKeys (k, keys, sigType))
|
||||
continue;
|
||||
localDestination = FindLocalDestination (k.GetPublic ()->GetIdentHash ());
|
||||
if (!localDestination)
|
||||
localDestination = CreateNewLocalDestination (k, true, &options);
|
||||
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_UDPSERVER)
|
||||
{
|
||||
// udp server tunnel
|
||||
// TODO: hostnames
|
||||
auto localAddress = boost::asio::ip::address::from_string(address);
|
||||
boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port);
|
||||
I2PUDPServerTunnel * serverTunnel = new I2PUDPServerTunnel(name, localDestination, localAddress, endpoint, port);
|
||||
if(!isUniqueLocal)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: disabling loopback address mapping");
|
||||
serverTunnel->SetUniqueLocal(isUniqueLocal);
|
||||
}
|
||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
||||
if(m_ServerForwards.insert(
|
||||
std::make_pair(
|
||||
std::make_pair(
|
||||
localDestination->GetIdentHash(), port),
|
||||
std::unique_ptr<I2PUDPServerTunnel>(serverTunnel))).second)
|
||||
{
|
||||
serverTunnel->Start();
|
||||
LogPrint(eLogInfo, "Clients: I2P Server Forward created for UDP Endpoint ", host, ":", port, " bound on ", address, " for ",localDestination->GetIdentHash().ToBase32());
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "Clients: I2P Server Forward for destination/port ", m_AddressBook.ToAddress(localDestination->GetIdentHash()), "/", port, "already exists");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
I2PServerTunnel * serverTunnel;
|
||||
if (type == I2P_TUNNELS_SECTION_TYPE_HTTP)
|
||||
serverTunnel = new I2PServerTunnelHTTP (name, host, port, localDestination, hostOverride, inPort, gzip);
|
||||
@@ -402,6 +578,14 @@ namespace client
|
||||
else // regular server tunnel by default
|
||||
serverTunnel = new I2PServerTunnel (name, host, port, localDestination, inPort, gzip);
|
||||
|
||||
LogPrint(eLogInfo, "Clients: Set Max Conns To ", maxConns);
|
||||
serverTunnel->SetMaxConnsPerMinute(maxConns);
|
||||
if(!isUniqueLocal)
|
||||
{
|
||||
LogPrint(eLogInfo, "Clients: disabling loopback address mapping");
|
||||
serverTunnel->SetUniqueLocal(isUniqueLocal);
|
||||
}
|
||||
|
||||
if (accessList.length () > 0)
|
||||
{
|
||||
std::set<i2p::data::IdentHash> idents;
|
||||
@@ -439,6 +623,26 @@ namespace client
|
||||
}
|
||||
LogPrint (eLogInfo, "Clients: ", numClientTunnels, " I2P client tunnels created");
|
||||
LogPrint (eLogInfo, "Clients: ", numServerTunnels, " I2P server tunnels created");
|
||||
}
|
||||
}
|
||||
|
||||
void ClientContext::ScheduleCleanupUDP()
|
||||
{
|
||||
if (m_CleanupUDPTimer)
|
||||
{
|
||||
// schedule cleanup in 17 seconds
|
||||
m_CleanupUDPTimer->expires_from_now (boost::posix_time::seconds (17));
|
||||
m_CleanupUDPTimer->async_wait(std::bind(&ClientContext::CleanupUDP, this, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
void ClientContext::CleanupUDP(const boost::system::error_code & ecode)
|
||||
{
|
||||
if(!ecode)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ForwardsMutex);
|
||||
for (auto & s : m_ServerForwards ) s.second->ExpireStale();
|
||||
ScheduleCleanupUDP();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ namespace client
|
||||
const char I2P_TUNNELS_SECTION_TYPE_SERVER[] = "server";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_HTTP[] = "http";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_IRC[] = "irc";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_UDPCLIENT[] = "udpclient";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_UDPSERVER[] = "udpserver";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_SOCKS[] = "socks";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_WEBSOCKS[] = "websocks";
|
||||
const char I2P_TUNNELS_SECTION_TYPE_HTTPPROXY[] = "httpproxy";
|
||||
const char I2P_CLIENT_TUNNEL_PORT[] = "port";
|
||||
const char I2P_CLIENT_TUNNEL_ADDRESS[] = "address";
|
||||
const char I2P_CLIENT_TUNNEL_DESTINATION[] = "destination";
|
||||
@@ -39,6 +44,8 @@ namespace client
|
||||
const char I2P_SERVER_TUNNEL_ACCESS_LIST[] = "accesslist";
|
||||
const char I2P_SERVER_TUNNEL_GZIP[] = "gzip";
|
||||
const char I2P_SERVER_TUNNEL_WEBIRC_PASSWORD[] = "webircpassword";
|
||||
const char I2P_SERVER_TUNNEL_ADDRESS[] = "address";
|
||||
const char I2P_SERVER_TUNNEL_ENABLE_UNIQUE_LOCAL[] = "enableuniquelocal";
|
||||
|
||||
class ClientContext
|
||||
{
|
||||
@@ -59,19 +66,26 @@ namespace client
|
||||
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;
|
||||
void LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
||||
bool LoadPrivateKeys (i2p::data::PrivateKeys& keys, const std::string& filename, i2p::data::SigningKeyType sigType = i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
|
||||
|
||||
AddressBook& GetAddressBook () { return m_AddressBook; };
|
||||
const SAMBridge * GetSAMBridge () const { return m_SamBridge; };
|
||||
|
||||
const I2CPServer * GetI2CPServer () const { return m_I2CPServer; };
|
||||
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetForwardInfosFor(const i2p::data::IdentHash & destination);
|
||||
|
||||
private:
|
||||
|
||||
void ReadTunnels ();
|
||||
template<typename Section, typename Type>
|
||||
std::string GetI2CPOption (const Section& section, const std::string& name, const Type& value) const;
|
||||
template<typename Section>
|
||||
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const;
|
||||
void ReadI2CPOptions (const Section& section, std::map<std::string, std::string>& options) const;
|
||||
void ReadI2CPOptionsFromConfig (const std::string& prefix, std::map<std::string, std::string>& options) const;
|
||||
|
||||
void CleanupUDP(const boost::system::error_code & ecode);
|
||||
void ScheduleCleanupUDP();
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_DestinationsMutex;
|
||||
@@ -82,17 +96,28 @@ namespace client
|
||||
|
||||
i2p::proxy::HTTPProxy * m_HttpProxy;
|
||||
i2p::proxy::SOCKSProxy * m_SocksProxy;
|
||||
std::map<boost::asio::ip::tcp::endpoint, std::unique_ptr<I2PClientTunnel> > m_ClientTunnels; // local endpoint->tunnel
|
||||
std::map<boost::asio::ip::tcp::endpoint, std::unique_ptr<I2PService> > m_ClientTunnels; // local endpoint->tunnel
|
||||
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PServerTunnel> > m_ServerTunnels; // <destination,port>->tunnel
|
||||
|
||||
std::mutex m_ForwardsMutex;
|
||||
std::map<boost::asio::ip::udp::endpoint, std::unique_ptr<I2PUDPClientTunnel> > m_ClientForwards; // local endpoint -> udp tunnel
|
||||
std::map<std::pair<i2p::data::IdentHash, int>, std::unique_ptr<I2PUDPServerTunnel> > m_ServerForwards; // <destination,port> -> udp tunnel
|
||||
|
||||
SAMBridge * m_SamBridge;
|
||||
BOBCommandChannel * m_BOBCommandChannel;
|
||||
I2CPServer * m_I2CPServer;
|
||||
|
||||
std::unique_ptr<boost::asio::deadline_timer> m_CleanupUDPTimer;
|
||||
|
||||
public:
|
||||
// for HTTP
|
||||
const decltype(m_Destinations)& GetDestinations () const { return m_Destinations; };
|
||||
const decltype(m_ClientTunnels)& GetClientTunnels () const { return m_ClientTunnels; };
|
||||
const decltype(m_ServerTunnels)& GetServerTunnels () const { return m_ServerTunnels; };
|
||||
const decltype(m_ClientForwards)& GetClientForwards () const { return m_ClientForwards; }
|
||||
const decltype(m_ServerForwards)& GetServerForwards () const { return m_ServerForwards; }
|
||||
const i2p::proxy::HTTPProxy * GetHttpProxy () const { return m_HttpProxy; }
|
||||
const i2p::proxy::SOCKSProxy * GetSocksProxy () const { return m_SocksProxy; }
|
||||
};
|
||||
|
||||
extern ClientContext context;
|
||||
|
||||
83
Config.cpp
83
Config.cpp
@@ -16,6 +16,7 @@
|
||||
#include <boost/program_options/parsers.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
|
||||
#include "Identity.h"
|
||||
#include "Config.h"
|
||||
#include "version.h"
|
||||
|
||||
@@ -27,10 +28,6 @@ namespace config {
|
||||
variables_map m_Options;
|
||||
|
||||
void Init() {
|
||||
bool nat = true;
|
||||
#ifdef MESHNET
|
||||
nat = false;
|
||||
#endif
|
||||
|
||||
options_description general("General options");
|
||||
general.add_options()
|
||||
@@ -44,27 +41,32 @@ namespace config {
|
||||
("family", value<std::string>()->default_value(""), "Specify a family, router belongs to")
|
||||
("datadir", value<std::string>()->default_value(""), "Path to storage of i2pd data (RI, keys, peer profiles, ...)")
|
||||
("host", value<std::string>()->default_value("0.0.0.0"), "External IP")
|
||||
("ifname", value<std::string>()->default_value(""), "network interface to bind to")
|
||||
("nat", value<bool>()->zero_tokens()->default_value(nat), "should we assume we are behind NAT?")
|
||||
("ifname", value<std::string>()->default_value(""), "Network interface to bind to")
|
||||
("ifname4", value<std::string>()->default_value(""), "Network interface to bind to for ipv4")
|
||||
("ifname6", value<std::string>()->default_value(""), "Network interface to bind to for ipv6")
|
||||
("nat", value<bool>()->zero_tokens()->default_value(true), "Should we assume we are behind NAT?")
|
||||
("port", value<uint16_t>()->default_value(0), "Port to listen for incoming connections (default: auto)")
|
||||
("ipv4", value<bool>()->zero_tokens()->default_value(true), "Enable communication through ipv4")
|
||||
("ipv6", value<bool>()->zero_tokens()->default_value(false), "Enable communication through ipv6")
|
||||
("netid", value<int>()->default_value(I2PD_NET_ID), "Specify NetID. Main I2P is 2")
|
||||
("daemon", value<bool>()->zero_tokens()->default_value(false), "Router will go to background after start")
|
||||
("service", value<bool>()->zero_tokens()->default_value(false), "Router will use system folders like '/var/lib/i2pd'")
|
||||
("notransit", value<bool>()->zero_tokens()->default_value(false), "Router will not accept transit tunnels at startup")
|
||||
("floodfill", value<bool>()->zero_tokens()->default_value(false), "Router will be floodfill")
|
||||
("bandwidth", value<std::string>()->default_value(""), "Bandwidth limit: integer in kbps or letters: L (32), O (256), P (2048), X (>9000)")
|
||||
("ntcp", value<bool>()->zero_tokens()->default_value(true), "enable ntcp transport")
|
||||
("ssu", value<bool>()->zero_tokens()->default_value(true), "enable ssu transport")
|
||||
("ntcp", value<bool>()->zero_tokens()->default_value(true), "Enable NTCP transport")
|
||||
("ssu", value<bool>()->zero_tokens()->default_value(true), "Enable SSU transport")
|
||||
#ifdef _WIN32
|
||||
("svcctl", value<std::string>()->default_value(""), "Windows service management ('install' or 'remove')")
|
||||
("insomnia", value<bool>()->zero_tokens()->default_value(false), "Prevent system from sleeping")
|
||||
("close", value<std::string>()->default_value("ask"), "Action on close: minimize, exit, ask") // TODO: add custom validator or something
|
||||
#endif
|
||||
;
|
||||
|
||||
|
||||
options_description limits("Limits options");
|
||||
limits.add_options()
|
||||
("limits.coresize", value<uint32_t>()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)")
|
||||
("limits.openfiles", value<uint16_t>()->default_value(0), "Maximum number of open files (0 - use system default)")
|
||||
("limits.transittunnels", value<uint16_t>()->default_value(2500), "Maximum active transit sessions (default:2500)")
|
||||
;
|
||||
|
||||
@@ -84,6 +86,14 @@ namespace config {
|
||||
("httpproxy.address", value<std::string>()->default_value("127.0.0.1"), "HTTP Proxy listen address")
|
||||
("httpproxy.port", value<uint16_t>()->default_value(4444), "HTTP Proxy listen port")
|
||||
("httpproxy.keys", value<std::string>()->default_value(""), "File to persist HTTP Proxy keys")
|
||||
("httpproxy.signaturetype", value<i2p::data::SigningKeyType>()->default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default")
|
||||
("httpproxy.inbound.length", value<std::string>()->default_value("3"), "HTTP proxy inbound tunnel length")
|
||||
("httpproxy.outbound.length", value<std::string>()->default_value("3"), "HTTP proxy outbound tunnel length")
|
||||
("httpproxy.inbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy inbound tunnels quantity")
|
||||
("httpproxy.outbound.quantity", value<std::string>()->default_value("5"), "HTTP proxy outbound tunnels quantity")
|
||||
("httpproxy.latency.min", value<std::string>()->default_value("0"), "HTTP proxy min latency for tunnels")
|
||||
("httpproxy.latency.max", value<std::string>()->default_value("0"), "HTTP proxy max latency for tunnels")
|
||||
("httpproxy.outproxy", value<std::string>()->default_value(""), "HTTP proxy upstream out proxy url")
|
||||
;
|
||||
|
||||
options_description socksproxy("SOCKS Proxy options");
|
||||
@@ -92,7 +102,14 @@ namespace config {
|
||||
("socksproxy.address", value<std::string>()->default_value("127.0.0.1"), "SOCKS Proxy listen address")
|
||||
("socksproxy.port", value<uint16_t>()->default_value(4447), "SOCKS Proxy listen port")
|
||||
("socksproxy.keys", value<std::string>()->default_value(""), "File to persist SOCKS Proxy keys")
|
||||
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
|
||||
("socksproxy.signaturetype", value<i2p::data::SigningKeyType>()->default_value(i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519), "Signature type for new keys. 7 (EdDSA) by default")
|
||||
("socksproxy.inbound.length", value<std::string>()->default_value("3"), "SOCKS proxy inbound tunnel length")
|
||||
("socksproxy.outbound.length", value<std::string>()->default_value("3"), "SOCKS proxy outbound tunnel length")
|
||||
("socksproxy.inbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy inbound tunnels quantity")
|
||||
("socksproxy.outbound.quantity", value<std::string>()->default_value("5"), "SOCKS proxy outbound tunnels quantity")
|
||||
("socksproxy.latency.min", value<std::string>()->default_value("0"), "SOCKS proxy min latency for tunnels")
|
||||
("socksproxy.latency.max", value<std::string>()->default_value("0"), "SOCKS proxy max latency for tunnels")
|
||||
("socksproxy.outproxy", value<std::string>()->default_value("127.0.0.1"), "Upstream outproxy address for SOCKS Proxy")
|
||||
("socksproxy.outproxyport", value<uint16_t>()->default_value(9050), "Upstream outproxy port for SOCKS Proxy")
|
||||
;
|
||||
|
||||
@@ -150,15 +167,49 @@ namespace config {
|
||||
|
||||
options_description reseed("Reseed options");
|
||||
reseed.add_options()
|
||||
("reseed.file", value<std::string>()->default_value(""), "Path to .su3 file")
|
||||
("reseed.verify", value<bool>()->default_value(false), "Verify .su3 signature")
|
||||
("reseed.threshold", value<uint16_t>()->default_value(25), "Minimum number of known routers before requesting reseed")
|
||||
("reseed.floodfill", value<std::string>()->default_value(""), "Path to router info of floodfill to reseed from")
|
||||
("reseed.file", value<std::string>()->default_value(""), "Path to local .su3 file or HTTPS URL to reseed from")
|
||||
("reseed.zipfile", value<std::string>()->default_value(""), "Path to local .zip file to reseed from")
|
||||
("reseed.urls", value<std::string>()->default_value(
|
||||
"https://reseed.i2p-projekt.de/,"
|
||||
"https://i2p.mooo.com/netDb/,"
|
||||
"https://netdb.i2p2.no/,"
|
||||
// "https://us.reseed.i2p2.no:444/," // mamoth's shit
|
||||
// "https://uk.reseed.i2p2.no:444/," // mamoth's shit
|
||||
"https://i2p-0.manas.ca:8443/,"
|
||||
"https://reseed.i2p.vzaws.com:8443/,"
|
||||
"https://download.xxlspeed.com/,"
|
||||
"https://reseed-ru.lngserv.ru/,"
|
||||
"https://reseed.atomike.ninja/,"
|
||||
"https://reseed.memcpy.io/,"
|
||||
"https://reseed.onion.im/,"
|
||||
"https://itoopie.atomike.ninja/"
|
||||
), "Reseed URLs, separated by comma")
|
||||
;
|
||||
|
||||
options_description addressbook("AddressBook options");
|
||||
addressbook.add_options()
|
||||
("addressbook.defaulturl", value<std::string>()->default_value(
|
||||
"http://joajgazyztfssty4w2on5oaqksz6tqoxbduy553y34mf4byv6gpq.b32.i2p/export/alive-hosts.txt"
|
||||
), "AddressBook subscription URL for initial setup")
|
||||
("addressbook.subscriptions", value<std::string>()->default_value(""),
|
||||
"AddressBook subscriptions URLs, separated by comma");
|
||||
|
||||
options_description trust("Trust options");
|
||||
trust.add_options()
|
||||
("trust.enabled", value<bool>()->default_value(false), "enable explicit trust options")
|
||||
("trust.enabled", value<bool>()->default_value(false), "Enable explicit trust options")
|
||||
("trust.family", value<std::string>()->default_value(""), "Router Familiy to trust for first hops")
|
||||
("trust.hidden", value<bool>()->default_value(false), "should we hide our router from other routers?");
|
||||
|
||||
("trust.routers", value<std::string>()->default_value(""), "Only Connect to these routers")
|
||||
("trust.hidden", value<bool>()->default_value(false), "Should we hide our router from other routers?");
|
||||
|
||||
options_description websocket("Websocket Options");
|
||||
websocket.add_options()
|
||||
("websockets.enabled", value<bool>()->default_value(false), "enable websocket server")
|
||||
("websockets.address", value<std::string>()->default_value("127.0.0.1"), "address to bind websocket server on")
|
||||
("websockets.port", value<uint16_t>()->default_value(7666), "port to bind websocket server on");
|
||||
|
||||
m_OptionsDesc
|
||||
.add(general)
|
||||
.add(limits)
|
||||
@@ -172,7 +223,9 @@ namespace config {
|
||||
.add(upnp)
|
||||
.add(precomputation)
|
||||
.add(reseed)
|
||||
.add(trust)
|
||||
.add(addressbook)
|
||||
.add(trust)
|
||||
.add(websocket)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
8
Config.h
8
Config.h
@@ -78,6 +78,12 @@ namespace config {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool GetOption(const std::string& name, T& value)
|
||||
{
|
||||
return GetOption (name.c_str (), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set value of given parameter
|
||||
* @param name Name of settable parameter
|
||||
@@ -93,7 +99,7 @@ namespace config {
|
||||
m_Options.at(name).value() = value;
|
||||
notify(m_Options);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check is value explicitly given or default
|
||||
|
||||
162
Crypto.cpp
162
Crypto.cpp
@@ -135,11 +135,8 @@ namespace crypto
|
||||
DSA * CreateDSA ()
|
||||
{
|
||||
DSA * dsa = DSA_new ();
|
||||
dsa->p = BN_dup (dsap);
|
||||
dsa->q = BN_dup (dsaq);
|
||||
dsa->g = BN_dup (dsag);
|
||||
dsa->priv_key = NULL;
|
||||
dsa->pub_key = NULL;
|
||||
DSA_set0_pqg (dsa, BN_dup (dsap), BN_dup (dsaq), BN_dup (dsag));
|
||||
DSA_set0_key (dsa, NULL, NULL);
|
||||
return dsa;
|
||||
}
|
||||
|
||||
@@ -227,13 +224,11 @@ namespace crypto
|
||||
|
||||
// DH
|
||||
|
||||
DHKeys::DHKeys (): m_IsUpdated (true)
|
||||
DHKeys::DHKeys ()
|
||||
{
|
||||
m_DH = DH_new ();
|
||||
m_DH->p = BN_dup (elgp);
|
||||
m_DH->g = BN_dup (elgg);
|
||||
m_DH->priv_key = NULL;
|
||||
m_DH->pub_key = NULL;
|
||||
DH_set0_pqg (m_DH, BN_dup (elgp), NULL, BN_dup (elgg));
|
||||
DH_set0_key (m_DH, NULL, NULL);
|
||||
}
|
||||
|
||||
DHKeys::~DHKeys ()
|
||||
@@ -241,41 +236,32 @@ namespace crypto
|
||||
DH_free (m_DH);
|
||||
}
|
||||
|
||||
void DHKeys::GenerateKeys (uint8_t * priv, uint8_t * pub)
|
||||
void DHKeys::GenerateKeys ()
|
||||
{
|
||||
if (m_DH->priv_key) { BN_free (m_DH->priv_key); m_DH->priv_key = NULL; };
|
||||
if (m_DH->pub_key) { BN_free (m_DH->pub_key); m_DH->pub_key = NULL; };
|
||||
BIGNUM * priv_key = NULL, * pub_key = NULL;
|
||||
#if !defined(__x86_64__) // use short exponent for non x64
|
||||
m_DH->priv_key = BN_new ();
|
||||
BN_rand (m_DH->priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
|
||||
priv_key = BN_new ();
|
||||
BN_rand (priv_key, ELGAMAL_SHORT_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
if (g_ElggTable)
|
||||
{
|
||||
#if defined(__x86_64__)
|
||||
m_DH->priv_key = BN_new ();
|
||||
BN_rand (m_DH->priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
|
||||
priv_key = BN_new ();
|
||||
BN_rand (priv_key, ELGAMAL_FULL_EXPONENT_NUM_BITS, 0, 1);
|
||||
#endif
|
||||
auto ctx = BN_CTX_new ();
|
||||
m_DH->pub_key = ElggPow (m_DH->priv_key, g_ElggTable, ctx);
|
||||
pub_key = ElggPow (priv_key, g_ElggTable, ctx);
|
||||
DH_set0_key (m_DH, pub_key, priv_key);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
else
|
||||
{
|
||||
DH_set0_key (m_DH, NULL, priv_key);
|
||||
DH_generate_key (m_DH);
|
||||
|
||||
if (priv) bn2buf (m_DH->priv_key, priv, 256);
|
||||
if (pub) bn2buf (m_DH->pub_key, pub, 256);
|
||||
m_IsUpdated = true;
|
||||
}
|
||||
|
||||
const uint8_t * DHKeys::GetPublicKey ()
|
||||
{
|
||||
if (m_IsUpdated)
|
||||
{
|
||||
bn2buf (m_DH->pub_key, m_PublicKey, 256);
|
||||
BN_free (m_DH->pub_key); m_DH->pub_key = NULL;
|
||||
m_IsUpdated= false;
|
||||
DH_get0_key (m_DH, (const BIGNUM **)&pub_key, (const BIGNUM **)&priv_key);
|
||||
}
|
||||
return m_PublicKey;
|
||||
|
||||
bn2buf (pub_key, m_PublicKey, 256);
|
||||
}
|
||||
|
||||
void DHKeys::Agree (const uint8_t * pub, uint8_t * shared)
|
||||
@@ -286,10 +272,9 @@ namespace crypto
|
||||
}
|
||||
|
||||
// ElGamal
|
||||
|
||||
ElGamalEncryption::ElGamalEncryption (const uint8_t * key)
|
||||
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, bool zeroPadding)
|
||||
{
|
||||
ctx = BN_CTX_new ();
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
// select random k
|
||||
BIGNUM * k = BN_new ();
|
||||
#if defined(__x86_64__)
|
||||
@@ -298,6 +283,7 @@ namespace crypto
|
||||
BN_rand (k, ELGAMAL_SHORT_EXPONENT_NUM_BITS, -1, 1); // short exponent of 226 bits
|
||||
#endif
|
||||
// calculate a
|
||||
BIGNUM * a;
|
||||
if (g_ElggTable)
|
||||
a = ElggPow (k, g_ElggTable, ctx);
|
||||
else
|
||||
@@ -309,30 +295,20 @@ namespace crypto
|
||||
BIGNUM * y = BN_new ();
|
||||
BN_bin2bn (key, 256, y);
|
||||
// calculate b1
|
||||
b1 = BN_new ();
|
||||
BIGNUM * b1 = BN_new ();
|
||||
BN_mod_exp (b1, y, k, elgp, ctx);
|
||||
BN_free (y);
|
||||
BN_free (k);
|
||||
}
|
||||
|
||||
ElGamalEncryption::~ElGamalEncryption ()
|
||||
{
|
||||
BN_CTX_free (ctx);
|
||||
BN_free (a);
|
||||
BN_free (b1);
|
||||
}
|
||||
|
||||
void ElGamalEncryption::Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding) const
|
||||
{
|
||||
// create m
|
||||
uint8_t m[255];
|
||||
m[0] = 0xFF;
|
||||
memcpy (m+33, data, len);
|
||||
memcpy (m+33, data, 222);
|
||||
SHA256 (m+33, 222, m+1);
|
||||
// calculate b = b1*m mod p
|
||||
BIGNUM * b = BN_new ();
|
||||
BN_bin2bn (m, 255, b);
|
||||
BN_mod_mul (b, b1, b, elgp, ctx);
|
||||
BN_free (b1);
|
||||
// copy a and b
|
||||
if (zeroPadding)
|
||||
{
|
||||
@@ -347,8 +323,10 @@ namespace crypto
|
||||
bn2buf (b, encrypted + 256, 256);
|
||||
}
|
||||
BN_free (b);
|
||||
BN_free (a);
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
|
||||
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted,
|
||||
uint8_t * data, bool zeroPadding)
|
||||
{
|
||||
@@ -400,44 +378,68 @@ namespace crypto
|
||||
// HMAC
|
||||
const uint64_t IPAD = 0x3636363636363636;
|
||||
const uint64_t OPAD = 0x5C5C5C5C5C5C5C5C;
|
||||
|
||||
|
||||
#if defined(__AVX__)
|
||||
static const uint64_t ipads[] = { IPAD, IPAD, IPAD, IPAD };
|
||||
static const uint64_t opads[] = { OPAD, OPAD, OPAD, OPAD };
|
||||
#endif
|
||||
|
||||
void HMACMD5Digest (uint8_t * msg, size_t len, const MACKey& key, uint8_t * digest)
|
||||
// key is 32 bytes
|
||||
// digest is 16 bytes
|
||||
// block size is 64 bytes
|
||||
{
|
||||
uint64_t buf[256];
|
||||
uint64_t hash[12]; // 96 bytes
|
||||
#if defined(__AVX__) // for AVX
|
||||
__asm__
|
||||
(
|
||||
"vmovups %[key], %%ymm0 \n"
|
||||
"vmovups %[ipad], %%ymm1 \n"
|
||||
"vmovups %%ymm1, 32(%[buf]) \n"
|
||||
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
|
||||
"vmovups %%ymm1, (%[buf]) \n"
|
||||
"vmovups %[opad], %%ymm1 \n"
|
||||
"vmovups %%ymm1, 32(%[hash]) \n"
|
||||
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
|
||||
"vmovups %%ymm1, (%[hash]) \n"
|
||||
"vzeroall \n" // end of AVX
|
||||
"movups %%xmm0, 80(%[hash]) \n" // zero last 16 bytes
|
||||
:
|
||||
: [key]"m"(*(const uint8_t *)key), [ipad]"m"(*ipads), [opad]"m"(*opads),
|
||||
[buf]"r"(buf), [hash]"r"(hash)
|
||||
: "memory", "%xmm0" // TODO: change to %ymm0 later
|
||||
);
|
||||
#else
|
||||
// ikeypad
|
||||
buf[0] = key.GetLL ()[0] ^ IPAD;
|
||||
buf[1] = key.GetLL ()[1] ^ IPAD;
|
||||
buf[2] = key.GetLL ()[2] ^ IPAD;
|
||||
buf[3] = key.GetLL ()[3] ^ IPAD;
|
||||
buf[4] = IPAD;
|
||||
buf[5] = IPAD;
|
||||
buf[6] = IPAD;
|
||||
buf[7] = IPAD;
|
||||
buf[4] = IPAD;
|
||||
buf[5] = IPAD;
|
||||
buf[6] = IPAD;
|
||||
buf[7] = IPAD;
|
||||
// okeypad
|
||||
hash[0] = key.GetLL ()[0] ^ OPAD;
|
||||
hash[1] = key.GetLL ()[1] ^ OPAD;
|
||||
hash[2] = key.GetLL ()[2] ^ OPAD;
|
||||
hash[3] = key.GetLL ()[3] ^ OPAD;
|
||||
hash[4] = OPAD;
|
||||
hash[5] = OPAD;
|
||||
hash[6] = OPAD;
|
||||
hash[7] = OPAD;
|
||||
// fill last 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
|
||||
memset (hash + 10, 0, 16);
|
||||
#endif
|
||||
|
||||
// concatenate with msg
|
||||
memcpy (buf + 8, msg, len);
|
||||
// calculate first hash
|
||||
uint8_t hash[16]; // MD5
|
||||
MD5((uint8_t *)buf, len + 64, hash);
|
||||
|
||||
// okeypad
|
||||
buf[0] = key.GetLL ()[0] ^ OPAD;
|
||||
buf[1] = key.GetLL ()[1] ^ OPAD;
|
||||
buf[2] = key.GetLL ()[2] ^ OPAD;
|
||||
buf[3] = key.GetLL ()[3] ^ OPAD;
|
||||
buf[4] = OPAD;
|
||||
buf[5] = OPAD;
|
||||
buf[6] = OPAD;
|
||||
buf[7] = OPAD;
|
||||
// copy first hash after okeypad
|
||||
memcpy (buf + 8, hash, 16);
|
||||
// fill next 16 bytes with zeros (first hash size assumed 32 bytes in I2P)
|
||||
memset (buf + 10, 0, 16);
|
||||
MD5((uint8_t *)buf, len + 64, (uint8_t *)(hash + 8)); // 16 bytes
|
||||
|
||||
// calculate digest
|
||||
MD5((uint8_t *)buf, 96, digest);
|
||||
MD5((uint8_t *)hash, 96, digest);
|
||||
}
|
||||
|
||||
// AES
|
||||
@@ -609,16 +611,16 @@ namespace crypto
|
||||
"jnz 1b \n"
|
||||
"movups %%xmm1, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
||||
: "%xmm0", "%xmm1", "cc", "memory"
|
||||
);
|
||||
#else
|
||||
for (int i = 0; i < numBlocks; i++)
|
||||
{
|
||||
m_LastBlock ^= in[i];
|
||||
m_ECBEncryption.Encrypt (&m_LastBlock, &m_LastBlock);
|
||||
out[i] = m_LastBlock;
|
||||
*m_LastBlock.GetChipherBlock () ^= in[i];
|
||||
m_ECBEncryption.Encrypt (m_LastBlock.GetChipherBlock (), m_LastBlock.GetChipherBlock ());
|
||||
out[i] = *m_LastBlock.GetChipherBlock ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -643,7 +645,7 @@ namespace crypto
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
"movups %%xmm0, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
: [iv]"r"((uint8_t *)m_LastBlock), [sched]"r"(m_ECBEncryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
@@ -671,7 +673,7 @@ namespace crypto
|
||||
"jnz 1b \n"
|
||||
"movups %%xmm1, (%[iv]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out), [num]"r"(numBlocks)
|
||||
: "%xmm0", "%xmm1", "%xmm2", "cc", "memory"
|
||||
);
|
||||
@@ -680,8 +682,8 @@ namespace crypto
|
||||
{
|
||||
ChipherBlock tmp = in[i];
|
||||
m_ECBDecryption.Decrypt (in + i, out + i);
|
||||
out[i] ^= m_IV;
|
||||
m_IV = tmp;
|
||||
out[i] ^= *m_IV.GetChipherBlock ();
|
||||
*m_IV.GetChipherBlock () = tmp;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -705,7 +707,7 @@ namespace crypto
|
||||
"pxor %%xmm1, %%xmm0 \n"
|
||||
"movups %%xmm0, (%[out]) \n"
|
||||
:
|
||||
: [iv]"r"(&m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
: [iv]"r"((uint8_t *)m_IV), [sched]"r"(m_ECBDecryption.GetKeySchedule ()),
|
||||
[in]"r"(in), [out]"r"(out)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
|
||||
101
Crypto.h
101
Crypto.h
@@ -7,7 +7,10 @@
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
#include "Base.h"
|
||||
@@ -33,33 +36,18 @@ namespace crypto
|
||||
DHKeys ();
|
||||
~DHKeys ();
|
||||
|
||||
void GenerateKeys (uint8_t * priv = nullptr, uint8_t * pub = nullptr);
|
||||
const uint8_t * GetPublicKey ();
|
||||
void GenerateKeys ();
|
||||
const uint8_t * GetPublicKey () const { return m_PublicKey; };
|
||||
void Agree (const uint8_t * pub, uint8_t * shared);
|
||||
|
||||
private:
|
||||
|
||||
DH * m_DH;
|
||||
uint8_t m_PublicKey[256];
|
||||
bool m_IsUpdated;
|
||||
};
|
||||
|
||||
// ElGamal
|
||||
class ElGamalEncryption
|
||||
{
|
||||
public:
|
||||
|
||||
ElGamalEncryption (const uint8_t * key);
|
||||
~ElGamalEncryption ();
|
||||
|
||||
void Encrypt (const uint8_t * data, int len, uint8_t * encrypted, bool zeroPadding = false) const;
|
||||
|
||||
private:
|
||||
|
||||
BN_CTX * ctx;
|
||||
BIGNUM * a, * b1;
|
||||
};
|
||||
|
||||
void ElGamalEncrypt (const uint8_t * key, const uint8_t * data, uint8_t * encrypted, bool zeroPadding = false);
|
||||
bool ElGamalDecrypt (const uint8_t * key, const uint8_t * encrypted, uint8_t * data, bool zeroPadding = false);
|
||||
void GenerateElGamalKeyPair (uint8_t * priv, uint8_t * pub);
|
||||
|
||||
@@ -74,7 +62,18 @@ namespace crypto
|
||||
|
||||
void operator^=(const ChipherBlock& other) // XOR
|
||||
{
|
||||
#if defined(__x86_64__) || defined(__SSE__) // for Intel x84 or with SSE
|
||||
#if defined(__AVX__) // AVX
|
||||
__asm__
|
||||
(
|
||||
"vmovups (%[buf]), %%xmm0 \n"
|
||||
"vmovups (%[other]), %%xmm1 \n"
|
||||
"vxorps %%xmm0, %%xmm1, %%xmm0 \n"
|
||||
"vmovups %%xmm0, (%[buf]) \n"
|
||||
:
|
||||
: [buf]"r"(buf), [other]"r"(other.buf)
|
||||
: "%xmm0", "%xmm1", "memory"
|
||||
);
|
||||
#elif defined(__SSE__) // SSE
|
||||
__asm__
|
||||
(
|
||||
"movups (%[buf]), %%xmm0 \n"
|
||||
@@ -110,7 +109,9 @@ namespace crypto
|
||||
|
||||
operator uint8_t * () { return m_Buf; };
|
||||
operator const uint8_t * () const { return m_Buf; };
|
||||
|
||||
ChipherBlock * GetChipherBlock () { return (ChipherBlock *)m_Buf; };
|
||||
const ChipherBlock * GetChipherBlock () const { return (const ChipherBlock *)m_Buf; };
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_UnalignedBuffer[sz + 15]; // up to 15 bytes alignment
|
||||
@@ -198,10 +199,10 @@ namespace crypto
|
||||
{
|
||||
public:
|
||||
|
||||
CBCEncryption () { memset (m_LastBlock.buf, 0, 16); };
|
||||
CBCEncryption () { memset ((uint8_t *)m_LastBlock, 0, 16); };
|
||||
|
||||
void SetKey (const AESKey& key) { m_ECBEncryption.SetKey (key); }; // 32 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy (m_LastBlock.buf, iv, 16); }; // 16 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_LastBlock, iv, 16); }; // 16 bytes
|
||||
|
||||
void Encrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||
void Encrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||
@@ -209,7 +210,7 @@ namespace crypto
|
||||
|
||||
private:
|
||||
|
||||
ChipherBlock m_LastBlock;
|
||||
AESAlignedBuffer<16> m_LastBlock;
|
||||
|
||||
ECBEncryption m_ECBEncryption;
|
||||
};
|
||||
@@ -218,10 +219,10 @@ namespace crypto
|
||||
{
|
||||
public:
|
||||
|
||||
CBCDecryption () { memset (m_IV.buf, 0, 16); };
|
||||
CBCDecryption () { memset ((uint8_t *)m_IV, 0, 16); };
|
||||
|
||||
void SetKey (const AESKey& key) { m_ECBDecryption.SetKey (key); }; // 32 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy (m_IV.buf, iv, 16); }; // 16 bytes
|
||||
void SetIV (const uint8_t * iv) { memcpy ((uint8_t *)m_IV, iv, 16); }; // 16 bytes
|
||||
|
||||
void Decrypt (int numBlocks, const ChipherBlock * in, ChipherBlock * out);
|
||||
void Decrypt (const uint8_t * in, std::size_t len, uint8_t * out);
|
||||
@@ -229,7 +230,7 @@ namespace crypto
|
||||
|
||||
private:
|
||||
|
||||
ChipherBlock m_IV;
|
||||
AESAlignedBuffer<16> m_IV;
|
||||
ECBDecryption m_ECBDecryption;
|
||||
};
|
||||
|
||||
@@ -282,4 +283,52 @@ namespace crypto
|
||||
}
|
||||
}
|
||||
|
||||
// take care about openssl version
|
||||
#include <openssl/opensslv.h>
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x010100000) || defined(LIBRESSL_VERSION_NUMBER) // 1.1.0 or LibreSSL
|
||||
// define getters and setters introduced in 1.1.0
|
||||
inline int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
||||
{ d->p = p; d->q = q; d->g = g; return 1; }
|
||||
inline int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
|
||||
{ d->pub_key = pub_key; d->priv_key = priv_key; return 1; }
|
||||
inline void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
||||
{ *pub_key = d->pub_key; *priv_key = d->priv_key; }
|
||||
inline int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
||||
{ sig->r = r; sig->s = s; return 1; }
|
||||
inline void DSA_SIG_get0(const DSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
||||
{ *pr = sig->r; *ps = sig->s; }
|
||||
|
||||
inline int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
|
||||
{
|
||||
if (sig->r) BN_free (sig->r);
|
||||
if (sig->s) BN_free (sig->s);
|
||||
sig->r = r; sig->s = s; return 1;
|
||||
}
|
||||
inline void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
|
||||
{ *pr = sig->r; *ps = sig->s; }
|
||||
|
||||
inline int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
|
||||
{ r->n = n; r->e = e; r->d = d; return 1; }
|
||||
inline void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
|
||||
{ *n = r->n; *e = r->e; *d = r->d; }
|
||||
|
||||
inline int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
||||
{ dh->p = p; dh->q = q; dh->g = g; return 1; }
|
||||
inline int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
|
||||
{
|
||||
if (dh->pub_key) BN_free (dh->pub_key);
|
||||
if (dh->priv_key) BN_free (dh->priv_key);
|
||||
dh->pub_key = pub_key; dh->priv_key = priv_key; return 1;
|
||||
}
|
||||
inline void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
||||
{ *pub_key = dh->pub_key; *priv_key = dh->priv_key; }
|
||||
|
||||
inline RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
|
||||
{ return pkey->pkey.rsa; }
|
||||
|
||||
// ssl
|
||||
#define TLS_method TLSv1_method
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
73
Daemon.cpp
73
Daemon.cpp
@@ -25,6 +25,9 @@
|
||||
#include "UPnP.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "Event.h"
|
||||
#include "Websocket.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
@@ -38,6 +41,9 @@ namespace i2p
|
||||
std::unique_ptr<i2p::http::HTTPServer> httpServer;
|
||||
std::unique_ptr<i2p::client::I2PControlService> m_I2PControlService;
|
||||
std::unique_ptr<i2p::transport::UPnP> UPnP;
|
||||
#ifdef WITH_EVENTS
|
||||
std::unique_ptr<i2p::event::WebsocketServer> m_WebsocketServer;
|
||||
#endif
|
||||
};
|
||||
|
||||
Daemon_Singleton::Daemon_Singleton() : isDaemon(false), running(true), d(*new Daemon_Singleton_Private()) {}
|
||||
@@ -107,7 +113,6 @@ namespace i2p
|
||||
} else {
|
||||
// use stdout -- default
|
||||
}
|
||||
i2p::log::Logger().Ready();
|
||||
|
||||
LogPrint(eLogInfo, "i2pd v", VERSION, " starting");
|
||||
LogPrint(eLogDebug, "FS: main config file: ", config);
|
||||
@@ -115,6 +120,9 @@ namespace i2p
|
||||
|
||||
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
|
||||
i2p::crypto::InitCrypto (precomputation);
|
||||
|
||||
int netID; i2p::config::GetOption("netid", netID);
|
||||
i2p::context.SetNetID (netID);
|
||||
i2p::context.Init ();
|
||||
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
@@ -191,12 +199,40 @@ namespace i2p
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: explicit trust enabled");
|
||||
std::string fam; i2p::config::GetOption("trust.family", fam);
|
||||
std::string routers; i2p::config::GetOption("trust.routers", routers);
|
||||
bool restricted = false;
|
||||
if (fam.length() > 0)
|
||||
{
|
||||
LogPrint(eLogInfo, "Daemon: setting restricted routes to use family ", fam);
|
||||
i2p::transport::transports.RestrictRoutes({fam});
|
||||
} else
|
||||
LogPrint(eLogError, "Daemon: no family specified for restricted routes");
|
||||
std::set<std::string> fams;
|
||||
size_t pos = 0, comma;
|
||||
do
|
||||
{
|
||||
comma = fam.find (',', pos);
|
||||
fams.insert (fam.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
|
||||
pos = comma + 1;
|
||||
}
|
||||
while (comma != std::string::npos);
|
||||
i2p::transport::transports.RestrictRoutesToFamilies(fams);
|
||||
restricted = fams.size() > 0;
|
||||
}
|
||||
if (routers.length() > 0) {
|
||||
std::set<i2p::data::IdentHash> idents;
|
||||
size_t pos = 0, comma;
|
||||
do
|
||||
{
|
||||
comma = routers.find (',', pos);
|
||||
i2p::data::IdentHash ident;
|
||||
ident.FromBase64 (routers.substr (pos, comma != std::string::npos ? comma - pos : std::string::npos));
|
||||
idents.insert (ident);
|
||||
pos = comma + 1;
|
||||
}
|
||||
while (comma != std::string::npos);
|
||||
LogPrint(eLogInfo, "Daemon: setting restricted routes to use ", idents.size(), " trusted routesrs");
|
||||
i2p::transport::transports.RestrictRoutesToRouters(idents);
|
||||
restricted = idents.size() > 0;
|
||||
}
|
||||
if(!restricted)
|
||||
LogPrint(eLogError, "Daemon: no trusted routers of families specififed");
|
||||
}
|
||||
bool hidden; i2p::config::GetOption("trust.hidden", hidden);
|
||||
if (hidden)
|
||||
@@ -209,6 +245,7 @@ namespace i2p
|
||||
|
||||
bool Daemon_Singleton::start()
|
||||
{
|
||||
i2p::log::Logger().Start();
|
||||
LogPrint(eLogInfo, "Daemon: starting NetDB");
|
||||
i2p::data::netdb.Start();
|
||||
|
||||
@@ -259,12 +296,26 @@ namespace i2p
|
||||
d.m_I2PControlService = std::unique_ptr<i2p::client::I2PControlService>(new i2p::client::I2PControlService (i2pcpAddr, i2pcpPort));
|
||||
d.m_I2PControlService->Start ();
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
|
||||
bool websocket; i2p::config::GetOption("websockets.enabled", websocket);
|
||||
if(websocket) {
|
||||
std::string websocketAddr; i2p::config::GetOption("websockets.address", websocketAddr);
|
||||
uint16_t websocketPort; i2p::config::GetOption("websockets.port", websocketPort);
|
||||
LogPrint(eLogInfo, "Daemon: starting Websocket server at ", websocketAddr, ":", websocketPort);
|
||||
d.m_WebsocketServer = std::unique_ptr<i2p::event::WebsocketServer>(new i2p::event::WebsocketServer (websocketAddr, websocketPort));
|
||||
d.m_WebsocketServer->Start();
|
||||
i2p::event::core.SetListener(d.m_WebsocketServer->ToListener());
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon_Singleton::stop()
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
i2p::event::core.SetListener(nullptr);
|
||||
#endif
|
||||
LogPrint(eLogInfo, "Daemon: shutting down");
|
||||
LogPrint(eLogInfo, "Daemon: stopping Client");
|
||||
i2p::client::context.Stop();
|
||||
@@ -290,10 +341,18 @@ namespace i2p
|
||||
LogPrint(eLogInfo, "Daemon: stopping I2PControl");
|
||||
d.m_I2PControlService->Stop ();
|
||||
d.m_I2PControlService = nullptr;
|
||||
}
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
if (d.m_WebsocketServer) {
|
||||
LogPrint(eLogInfo, "Daemon: stopping Websocket server");
|
||||
d.m_WebsocketServer->Stop();
|
||||
d.m_WebsocketServer = nullptr;
|
||||
}
|
||||
#endif
|
||||
i2p::crypto::TerminateCrypto ();
|
||||
i2p::log::Logger().Stop();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
Daemon.h
2
Daemon.h
@@ -97,7 +97,7 @@ namespace i2p
|
||||
|
||||
public:
|
||||
|
||||
int gracefullShutdownInterval; // in seconds
|
||||
int gracefulShutdownInterval; // in seconds
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include "Config.h"
|
||||
#include "FS.h"
|
||||
@@ -20,20 +21,23 @@ void handle_signal(int sig)
|
||||
switch (sig)
|
||||
{
|
||||
case SIGHUP:
|
||||
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening log...");
|
||||
i2p::log::Logger().Reopen ();
|
||||
LogPrint(eLogInfo, "Daemon: Got SIGHUP, reopening tunnel configuration...");
|
||||
i2p::client::context.ReloadConfig();
|
||||
break;
|
||||
case SIGUSR1:
|
||||
LogPrint(eLogInfo, "Daemon: Got SIGUSR1, reopening logs...");
|
||||
i2p::log::Logger().Reopen ();
|
||||
break;
|
||||
case SIGINT:
|
||||
if (i2p::context.AcceptsTunnels () && !Daemon.gracefullShutdownInterval)
|
||||
{
|
||||
if (i2p::context.AcceptsTunnels () && !Daemon.gracefulShutdownInterval)
|
||||
{
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
Daemon.gracefullShutdownInterval = 10*60; // 10 minutes
|
||||
LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefullShutdownInterval, " seconds");
|
||||
}
|
||||
Daemon.gracefulShutdownInterval = 10*60; // 10 minutes
|
||||
LogPrint(eLogInfo, "Graceful shutdown after ", Daemon.gracefulShutdownInterval, " seconds");
|
||||
}
|
||||
else
|
||||
Daemon.running = 0;
|
||||
break;
|
||||
Daemon.running = 0;
|
||||
break;
|
||||
case SIGABRT:
|
||||
case SIGTERM:
|
||||
Daemon.running = 0; // Exit loop
|
||||
@@ -76,9 +80,45 @@ namespace i2p
|
||||
}
|
||||
|
||||
// point std{in,out,err} descriptors to /dev/null
|
||||
stdin = freopen("/dev/null", "r", stdin);
|
||||
stdout = freopen("/dev/null", "w", stdout);
|
||||
stderr = freopen("/dev/null", "w", stderr);
|
||||
freopen("/dev/null", "r", stdin);
|
||||
freopen("/dev/null", "w", stdout);
|
||||
freopen("/dev/null", "w", stderr);
|
||||
}
|
||||
|
||||
// set proc limits
|
||||
struct rlimit limit;
|
||||
uint16_t nfiles; i2p::config::GetOption("limits.openfiles", nfiles);
|
||||
getrlimit(RLIMIT_NOFILE, &limit);
|
||||
if (nfiles == 0) {
|
||||
LogPrint(eLogInfo, "Daemon: using system limit in ", limit.rlim_cur, " max open files");
|
||||
} else if (nfiles <= limit.rlim_max) {
|
||||
limit.rlim_cur = nfiles;
|
||||
if (setrlimit(RLIMIT_NOFILE, &limit) == 0) {
|
||||
LogPrint(eLogInfo, "Daemon: set max number of open files to ",
|
||||
nfiles, " (system limit is ", limit.rlim_max, ")");
|
||||
} else {
|
||||
LogPrint(eLogError, "Daemon: can't set max number of open files: ", strerror(errno));
|
||||
}
|
||||
} else {
|
||||
LogPrint(eLogError, "Daemon: limits.openfiles exceeds system limit: ", limit.rlim_max);
|
||||
}
|
||||
uint32_t cfsize; i2p::config::GetOption("limits.coresize", cfsize);
|
||||
if (cfsize) // core file size set
|
||||
{
|
||||
cfsize *= 1024;
|
||||
getrlimit(RLIMIT_CORE, &limit);
|
||||
if (cfsize <= limit.rlim_max) {
|
||||
limit.rlim_cur = cfsize;
|
||||
if (setrlimit(RLIMIT_CORE, &limit) != 0) {
|
||||
LogPrint(eLogError, "Daemon: can't set max size of coredump: ", strerror(errno));
|
||||
} else if (cfsize == 0) {
|
||||
LogPrint(eLogInfo, "Daemon: coredumps disabled");
|
||||
} else {
|
||||
LogPrint(eLogInfo, "Daemon: set max size of core files to ", cfsize / 1024, "Kb");
|
||||
}
|
||||
} else {
|
||||
LogPrint(eLogError, "Daemon: limits.coresize exceeds system limit: ", limit.rlim_max);
|
||||
}
|
||||
}
|
||||
|
||||
// Pidfile
|
||||
@@ -108,7 +148,7 @@ namespace i2p
|
||||
return false;
|
||||
}
|
||||
}
|
||||
gracefullShutdownInterval = 0; // not specified
|
||||
gracefulShutdownInterval = 0; // not specified
|
||||
|
||||
// Signal handler
|
||||
struct sigaction sa;
|
||||
@@ -116,6 +156,7 @@ namespace i2p
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sigaction(SIGHUP, &sa, 0);
|
||||
sigaction(SIGUSR1, &sa, 0);
|
||||
sigaction(SIGABRT, &sa, 0);
|
||||
sigaction(SIGTERM, &sa, 0);
|
||||
sigaction(SIGINT, &sa, 0);
|
||||
@@ -127,7 +168,7 @@ namespace i2p
|
||||
{
|
||||
i2p::fs::Remove(pidfile);
|
||||
|
||||
return Daemon_Singleton::stop();
|
||||
return Daemon_Singleton::stop();
|
||||
}
|
||||
|
||||
void DaemonLinux::run ()
|
||||
@@ -135,15 +176,15 @@ namespace i2p
|
||||
while (running)
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
if (gracefullShutdownInterval)
|
||||
if (gracefulShutdownInterval)
|
||||
{
|
||||
gracefullShutdownInterval--; // - 1 second
|
||||
if (gracefullShutdownInterval <= 0)
|
||||
{
|
||||
gracefulShutdownInterval--; // - 1 second
|
||||
if (gracefulShutdownInterval <= 0)
|
||||
{
|
||||
LogPrint(eLogInfo, "Graceful shutdown");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
325
Datagram.cpp
325
Datagram.cpp
@@ -12,74 +12,45 @@ namespace i2p
|
||||
namespace datagram
|
||||
{
|
||||
DatagramDestination::DatagramDestination (std::shared_ptr<i2p::client::ClientDestination> owner):
|
||||
m_Owner (owner), m_Receiver (nullptr)
|
||||
m_Owner (owner.get()),
|
||||
m_Receiver (nullptr)
|
||||
{
|
||||
m_Identity.FromBase64 (owner->GetIdentity()->ToBase64());
|
||||
}
|
||||
|
||||
DatagramDestination::~DatagramDestination ()
|
||||
{
|
||||
m_Sessions.clear();
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
void DatagramDestination::SendDatagramTo(const uint8_t * payload, size_t len, const i2p::data::IdentHash & identity, uint16_t fromPort, uint16_t toPort)
|
||||
{
|
||||
auto owner = m_Owner;
|
||||
std::vector<uint8_t> v(MAX_DATAGRAM_SIZE);
|
||||
uint8_t * buf = v.data();
|
||||
auto identityLen = m_Identity.ToBuffer (buf, MAX_DATAGRAM_SIZE);
|
||||
uint8_t * signature = buf + identityLen;
|
||||
auto signatureLen = m_Owner->GetIdentity ()->GetSignatureLen ();
|
||||
auto signatureLen = m_Identity.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)
|
||||
if (m_Identity.GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
{
|
||||
uint8_t hash[32];
|
||||
SHA256(buf1, len, hash);
|
||||
m_Owner->Sign (hash, 32, signature);
|
||||
owner->Sign (hash, 32, signature);
|
||||
}
|
||||
else
|
||||
m_Owner->Sign (buf1, len, signature);
|
||||
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));
|
||||
auto msg = CreateDataMessage (buf, len + headerLen, fromPort, toPort);
|
||||
auto session = ObtainSession(identity);
|
||||
session->SendMsg(msg);
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> remote, std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
if (remote)
|
||||
SendMsg (msg, remote);
|
||||
}
|
||||
|
||||
void DatagramDestination::SendMsg (std::shared_ptr<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 = rand () % leases.size ();
|
||||
auto garlic = m_Owner->WrapMessage (remote, msg, true);
|
||||
msgs.push_back (i2p::tunnel::TunnelMessageBlock
|
||||
{
|
||||
i2p::tunnel::eDeliveryTypeTunnel,
|
||||
leases[i]->tunnelGateway, leases[i]->tunnelID,
|
||||
garlic
|
||||
});
|
||||
outboundTunnel->SendTunnelDataMsg (msgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outboundTunnel)
|
||||
LogPrint (eLogWarning, "Failed to send datagram. All leases expired");
|
||||
else
|
||||
LogPrint (eLogWarning, "Failed to send datagram. No outbound tunnels");
|
||||
}
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
void DatagramDestination::HandleDatagram (uint16_t fromPort, uint16_t toPort,uint8_t * const &buf, size_t len)
|
||||
{
|
||||
i2p::data::IdentityEx identity;
|
||||
size_t identityLen = identity.FromBuffer (buf, len);
|
||||
@@ -98,25 +69,38 @@ namespace datagram
|
||||
|
||||
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);
|
||||
auto h = identity.GetIdentHash();
|
||||
auto session = ObtainSession(h);
|
||||
session->Ack();
|
||||
auto r = FindReceiver(toPort);
|
||||
if(r)
|
||||
r(identity, fromPort, toPort, buf + headerLen, len -headerLen);
|
||||
else
|
||||
LogPrint (eLogWarning, "Receiver for datagram is not set");
|
||||
LogPrint (eLogWarning, "DatagramDestination: no receiver for port ", toPort);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram signature verification failed");
|
||||
}
|
||||
|
||||
DatagramDestination::Receiver DatagramDestination::FindReceiver(uint16_t port)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_ReceiversMutex);
|
||||
Receiver r = m_Receiver;
|
||||
auto itr = m_ReceiversByPorts.find(port);
|
||||
if (itr != m_ReceiversByPorts.end())
|
||||
r = itr->second;
|
||||
return r;
|
||||
}
|
||||
|
||||
void DatagramDestination::HandleDataMessagePayload (uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
{
|
||||
// unzip it
|
||||
uint8_t uncompressed[MAX_DATAGRAM_SIZE];
|
||||
size_t uncompressedLen = m_Inflator.Inflate (buf, len, uncompressed, MAX_DATAGRAM_SIZE);
|
||||
if (uncompressedLen)
|
||||
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
HandleDatagram (fromPort, toPort, uncompressed, uncompressedLen);
|
||||
else
|
||||
LogPrint (eLogWarning, "Datagram: decompression failed");
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> DatagramDestination::CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort)
|
||||
@@ -137,7 +121,236 @@ namespace datagram
|
||||
else
|
||||
msg = nullptr;
|
||||
return msg;
|
||||
}
|
||||
}
|
||||
|
||||
void DatagramDestination::CleanUp ()
|
||||
{
|
||||
if (m_Sessions.empty ()) return;
|
||||
auto now = i2p::util::GetMillisecondsSinceEpoch();
|
||||
LogPrint(eLogDebug, "DatagramDestination: clean up sessions");
|
||||
std::unique_lock<std::mutex> lock(m_SessionsMutex);
|
||||
// for each session ...
|
||||
for (auto it = m_Sessions.begin (); it != m_Sessions.end (); )
|
||||
{
|
||||
// check if expired
|
||||
if (now - it->second->LastActivity() >= DATAGRAM_SESSION_MAX_IDLE)
|
||||
{
|
||||
LogPrint(eLogInfo, "DatagramDestination: expiring idle session with ", it->first.ToBase32());
|
||||
it->second->Stop ();
|
||||
it = m_Sessions.erase (it); // we are expired
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<DatagramSession> DatagramDestination::ObtainSession(const i2p::data::IdentHash & identity)
|
||||
{
|
||||
std::shared_ptr<DatagramSession> session = nullptr;
|
||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||
auto itr = m_Sessions.find(identity);
|
||||
if (itr == m_Sessions.end()) {
|
||||
// not found, create new session
|
||||
session = std::make_shared<DatagramSession>(m_Owner, identity);
|
||||
session->Start ();
|
||||
m_Sessions[identity] = session;
|
||||
} else {
|
||||
session = itr->second;
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
std::shared_ptr<DatagramSession::Info> DatagramDestination::GetInfoForRemote(const i2p::data::IdentHash & remote)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||
for ( auto & item : m_Sessions)
|
||||
{
|
||||
if(item.first == remote) return std::make_shared<DatagramSession::Info>(item.second->GetSessionInfo());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DatagramSession::DatagramSession(i2p::client::ClientDestination * localDestination,
|
||||
const i2p::data::IdentHash & remoteIdent) :
|
||||
m_LocalDestination(localDestination),
|
||||
m_RemoteIdent(remoteIdent),
|
||||
m_SendQueueTimer(localDestination->GetService()),
|
||||
m_RequestingLS(false)
|
||||
{
|
||||
}
|
||||
|
||||
void DatagramSession::Start ()
|
||||
{
|
||||
m_LastUse = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
ScheduleFlushSendQueue();
|
||||
}
|
||||
|
||||
void DatagramSession::Stop ()
|
||||
{
|
||||
m_SendQueueTimer.cancel ();
|
||||
}
|
||||
|
||||
void DatagramSession::SendMsg(std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
// we used this session
|
||||
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
|
||||
// schedule send
|
||||
auto self = shared_from_this();
|
||||
m_LocalDestination->GetService().post(std::bind(&DatagramSession::HandleSend, self, msg));
|
||||
}
|
||||
|
||||
DatagramSession::Info DatagramSession::GetSessionInfo() const
|
||||
{
|
||||
if(!m_RoutingSession)
|
||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
|
||||
|
||||
auto routingPath = m_RoutingSession->GetSharedRoutingPath();
|
||||
if (!routingPath)
|
||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
|
||||
auto lease = routingPath->remoteLease;
|
||||
auto tunnel = routingPath->outboundTunnel;
|
||||
if(lease)
|
||||
{
|
||||
if(tunnel)
|
||||
return DatagramSession::Info(lease->tunnelGateway, tunnel->GetEndpointIdentHash(), m_LastUse);
|
||||
else
|
||||
return DatagramSession::Info(lease->tunnelGateway, nullptr, m_LastUse);
|
||||
}
|
||||
else if(tunnel)
|
||||
return DatagramSession::Info(nullptr, tunnel->GetEndpointIdentHash(), m_LastUse);
|
||||
else
|
||||
return DatagramSession::Info(nullptr, nullptr, m_LastUse);
|
||||
}
|
||||
|
||||
void DatagramSession::Ack()
|
||||
{
|
||||
m_LastUse = i2p::util::GetMillisecondsSinceEpoch();
|
||||
auto path = GetSharedRoutingPath();
|
||||
if(path)
|
||||
path->updateTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> DatagramSession::GetSharedRoutingPath ()
|
||||
{
|
||||
if(!m_RoutingSession) {
|
||||
if(!m_RemoteLeaseSet) {
|
||||
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
|
||||
}
|
||||
if(!m_RemoteLeaseSet) {
|
||||
// no remote lease set
|
||||
if(!m_RequestingLS) {
|
||||
m_RequestingLS = true;
|
||||
m_LocalDestination->RequestDestination(m_RemoteIdent, std::bind(&DatagramSession::HandleLeaseSetUpdated, this, std::placeholders::_1));
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
|
||||
}
|
||||
auto path = m_RoutingSession->GetSharedRoutingPath();
|
||||
if(path) {
|
||||
if (m_CurrentOutboundTunnel && !m_CurrentOutboundTunnel->IsEstablished()) {
|
||||
// bad outbound tunnel, switch outbound tunnel
|
||||
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel);
|
||||
path->outboundTunnel = m_CurrentOutboundTunnel;
|
||||
}
|
||||
if(m_CurrentRemoteLease && m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
|
||||
// bad lease, switch to next one
|
||||
if(m_RemoteLeaseSet) {
|
||||
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&](const i2p::data::Lease& l) -> bool {
|
||||
return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway;
|
||||
});
|
||||
auto sz = ls.size();
|
||||
if (sz) {
|
||||
auto idx = rand() % sz;
|
||||
m_CurrentRemoteLease = ls[idx];
|
||||
}
|
||||
} else {
|
||||
// no remote lease set?
|
||||
LogPrint(eLogWarning, "DatagramSession: no cached remote lease set for ", m_RemoteIdent.ToBase32());
|
||||
}
|
||||
path->remoteLease = m_CurrentRemoteLease;
|
||||
}
|
||||
} else {
|
||||
// no current path, make one
|
||||
path = std::make_shared<i2p::garlic::GarlicRoutingPath>();
|
||||
// switch outbound tunnel if bad
|
||||
if(m_CurrentOutboundTunnel == nullptr || ! m_CurrentOutboundTunnel->IsEstablished()) {
|
||||
m_CurrentOutboundTunnel = m_LocalDestination->GetTunnelPool()->GetNextOutboundTunnel(m_CurrentOutboundTunnel);
|
||||
}
|
||||
// switch lease if bad
|
||||
if(m_CurrentRemoteLease == nullptr || m_CurrentRemoteLease->ExpiresWithin(DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW)) {
|
||||
if(!m_RemoteLeaseSet) {
|
||||
m_RemoteLeaseSet = m_LocalDestination->FindLeaseSet(m_RemoteIdent);
|
||||
}
|
||||
if(m_RemoteLeaseSet) {
|
||||
// pick random next good lease
|
||||
auto ls = m_RemoteLeaseSet->GetNonExpiredLeasesExcluding([&] (const i2p::data::Lease & l) -> bool {
|
||||
if(m_CurrentRemoteLease)
|
||||
return l.tunnelGateway == m_CurrentRemoteLease->tunnelGateway;
|
||||
return false;
|
||||
});
|
||||
auto sz = ls.size();
|
||||
if(sz) {
|
||||
auto idx = rand() % sz;
|
||||
m_CurrentRemoteLease = ls[idx];
|
||||
}
|
||||
} else {
|
||||
// no remote lease set currently, bail
|
||||
LogPrint(eLogWarning, "DatagramSession: no remote lease set found for ", m_RemoteIdent.ToBase32());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
path->outboundTunnel = m_CurrentOutboundTunnel;
|
||||
path->remoteLease = m_CurrentRemoteLease;
|
||||
m_RoutingSession->SetSharedRoutingPath(path);
|
||||
}
|
||||
return path;
|
||||
|
||||
}
|
||||
|
||||
void DatagramSession::HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
m_RequestingLS = false;
|
||||
if(!ls) return;
|
||||
// only update lease set if found and newer than previous lease set
|
||||
uint64_t oldExpire = 0;
|
||||
if(m_RemoteLeaseSet) oldExpire = m_RemoteLeaseSet->GetExpirationTime();
|
||||
if(ls && ls->GetExpirationTime() > oldExpire) m_RemoteLeaseSet = ls;
|
||||
}
|
||||
|
||||
void DatagramSession::HandleSend(std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
m_SendQueue.push_back(msg);
|
||||
// flush queue right away if full
|
||||
if(m_SendQueue.size() >= DATAGRAM_SEND_QUEUE_MAX_SIZE) FlushSendQueue();
|
||||
}
|
||||
|
||||
void DatagramSession::FlushSendQueue ()
|
||||
{
|
||||
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> send;
|
||||
auto routingPath = GetSharedRoutingPath();
|
||||
// if we don't have a routing path we will drop all queued messages
|
||||
if(routingPath && routingPath->outboundTunnel && routingPath->remoteLease)
|
||||
{
|
||||
for (const auto & msg : m_SendQueue)
|
||||
{
|
||||
auto m = m_RoutingSession->WrapSingleMessage(msg);
|
||||
send.push_back(i2p::tunnel::TunnelMessageBlock{i2p::tunnel::eDeliveryTypeTunnel,routingPath->remoteLease->tunnelGateway, routingPath->remoteLease->tunnelID, m});
|
||||
}
|
||||
routingPath->outboundTunnel->SendTunnelDataMsg(send);
|
||||
}
|
||||
m_SendQueue.clear();
|
||||
ScheduleFlushSendQueue();
|
||||
}
|
||||
|
||||
void DatagramSession::ScheduleFlushSendQueue()
|
||||
{
|
||||
boost::posix_time::milliseconds dlt(100);
|
||||
m_SendQueueTimer.expires_from_now(dlt);
|
||||
auto self = shared_from_this();
|
||||
m_SendQueueTimer.async_wait([self](const boost::system::error_code & ec) { if(ec) return; self->FlushSendQueue(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
111
Datagram.h
111
Datagram.h
@@ -9,6 +9,7 @@
|
||||
#include "Identity.h"
|
||||
#include "LeaseSet.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Garlic.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -18,37 +19,125 @@ namespace client
|
||||
}
|
||||
namespace datagram
|
||||
{
|
||||
const size_t MAX_DATAGRAM_SIZE = 32768;
|
||||
// milliseconds for max session idle time
|
||||
const uint64_t DATAGRAM_SESSION_MAX_IDLE = 10 * 60 * 1000;
|
||||
// milliseconds for how long we try sticking to a dead routing path before trying to switch
|
||||
const uint64_t DATAGRAM_SESSION_PATH_TIMEOUT = 10 * 1000;
|
||||
// milliseconds interval a routing path is used before switching
|
||||
const uint64_t DATAGRAM_SESSION_PATH_SWITCH_INTERVAL = 20 * 60 * 1000;
|
||||
// milliseconds before lease expire should we try switching leases
|
||||
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_WINDOW = 10 * 1000;
|
||||
// milliseconds fudge factor for leases handover
|
||||
const uint64_t DATAGRAM_SESSION_LEASE_HANDOVER_FUDGE = 1000;
|
||||
// milliseconds minimum time between path switches
|
||||
const uint64_t DATAGRAM_SESSION_PATH_MIN_LIFETIME = 5 * 1000;
|
||||
// max 64 messages buffered in send queue for each datagram session
|
||||
const size_t DATAGRAM_SEND_QUEUE_MAX_SIZE = 64;
|
||||
|
||||
class DatagramSession : public std::enable_shared_from_this<DatagramSession>
|
||||
{
|
||||
public:
|
||||
DatagramSession(i2p::client::ClientDestination * localDestination, const i2p::data::IdentHash & remoteIdent);
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
|
||||
/** @brief ack the garlic routing path */
|
||||
void Ack();
|
||||
|
||||
/** send an i2np message to remote endpoint for this session */
|
||||
void SendMsg(std::shared_ptr<I2NPMessage> msg);
|
||||
/** get the last time in milliseconds for when we used this datagram session */
|
||||
uint64_t LastActivity() const { return m_LastUse; }
|
||||
|
||||
struct Info
|
||||
{
|
||||
std::shared_ptr<const i2p::data::IdentHash> IBGW;
|
||||
std::shared_ptr<const i2p::data::IdentHash> OBEP;
|
||||
const uint64_t activity;
|
||||
|
||||
Info() : IBGW(nullptr), OBEP(nullptr), activity(0) {}
|
||||
Info(const uint8_t * ibgw, const uint8_t * obep, const uint64_t a) :
|
||||
activity(a) {
|
||||
if(ibgw) IBGW = std::make_shared<i2p::data::IdentHash>(ibgw);
|
||||
else IBGW = nullptr;
|
||||
if(obep) OBEP = std::make_shared<i2p::data::IdentHash>(obep);
|
||||
else OBEP = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
Info GetSessionInfo() const;
|
||||
|
||||
private:
|
||||
|
||||
void FlushSendQueue();
|
||||
void ScheduleFlushSendQueue();
|
||||
|
||||
void HandleSend(std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingPath> GetSharedRoutingPath();
|
||||
|
||||
void HandleLeaseSetUpdated(std::shared_ptr<i2p::data::LeaseSet> ls);
|
||||
|
||||
private:
|
||||
i2p::client::ClientDestination * m_LocalDestination;
|
||||
i2p::data::IdentHash m_RemoteIdent;
|
||||
std::shared_ptr<const i2p::data::LeaseSet> m_RemoteLeaseSet;
|
||||
std::shared_ptr<i2p::garlic::GarlicRoutingSession> m_RoutingSession;
|
||||
std::shared_ptr<const i2p::data::Lease> m_CurrentRemoteLease;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> m_CurrentOutboundTunnel;
|
||||
boost::asio::deadline_timer m_SendQueueTimer;
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_SendQueue;
|
||||
uint64_t m_LastUse;
|
||||
bool m_RequestingLS;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<DatagramSession> DatagramSession_ptr;
|
||||
|
||||
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 (std::shared_ptr<i2p::client::ClientDestination> owner);
|
||||
~DatagramDestination ();
|
||||
~DatagramDestination ();
|
||||
|
||||
void SendDatagramTo (const uint8_t * payload, size_t len, const i2p::data::IdentHash& ident, uint16_t fromPort = 0, uint16_t toPort = 0);
|
||||
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); };
|
||||
void SetReceiver (const Receiver& receiver, uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts[port] = receiver; };
|
||||
void ResetReceiver (uint16_t port) { std::lock_guard<std::mutex> lock(m_ReceiversMutex); m_ReceiversByPorts.erase (port); };
|
||||
|
||||
std::shared_ptr<DatagramSession::Info> GetInfoForRemote(const i2p::data::IdentHash & remote);
|
||||
|
||||
// clean up stale sessions
|
||||
void CleanUp ();
|
||||
|
||||
private:
|
||||
|
||||
void HandleLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
std::shared_ptr<DatagramSession> ObtainSession(const i2p::data::IdentHash & ident);
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t fromPort, uint16_t toPort);
|
||||
void SendMsg (std::shared_ptr<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);
|
||||
|
||||
void HandleDatagram (uint16_t fromPort, uint16_t toPort, uint8_t *const& buf, size_t len);
|
||||
|
||||
/** find a receiver by port, if none by port is found try default receiever, otherwise returns nullptr */
|
||||
Receiver FindReceiver(uint16_t port);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
|
||||
i2p::client::ClientDestination * m_Owner;
|
||||
i2p::data::IdentityEx m_Identity;
|
||||
Receiver m_Receiver; // default
|
||||
std::mutex m_SessionsMutex;
|
||||
std::map<i2p::data::IdentHash, DatagramSession_ptr > m_Sessions;
|
||||
std::mutex m_ReceiversMutex;
|
||||
std::map<uint16_t, Receiver> m_ReceiversByPorts;
|
||||
|
||||
i2p::data::GzipInflator m_Inflator;
|
||||
|
||||
402
Destination.cpp
402
Destination.cpp
@@ -1,6 +1,5 @@
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Crypto.h"
|
||||
#include "Log.h"
|
||||
#include "FS.h"
|
||||
@@ -14,100 +13,82 @@ namespace i2p
|
||||
namespace client
|
||||
{
|
||||
LeaseSetDestination::LeaseSetDestination (bool isPublic, const std::map<std::string, std::string> * params):
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_IsPublic (isPublic),
|
||||
m_PublishReplyToken (0), m_PublishConfirmationTimer (m_Service),
|
||||
m_PublishVerificationTimer (m_Service), m_CleanupTimer (m_Service)
|
||||
m_IsRunning (false), m_Thread (nullptr), m_IsPublic (isPublic),
|
||||
m_PublishReplyToken (0), m_LastSubmissionTime (0), m_PublishConfirmationTimer (m_Service),
|
||||
m_PublishVerificationTimer (m_Service), m_PublishDelayTimer (m_Service), m_CleanupTimer (m_Service)
|
||||
{
|
||||
int inboundTunnelLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
|
||||
int outboundTunnelLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
||||
int inboundTunnelsQuantity = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
||||
int outboundTunnelsQuantity = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
|
||||
int inLen = DEFAULT_INBOUND_TUNNEL_LENGTH;
|
||||
int inQty = DEFAULT_INBOUND_TUNNELS_QUANTITY;
|
||||
int outLen = DEFAULT_OUTBOUND_TUNNEL_LENGTH;
|
||||
int outQty = DEFAULT_OUTBOUND_TUNNELS_QUANTITY;
|
||||
int numTags = DEFAULT_TAGS_TO_SEND;
|
||||
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 = i2p::util::lexical_cast<int>(it->second, inboundTunnelLen);
|
||||
if (len >= 0)
|
||||
try {
|
||||
if (params) {
|
||||
auto it = params->find (I2CP_PARAM_INBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
inLen = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
outLen = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
inQty = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
outQty = std::stoi(it->second);
|
||||
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
|
||||
if (it != params->end ())
|
||||
numTags = std::stoi(it->second);
|
||||
LogPrint (eLogInfo, "Destination: parameters for tunnel set to: ", inQty, " inbound (", inLen, " hops), ", outQty, " outbound (", outLen, " hops), ", numTags, " tags");
|
||||
it = params->find (I2CP_PARAM_EXPLICIT_PEERS);
|
||||
if (it != params->end ())
|
||||
{
|
||||
inboundTunnelLen = len;
|
||||
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, "Destination: Added to explicit peers list: ", b64);
|
||||
}
|
||||
}
|
||||
LogPrint (eLogInfo, "Destination: Inbound tunnel length set to ", inboundTunnelLen);
|
||||
}
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNEL_LENGTH);
|
||||
if (it != params->end ())
|
||||
{
|
||||
|
||||
int len = i2p::util::lexical_cast<int>(it->second, outboundTunnelLen);
|
||||
if (len >= 0)
|
||||
{
|
||||
outboundTunnelLen = len;
|
||||
}
|
||||
LogPrint (eLogInfo, "Destination: Outbound tunnel length set to ", outboundTunnelLen);
|
||||
}
|
||||
it = params->find (I2CP_PARAM_INBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int quantity = i2p::util::lexical_cast<int>(it->second, inboundTunnelsQuantity);
|
||||
if (quantity > 0)
|
||||
{
|
||||
inboundTunnelsQuantity = quantity;
|
||||
LogPrint (eLogInfo, "Destination: Inbound tunnels quantity set to ", quantity);
|
||||
}
|
||||
}
|
||||
it = params->find (I2CP_PARAM_OUTBOUND_TUNNELS_QUANTITY);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int quantity = i2p::util::lexical_cast<int>(it->second, outboundTunnelsQuantity);
|
||||
if (quantity > 0)
|
||||
{
|
||||
outboundTunnelsQuantity = quantity;
|
||||
LogPrint (eLogInfo, "Destination: Outbound tunnels quantity set to ", quantity);
|
||||
}
|
||||
}
|
||||
it = params->find (I2CP_PARAM_TAGS_TO_SEND);
|
||||
if (it != params->end ())
|
||||
{
|
||||
int tagsToSend = i2p::util::lexical_cast<int>(it->second, numTags);
|
||||
if (tagsToSend > 0)
|
||||
{
|
||||
numTags = tagsToSend;
|
||||
LogPrint (eLogInfo, "Destination: Tags to send set to ", tagsToSend);
|
||||
}
|
||||
}
|
||||
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, "Destination: Explicit peers set to ", it->second);
|
||||
}
|
||||
}
|
||||
} catch (std::exception & ex) {
|
||||
LogPrint(eLogError, "Destination: unable to parse parameters for destination: ", ex.what());
|
||||
}
|
||||
SetNumTags (numTags);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inboundTunnelLen, outboundTunnelLen, inboundTunnelsQuantity, outboundTunnelsQuantity);
|
||||
m_Pool = i2p::tunnel::tunnels.CreateTunnelPool (inLen, outLen, inQty, outQty);
|
||||
if (explicitPeers)
|
||||
m_Pool->SetExplicitPeers (explicitPeers);
|
||||
if(params)
|
||||
{
|
||||
auto itr = params->find(I2CP_PARAM_MAX_TUNNEL_LATENCY);
|
||||
if (itr != params->end()) {
|
||||
auto maxlatency = std::stoi(itr->second);
|
||||
itr = params->find(I2CP_PARAM_MIN_TUNNEL_LATENCY);
|
||||
if (itr != params->end()) {
|
||||
auto minlatency = std::stoi(itr->second);
|
||||
if ( minlatency > 0 && maxlatency > 0 ) {
|
||||
// set tunnel pool latency
|
||||
LogPrint(eLogInfo, "Destination: requiring tunnel latency [", minlatency, "ms, ", maxlatency, "ms]");
|
||||
m_Pool->RequireLatency(minlatency, maxlatency);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LeaseSetDestination::~LeaseSetDestination ()
|
||||
{
|
||||
if (m_IsRunning)
|
||||
Stop ();
|
||||
for (auto& it: m_LeaseSetRequests)
|
||||
if (it.second->requestComplete) it.second->requestComplete (nullptr);
|
||||
m_LeaseSetRequests.clear ();
|
||||
if (m_Pool)
|
||||
i2p::tunnel::tunnels.DeleteTunnelPool (m_Pool);
|
||||
for (auto& it: m_LeaseSetRequests)
|
||||
it.second->Complete (nullptr);
|
||||
}
|
||||
|
||||
void LeaseSetDestination::Run ()
|
||||
@@ -131,12 +112,12 @@ namespace client
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Pool->SetLocalDestination (shared_from_this ());
|
||||
m_Pool->SetActive (true);
|
||||
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
||||
|
||||
m_Pool->SetActive (true);
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
m_Thread = new std::thread (std::bind (&LeaseSetDestination::Run, shared_from_this ()));
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -149,7 +130,8 @@ namespace client
|
||||
{
|
||||
m_CleanupTimer.cancel ();
|
||||
m_PublishConfirmationTimer.cancel ();
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
|
||||
m_IsRunning = false;
|
||||
if (m_Pool)
|
||||
{
|
||||
@@ -163,21 +145,52 @@ namespace client
|
||||
delete m_Thread;
|
||||
m_Thread = 0;
|
||||
}
|
||||
CleanUp (); // GarlicDestination
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<const i2p::data::LeaseSet> LeaseSetDestination::FindLeaseSet (const i2p::data::IdentHash& ident)
|
||||
{
|
||||
auto it = m_RemoteLeaseSets.find (ident);
|
||||
if (it != m_RemoteLeaseSets.end ())
|
||||
{
|
||||
if (!it->second->IsExpired ())
|
||||
return it->second;
|
||||
std::shared_ptr<i2p::data::LeaseSet> remoteLS;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
||||
auto it = m_RemoteLeaseSets.find (ident);
|
||||
if (it != m_RemoteLeaseSets.end ())
|
||||
remoteLS = it->second;
|
||||
}
|
||||
|
||||
if (remoteLS)
|
||||
{
|
||||
if (!remoteLS->IsExpired ())
|
||||
{
|
||||
if (remoteLS->ExpiresSoon())
|
||||
{
|
||||
LogPrint(eLogDebug, "Destination: Lease Set expires soon, updating before expire");
|
||||
// update now before expiration for smooth handover
|
||||
auto s = shared_from_this ();
|
||||
RequestDestination(ident, [s, ident] (std::shared_ptr<i2p::data::LeaseSet> ls) {
|
||||
if(ls && !ls->IsExpired())
|
||||
{
|
||||
ls->PopulateLeases();
|
||||
{
|
||||
std::lock_guard<std::mutex> _lock(s->m_RemoteLeaseSetsMutex);
|
||||
s->m_RemoteLeaseSets[ident] = ls;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return remoteLS;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: remote LeaseSet expired");
|
||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
||||
m_RemoteLeaseSets.erase (ident);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -185,24 +198,30 @@ namespace client
|
||||
if (ls && !ls->IsExpired ())
|
||||
{
|
||||
ls->PopulateLeases (); // since we don't store them in netdb
|
||||
m_RemoteLeaseSets[ident] = ls;
|
||||
std::lock_guard<std::mutex> _lock(m_RemoteLeaseSetsMutex);
|
||||
m_RemoteLeaseSets[ident] = ls;
|
||||
return ls;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const i2p::data::LocalLeaseSet> LeaseSetDestination::GetLeaseSet ()
|
||||
{
|
||||
if (!m_Pool) return nullptr;
|
||||
if (!m_LeaseSet)
|
||||
UpdateLeaseSet ();
|
||||
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
|
||||
return m_LeaseSet;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet)
|
||||
{
|
||||
m_LeaseSet.reset (newLeaseSet);
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_LeaseSetMutex);
|
||||
m_LeaseSet.reset (newLeaseSet);
|
||||
}
|
||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
||||
if (m_IsPublic)
|
||||
{
|
||||
m_PublishVerificationTimer.cancel ();
|
||||
@@ -275,45 +294,47 @@ namespace client
|
||||
LogPrint (eLogInfo, "Destination: Reply token is ignored for DatabaseStore");
|
||||
offset += 36;
|
||||
}
|
||||
i2p::data::IdentHash key (buf + DATABASE_STORE_KEY_OFFSET);
|
||||
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);
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet");
|
||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
||||
auto it = m_RemoteLeaseSets.find (key);
|
||||
if (it != m_RemoteLeaseSets.end ())
|
||||
{
|
||||
leaseSet = it->second;
|
||||
if (leaseSet->IsNewer (buf + offset, len - offset))
|
||||
{
|
||||
leaseSet->Update (buf + offset, len - offset);
|
||||
if (leaseSet->IsValid ())
|
||||
LogPrint (eLogDebug, "Remote LeaseSet updated");
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet updated");
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "Remote LeaseSet update failed");
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet update failed");
|
||||
m_RemoteLeaseSets.erase (it);
|
||||
leaseSet = nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Remote LeaseSet is older. Not updated");
|
||||
LogPrint (eLogDebug, "Destination: Remote LeaseSet is older. Not updated");
|
||||
}
|
||||
else
|
||||
{
|
||||
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset);
|
||||
if (leaseSet->IsValid ())
|
||||
if (leaseSet->IsValid () && leaseSet->GetIdentHash () == key)
|
||||
{
|
||||
if (leaseSet->GetIdentHash () != GetIdentHash ())
|
||||
{
|
||||
LogPrint (eLogDebug, "New remote LeaseSet added");
|
||||
m_RemoteLeaseSets[buf + DATABASE_STORE_KEY_OFFSET] = leaseSet;
|
||||
LogPrint (eLogDebug, "Destination: New remote LeaseSet added");
|
||||
m_RemoteLeaseSets[key] = leaseSet;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Own remote LeaseSet dropped");
|
||||
LogPrint (eLogDebug, "Destination: Own remote LeaseSet dropped");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "New remote LeaseSet failed");
|
||||
LogPrint (eLogError, "Destination: New remote LeaseSet failed");
|
||||
leaseSet = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -321,11 +342,11 @@ namespace client
|
||||
else
|
||||
LogPrint (eLogError, "Destination: Unexpected client's DatabaseStore type ", buf[DATABASE_STORE_TYPE_OFFSET], ", dropped");
|
||||
|
||||
auto it1 = m_LeaseSetRequests.find (buf + DATABASE_STORE_KEY_OFFSET);
|
||||
auto it1 = m_LeaseSetRequests.find (key);
|
||||
if (it1 != m_LeaseSetRequests.end ())
|
||||
{
|
||||
it1->second->requestTimeoutTimer.cancel ();
|
||||
if (it1->second->requestComplete) it1->second->requestComplete (leaseSet);
|
||||
if (it1->second) it1->second->Complete (leaseSet);
|
||||
m_LeaseSetRequests.erase (it1);
|
||||
}
|
||||
}
|
||||
@@ -343,29 +364,27 @@ namespace client
|
||||
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, "Destination: Requesting ", key.ToBase64 (), " at ", peerHash.ToBase64 ());
|
||||
if (SendLeaseSetRequest (key, floodfill, request))
|
||||
found = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message
|
||||
i2p::data::netdb.RequestDestination (peerHash);
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
LogPrint (eLogError, "Destination: Suggested floodfills are not presented in netDb");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
|
||||
if (!found)
|
||||
{
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
i2p::data::IdentHash peerHash (buf + 33 + i*32);
|
||||
if (!request->excluded.count (peerHash) && !i2p::data::netdb.FindRouter (peerHash))
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: Found new floodfill, request it"); // TODO: recheck this message
|
||||
i2p::data::netdb.RequestDestination (peerHash);
|
||||
}
|
||||
}
|
||||
|
||||
auto floodfill = i2p::data::netdb.GetClosestFloodfill (key, request->excluded);
|
||||
if (floodfill)
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: Requesting ", key.ToBase64 (), " at ", floodfill->GetIdentHash ().ToBase64 ());
|
||||
if (SendLeaseSetRequest (key, floodfill, request))
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
LogPrint (eLogInfo, "Destination: ", key.ToBase64 (), " was not found on ", MAX_NUM_FLOODFILLS_PER_REQUEST, " floodfills");
|
||||
request->Complete (nullptr);
|
||||
m_LeaseSetRequests.erase (key);
|
||||
}
|
||||
}
|
||||
@@ -391,8 +410,7 @@ namespace client
|
||||
}
|
||||
|
||||
void LeaseSetDestination::SetLeaseSetUpdated ()
|
||||
{
|
||||
i2p::garlic::GarlicDestination::SetLeaseSetUpdated ();
|
||||
{
|
||||
UpdateLeaseSet ();
|
||||
}
|
||||
|
||||
@@ -408,6 +426,16 @@ namespace client
|
||||
LogPrint (eLogDebug, "Destination: Publishing LeaseSet is pending");
|
||||
return;
|
||||
}
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (ts < m_LastSubmissionTime + PUBLISH_MIN_INTERVAL)
|
||||
{
|
||||
LogPrint (eLogDebug, "Destination: Publishing LeaseSet is too fast. Wait for ", PUBLISH_MIN_INTERVAL, " seconds");
|
||||
m_PublishDelayTimer.cancel ();
|
||||
m_PublishDelayTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_MIN_INTERVAL));
|
||||
m_PublishDelayTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishDelayTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
return;
|
||||
}
|
||||
auto outbound = m_Pool->GetNextOutboundTunnel ();
|
||||
if (!outbound)
|
||||
{
|
||||
@@ -435,6 +463,7 @@ namespace client
|
||||
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
outbound->SendTunnelDataMsg (floodfill->GetIdentHash (), 0, msg);
|
||||
m_LastSubmissionTime = ts;
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandlePublishConfirmationTimer (const boost::system::error_code& ecode)
|
||||
@@ -459,13 +488,18 @@ namespace client
|
||||
// "this" added due to bug in gcc 4.7-4.8
|
||||
[s,this](std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
||||
{
|
||||
if (leaseSet && s->m_LeaseSet)
|
||||
if (leaseSet)
|
||||
{
|
||||
// we got latest LeasetSet
|
||||
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32());
|
||||
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
|
||||
s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
|
||||
return;
|
||||
if (s->m_LeaseSet && *s->m_LeaseSet == *leaseSet)
|
||||
{
|
||||
// we got latest LeasetSet
|
||||
LogPrint (eLogDebug, "Destination: published LeaseSet verified for ", GetIdentHash().ToBase32());
|
||||
s->m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_REGULAR_VERIFICATION_INTERNAL));
|
||||
s->m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, s, std::placeholders::_1));
|
||||
return;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "Destination: LeaseSet is different than just published for ", GetIdentHash().ToBase32());
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Destination: couldn't find published LeaseSet for ", GetIdentHash().ToBase32());
|
||||
@@ -475,6 +509,12 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void LeaseSetDestination::HandlePublishDelayTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Publish ();
|
||||
}
|
||||
|
||||
bool LeaseSetDestination::RequestDestination (const i2p::data::IdentHash& dest, RequestComplete requestComplete)
|
||||
{
|
||||
if (!m_Pool || !IsReady ())
|
||||
@@ -495,9 +535,9 @@ namespace client
|
||||
auto it = s->m_LeaseSetRequests.find (dest);
|
||||
if (it != s->m_LeaseSetRequests.end ())
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
auto requestComplete = it->second;
|
||||
s->m_LeaseSetRequests.erase (it);
|
||||
if (notify && requestComplete) requestComplete (nullptr);
|
||||
if (notify && requestComplete) requestComplete->Complete (nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -509,22 +549,31 @@ namespace client
|
||||
if (floodfill)
|
||||
{
|
||||
auto request = std::make_shared<LeaseSetRequest> (m_Service);
|
||||
request->requestComplete = requestComplete;
|
||||
if (requestComplete)
|
||||
request->requestComplete.push_back (requestComplete);
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto ret = m_LeaseSetRequests.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> >(dest,request));
|
||||
if (ret.second) // inserted
|
||||
{
|
||||
request->requestTime = ts;
|
||||
if (!SendLeaseSetRequest (dest, floodfill, request))
|
||||
{
|
||||
// request failed
|
||||
m_LeaseSetRequests.erase (dest);
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
m_LeaseSetRequests.erase (ret.first);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
}
|
||||
else // duplicate
|
||||
{
|
||||
LogPrint (eLogWarning, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
|
||||
// TODO: queue up requests
|
||||
if (request->requestComplete) request->requestComplete (nullptr);
|
||||
LogPrint (eLogInfo, "Destination: Request of LeaseSet ", dest.ToBase64 (), " is pending already");
|
||||
if (ts > ret.first->second->requestTime + MAX_LEASESET_REQUEST_TIMEOUT)
|
||||
{
|
||||
// something went wrong
|
||||
m_LeaseSetRequests.erase (ret.first);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
}
|
||||
else if (requestComplete)
|
||||
ret.first->second->requestComplete.push_back (requestComplete);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -547,7 +596,6 @@ namespace client
|
||||
if (request->replyTunnel && request->outboundTunnel)
|
||||
{
|
||||
request->excluded.insert (nextFloodfill->GetIdentHash ());
|
||||
request->requestTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
request->requestTimeoutTimer.cancel ();
|
||||
|
||||
uint8_t replyKey[32], replyTag[32];
|
||||
@@ -605,9 +653,9 @@ namespace client
|
||||
|
||||
if (done)
|
||||
{
|
||||
auto requestComplete = it->second->requestComplete;
|
||||
auto requestComplete = it->second;
|
||||
m_LeaseSetRequests.erase (it);
|
||||
if (requestComplete) requestComplete (nullptr);
|
||||
if (requestComplete) requestComplete->Complete (nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,6 +667,7 @@ namespace client
|
||||
{
|
||||
CleanupExpiredTags ();
|
||||
CleanupRemoteLeaseSets ();
|
||||
CleanupDestination ();
|
||||
m_CleanupTimer.expires_from_now (boost::posix_time::minutes (DESTINATION_CLEANUP_TIMEOUT));
|
||||
m_CleanupTimer.async_wait (std::bind (&LeaseSetDestination::HandleCleanupTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
@@ -628,6 +677,7 @@ namespace client
|
||||
void LeaseSetDestination::CleanupRemoteLeaseSets ()
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
|
||||
for (auto it = m_RemoteLeaseSets.begin (); it != m_RemoteLeaseSets.end ();)
|
||||
{
|
||||
if (it->second->IsEmpty () || ts > it->second->GetExpirationTime ()) // leaseset expired
|
||||
@@ -642,7 +692,8 @@ namespace client
|
||||
|
||||
ClientDestination::ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params):
|
||||
LeaseSetDestination (isPublic, params),
|
||||
m_Keys (keys), m_DatagramDestination (nullptr)
|
||||
m_Keys (keys), m_DatagramDestination (nullptr),
|
||||
m_ReadyChecker(GetService())
|
||||
{
|
||||
if (isPublic)
|
||||
PersistTemporaryKeys ();
|
||||
@@ -654,8 +705,6 @@ namespace client
|
||||
|
||||
ClientDestination::~ClientDestination ()
|
||||
{
|
||||
if (m_DatagramDestination)
|
||||
delete m_DatagramDestination;
|
||||
}
|
||||
|
||||
bool ClientDestination::Start ()
|
||||
@@ -676,22 +725,53 @@ namespace client
|
||||
{
|
||||
if (LeaseSetDestination::Stop ())
|
||||
{
|
||||
m_ReadyChecker.cancel();
|
||||
m_StreamingDestination->Stop ();
|
||||
//m_StreamingDestination->SetOwner (nullptr);
|
||||
m_StreamingDestination = nullptr;
|
||||
for (auto& it: m_StreamingDestinationsByPorts)
|
||||
{
|
||||
it.second->Stop ();
|
||||
//it.second->SetOwner (nullptr);
|
||||
}
|
||||
m_StreamingDestinationsByPorts.clear ();
|
||||
if (m_DatagramDestination)
|
||||
{
|
||||
auto d = m_DatagramDestination;
|
||||
{
|
||||
delete m_DatagramDestination;
|
||||
m_DatagramDestination = nullptr;
|
||||
delete d;
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef I2LUA
|
||||
void ClientDestination::Ready(ReadyPromise & p)
|
||||
{
|
||||
ScheduleCheckForReady(&p);
|
||||
}
|
||||
|
||||
void ClientDestination::ScheduleCheckForReady(ReadyPromise * p)
|
||||
{
|
||||
// tick every 100ms
|
||||
m_ReadyChecker.expires_from_now(boost::posix_time::milliseconds(100));
|
||||
m_ReadyChecker.async_wait([&, p] (const boost::system::error_code & ecode) {
|
||||
HandleCheckForReady(ecode, p);
|
||||
});
|
||||
}
|
||||
|
||||
void ClientDestination::HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p)
|
||||
{
|
||||
if(ecode) // error happened
|
||||
p->set_value(nullptr);
|
||||
else if(IsReady()) // we are ready
|
||||
p->set_value(std::shared_ptr<ClientDestination>(this));
|
||||
else // we are not ready
|
||||
ScheduleCheckForReady(p);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ClientDestination::HandleDataMessage (const uint8_t * buf, size_t len)
|
||||
{
|
||||
uint32_t length = bufbe32toh (buf);
|
||||
@@ -786,6 +866,12 @@ namespace client
|
||||
return false;
|
||||
}
|
||||
|
||||
void ClientDestination::AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor)
|
||||
{
|
||||
if (m_StreamingDestination)
|
||||
m_StreamingDestination->AcceptOnce (acceptor);
|
||||
}
|
||||
|
||||
std::shared_ptr<i2p::stream::StreamingDestination> ClientDestination::CreateStreamingDestination (int port, bool gzip)
|
||||
{
|
||||
auto dest = std::make_shared<i2p::stream::StreamingDestination> (GetSharedFromThis (), port, gzip);
|
||||
@@ -796,9 +882,9 @@ namespace client
|
||||
return dest;
|
||||
}
|
||||
|
||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
|
||||
i2p::datagram::DatagramDestination * ClientDestination::CreateDatagramDestination ()
|
||||
{
|
||||
if (!m_DatagramDestination)
|
||||
if (m_DatagramDestination == nullptr)
|
||||
m_DatagramDestination = new i2p::datagram::DatagramDestination (GetSharedFromThis ());
|
||||
return m_DatagramDestination;
|
||||
}
|
||||
@@ -848,5 +934,11 @@ namespace client
|
||||
Sign (leaseSet->GetBuffer (), leaseSet->GetBufferLen () - leaseSet->GetSignatureLen (), leaseSet->GetSignature ()); // TODO
|
||||
SetLeaseSet (leaseSet);
|
||||
}
|
||||
|
||||
void ClientDestination::CleanupDestination ()
|
||||
{
|
||||
if (m_DatagramDestination) m_DatagramDestination->CleanUp ();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#ifdef I2LUA
|
||||
#include <future>
|
||||
#endif
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "TunnelPool.h"
|
||||
@@ -27,6 +30,7 @@ namespace client
|
||||
const uint8_t PROTOCOL_TYPE_RAW = 18;
|
||||
const int PUBLISH_CONFIRMATION_TIMEOUT = 5; // in seconds
|
||||
const int PUBLISH_VERIFICATION_TIMEOUT = 10; // in seconds after successfull publish
|
||||
const int PUBLISH_MIN_INTERVAL = 20; // in seconds
|
||||
const int PUBLISH_REGULAR_VERIFICATION_INTERNAL = 100; // in seconds periodically
|
||||
const int LEASESET_REQUEST_TIMEOUT = 5; // in seconds
|
||||
const int MAX_LEASESET_REQUEST_TIMEOUT = 40; // in seconds
|
||||
@@ -47,6 +51,12 @@ namespace client
|
||||
const char I2CP_PARAM_TAGS_TO_SEND[] = "crypto.tagsToSend";
|
||||
const int DEFAULT_TAGS_TO_SEND = 40;
|
||||
|
||||
// latency
|
||||
const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min";
|
||||
const int DEFAULT_MIN_TUNNEL_LATENCY = 0;
|
||||
const char I2CP_PARAM_MAX_TUNNEL_LATENCY[] = "latency.max";
|
||||
const int DEFAULT_MAX_TUNNEL_LATENCY = 0;
|
||||
|
||||
typedef std::function<void (std::shared_ptr<i2p::stream::Stream> stream)> StreamRequestComplete;
|
||||
|
||||
class LeaseSetDestination: public i2p::garlic::GarlicDestination,
|
||||
@@ -60,9 +70,15 @@ namespace client
|
||||
std::set<i2p::data::IdentHash> excluded;
|
||||
uint64_t requestTime;
|
||||
boost::asio::deadline_timer requestTimeoutTimer;
|
||||
RequestComplete requestComplete;
|
||||
std::list<RequestComplete> requestComplete;
|
||||
std::shared_ptr<i2p::tunnel::OutboundTunnel> outboundTunnel;
|
||||
std::shared_ptr<i2p::tunnel::InboundTunnel> replyTunnel;
|
||||
|
||||
void Complete (std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
for (auto& it: requestComplete) it (ls);
|
||||
requestComplete.clear ();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -95,6 +111,7 @@ namespace client
|
||||
protected:
|
||||
|
||||
void SetLeaseSet (i2p::data::LocalLeaseSet * newLeaseSet);
|
||||
virtual void CleanupDestination () {}; // additional clean up in derived classes
|
||||
// I2CP
|
||||
virtual void HandleDataMessage (const uint8_t * buf, size_t len) = 0;
|
||||
virtual void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) = 0;
|
||||
@@ -106,6 +123,7 @@ namespace client
|
||||
void Publish ();
|
||||
void HandlePublishConfirmationTimer (const boost::system::error_code& ecode);
|
||||
void HandlePublishVerificationTimer (const boost::system::error_code& ecode);
|
||||
void HandlePublishDelayTimer (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);
|
||||
@@ -121,34 +139,45 @@ namespace client
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
mutable std::mutex m_RemoteLeaseSetsMutex;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<i2p::data::LeaseSet> > m_RemoteLeaseSets;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<LeaseSetRequest> > m_LeaseSetRequests;
|
||||
|
||||
std::shared_ptr<i2p::tunnel::TunnelPool> m_Pool;
|
||||
std::mutex m_LeaseSetMutex;
|
||||
std::shared_ptr<i2p::data::LocalLeaseSet> m_LeaseSet;
|
||||
bool m_IsPublic;
|
||||
uint32_t m_PublishReplyToken;
|
||||
uint64_t m_LastSubmissionTime; // in seconds
|
||||
std::set<i2p::data::IdentHash> m_ExcludedFloodfills; // for publishing
|
||||
|
||||
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer, m_CleanupTimer;
|
||||
boost::asio::deadline_timer m_PublishConfirmationTimer, m_PublishVerificationTimer,
|
||||
m_PublishDelayTimer, m_CleanupTimer;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
int GetNumRemoteLeaseSets () const { return m_RemoteLeaseSets.size (); };
|
||||
const decltype(m_RemoteLeaseSets)& GetLeaseSets () const { return m_RemoteLeaseSets; };
|
||||
};
|
||||
|
||||
class ClientDestination: public LeaseSetDestination
|
||||
{
|
||||
public:
|
||||
|
||||
#ifdef I2LUA
|
||||
// type for informing that a client destination is ready
|
||||
typedef std::promise<std::shared_ptr<ClientDestination> > ReadyPromise;
|
||||
// informs promise with shared_from_this() when this destination is ready to use
|
||||
// if cancelled before ready, informs promise with nullptr
|
||||
void Ready(ReadyPromise & p);
|
||||
#endif
|
||||
|
||||
ClientDestination (const i2p::data::PrivateKeys& keys, bool isPublic, const std::map<std::string, std::string> * params = nullptr);
|
||||
~ClientDestination ();
|
||||
|
||||
bool Start ();
|
||||
bool Stop ();
|
||||
|
||||
|
||||
const i2p::data::PrivateKeys& GetPrivateKeys () const { return m_Keys; };
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const { m_Keys.Sign (buf, len, signature); };
|
||||
|
||||
@@ -161,10 +190,11 @@ namespace client
|
||||
void AcceptStreams (const i2p::stream::StreamingDestination::Acceptor& acceptor);
|
||||
void StopAcceptingStreams ();
|
||||
bool IsAcceptingStreams () const;
|
||||
|
||||
void AcceptOnce (const i2p::stream::StreamingDestination::Acceptor& acceptor);
|
||||
|
||||
// datagram
|
||||
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
|
||||
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
|
||||
i2p::datagram::DatagramDestination * GetDatagramDestination () const { return m_DatagramDestination; };
|
||||
i2p::datagram::DatagramDestination * CreateDatagramDestination ();
|
||||
|
||||
// implements LocalDestination
|
||||
const uint8_t * GetEncryptionPrivateKey () const { return m_EncryptionPrivateKey; };
|
||||
@@ -172,6 +202,7 @@ namespace client
|
||||
|
||||
protected:
|
||||
|
||||
void CleanupDestination ();
|
||||
// I2CP
|
||||
void HandleDataMessage (const uint8_t * buf, size_t len);
|
||||
void CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels);
|
||||
@@ -181,7 +212,10 @@ namespace client
|
||||
std::shared_ptr<ClientDestination> GetSharedFromThis ()
|
||||
{ return std::static_pointer_cast<ClientDestination>(shared_from_this ()); }
|
||||
void PersistTemporaryKeys ();
|
||||
|
||||
#ifdef I2LUA
|
||||
void ScheduleCheckForReady(ReadyPromise * p);
|
||||
void HandleCheckForReady(const boost::system::error_code & ecode, ReadyPromise * p);
|
||||
#endif
|
||||
private:
|
||||
|
||||
i2p::data::PrivateKeys m_Keys;
|
||||
@@ -189,8 +223,10 @@ namespace client
|
||||
|
||||
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;
|
||||
i2p::datagram::DatagramDestination * m_DatagramDestination;
|
||||
|
||||
boost::asio::deadline_timer m_ReadyChecker;
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
|
||||
54
Dockerfile
Normal file
54
Dockerfile
Normal file
@@ -0,0 +1,54 @@
|
||||
FROM alpine:latest
|
||||
|
||||
MAINTAINER Mikal Villa <mikal@sigterm.no>
|
||||
|
||||
ENV GIT_BRANCH="master"
|
||||
ENV I2PD_PREFIX="/opt/i2pd-${GIT_BRANCH}"
|
||||
ENV PATH=${I2PD_PREFIX}/bin:$PATH
|
||||
|
||||
ENV GOSU_VERSION=1.7
|
||||
ENV GOSU_SHASUM="34049cfc713e8b74b90d6de49690fa601dc040021980812b2f1f691534be8a50 /usr/local/bin/gosu"
|
||||
|
||||
RUN mkdir /user && adduser -S -h /user i2pd && chown -R i2pd:nobody /user
|
||||
|
||||
|
||||
#
|
||||
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
|
||||
# image under 20mb we need to remove all the build dependencies in the same "RUN" / layer.
|
||||
#
|
||||
|
||||
# 1. install deps, clone and build.
|
||||
# 2. strip binaries.
|
||||
# 3. Purge all dependencies and other unrelated packages, including build directory.
|
||||
RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool boost-dev build-base openssl-dev openssl git \
|
||||
&& mkdir -p /tmp/build \
|
||||
&& cd /tmp/build && git clone -b ${GIT_BRANCH} https://github.com/PurpleI2P/i2pd.git \
|
||||
&& cd i2pd \
|
||||
&& make -j4 \
|
||||
&& mkdir -p ${I2PD_PREFIX}/bin \
|
||||
&& mv i2pd ${I2PD_PREFIX}/bin/ \
|
||||
&& cd ${I2PD_PREFIX}/bin \
|
||||
&& strip i2pd \
|
||||
&& rm -fr /tmp/build && apk --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \
|
||||
boost-python3 python3 gdbm boost-unit_test_framework boost-python linux-headers boost-prg_exec_monitor \
|
||||
boost-serialization boost-signals boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre \
|
||||
libtool g++ gcc pkgconfig
|
||||
|
||||
# 2. Adding required libraries to run i2pd to ensure it will run.
|
||||
RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl musl-utils libstdc++
|
||||
|
||||
# Gosu is a replacement for su/sudo in docker and not a backdoor :) See https://github.com/tianon/gosu
|
||||
RUN wget -O /usr/local/bin/gosu https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64 \
|
||||
&& echo "${GOSU_SHASUM}" | sha256sum -c && chmod +x /usr/local/bin/gosu
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
RUN chmod a+x /entrypoint.sh
|
||||
RUN echo "export PATH=${PATH}" >> /etc/profile
|
||||
|
||||
VOLUME [ "/var/lib/i2pd" ]
|
||||
|
||||
EXPOSE 7070 4444 4447 7656 2827 7654 7650
|
||||
|
||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
||||
|
||||
61
Event.cpp
Normal file
61
Event.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "Event.h"
|
||||
#include "Log.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace event
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EventCore core;
|
||||
#endif
|
||||
|
||||
void EventCore::SetListener(EventListener * l)
|
||||
{
|
||||
m_listener = l;
|
||||
LogPrint(eLogInfo, "Event: listener set");
|
||||
}
|
||||
|
||||
void EventCore::QueueEvent(const EventType & ev)
|
||||
{
|
||||
if(m_listener) m_listener->HandleEvent(ev);
|
||||
}
|
||||
|
||||
void EventCore::CollectEvent(const std::string & type, const std::string & ident, uint64_t val)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_collect_mutex);
|
||||
std::string key = type + "." + ident;
|
||||
if (m_collected.find(key) == m_collected.end())
|
||||
{
|
||||
m_collected[key] = {type, key, 0};
|
||||
}
|
||||
m_collected[key].Val += val;
|
||||
}
|
||||
|
||||
void EventCore::PumpCollected(EventListener * listener)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_collect_mutex);
|
||||
if(listener)
|
||||
{
|
||||
for(const auto & ev : m_collected) {
|
||||
listener->HandlePumpEvent({{"type", ev.second.Key}, {"ident", ev.second.Ident}}, ev.second.Val);
|
||||
}
|
||||
}
|
||||
m_collected.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
i2p::event::core.CollectEvent(type, ident, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EmitEvent(const EventType & e)
|
||||
{
|
||||
#if WITH_EVENTS
|
||||
i2p::event::core.QueueEvent(e);
|
||||
#endif
|
||||
}
|
||||
|
||||
53
Event.h
Normal file
53
Event.h
Normal file
@@ -0,0 +1,53 @@
|
||||
#ifndef EVENT_H__
|
||||
#define EVENT_H__
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <tuple>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
typedef std::map<std::string, std::string> EventType;
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace event
|
||||
{
|
||||
class EventListener {
|
||||
public:
|
||||
virtual ~EventListener() {};
|
||||
virtual void HandleEvent(const EventType & ev) = 0;
|
||||
/** @brief handle collected event when pumped */
|
||||
virtual void HandlePumpEvent(const EventType & ev, const uint64_t & val) = 0;
|
||||
};
|
||||
|
||||
class EventCore
|
||||
{
|
||||
public:
|
||||
void QueueEvent(const EventType & ev);
|
||||
void CollectEvent(const std::string & type, const std::string & ident, uint64_t val);
|
||||
void SetListener(EventListener * l);
|
||||
void PumpCollected(EventListener * l);
|
||||
|
||||
private:
|
||||
std::mutex m_collect_mutex;
|
||||
struct CollectedEvent
|
||||
{
|
||||
std::string Key;
|
||||
std::string Ident;
|
||||
uint64_t Val;
|
||||
};
|
||||
std::map<std::string, CollectedEvent> m_collected;
|
||||
EventListener * m_listener = nullptr;
|
||||
};
|
||||
#ifdef WITH_EVENTS
|
||||
extern EventCore core;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void QueueIntEvent(const std::string & type, const std::string & ident, uint64_t val);
|
||||
void EmitEvent(const EventType & ev);
|
||||
|
||||
#endif
|
||||
43
FS.cpp
43
FS.cpp
@@ -46,8 +46,18 @@ namespace fs {
|
||||
}
|
||||
#if defined(WIN32) || defined(_WIN32)
|
||||
char localAppData[MAX_PATH];
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
|
||||
dataDir = std::string(localAppData) + "\\" + appName;
|
||||
// check executable directory first
|
||||
GetModuleFileName (NULL, localAppData, MAX_PATH);
|
||||
auto execPath = boost::filesystem::path(localAppData).parent_path();
|
||||
// if config file exists in .exe's folder use it
|
||||
if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string
|
||||
dataDir = execPath.string ();
|
||||
else
|
||||
{
|
||||
// otherwise %appdata%
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, 0, NULL, localAppData);
|
||||
dataDir = std::string(localAppData) + "\\" + appName;
|
||||
}
|
||||
return;
|
||||
#elif defined(MAC_OSX)
|
||||
char *home = getenv("HOME");
|
||||
@@ -56,13 +66,15 @@ namespace fs {
|
||||
return;
|
||||
#else /* other unix */
|
||||
#if defined(ANDROID)
|
||||
if (boost::filesystem::exists("/sdcard"))
|
||||
{
|
||||
dataDir = "/sdcard/" + appName;
|
||||
const char * ext = getenv("EXTERNAL_STORAGE");
|
||||
if (!ext) ext = "/sdcard";
|
||||
if (boost::filesystem::exists(ext))
|
||||
{
|
||||
dataDir = std::string (ext) + "/" + appName;
|
||||
return;
|
||||
}
|
||||
// otherwise use /data/files
|
||||
#endif
|
||||
}
|
||||
// otherwise use /data/files
|
||||
#endif
|
||||
char *home = getenv("HOME");
|
||||
if (isService) {
|
||||
dataDir = "/var/lib/" + appName;
|
||||
@@ -112,10 +124,10 @@ namespace fs {
|
||||
|
||||
bool CreateDirectory (const std::string& path)
|
||||
{
|
||||
if (boost::filesystem::exists(path) &&
|
||||
if (boost::filesystem::exists(path) &&
|
||||
boost::filesystem::is_directory (boost::filesystem::status (path))) return true;
|
||||
return boost::filesystem::create_directory(path);
|
||||
}
|
||||
}
|
||||
|
||||
void HashedStorage::SetPlace(const std::string &path) {
|
||||
root = path + i2p::fs::dirSep + name;
|
||||
@@ -125,7 +137,7 @@ namespace fs {
|
||||
if (!boost::filesystem::exists(root)) {
|
||||
boost::filesystem::create_directories(root);
|
||||
}
|
||||
|
||||
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
auto p = root + i2p::fs::dirSep + prefix1 + chars[i];
|
||||
if (boost::filesystem::exists(p))
|
||||
@@ -158,6 +170,13 @@ namespace fs {
|
||||
}
|
||||
|
||||
void HashedStorage::Traverse(std::vector<std::string> & files) {
|
||||
Iterate([&files] (const std::string & fname) {
|
||||
files.push_back(fname);
|
||||
});
|
||||
}
|
||||
|
||||
void HashedStorage::Iterate(FilenameVisitor v)
|
||||
{
|
||||
boost::filesystem::path p(root);
|
||||
boost::filesystem::recursive_directory_iterator it(p);
|
||||
boost::filesystem::recursive_directory_iterator end;
|
||||
@@ -166,7 +185,7 @@ namespace fs {
|
||||
if (!boost::filesystem::is_regular_file( it->status() ))
|
||||
continue;
|
||||
const std::string & t = it->path().string();
|
||||
files.push_back(t);
|
||||
v(t);
|
||||
}
|
||||
}
|
||||
} // fs
|
||||
|
||||
4
FS.h
4
FS.h
@@ -13,6 +13,7 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
|
||||
namespace i2p {
|
||||
namespace fs {
|
||||
@@ -43,6 +44,7 @@ namespace fs {
|
||||
std::string suffix; /**< suffix of file in storage (extension) */
|
||||
|
||||
public:
|
||||
typedef std::function<void(const std::string &)> FilenameVisitor;
|
||||
HashedStorage(const char *n, const char *p1, const char *p2, const char *s):
|
||||
name(n), prefix1(p1), prefix2(p2), suffix(s) {};
|
||||
|
||||
@@ -58,6 +60,8 @@ namespace fs {
|
||||
void Remove(const std::string & ident);
|
||||
/** find all files in storage and store list in provided vector */
|
||||
void Traverse(std::vector<std::string> & files);
|
||||
/** visit every file in this storage with a visitor */
|
||||
void Iterate(FilenameVisitor v);
|
||||
};
|
||||
|
||||
/** @brief Returns current application name, default 'i2pd' */
|
||||
|
||||
12
Family.cpp
12
Family.cpp
@@ -20,7 +20,7 @@ namespace data
|
||||
|
||||
void Families::LoadCertificate (const std::string& filename)
|
||||
{
|
||||
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
|
||||
SSL_CTX * ctx = SSL_CTX_new (TLS_method ());
|
||||
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
|
||||
if (ret)
|
||||
{
|
||||
@@ -40,7 +40,7 @@ namespace data
|
||||
if (family) family[0] = 0;
|
||||
}
|
||||
auto pkey = X509_get_pubkey (cert);
|
||||
int keyType = EVP_PKEY_type(pkey->type);
|
||||
int keyType = EVP_PKEY_base_id (pkey);
|
||||
switch (keyType)
|
||||
{
|
||||
case EVP_PKEY_DSA:
|
||||
@@ -114,6 +114,12 @@ namespace data
|
||||
{
|
||||
uint8_t buf[50], signatureBuf[64];
|
||||
size_t len = family.length (), signatureLen = strlen (signature);
|
||||
if (len + 32 > 50)
|
||||
{
|
||||
LogPrint (eLogError, "Family: ", family, " is too long");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy (buf, family.c_str (), len);
|
||||
memcpy (buf + len, (const uint8_t *)ident, 32);
|
||||
len += 32;
|
||||
@@ -129,7 +135,7 @@ namespace data
|
||||
{
|
||||
auto filename = i2p::fs::DataDirPath("family", (family + ".key"));
|
||||
std::string sig;
|
||||
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
|
||||
SSL_CTX * ctx = SSL_CTX_new (TLS_method ());
|
||||
int ret = SSL_CTX_use_PrivateKey_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
|
||||
if (ret)
|
||||
{
|
||||
|
||||
118
Garlic.cpp
118
Garlic.cpp
@@ -20,7 +20,7 @@ namespace garlic
|
||||
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),
|
||||
m_ElGamalEncryption (new i2p::crypto::ElGamalEncryption (destination->GetEncryptionPublicKey ()))
|
||||
m_LeaseSetUpdateMsgID (0)
|
||||
{
|
||||
// create new session tags and session key
|
||||
RAND_bytes (m_SessionKey, 32);
|
||||
@@ -28,7 +28,7 @@ namespace garlic
|
||||
}
|
||||
|
||||
GarlicRoutingSession::GarlicRoutingSession (const uint8_t * sessionKey, const SessionTag& sessionTag):
|
||||
m_Owner (nullptr), m_Destination (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend)
|
||||
m_Owner (nullptr), m_NumTags (1), m_LeaseSetUpdateStatus (eLeaseSetDoNotSend), m_LeaseSetUpdateMsgID (0)
|
||||
{
|
||||
memcpy (m_SessionKey, sessionKey, 32);
|
||||
m_Encryption.SetKey (m_SessionKey);
|
||||
@@ -83,6 +83,7 @@ namespace garlic
|
||||
if (msgID == m_LeaseSetUpdateMsgID)
|
||||
{
|
||||
m_LeaseSetUpdateStatus = eLeaseSetUpToDate;
|
||||
m_LeaseSetUpdateMsgID = 0;
|
||||
LogPrint (eLogInfo, "Garlic: LeaseSet update confirmed");
|
||||
}
|
||||
else
|
||||
@@ -92,32 +93,22 @@ namespace garlic
|
||||
void GarlicRoutingSession::TagsConfirmed (uint32_t msgID)
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
auto it = m_UnconfirmedTagsMsgs.find (msgID);
|
||||
if (it != m_UnconfirmedTagsMsgs.end ())
|
||||
{
|
||||
auto& tags = *it;
|
||||
if (tags->msgID == msgID)
|
||||
{
|
||||
if (ts < tags->tagsCreationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
{
|
||||
for (int i = 0; i < tags->numTags; i++)
|
||||
m_SessionTags.push_back (tags->sessionTags[i]);
|
||||
}
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
auto& 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]);
|
||||
}
|
||||
else if (ts >= tags->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession (tags->msgID);
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
m_UnconfirmedTagsMsgs.erase (it);
|
||||
}
|
||||
}
|
||||
|
||||
bool GarlicRoutingSession::CleanupExpiredTags ()
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_SessionTags.begin (); it != m_SessionTags.end ();)
|
||||
{
|
||||
if (ts >= it->creationTime + OUTGOING_TAGS_EXPIRATION_TIMEOUT)
|
||||
@@ -126,6 +117,12 @@ namespace garlic
|
||||
++it;
|
||||
}
|
||||
CleanupUnconfirmedTags ();
|
||||
if (m_LeaseSetUpdateMsgID && ts*1000LL > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID);
|
||||
m_LeaseSetUpdateMsgID = 0;
|
||||
}
|
||||
return !m_SessionTags.empty () || !m_UnconfirmedTagsMsgs.empty ();
|
||||
}
|
||||
|
||||
@@ -136,10 +133,10 @@ namespace garlic
|
||||
// delete expired unconfirmed tags
|
||||
for (auto it = m_UnconfirmedTagsMsgs.begin (); it != m_UnconfirmedTagsMsgs.end ();)
|
||||
{
|
||||
if (ts >= (*it)->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
if (ts >= it->second->tagsCreationTime + OUTGOING_TAGS_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
if (m_Owner)
|
||||
m_Owner->RemoveDeliveryStatusSession ((*it)->msgID);
|
||||
m_Owner->RemoveDeliveryStatusSession (it->first);
|
||||
it = m_UnconfirmedTagsMsgs.erase (it);
|
||||
ret = true;
|
||||
}
|
||||
@@ -178,7 +175,7 @@ namespace garlic
|
||||
// create message
|
||||
if (!tagFound) // new session
|
||||
{
|
||||
LogPrint (eLogWarning, "Garlic: No tags available, will use ElGamal");
|
||||
LogPrint (eLogInfo, "Garlic: No tags available, will use ElGamal");
|
||||
if (!m_Destination)
|
||||
{
|
||||
LogPrint (eLogError, "Garlic: Can't use ElGamal for unknown destination");
|
||||
@@ -190,7 +187,7 @@ namespace garlic
|
||||
RAND_bytes (elGamal.preIV, 32); // Pre-IV
|
||||
uint8_t iv[32]; // IV is first 16 bytes
|
||||
SHA256(elGamal.preIV, 32, iv);
|
||||
m_ElGamalEncryption->Encrypt ((uint8_t *)&elGamal, sizeof(elGamal), buf, true);
|
||||
i2p::crypto::ElGamalEncrypt (m_Destination->GetEncryptionPublicKey (), (uint8_t *)&elGamal, buf, true);
|
||||
m_Encryption.SetIV (iv);
|
||||
buf += 514;
|
||||
len += 514;
|
||||
@@ -247,7 +244,7 @@ namespace garlic
|
||||
|
||||
size_t GarlicRoutingSession::CreateGarlicPayload (uint8_t * payload, std::shared_ptr<const I2NPMessage> msg, UnconfirmedTags * newTags)
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
uint32_t msgID;
|
||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
||||
size_t size = 0;
|
||||
@@ -258,9 +255,11 @@ namespace garlic
|
||||
if (m_Owner)
|
||||
{
|
||||
// resubmit non-confirmed LeaseSet
|
||||
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted &&
|
||||
i2p::util::GetMillisecondsSinceEpoch () > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
||||
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
if (m_LeaseSetUpdateStatus == eLeaseSetSubmitted && ts > m_LeaseSetSubmissionTime + LEASET_CONFIRMATION_TIMEOUT)
|
||||
{
|
||||
m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
SetSharedRoutingPath (nullptr); // invalidate path since leaseset was not confirmed
|
||||
}
|
||||
|
||||
// attach DeviveryStatus if necessary
|
||||
if (newTags || m_LeaseSetUpdateStatus == eLeaseSetUpdated) // new tags created or leaseset updated
|
||||
@@ -274,7 +273,8 @@ namespace garlic
|
||||
if (newTags) // new tags created
|
||||
{
|
||||
newTags->msgID = msgID;
|
||||
m_UnconfirmedTagsMsgs.emplace_back (newTags);
|
||||
m_UnconfirmedTagsMsgs.insert (std::make_pair(msgID, std::unique_ptr<UnconfirmedTags>(newTags)));
|
||||
newTags = nullptr; // got acquired
|
||||
}
|
||||
m_Owner->DeliveryStatusSent (shared_from_this (), msgID);
|
||||
}
|
||||
@@ -284,9 +284,10 @@ namespace garlic
|
||||
// attach LeaseSet
|
||||
if (m_LeaseSetUpdateStatus == eLeaseSetUpdated)
|
||||
{
|
||||
if (m_LeaseSetUpdateMsgID) m_Owner->RemoveDeliveryStatusSession (m_LeaseSetUpdateMsgID); // remove previous
|
||||
m_LeaseSetUpdateStatus = eLeaseSetSubmitted;
|
||||
m_LeaseSetUpdateMsgID = msgID;
|
||||
m_LeaseSetSubmissionTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
m_LeaseSetSubmissionTime = ts;
|
||||
// clove if our leaseSet must be attached
|
||||
auto leaseSet = CreateDatabaseStoreMsg (m_Owner->GetLeaseSet ());
|
||||
size += CreateGarlicClove (payload + size, leaseSet, false);
|
||||
@@ -298,13 +299,14 @@ namespace garlic
|
||||
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
|
||||
htobe64buf (payload + size, ts + 8000); // Expiration of message, 8 sec
|
||||
size += 8;
|
||||
|
||||
if (newTags) delete newTags; // not acquired, delete
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -312,7 +314,7 @@ namespace garlic
|
||||
{
|
||||
uint64_t ts = i2p::util::GetMillisecondsSinceEpoch () + 8000; // 8 sec
|
||||
size_t size = 0;
|
||||
if (isDestination && m_Destination)
|
||||
if (isDestination)
|
||||
{
|
||||
buf[size] = eGarlicDeliveryTypeDestination << 5;// delivery instructions flag destination
|
||||
size++;
|
||||
@@ -391,6 +393,12 @@ namespace garlic
|
||||
{
|
||||
}
|
||||
|
||||
void GarlicDestination::CleanUp ()
|
||||
{
|
||||
m_Sessions.clear ();
|
||||
m_DeliveryStatusSessions.clear ();
|
||||
m_Tags.clear ();
|
||||
}
|
||||
void GarlicDestination::AddSessionKey (const uint8_t * key, const uint8_t * tag)
|
||||
{
|
||||
if (key)
|
||||
@@ -621,42 +629,64 @@ namespace garlic
|
||||
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " tags expired for ", GetIdentHash().ToBase64 ());
|
||||
|
||||
// outgoing
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
|
||||
{
|
||||
it->second->GetSharedRoutingPath (); // delete shared path if necessary
|
||||
if (!it->second->CleanupExpiredTags ())
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
for (auto it = m_Sessions.begin (); it != m_Sessions.end ();)
|
||||
{
|
||||
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
|
||||
it = m_Sessions.erase (it);
|
||||
it->second->GetSharedRoutingPath (); // delete shared path if necessary
|
||||
if (!it->second->CleanupExpiredTags ())
|
||||
{
|
||||
LogPrint (eLogInfo, "Routing session to ", it->first.ToBase32 (), " deleted");
|
||||
it->second->SetOwner (nullptr);
|
||||
it = m_Sessions.erase (it);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
// delivery status sessions
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
||||
for (auto it = m_DeliveryStatusSessions.begin (); it != m_DeliveryStatusSessions.end (); )
|
||||
{
|
||||
if (it->second->GetOwner () != this)
|
||||
it = m_DeliveryStatusSessions.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
||||
m_DeliveryStatusSessions.erase (msgID);
|
||||
}
|
||||
|
||||
void GarlicDestination::DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
||||
m_DeliveryStatusSessions[msgID] = session;
|
||||
}
|
||||
|
||||
void GarlicDestination::HandleDeliveryStatusMessage (std::shared_ptr<I2NPMessage> msg)
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (msg->GetPayload ());
|
||||
GarlicRoutingSessionPtr session;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_DeliveryStatusSessionsMutex);
|
||||
auto it = m_DeliveryStatusSessions.find (msgID);
|
||||
if (it != m_DeliveryStatusSessions.end ())
|
||||
{
|
||||
it->second->MessageConfirmed (msgID);
|
||||
session = it->second;
|
||||
m_DeliveryStatusSessions.erase (it);
|
||||
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
{
|
||||
session->MessageConfirmed (msgID);
|
||||
LogPrint (eLogDebug, "Garlic: message ", msgID, " acknowledged");
|
||||
}
|
||||
}
|
||||
|
||||
void GarlicDestination::SetLeaseSetUpdated ()
|
||||
|
||||
16
Garlic.h
16
Garlic.h
@@ -104,10 +104,16 @@ namespace garlic
|
||||
{
|
||||
if (m_LeaseSetUpdateStatus != eLeaseSetDoNotSend) m_LeaseSetUpdateStatus = eLeaseSetUpdated;
|
||||
};
|
||||
|
||||
bool IsLeaseSetNonConfirmed () const { return m_LeaseSetUpdateStatus == eLeaseSetSubmitted; };
|
||||
bool IsLeaseSetUpdated () const { return m_LeaseSetUpdateStatus == eLeaseSetUpdated; };
|
||||
uint64_t GetLeaseSetSubmissionTime () const { return m_LeaseSetSubmissionTime; }
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> GetSharedRoutingPath ();
|
||||
void SetSharedRoutingPath (std::shared_ptr<GarlicRoutingPath> path);
|
||||
|
||||
|
||||
const GarlicDestination * GetOwner () const { return m_Owner; }
|
||||
void SetOwner (GarlicDestination * owner) { m_Owner = owner; }
|
||||
|
||||
private:
|
||||
|
||||
size_t CreateAESBlock (uint8_t * buf, std::shared_ptr<const I2NPMessage> msg);
|
||||
@@ -122,17 +128,17 @@ namespace garlic
|
||||
|
||||
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::list<std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs;
|
||||
std::map<uint32_t, std::unique_ptr<UnconfirmedTags> > m_UnconfirmedTagsMsgs; // msgID->tags
|
||||
|
||||
LeaseSetUpdateStatus m_LeaseSetUpdateStatus;
|
||||
uint32_t m_LeaseSetUpdateMsgID;
|
||||
uint64_t m_LeaseSetSubmissionTime; // in milliseconds
|
||||
|
||||
i2p::crypto::CBCEncryption m_Encryption;
|
||||
std::unique_ptr<const i2p::crypto::ElGamalEncryption> m_ElGamalEncryption;
|
||||
|
||||
std::shared_ptr<GarlicRoutingPath> m_SharedRoutingPath;
|
||||
|
||||
@@ -150,6 +156,7 @@ namespace garlic
|
||||
GarlicDestination (): m_NumTags (32) {}; // 32 tags by default
|
||||
~GarlicDestination ();
|
||||
|
||||
void CleanUp ();
|
||||
void SetNumTags (int numTags) { m_NumTags = numTags; };
|
||||
std::shared_ptr<GarlicRoutingSession> GetRoutingSession (std::shared_ptr<const i2p::data::RoutingDestination> destination, bool attachLeaseSet);
|
||||
void CleanupExpiredTags ();
|
||||
@@ -189,6 +196,7 @@ namespace garlic
|
||||
// incoming
|
||||
std::map<SessionTag, std::shared_ptr<i2p::crypto::CBCDecryption>> m_Tags;
|
||||
// DeliveryStatus
|
||||
std::mutex m_DeliveryStatusSessionsMutex;
|
||||
std::map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session
|
||||
|
||||
public:
|
||||
|
||||
29
Gzip.cpp
29
Gzip.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2017, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
@@ -9,11 +9,13 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h> /* memset */
|
||||
#include <iostream>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Gzip.h"
|
||||
|
||||
namespace i2p {
|
||||
namespace data {
|
||||
namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
const size_t GZIP_CHUNK_SIZE = 16384;
|
||||
|
||||
GzipInflator::GzipInflator (): m_IsDirty (false)
|
||||
@@ -36,9 +38,10 @@ namespace data {
|
||||
m_Inflator.next_out = out;
|
||||
m_Inflator.avail_out = outLen;
|
||||
int err;
|
||||
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END) {
|
||||
if ((err = inflate (&m_Inflator, Z_NO_FLUSH)) == Z_STREAM_END)
|
||||
return outLen - m_Inflator.avail_out;
|
||||
}
|
||||
// else
|
||||
LogPrint (eLogError, "Gzip: Inflate error ", err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -49,17 +52,20 @@ namespace data {
|
||||
m_Inflator.next_in = const_cast<uint8_t *>(in);
|
||||
m_Inflator.avail_in = inLen;
|
||||
int ret;
|
||||
do {
|
||||
do
|
||||
{
|
||||
m_Inflator.next_out = out;
|
||||
m_Inflator.avail_out = GZIP_CHUNK_SIZE;
|
||||
ret = inflate (&m_Inflator, Z_NO_FLUSH);
|
||||
if (ret < 0) {
|
||||
if (ret < 0)
|
||||
{
|
||||
inflateEnd (&m_Inflator);
|
||||
os.setstate(std::ios_base::failbit);
|
||||
break;
|
||||
}
|
||||
os.write ((char *)out, GZIP_CHUNK_SIZE - m_Inflator.avail_out);
|
||||
} while (!m_Inflator.avail_out); // more data to read
|
||||
}
|
||||
while (!m_Inflator.avail_out); // more data to read
|
||||
delete[] out;
|
||||
}
|
||||
|
||||
@@ -99,9 +105,10 @@ namespace data {
|
||||
m_Deflator.next_out = out;
|
||||
m_Deflator.avail_out = outLen;
|
||||
int err;
|
||||
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END) {
|
||||
if ((err = deflate (&m_Deflator, Z_FINISH)) == Z_STREAM_END)
|
||||
return outLen - m_Deflator.avail_out;
|
||||
} /* else */
|
||||
// else
|
||||
LogPrint (eLogError, "Gzip: Deflate error ", err);
|
||||
return 0;
|
||||
}
|
||||
} // data
|
||||
|
||||
99
HTTP.cpp
99
HTTP.cpp
@@ -1,14 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2013-2016, The PurpleI2P Project
|
||||
* Copyright (c) 2013-2017, The PurpleI2P Project
|
||||
*
|
||||
* This file is part of Purple i2pd project and licensed under BSD3
|
||||
*
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include "util.h"
|
||||
#include "HTTP.h"
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
|
||||
namespace i2p {
|
||||
@@ -43,18 +44,16 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
bool parse_header_line(const std::string & line, std::map<std::string, std::string> & headers) {
|
||||
static std::pair<std::string, std::string> parse_header_line(const std::string& line)
|
||||
{
|
||||
std::size_t pos = 0;
|
||||
std::size_t len = 2; /* strlen(": ") */
|
||||
std::size_t max = line.length();
|
||||
if ((pos = line.find(": ", pos)) == std::string::npos)
|
||||
return false;
|
||||
return std::make_pair("", "");
|
||||
while ((pos + len) < max && isspace(line.at(pos + len)))
|
||||
len++;
|
||||
std::string name = line.substr(0, pos);
|
||||
std::string value = line.substr(pos + len);
|
||||
headers[name] = value;
|
||||
return true;
|
||||
return std::make_pair(line.substr(0, pos), line.substr(pos + len));
|
||||
}
|
||||
|
||||
void gen_rfc1123_date(std::string & out) {
|
||||
@@ -247,10 +246,15 @@ namespace http {
|
||||
uri = tokens[1];
|
||||
version = tokens[2];
|
||||
expect = HEADER_LINE;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
if (!parse_header_line(line, headers))
|
||||
return -1;
|
||||
auto p = parse_header_line(line);
|
||||
if (p.first.length () > 0)
|
||||
headers.push_back (p);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
pos = eol + strlen(CRLF);
|
||||
if (pos >= eoh)
|
||||
@@ -259,17 +263,57 @@ namespace http {
|
||||
return eoh + strlen(HTTP_EOH);
|
||||
}
|
||||
|
||||
std::string HTTPReq::to_string() {
|
||||
std::stringstream ss;
|
||||
ss << method << " " << uri << " " << version << CRLF;
|
||||
for (auto & h : headers) {
|
||||
ss << h.first << ": " << h.second << CRLF;
|
||||
}
|
||||
ss << CRLF;
|
||||
return ss.str();
|
||||
void HTTPReq::write(std::ostream & o)
|
||||
{
|
||||
o << method << " " << uri << " " << version << CRLF;
|
||||
for (auto & h : headers)
|
||||
o << h.first << ": " << h.second << CRLF;
|
||||
o << CRLF;
|
||||
}
|
||||
|
||||
bool HTTPRes::is_chunked() {
|
||||
std::string HTTPReq::to_string()
|
||||
{
|
||||
std::stringstream ss;
|
||||
write(ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void HTTPReq::AddHeader (const std::string& name, const std::string& value)
|
||||
{
|
||||
headers.push_back (std::make_pair(name, value));
|
||||
}
|
||||
|
||||
void HTTPReq::UpdateHeader (const std::string& name, const std::string& value)
|
||||
{
|
||||
for (auto& it : headers)
|
||||
if (it.first == name)
|
||||
{
|
||||
it.second = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPReq::RemoveHeader (const std::string& name)
|
||||
{
|
||||
for (auto it = headers.begin (); it != headers.end ();)
|
||||
{
|
||||
if (!it->first.compare(0, name.length (), name))
|
||||
it = headers.erase (it);
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
std::string HTTPReq::GetHeader (const std::string& name) const
|
||||
{
|
||||
for (auto& it : headers)
|
||||
if (it.first == name)
|
||||
return it.second;
|
||||
return "";
|
||||
}
|
||||
|
||||
bool HTTPRes::is_chunked() const
|
||||
{
|
||||
auto it = headers.find("Transfer-Encoding");
|
||||
if (it == headers.end())
|
||||
return false;
|
||||
@@ -278,16 +322,20 @@ namespace http {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HTTPRes::is_gzipped() {
|
||||
bool HTTPRes::is_gzipped(bool includingI2PGzip) const
|
||||
{
|
||||
auto it = headers.find("Content-Encoding");
|
||||
if (it == headers.end())
|
||||
return false; /* no header */
|
||||
if (it->second.find("gzip") != std::string::npos)
|
||||
return true; /* gotcha! */
|
||||
if (includingI2PGzip && it->second.find("x-i2p-gzip") != std::string::npos)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
long int HTTPMsg::content_length() {
|
||||
|
||||
long int HTTPMsg::content_length() const
|
||||
{
|
||||
unsigned long int length = 0;
|
||||
auto it = headers.find("Content-Length");
|
||||
if (it == headers.end())
|
||||
@@ -330,7 +378,10 @@ namespace http {
|
||||
expect = HEADER_LINE;
|
||||
} else {
|
||||
std::string line = str.substr(pos, eol - pos);
|
||||
if (!parse_header_line(line, headers))
|
||||
auto p = parse_header_line(line);
|
||||
if (p.first.length () > 0)
|
||||
headers.insert (p);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
pos = eol + strlen(CRLF);
|
||||
|
||||
22
HTTP.h
22
HTTP.h
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -54,7 +55,8 @@ namespace http {
|
||||
std::string to_string ();
|
||||
};
|
||||
|
||||
struct HTTPMsg {
|
||||
struct HTTPMsg
|
||||
{
|
||||
std::map<std::string, std::string> headers;
|
||||
|
||||
void add_header(const char *name, std::string & value, bool replace = false);
|
||||
@@ -62,10 +64,12 @@ namespace http {
|
||||
void del_header(const char *name);
|
||||
|
||||
/** @brief Returns declared message length or -1 if unknown */
|
||||
long int content_length();
|
||||
long int content_length() const;
|
||||
};
|
||||
|
||||
struct HTTPReq : HTTPMsg {
|
||||
struct HTTPReq
|
||||
{
|
||||
std::list<std::pair<std::string, std::string> > headers;
|
||||
std::string version;
|
||||
std::string method;
|
||||
std::string uri;
|
||||
@@ -82,6 +86,12 @@ namespace http {
|
||||
|
||||
/** @brief Serialize HTTP request to string */
|
||||
std::string to_string();
|
||||
void write(std::ostream & o);
|
||||
|
||||
void AddHeader (const std::string& name, const std::string& value);
|
||||
void UpdateHeader (const std::string& name, const std::string& value);
|
||||
void RemoveHeader (const std::string& name);
|
||||
std::string GetHeader (const std::string& name) const;
|
||||
};
|
||||
|
||||
struct HTTPRes : HTTPMsg {
|
||||
@@ -116,11 +126,13 @@ namespace http {
|
||||
*/
|
||||
std::string to_string();
|
||||
|
||||
void write(std::ostream & o);
|
||||
|
||||
/** @brief Checks that response declared as chunked data */
|
||||
bool is_chunked();
|
||||
bool is_chunked() const ;
|
||||
|
||||
/** @brief Checks that response contains compressed data */
|
||||
bool is_gzipped();
|
||||
bool is_gzipped(bool includingI2PGzip = true) const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
315
HTTPProxy.cpp
315
HTTPProxy.cpp
@@ -29,7 +29,7 @@ namespace proxy {
|
||||
|
||||
static const char *pageHead =
|
||||
"<head>\r\n"
|
||||
" <title>I2P HTTP proxy: error</title>\r\n"
|
||||
" <title>I2Pd HTTP proxy</title>\r\n"
|
||||
" <style type=\"text/css\">\r\n"
|
||||
" body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: #FAFAFA; color: #103456; }\r\n"
|
||||
" .header { font-size: 2.5em; text-align: center; margin: 1.5em 0; color: #894C84; }\r\n"
|
||||
@@ -60,18 +60,44 @@ namespace proxy {
|
||||
void HandleStreamRequestComplete (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
/* error helpers */
|
||||
void GenericProxyError(const char *title, const char *description);
|
||||
void GenericProxyInfo(const char *title, const char *description);
|
||||
void HostNotFound(std::string & host);
|
||||
void SendProxyError(std::string & content);
|
||||
|
||||
void ForwardToUpstreamProxy();
|
||||
void HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec);
|
||||
void HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec);
|
||||
|
||||
void HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transfered);
|
||||
void HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transfered);
|
||||
|
||||
typedef std::function<void(boost::asio::ip::tcp::endpoint)> ProxyResolvedHandler;
|
||||
|
||||
void HandleUpstreamProxyResolved(const boost::system::error_code & ecode, boost::asio::ip::tcp::resolver::iterator itr, ProxyResolvedHandler handler);
|
||||
|
||||
void SocksProxySuccess();
|
||||
void HandoverToUpstreamProxy();
|
||||
|
||||
uint8_t m_recv_chunk[8192];
|
||||
std::string m_recv_buf; // from client
|
||||
std::string m_send_buf; // to upstream
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_sock;
|
||||
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> m_proxysock;
|
||||
boost::asio::ip::tcp::resolver m_proxy_resolver;
|
||||
i2p::http::URL m_ProxyURL;
|
||||
i2p::http::URL m_RequestURL;
|
||||
uint8_t m_socks_buf[255+8]; // for socks request/response
|
||||
ssize_t m_req_len;
|
||||
i2p::http::URL m_ClientRequestURL;
|
||||
i2p::http::HTTPReq m_ClientRequest;
|
||||
i2p::http::HTTPRes m_ClientResponse;
|
||||
std::stringstream m_ClientRequestBuffer;
|
||||
public:
|
||||
|
||||
HTTPReqHandler(HTTPProxy * parent, std::shared_ptr<boost::asio::ip::tcp::socket> sock) :
|
||||
I2PServiceHandler(parent), m_sock(sock) {}
|
||||
I2PServiceHandler(parent), m_sock(sock),
|
||||
m_proxysock(std::make_shared<boost::asio::ip::tcp::socket>(parent->GetService())),
|
||||
m_proxy_resolver(parent->GetService()) {}
|
||||
~HTTPReqHandler() { Terminate(); }
|
||||
void Handle () { AsyncSockRead(); } /* overload */
|
||||
};
|
||||
@@ -96,6 +122,13 @@ namespace proxy {
|
||||
m_sock->close();
|
||||
m_sock = nullptr;
|
||||
}
|
||||
if(m_proxysock)
|
||||
{
|
||||
LogPrint(eLogDebug, "HTTPProxy: close proxysock");
|
||||
if(m_proxysock->is_open())
|
||||
m_proxysock->close();
|
||||
m_proxysock = nullptr;
|
||||
}
|
||||
Done(shared_from_this());
|
||||
}
|
||||
|
||||
@@ -107,6 +140,14 @@ namespace proxy {
|
||||
SendProxyError(content);
|
||||
}
|
||||
|
||||
void HTTPReqHandler::GenericProxyInfo(const char *title, const char *description) {
|
||||
std::stringstream ss;
|
||||
ss << "<h1>Proxy info: " << title << "</h1>\r\n";
|
||||
ss << "<p>" << description << "</p>\r\n";
|
||||
std::string content = ss.str();
|
||||
SendProxyError(content);
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HostNotFound(std::string & host) {
|
||||
std::stringstream ss;
|
||||
ss << "<h1>Proxy error: Host not found</h1>\r\n"
|
||||
@@ -133,7 +174,7 @@ namespace proxy {
|
||||
<< "</html>\r\n";
|
||||
res.body = ss.str();
|
||||
std::string response = res.to_string();
|
||||
boost::asio::async_write(*m_sock, boost::asio::buffer(response),
|
||||
boost::asio::async_write(*m_sock, boost::asio::buffer(response), boost::asio::transfer_all(),
|
||||
std::bind(&HTTPReqHandler::SentHTTPFailed, shared_from_this(), std::placeholders::_1));
|
||||
}
|
||||
|
||||
@@ -159,26 +200,16 @@ namespace proxy {
|
||||
void HTTPReqHandler::SanitizeHTTPRequest(i2p::http::HTTPReq & req)
|
||||
{
|
||||
/* drop common headers */
|
||||
req.del_header("Referer");
|
||||
req.del_header("Via");
|
||||
req.del_header("Forwarded");
|
||||
req.RemoveHeader ("Referer");
|
||||
req.RemoveHeader("Via");
|
||||
req.RemoveHeader("Forwarded");
|
||||
/* drop proxy-disclosing headers */
|
||||
std::vector<std::string> toErase;
|
||||
for (const auto& it : req.headers) {
|
||||
if (it.first.compare(0, 12, "X-Forwarded-") == 0) {
|
||||
toErase.push_back(it.first);
|
||||
} else if (it.first.compare(0, 6, "Proxy-") == 0) {
|
||||
toErase.push_back(it.first);
|
||||
} else {
|
||||
/* allow */
|
||||
}
|
||||
}
|
||||
for (const auto& header : toErase) {
|
||||
req.headers.erase(header);
|
||||
}
|
||||
req.RemoveHeader("X-Forwarded");
|
||||
req.RemoveHeader("Proxy-");
|
||||
/* replace headers */
|
||||
req.add_header("Connection", "close", true); /* keep-alive conns not supported yet */
|
||||
req.add_header("User-Agent", "MYOB/6.66 (AN/ON)", true); /* privacy */
|
||||
req.UpdateHeader("User-Agent", "MYOB/6.66 (AN/ON)");
|
||||
/* add headers */
|
||||
req.AddHeader("Connection", "close"); /* keep-alive conns not supported yet */
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,65 +220,69 @@ namespace proxy {
|
||||
*/
|
||||
bool HTTPReqHandler::HandleRequest()
|
||||
{
|
||||
i2p::http::HTTPReq req;
|
||||
i2p::http::URL url;
|
||||
std::string b64;
|
||||
int req_len = 0;
|
||||
|
||||
req_len = req.parse(m_recv_buf);
|
||||
m_req_len = m_ClientRequest.parse(m_recv_buf);
|
||||
|
||||
if (req_len == 0)
|
||||
if (m_req_len == 0)
|
||||
return false; /* need more data */
|
||||
|
||||
if (req_len < 0) {
|
||||
if (m_req_len < 0) {
|
||||
LogPrint(eLogError, "HTTPProxy: unable to parse request");
|
||||
GenericProxyError("Invalid request", "Proxy unable to parse your request");
|
||||
return true; /* parse error */
|
||||
}
|
||||
|
||||
/* parsing success, now let's look inside request */
|
||||
LogPrint(eLogDebug, "HTTPProxy: requested: ", req.uri);
|
||||
url.parse(req.uri);
|
||||
LogPrint(eLogDebug, "HTTPProxy: requested: ", m_ClientRequest.uri);
|
||||
m_RequestURL.parse(m_ClientRequest.uri);
|
||||
|
||||
if (ExtractAddressHelper(url, b64)) {
|
||||
i2p::client::context.GetAddressBook ().InsertAddress (url.host, b64);
|
||||
LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", url.host);
|
||||
std::string full_url = url.to_string();
|
||||
if (ExtractAddressHelper(m_RequestURL, b64)) {
|
||||
i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, b64);
|
||||
LogPrint (eLogInfo, "HTTPProxy: added b64 from addresshelper for ", m_RequestURL.host);
|
||||
std::string full_url = m_RequestURL.to_string();
|
||||
std::stringstream ss;
|
||||
ss << "Host " << url.host << " added to router's addressbook from helper. "
|
||||
ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. "
|
||||
<< "Click <a href=\"" << full_url << "\">here</a> to proceed.";
|
||||
GenericProxyError("Addresshelper found", ss.str().c_str());
|
||||
GenericProxyInfo("Addresshelper found", ss.str().c_str());
|
||||
return true; /* request processed */
|
||||
}
|
||||
|
||||
SanitizeHTTPRequest(req);
|
||||
SanitizeHTTPRequest(m_ClientRequest);
|
||||
|
||||
std::string dest_host = url.host;
|
||||
uint16_t dest_port = url.port;
|
||||
std::string dest_host = m_RequestURL.host;
|
||||
uint16_t dest_port = m_RequestURL.port;
|
||||
/* always set port, even if missing in request */
|
||||
if (!dest_port) {
|
||||
dest_port = (url.schema == "https") ? 443 : 80;
|
||||
}
|
||||
if (!dest_port)
|
||||
dest_port = (m_RequestURL.schema == "https") ? 443 : 80;
|
||||
/* detect dest_host, set proper 'Host' header in upstream request */
|
||||
auto h = req.headers.find("Host");
|
||||
if (dest_host != "") {
|
||||
if (dest_host != "")
|
||||
{
|
||||
/* absolute url, replace 'Host' header */
|
||||
std::string h = dest_host;
|
||||
if (dest_port != 0 && dest_port != 80)
|
||||
h += ":" + std::to_string(dest_port);
|
||||
req.add_header("Host", h, true);
|
||||
} else if (h != req.headers.end()) {
|
||||
/* relative url and 'Host' header provided. transparent proxy mode? */
|
||||
i2p::http::URL u;
|
||||
std::string t = "http://" + h->second;
|
||||
u.parse(t);
|
||||
dest_host = u.host;
|
||||
dest_port = u.port;
|
||||
} else {
|
||||
/* relative url and missing 'Host' header */
|
||||
GenericProxyError("Invalid request", "Can't detect destination host from request");
|
||||
return true;
|
||||
}
|
||||
m_ClientRequest.UpdateHeader("Host", h);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto h = m_ClientRequest.GetHeader ("Host");
|
||||
if (h.length () > 0)
|
||||
{
|
||||
/* relative url and 'Host' header provided. transparent proxy mode? */
|
||||
i2p::http::URL u;
|
||||
std::string t = "http://" + h;
|
||||
u.parse(t);
|
||||
dest_host = u.host;
|
||||
dest_port = u.port;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* relative url and missing 'Host' header */
|
||||
GenericProxyError("Invalid request", "Can't detect destination host from request");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* check dest_host really exists and inside I2P network */
|
||||
i2p::data::IdentHash identHash;
|
||||
@@ -256,23 +291,31 @@ namespace proxy {
|
||||
HostNotFound(dest_host);
|
||||
return true; /* request processed */
|
||||
}
|
||||
/* TODO: outproxy handler here */
|
||||
} else {
|
||||
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": not implemented yet");
|
||||
std::string message = "Host" + dest_host + "not inside I2P network, but outproxy support not implemented yet";
|
||||
GenericProxyError("Outproxy failure", message.c_str());
|
||||
std::string outproxyUrl; i2p::config::GetOption("httpproxy.outproxy", outproxyUrl);
|
||||
if(outproxyUrl.size()) {
|
||||
LogPrint (eLogDebug, "HTTPProxy: use outproxy ", outproxyUrl);
|
||||
if(m_ProxyURL.parse(outproxyUrl))
|
||||
ForwardToUpstreamProxy();
|
||||
else
|
||||
GenericProxyError("Outproxy failure", "bad outproxy settings");
|
||||
} else {
|
||||
LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outprxy enabled");
|
||||
std::string message = "Host" + dest_host + "not inside I2P network, but outproxy is not enabled";
|
||||
GenericProxyError("Outproxy failure", message.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* make relative url */
|
||||
url.schema = "";
|
||||
url.host = "";
|
||||
req.uri = url.to_string();
|
||||
m_RequestURL.schema = "";
|
||||
m_RequestURL.host = "";
|
||||
m_ClientRequest.uri = m_RequestURL.to_string();
|
||||
|
||||
/* drop original request from recv buffer */
|
||||
m_recv_buf.erase(0, req_len);
|
||||
m_recv_buf.erase(0, m_req_len);
|
||||
/* build new buffer from modified request and data from original request */
|
||||
m_send_buf = req.to_string();
|
||||
m_send_buf = m_ClientRequest.to_string();
|
||||
m_send_buf.append(m_recv_buf);
|
||||
/* connect to destination */
|
||||
LogPrint(eLogDebug, "HTTPProxy: connecting to host ", dest_host, ":", dest_port);
|
||||
@@ -281,6 +324,144 @@ namespace proxy {
|
||||
return true;
|
||||
}
|
||||
|
||||
void HTTPReqHandler::ForwardToUpstreamProxy()
|
||||
{
|
||||
LogPrint(eLogDebug, "HTTPProxy: forward to upstream");
|
||||
// build http requset
|
||||
|
||||
m_ClientRequestURL = m_RequestURL;
|
||||
LogPrint(eLogDebug, "HTTPProxy: ", m_ClientRequestURL.host);
|
||||
m_ClientRequestURL.schema = "";
|
||||
m_ClientRequestURL.host = "";
|
||||
m_ClientRequest.uri = m_ClientRequestURL.to_string();
|
||||
|
||||
m_ClientRequest.write(m_ClientRequestBuffer);
|
||||
m_ClientRequestBuffer << m_recv_buf.substr(m_req_len);
|
||||
|
||||
// assume http if empty schema
|
||||
if (m_ProxyURL.schema == "" || m_ProxyURL.schema == "http") {
|
||||
// handle upstream http proxy
|
||||
if (!m_ProxyURL.port) m_ProxyURL.port = 80;
|
||||
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
|
||||
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
|
||||
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamHTTPProxyConnect, this, std::placeholders::_1));
|
||||
}));
|
||||
} else if (m_ProxyURL.schema == "socks") {
|
||||
// handle upstream socks proxy
|
||||
if (!m_ProxyURL.port) m_ProxyURL.port = 9050; // default to tor default if not specified
|
||||
boost::asio::ip::tcp::resolver::query q(m_ProxyURL.host, std::to_string(m_ProxyURL.port));
|
||||
m_proxy_resolver.async_resolve(q, std::bind(&HTTPReqHandler::HandleUpstreamProxyResolved, this, std::placeholders::_1, std::placeholders::_2, [&](boost::asio::ip::tcp::endpoint ep) {
|
||||
m_proxysock->async_connect(ep, std::bind(&HTTPReqHandler::HandleUpstreamSocksProxyConnect, this, std::placeholders::_1));
|
||||
}));
|
||||
} else {
|
||||
// unknown type, complain
|
||||
GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler)
|
||||
{
|
||||
if(ec) GenericProxyError("cannot resolve upstream proxy", ec.message().c_str());
|
||||
else handler(*it);
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HandleUpstreamSocksProxyConnect(const boost::system::error_code & ec)
|
||||
{
|
||||
if(!ec) {
|
||||
if(m_RequestURL.host.size() > 255) {
|
||||
GenericProxyError("hostname too long", m_RequestURL.host.c_str());
|
||||
return;
|
||||
}
|
||||
uint16_t port = m_RequestURL.port;
|
||||
if(!port) port = 80;
|
||||
LogPrint(eLogDebug, "HTTPProxy: connected to socks upstream");
|
||||
|
||||
std::string host = m_RequestURL.host;
|
||||
std::size_t reqsize = 0;
|
||||
m_socks_buf[0] = '\x04';
|
||||
m_socks_buf[1] = 1;
|
||||
htobe16buf(m_socks_buf+2, port);
|
||||
m_socks_buf[4] = 0;
|
||||
m_socks_buf[5] = 0;
|
||||
m_socks_buf[6] = 0;
|
||||
m_socks_buf[7] = 1;
|
||||
// user id
|
||||
m_socks_buf[8] = 'i';
|
||||
m_socks_buf[9] = '2';
|
||||
m_socks_buf[10] = 'p';
|
||||
m_socks_buf[11] = 'd';
|
||||
m_socks_buf[12] = 0;
|
||||
reqsize += 13;
|
||||
memcpy(m_socks_buf+ reqsize, host.c_str(), host.size());
|
||||
reqsize += host.size();
|
||||
m_socks_buf[++reqsize] = 0;
|
||||
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2));
|
||||
} else GenericProxyError("cannot connect to upstream socks proxy", ec.message().c_str());
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred)
|
||||
{
|
||||
LogPrint(eLogDebug, "HTTPProxy: upstream socks handshake sent");
|
||||
if(ec) GenericProxyError("Cannot negotiate with socks proxy", ec.message().c_str());
|
||||
else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HandoverToUpstreamProxy()
|
||||
{
|
||||
LogPrint(eLogDebug, "HTTPProxy: handover to socks proxy");
|
||||
auto connection = std::make_shared<i2p::client::TCPIPPipe>(GetOwner(), m_proxysock, m_sock);
|
||||
m_sock = nullptr;
|
||||
m_proxysock = nullptr;
|
||||
GetOwner()->AddHandler(connection);
|
||||
connection->Start();
|
||||
Terminate();
|
||||
}
|
||||
|
||||
void HTTPReqHandler::SocksProxySuccess()
|
||||
{
|
||||
if(m_ClientRequest.method == "CONNECT") {
|
||||
m_ClientResponse.code = 200;
|
||||
m_send_buf = m_ClientResponse.to_string();
|
||||
boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) {
|
||||
if(ec) GenericProxyError("socks proxy error", ec.message().c_str());
|
||||
else HandoverToUpstreamProxy();
|
||||
});
|
||||
} else {
|
||||
m_send_buf = m_ClientRequestBuffer.str();
|
||||
LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes");
|
||||
boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) {
|
||||
if(ec) GenericProxyError("failed to send request to upstream", ec.message().c_str());
|
||||
else HandoverToUpstreamProxy();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HandleSocksProxyReply(const boost::system::error_code & ec, std::size_t bytes_transferred)
|
||||
{
|
||||
if(!ec)
|
||||
{
|
||||
if(m_socks_buf[1] == 90) {
|
||||
// success
|
||||
SocksProxySuccess();
|
||||
} else {
|
||||
std::stringstream ss;
|
||||
ss << "error code: ";
|
||||
ss << (int) m_socks_buf[1];
|
||||
std::string msg = ss.str();
|
||||
GenericProxyError("Socks Proxy error", msg.c_str());
|
||||
}
|
||||
}
|
||||
else GenericProxyError("No Reply From socks proxy", ec.message().c_str());
|
||||
}
|
||||
|
||||
void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec)
|
||||
{
|
||||
if(!ec) {
|
||||
LogPrint(eLogDebug, "HTTPProxy: connected to http upstream");
|
||||
GenericProxyError("cannot connect", "http out proxy not implemented");
|
||||
} else GenericProxyError("cannot connect to upstream http proxy", ec.message().c_str());
|
||||
}
|
||||
|
||||
/* will be called after some data received from client */
|
||||
void HTTPReqHandler::HandleSockRecv(const boost::system::error_code & ecode, std::size_t len)
|
||||
{
|
||||
@@ -317,7 +498,7 @@ namespace proxy {
|
||||
if (Kill())
|
||||
return;
|
||||
LogPrint (eLogDebug, "HTTPProxy: Created new I2PTunnel stream, sSID=", stream->GetSendStreamID(), ", rSID=", stream->GetRecvStreamID());
|
||||
auto connection = std::make_shared<i2p::client::I2PTunnelConnection>(GetOwner(), m_sock, stream);
|
||||
auto connection = std::make_shared<i2p::client::I2PClientTunnelConnectionHTTP>(GetOwner(), m_sock, stream);
|
||||
GetOwner()->AddHandler (connection);
|
||||
connection->I2PConnect (reinterpret_cast<const uint8_t*>(m_send_buf.data()), m_send_buf.length());
|
||||
Done (shared_from_this());
|
||||
|
||||
417
HTTPServer.cpp
417
HTTPServer.cpp
@@ -33,18 +33,21 @@ namespace i2p {
|
||||
namespace http {
|
||||
const char *itoopieFavicon =
|
||||
"data:image/png;base64,"
|
||||
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv"
|
||||
"8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAYdEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My4wOGVynO"
|
||||
"EAAAIzSURBVDhPjZNdSFNhGMf3nm3n7OzMs+8JtfJGzdlgoPtoWBrkqc1OsLTMKEY3eZOQbbS6aBVYO"
|
||||
"oO8CKSLXEulQtZNahAM9Cq6lS533UUaeDEEKcN/79x7kbQT/eDhfPB7/u/7Poej08JqtXoEQbhoMpmG"
|
||||
"ZFn2stf/h8nEZ4aHue1SiWBlhSCV4n41NBifBINBjina8DyfzOUIVlcJtrYINjcJ3rw1oFAg4HnjHaZ"
|
||||
"p4/Ppv8zPH0G5XKZNPZibO4lKpYJ8vgOqqv+uKMq/d9Hfz/0sFr3w+/3IZt2YnbWhszOAxUUv0mkCs9"
|
||||
"ncyNT6hEL6dYBgY4Ngd5eger+zU7sODHA/mpubzUytj9FofLa0VGv4s9bWCCTJUGSaNvSzXT3stuHDM"
|
||||
"rc3xEqF4N2CERciURyyHfgqSZKPqfuxUMyC+OKcL4YHyl28nDFAPdqDZMcQ7tPnSfURUt0jMBgMH1nL"
|
||||
"fkRRDPvcLds3otfhbRTwasaE8b6He43VSrT3QW3tBT3iPdbyN3T7Ibsor988H8OxtiaMx2sB1aBbCRW"
|
||||
"R1hbQhbqYXh+6QkaJn8DZyzF09x6HeiaOTC6NK9cSsFqkb3aH3cLU+tCAx9l8FoXPBUy9n8LgyCCmS9"
|
||||
"MYez0Gm9P2iWna0GOcDp8KY2JhAsnbSQS6Ahh9OgrlklINeM40bWhAkBd4SLIEh8cBURLhOeiBIArVA"
|
||||
"U4yTRvJItk5PRehQVFaYfpbt9PBtTmdziaXyyUzjaHT/QZBQuKHAA0UxAAAAABJRU5ErkJggg==";
|
||||
"iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx"
|
||||
"jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0"
|
||||
"d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB"
|
||||
"vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn"
|
||||
"cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP"
|
||||
"6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps"
|
||||
"/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X"
|
||||
"+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/"
|
||||
"z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/"
|
||||
"P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am"
|
||||
"+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM"
|
||||
"EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1"
|
||||
"oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ"
|
||||
"JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ"
|
||||
"RU5ErkJggg==";
|
||||
|
||||
const char *cssStyles =
|
||||
"<style>\r\n"
|
||||
@@ -71,6 +74,7 @@ namespace http {
|
||||
const char HTTP_PAGE_TRANSPORTS[] = "transports";
|
||||
const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations";
|
||||
const char HTTP_PAGE_LOCAL_DESTINATION[] = "local_destination";
|
||||
const char HTTP_PAGE_I2CP_LOCAL_DESTINATION[] = "i2cp_local_destination";
|
||||
const char HTTP_PAGE_SAM_SESSIONS[] = "sam_sessions";
|
||||
const char HTTP_PAGE_SAM_SESSION[] = "sam_session";
|
||||
const char HTTP_PAGE_I2P_TUNNELS[] = "i2p_tunnels";
|
||||
@@ -86,7 +90,8 @@ namespace http {
|
||||
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
|
||||
const char HTTP_PARAM_ADDRESS[] = "address";
|
||||
|
||||
void ShowUptime (std::stringstream& s, int seconds) {
|
||||
static void ShowUptime (std::stringstream& s, int seconds)
|
||||
{
|
||||
int num;
|
||||
|
||||
if ((num = seconds / 86400) > 0) {
|
||||
@@ -104,7 +109,7 @@ namespace http {
|
||||
s << seconds << " seconds";
|
||||
}
|
||||
|
||||
void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, int bytes)
|
||||
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, int bytes)
|
||||
{
|
||||
std::string state;
|
||||
switch (eState) {
|
||||
@@ -121,7 +126,7 @@ namespace http {
|
||||
s << " " << (int) (bytes / 1024) << " KiB<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowPageHead (std::stringstream& s)
|
||||
static void ShowPageHead (std::stringstream& s)
|
||||
{
|
||||
s <<
|
||||
"<!DOCTYPE html>\r\n"
|
||||
@@ -156,7 +161,7 @@ namespace http {
|
||||
"<div class=right>";
|
||||
}
|
||||
|
||||
void ShowPageTail (std::stringstream& s)
|
||||
static void ShowPageTail (std::stringstream& s)
|
||||
{
|
||||
s <<
|
||||
"</div></div>\r\n"
|
||||
@@ -164,25 +169,44 @@ namespace http {
|
||||
"</html>\r\n";
|
||||
}
|
||||
|
||||
void ShowError(std::stringstream& s, const std::string& string)
|
||||
static void ShowError(std::stringstream& s, const std::string& string)
|
||||
{
|
||||
s << "<b>ERROR:</b> " << string << "<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowStatus (std::stringstream& s)
|
||||
static void ShowStatus (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Uptime:</b> ";
|
||||
ShowUptime(s, i2p::context.GetUptime ());
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Status:</b> ";
|
||||
s << "<b>Network status:</b> ";
|
||||
switch (i2p::context.GetStatus ())
|
||||
{
|
||||
case eRouterStatusOK: s << "OK"; break;
|
||||
case eRouterStatusTesting: s << "Testing"; break;
|
||||
case eRouterStatusFirewalled: s << "Firewalled"; break;
|
||||
case eRouterStatusError:
|
||||
{
|
||||
s << "Error";
|
||||
switch (i2p::context.GetError ())
|
||||
{
|
||||
case eRouterErrorClockSkew:
|
||||
s << "<br>Clock skew";
|
||||
break;
|
||||
default: ;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: s << "Unknown";
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
||||
if (auto remains = Daemon.gracefulShutdownInterval) {
|
||||
s << "<b>Stopping in:</b> ";
|
||||
s << remains << " seconds";
|
||||
s << "<br>\r\n";
|
||||
}
|
||||
#endif
|
||||
auto family = i2p::context.GetFamily ();
|
||||
if (family.length () > 0)
|
||||
s << "<b>Family:</b> " << family << "<br>\r\n";
|
||||
@@ -206,7 +230,7 @@ namespace http {
|
||||
else
|
||||
s << numKBytesSent / 1024 / 1024 << " GiB";
|
||||
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)<br>\r\n";
|
||||
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n<br>\r\n";
|
||||
s << "<b>Data path:</b> " << i2p::fs::GetDataDir() << "<br>\r\n";
|
||||
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
|
||||
s << "<b>Router Ident:</b> " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "<br>\r\n";
|
||||
s << "<b>Router Family:</b> " << i2p::context.GetRouterInfo().GetProperty("family") << "<br>\r\n";
|
||||
@@ -234,7 +258,7 @@ namespace http {
|
||||
s << address->host.to_string() << ":" << address->port << "<br>\r\n";
|
||||
}
|
||||
s << "</p>\r\n</div>\r\n";
|
||||
s << "<br>\r\n<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
|
||||
s << "<b>Routers:</b> " << i2p::data::netdb.GetNumRouters () << " ";
|
||||
s << "<b>Floodfills:</b> " << i2p::data::netdb.GetNumFloodfills () << " ";
|
||||
s << "<b>LeaseSets:</b> " << i2p::data::netdb.GetNumLeaseSets () << "<br>\r\n";
|
||||
|
||||
@@ -246,18 +270,75 @@ namespace http {
|
||||
s << "<b>Transit Tunnels:</b> " << std::to_string(transitTunnelCount) << "<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowLocalDestinations (std::stringstream& s)
|
||||
static void ShowLocalDestinations (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Local Destinations:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: i2p::client::context.GetDestinations ())
|
||||
{
|
||||
auto ident = it.second->GetIdentHash ();;
|
||||
auto ident = it.second->GetIdentHash ();
|
||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
|
||||
}
|
||||
|
||||
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
||||
if (i2cpServer)
|
||||
{
|
||||
s << "<br><b>I2CP Local Destinations:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: i2cpServer->GetSessions ())
|
||||
{
|
||||
auto dest = it.second->GetDestination ();
|
||||
if (dest)
|
||||
{
|
||||
auto ident = dest->GetIdentHash ();
|
||||
s << "<a href=\"/?page=" << HTTP_PAGE_I2CP_LOCAL_DESTINATION << "&i2cp_id=" << it.first << "\">";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident) << "</a><br>\r\n" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShowLocalDestination (std::stringstream& s, const std::string& b32)
|
||||
static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr<const i2p::client::LeaseSetDestination> dest)
|
||||
{
|
||||
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
|
||||
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
|
||||
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>\r\n";
|
||||
if(dest->GetNumRemoteLeaseSets())
|
||||
{
|
||||
s << "<div class='slide'\r\n><label for='slide1'>Hidden content. Press on text to see.</label>\r\n<input type='checkbox' id='slide1'/>\r\n<p class='content'>\r\n";
|
||||
for(auto& it: dest->GetLeaseSets ())
|
||||
s << it.second->GetIdentHash ().ToBase32 () << "<br>\r\n";
|
||||
s << "</p>\r\n</div>\r\n";
|
||||
}
|
||||
auto pool = dest->GetTunnelPool ();
|
||||
if (pool)
|
||||
{
|
||||
s << "<b>Inbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : pool->GetInboundTunnels ()) {
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Outbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : pool->GetOutboundTunnels ()) {
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
|
||||
}
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Tags</b><br>Incoming: " << dest->GetNumIncomingTags () << "<br>Outgoing:<br>" << std::endl;
|
||||
for (const auto& it: dest->GetSessions ())
|
||||
{
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " ";
|
||||
s << it.second->GetNumOutgoingTags () << "<br>" << std::endl;
|
||||
}
|
||||
s << "<br>" << std::endl;
|
||||
}
|
||||
|
||||
static void ShowLocalDestination (std::stringstream& s, const std::string& b32)
|
||||
{
|
||||
s << "<b>Local Destination:</b><br>\r\n<br>\r\n";
|
||||
i2p::data::IdentHash ident;
|
||||
@@ -265,44 +346,8 @@ namespace http {
|
||||
auto dest = i2p::client::context.FindLocalDestination (ident);
|
||||
if (dest)
|
||||
{
|
||||
s << "<b>Base64:</b><br>\r\n<textarea readonly=\"readonly\" cols=\"64\" rows=\"11\" wrap=\"on\">";
|
||||
s << dest->GetIdentity ()->ToBase64 () << "</textarea><br>\r\n<br>\r\n";
|
||||
s << "<b>LeaseSets:</b> <i>" << dest->GetNumRemoteLeaseSets () << "</i><br>\r\n";
|
||||
auto pool = dest->GetTunnelPool ();
|
||||
if (pool)
|
||||
{
|
||||
s << "<b>Inbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : pool->GetInboundTunnels ()) {
|
||||
it->Print(s);
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Outbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : pool->GetOutboundTunnels ()) {
|
||||
it->Print(s);
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
|
||||
}
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Tags</b><br>Incoming: " << dest->GetNumIncomingTags () << "<br>Outgoing:<br>" << std::endl;
|
||||
for (const auto& it: dest->GetSessions ())
|
||||
{
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(it.first) << " ";
|
||||
s << it.second->GetNumOutgoingTags () << "<br>" << std::endl;
|
||||
}
|
||||
s << "<br>" << std::endl;
|
||||
// s << "<br>\r\n<b>Streams:</b><br>\r\n";
|
||||
// for (auto it: dest->GetStreamingDestination ()->GetStreams ())
|
||||
// {
|
||||
// s << it.first << "->" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetRemoteIdentity ()) << " ";
|
||||
// s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]";
|
||||
// s << " [out:" << it.second->GetSendQueueSize () << "][in:" << it.second->GetReceiveQueueSize () << "]";
|
||||
// s << "[buf:" << it.second->GetSendBufferSize () << "]";
|
||||
// s << "[RTT:" << it.second->GetRTT () << "]";
|
||||
// s << "[Window:" << it.second->GetWindowSize () << "]";
|
||||
// s << "[Status:" << (int)it.second->GetStatus () << "]";
|
||||
// s << "<br>\r\n"<< std::endl;
|
||||
// }
|
||||
ShowLeaseSetDestination (s, dest);
|
||||
// show streams
|
||||
s << "<br>\r\n<table><caption>Streams</caption><tr>";
|
||||
s << "<th>StreamID</th>";
|
||||
s << "<th>Destination</th>";
|
||||
@@ -330,108 +375,105 @@ namespace http {
|
||||
s << "<td>" << it->GetWindowSize () << "</td>";
|
||||
s << "<td>" << (int)it->GetStatus () << "</td>";
|
||||
s << "</tr><br>\r\n" << std::endl;
|
||||
}
|
||||
}
|
||||
s << "</table>";
|
||||
}
|
||||
}
|
||||
|
||||
void ShowLeasesSets(std::stringstream& s)
|
||||
static void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
|
||||
{
|
||||
s << "<div id='leasesets'><b>LeaseSets:</b></div><br>";
|
||||
auto i2cpServer = i2p::client::context.GetI2CPServer ();
|
||||
if (i2cpServer)
|
||||
{
|
||||
s << "<b>I2CP Local Destination:</b><br>\r\n<br>\r\n";
|
||||
auto it = i2cpServer->GetSessions ().find (std::stoi (id));
|
||||
if (it != i2cpServer->GetSessions ().end ())
|
||||
ShowLeaseSetDestination (s, it->second->GetDestination ());
|
||||
else
|
||||
ShowError(s, "I2CP session not found");
|
||||
}
|
||||
else
|
||||
ShowError(s, "I2CP is not enabled");
|
||||
}
|
||||
|
||||
static void ShowLeasesSets(std::stringstream& s)
|
||||
{
|
||||
s << "<div id='leasesets'><b>LeaseSets (click on to show info):</b></div><br>\r\n";
|
||||
int counter = 1;
|
||||
// for each lease set
|
||||
i2p::data::netdb.VisitLeaseSets(
|
||||
[&s](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
||||
[&s, &counter](const i2p::data::IdentHash dest, std::shared_ptr<i2p::data::LeaseSet> leaseSet)
|
||||
{
|
||||
// create copy of lease set so we extract leases
|
||||
i2p::data::LeaseSet ls(leaseSet->GetBuffer(), leaseSet->GetBufferLen());
|
||||
// begin lease set entry
|
||||
s << "<div class='leaseset";
|
||||
if (ls.IsExpired())
|
||||
s << " expired"; // additional css class for expired
|
||||
s << "'>";
|
||||
// invalid ?
|
||||
s << "'>\r\n";
|
||||
if (!ls.IsValid())
|
||||
s << "<div class='invalid'>!! Invalid !! </div>";
|
||||
// ident
|
||||
s << "<div class='ident'>" << dest.ToBase32() << "</div>";
|
||||
// LeaseSet time
|
||||
s << "<div class='expires'>expires: " << ls.GetExpirationTime() << "</div>";
|
||||
// get non expired leases
|
||||
s << "<div class='invalid'>!! Invalid !! </div>\r\n";
|
||||
s << "<div class='slide'><label for='slide" << counter << "'>" << dest.ToBase32() << "</label>\r\n";
|
||||
s << "<input type='checkbox' id='slide" << (counter++) << "'/>\r\n<p class='content'>\r\n";
|
||||
s << "<b>Expires:</b> " << ls.GetExpirationTime() << "<br>\r\n";
|
||||
auto leases = ls.GetNonExpiredLeases();
|
||||
// show non expired leases
|
||||
s << "<div class='leasecount'>Non Expired Leases: " << leases.size() << "</div>";
|
||||
// for each lease
|
||||
s << "<div class='leases'>";
|
||||
s << "<b>Non Expired Leases: " << leases.size() << "</b><br>\r\n";
|
||||
for ( auto & l : leases )
|
||||
{
|
||||
// begin lease
|
||||
s << "<div class='lease'>";
|
||||
// gateway
|
||||
s << "<div class='gateway'>Gateway: " << l->tunnelGateway.ToBase64() << "</div>";
|
||||
// tunnel id
|
||||
s << "<div class='tunnelID'>TunnelID: " << l->tunnelID << "</div>";
|
||||
// end date
|
||||
s << "<div class='endDate'>EndDate: " << l->endDate << "</div>";
|
||||
// end lease
|
||||
s << "</div>";
|
||||
s << "<b>Gateway:</b> " << l->tunnelGateway.ToBase64() << "<br>\r\n";
|
||||
s << "<b>TunnelID:</b> " << l->tunnelID << "<br>\r\n";
|
||||
s << "<b>EndDate:</b> " << l->endDate << "<br>\r\n";
|
||||
}
|
||||
// end for each lease
|
||||
s << "</div>";
|
||||
// end lease set entry
|
||||
s << "</div>";
|
||||
// linebreak
|
||||
s << "<br>";
|
||||
s << "</p>\r\n</div>\r\n</div>\r\n";
|
||||
}
|
||||
);
|
||||
// end for each lease set
|
||||
}
|
||||
|
||||
void ShowTunnels (std::stringstream& s)
|
||||
static void ShowTunnels (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Queue size:</b> " << i2p::tunnel::tunnels.GetQueueSize () << "<br>\r\n";
|
||||
|
||||
s << "<b>Inbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) {
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumReceivedBytes ());
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
s << "<b>Outbound tunnels:</b><br>\r\n";
|
||||
for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) {
|
||||
it->Print(s);
|
||||
if(it->LatencyIsKnown())
|
||||
s << " ( " << it->GetMeanLatency() << "ms )";
|
||||
ShowTunnelDetails(s, it->GetState (), it->GetNumSentBytes ());
|
||||
}
|
||||
s << "<br>\r\n";
|
||||
}
|
||||
|
||||
void ShowCommands (std::stringstream& s)
|
||||
static void ShowCommands (std::stringstream& s, uint32_t token)
|
||||
{
|
||||
/* commands */
|
||||
s << "<b>Router Commands</b><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "\">Run peer test</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">Run peer test</a><br>\r\n";
|
||||
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
|
||||
if (i2p::context.AcceptsTunnels ())
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "\">Decline transit tunnels</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">Decline transit tunnels</a><br>\r\n";
|
||||
else
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "\">Accept transit tunnels</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">Accept transit tunnels</a><br>\r\n";
|
||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
||||
if (Daemon.gracefullShutdownInterval)
|
||||
{
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "\">Cancel gracefull shutdown (";
|
||||
s << Daemon.gracefullShutdownInterval;
|
||||
s << " seconds remains)</a><br>\r\n";
|
||||
}
|
||||
if (Daemon.gracefulShutdownInterval)
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">Cancel graceful shutdown</a><br>";
|
||||
else
|
||||
{
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Start gracefull shutdown</a><br>\r\n";
|
||||
}
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Start graceful shutdown</a><br>\r\n";
|
||||
#endif
|
||||
#ifdef WIN32_APP
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "\">Gracefull shutdown</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">Graceful shutdown</a><br>\r\n";
|
||||
#endif
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "\">Force shutdown</a><br>\r\n";
|
||||
s << " <a href=\"/?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">Force shutdown</a><br>\r\n";
|
||||
}
|
||||
|
||||
void ShowTransitTunnels (std::stringstream& s)
|
||||
static void ShowTransitTunnels (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Transit tunnels:</b><br>\r\n<br>\r\n";
|
||||
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
|
||||
@@ -446,14 +488,15 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
void ShowTransports (std::stringstream& s)
|
||||
static void ShowTransports (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Transports:</b><br>\r\n<br>\r\n";
|
||||
auto ntcpServer = i2p::transport::transports.GetNTCPServer ();
|
||||
if (ntcpServer)
|
||||
{
|
||||
s << "<b>NTCP</b><br>\r\n";
|
||||
for (const auto& it: ntcpServer->GetNTCPSessions ())
|
||||
auto sessions = ntcpServer->GetNTCPSessions ();
|
||||
s << "<b>NTCP</b> ( " << (int) sessions.size() << " )<br>\r\n";
|
||||
for (const auto& it: sessions )
|
||||
{
|
||||
if (it.second && it.second->IsEstablished ())
|
||||
{
|
||||
@@ -470,8 +513,9 @@ namespace http {
|
||||
auto ssuServer = i2p::transport::transports.GetSSUServer ();
|
||||
if (ssuServer)
|
||||
{
|
||||
s << "<br>\r\n<b>SSU</b><br>\r\n";
|
||||
for (const auto& it: ssuServer->GetSessions ())
|
||||
auto sessions = ssuServer->GetSessions ();
|
||||
s << "<br>\r\n<b>SSU</b> ( " << (int) sessions.size() << " )<br>\r\n";
|
||||
for (const auto& it: sessions)
|
||||
{
|
||||
auto endpoint = it.second->GetRemoteEndpoint ();
|
||||
if (it.second->IsOutgoing ()) s << " ⇒ ";
|
||||
@@ -495,7 +539,7 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
void ShowSAMSessions (std::stringstream& s)
|
||||
static void ShowSAMSessions (std::stringstream& s)
|
||||
{
|
||||
auto sam = i2p::client::context.GetSAMBridge ();
|
||||
if (!sam) {
|
||||
@@ -510,7 +554,7 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
void ShowSAMSession (std::stringstream& s, const std::string& id)
|
||||
static void ShowSAMSession (std::stringstream& s, const std::string& id)
|
||||
{
|
||||
s << "<b>SAM Session:</b><br>\r\n<br>\r\n";
|
||||
auto sam = i2p::client::context.GetSAMBridge ();
|
||||
@@ -542,7 +586,7 @@ namespace http {
|
||||
}
|
||||
}
|
||||
|
||||
void ShowI2PTunnels (std::stringstream& s)
|
||||
static void ShowI2PTunnels (std::stringstream& s)
|
||||
{
|
||||
s << "<b>Client Tunnels:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: i2p::client::context.GetClientTunnels ())
|
||||
@@ -553,6 +597,24 @@ namespace http {
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
||||
s << "<br>\r\n"<< std::endl;
|
||||
}
|
||||
auto httpProxy = i2p::client::context.GetHttpProxy ();
|
||||
if (httpProxy)
|
||||
{
|
||||
auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash();
|
||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << "HTTP Proxy" << "</a> ⇐ ";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
||||
s << "<br>\r\n"<< std::endl;
|
||||
}
|
||||
auto socksProxy = i2p::client::context.GetSocksProxy ();
|
||||
if (socksProxy)
|
||||
{
|
||||
auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash();
|
||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << "SOCKS Proxy" << "</a> ⇐ ";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
||||
s << "<br>\r\n"<< std::endl;
|
||||
}
|
||||
s << "<br>\r\n<b>Server Tunnels:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: i2p::client::context.GetServerTunnels ())
|
||||
{
|
||||
@@ -563,6 +625,32 @@ namespace http {
|
||||
s << ":" << it.second->GetLocalPort ();
|
||||
s << "</a><br>\r\n"<< std::endl;
|
||||
}
|
||||
auto& clientForwards = i2p::client::context.GetClientForwards ();
|
||||
if (!clientForwards.empty ())
|
||||
{
|
||||
s << "<br>\r\n<b>Client Forwards:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: clientForwards)
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << it.second->GetName () << "</a> ⇐ ";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
||||
s << "<br>\r\n"<< std::endl;
|
||||
}
|
||||
}
|
||||
auto& serverForwards = i2p::client::context.GetServerForwards ();
|
||||
if (!serverForwards.empty ())
|
||||
{
|
||||
s << "<br>\r\n<b>Server Forwards:</b><br>\r\n<br>\r\n";
|
||||
for (auto& it: serverForwards)
|
||||
{
|
||||
auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
|
||||
s << "<a href=\"/?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
|
||||
s << it.second->GetName () << "</a> ⇐ ";
|
||||
s << i2p::client::context.GetAddressBook ().ToAddress(ident);
|
||||
s << "<br>\r\n"<< std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HTTPConnection::HTTPConnection (std::shared_ptr<boost::asio::ip::tcp::socket> socket):
|
||||
@@ -626,17 +714,25 @@ namespace http {
|
||||
return true;
|
||||
}
|
||||
/* method #2: 'Authorization' header sent */
|
||||
if (req.headers.count("Authorization") > 0) {
|
||||
std::string provided = req.headers.find("Authorization")->second;
|
||||
auto provided = req.GetHeader ("Authorization");
|
||||
if (provided.length () > 0)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
std::string expected = user + ":" + pass;
|
||||
char b64_creds[64];
|
||||
size_t b64_sz = i2p::data::Base64EncodingBufferSize(expected.length()) + 1;
|
||||
char * b64_creds = new char[b64_sz];
|
||||
std::size_t len = 0;
|
||||
len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, sizeof(b64_creds));
|
||||
b64_creds[len] = '\0';
|
||||
expected = "Basic ";
|
||||
expected += b64_creds;
|
||||
if (provided == expected)
|
||||
return true;
|
||||
len = i2p::data::ByteStreamToBase64((unsigned char *)expected.c_str(), expected.length(), b64_creds, b64_sz);
|
||||
/* if we decoded properly then check credentials */
|
||||
if(len) {
|
||||
b64_creds[len] = '\0';
|
||||
expected = "Basic ";
|
||||
expected += b64_creds;
|
||||
result = expected == provided;
|
||||
}
|
||||
delete [] b64_creds;
|
||||
return result;
|
||||
}
|
||||
|
||||
LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ());
|
||||
@@ -675,6 +771,7 @@ namespace http {
|
||||
SendReply (res, content);
|
||||
}
|
||||
|
||||
std::map<uint32_t, uint32_t> HTTPConnection::m_Tokens;
|
||||
void HTTPConnection::HandlePage (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
@@ -690,13 +787,29 @@ namespace http {
|
||||
else if (page == HTTP_PAGE_TUNNELS)
|
||||
ShowTunnels (s);
|
||||
else if (page == HTTP_PAGE_COMMANDS)
|
||||
ShowCommands (s);
|
||||
{
|
||||
uint32_t token;
|
||||
RAND_bytes ((uint8_t *)&token, 4);
|
||||
token &= 0x7FFFFFFF; // clear first bit
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_Tokens.begin (); it != m_Tokens.end (); )
|
||||
{
|
||||
if (ts > it->second + TOKEN_EXPIRATION_TIMEOUT)
|
||||
it = m_Tokens.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
m_Tokens[token] = ts;
|
||||
ShowCommands (s, token);
|
||||
}
|
||||
else if (page == HTTP_PAGE_TRANSIT_TUNNELS)
|
||||
ShowTransitTunnels (s);
|
||||
else if (page == HTTP_PAGE_LOCAL_DESTINATIONS)
|
||||
ShowLocalDestinations (s);
|
||||
else if (page == HTTP_PAGE_LOCAL_DESTINATION)
|
||||
ShowLocalDestination (s, params["b32"]);
|
||||
else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION)
|
||||
ShowI2CPLocalDestination (s, params["i2cp_id"]);
|
||||
else if (page == HTTP_PAGE_SAM_SESSIONS)
|
||||
ShowSAMSessions (s);
|
||||
else if (page == HTTP_PAGE_SAM_SESSION)
|
||||
@@ -715,13 +828,19 @@ namespace http {
|
||||
void HTTPConnection::HandleCommand (const HTTPReq& req, HTTPRes& res, std::stringstream& s)
|
||||
{
|
||||
std::map<std::string, std::string> params;
|
||||
std::string cmd("");
|
||||
URL url;
|
||||
|
||||
url.parse(req.uri);
|
||||
url.parse_query(params);
|
||||
cmd = params["cmd"];
|
||||
|
||||
std::string token = params["token"];
|
||||
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
|
||||
{
|
||||
ShowError(s, "Invalid token");
|
||||
return;
|
||||
}
|
||||
|
||||
std::string cmd = params["cmd"];
|
||||
if (cmd == HTTP_COMMAND_RUN_PEER_TEST)
|
||||
i2p::transport::transports.PeerTest ();
|
||||
else if (cmd == HTTP_COMMAND_RELOAD_CONFIG)
|
||||
@@ -733,7 +852,7 @@ namespace http {
|
||||
else if (cmd == HTTP_COMMAND_SHUTDOWN_START) {
|
||||
i2p::context.SetAcceptsTunnels (false);
|
||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
||||
Daemon.gracefullShutdownInterval = 10*60;
|
||||
Daemon.gracefulShutdownInterval = 10*60;
|
||||
#endif
|
||||
#ifdef WIN32_APP
|
||||
i2p::win32::GracefulShutdown ();
|
||||
@@ -741,7 +860,7 @@ namespace http {
|
||||
} else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) {
|
||||
i2p::context.SetAcceptsTunnels (true);
|
||||
#if (!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID))
|
||||
Daemon.gracefullShutdownInterval = 0;
|
||||
Daemon.gracefulShutdownInterval = 0;
|
||||
#endif
|
||||
} else if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) {
|
||||
Daemon.running = false;
|
||||
@@ -758,16 +877,17 @@ namespace http {
|
||||
|
||||
void HTTPConnection::SendReply (HTTPRes& reply, std::string& content)
|
||||
{
|
||||
reply.add_header("X-Frame-Options", "SAMEORIGIN");
|
||||
reply.add_header("Content-Type", "text/html");
|
||||
reply.body = content;
|
||||
|
||||
std::string res = reply.to_string();
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer(res),
|
||||
m_SendBuffer = reply.to_string();
|
||||
boost::asio::async_write (*m_Socket, boost::asio::buffer(m_SendBuffer),
|
||||
std::bind (&HTTPConnection::Terminate, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
HTTPServer::HTTPServer (const std::string& address, int port):
|
||||
m_Thread (nullptr), m_Work (m_Service),
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||
m_Acceptor (m_Service, boost::asio::ip::tcp::endpoint (boost::asio::ip::address::from_string(address), port))
|
||||
{
|
||||
}
|
||||
@@ -796,6 +916,7 @@ namespace http {
|
||||
i2p::config::SetOption("http.pass", pass);
|
||||
LogPrint(eLogInfo, "HTTPServer: password set to ", pass);
|
||||
}
|
||||
m_IsRunning = true;
|
||||
m_Thread = std::unique_ptr<std::thread>(new std::thread (std::bind (&HTTPServer::Run, this)));
|
||||
m_Acceptor.listen ();
|
||||
Accept ();
|
||||
@@ -803,9 +924,11 @@ namespace http {
|
||||
|
||||
void HTTPServer::Stop ()
|
||||
{
|
||||
m_IsRunning = false;
|
||||
m_Acceptor.close();
|
||||
m_Service.stop ();
|
||||
if (m_Thread) {
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
@@ -813,7 +936,17 @@ namespace http {
|
||||
|
||||
void HTTPServer::Run ()
|
||||
{
|
||||
m_Service.run ();
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "HTTPServer: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HTTPServer::Accept ()
|
||||
@@ -827,7 +960,13 @@ namespace http {
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> newSocket)
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
if(newSocket) newSocket->close();
|
||||
LogPrint(eLogError, "HTTP Server: error handling accept ", ecode.message());
|
||||
if(ecode != boost::asio::error::operation_aborted)
|
||||
Accept();
|
||||
return;
|
||||
}
|
||||
CreateConnection(newSocket);
|
||||
Accept ();
|
||||
}
|
||||
|
||||
22
HTTPServer.h
22
HTTPServer.h
@@ -1,10 +1,20 @@
|
||||
#ifndef HTTP_SERVER_H__
|
||||
#define HTTP_SERVER_H__
|
||||
|
||||
namespace i2p {
|
||||
namespace http {
|
||||
extern const char *itoopieFavicon;
|
||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <boost/asio.hpp>
|
||||
#include "HTTP.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace http
|
||||
{
|
||||
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
|
||||
|
||||
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
|
||||
{
|
||||
@@ -31,9 +41,12 @@ namespace http {
|
||||
boost::asio::deadline_timer m_Timer;
|
||||
char m_Buffer[HTTP_CONNECTION_BUFFER_SIZE + 1];
|
||||
size_t m_BufferLen;
|
||||
std::string m_SendBuffer;
|
||||
bool needAuth;
|
||||
std::string user;
|
||||
std::string pass;
|
||||
|
||||
static std::map<uint32_t, uint32_t> m_Tokens; // token->timestamp in seconds
|
||||
};
|
||||
|
||||
class HTTPServer
|
||||
@@ -56,6 +69,7 @@ namespace http {
|
||||
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::unique_ptr<std::thread> m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
|
||||
60
I2CP.cpp
60
I2CP.cpp
@@ -66,12 +66,19 @@ namespace client
|
||||
memcpy (buf + 4, payload, len);
|
||||
msg->len += len + 4;
|
||||
msg->FillI2NPMessageHeader (eI2NPData);
|
||||
auto s = GetSharedFromThis ();
|
||||
auto remote = FindLeaseSet (ident);
|
||||
if (remote)
|
||||
GetService ().post (std::bind (&I2CPDestination::SendMsg, GetSharedFromThis (), msg, remote));
|
||||
{
|
||||
GetService ().post (
|
||||
[s, msg, remote, nonce]()
|
||||
{
|
||||
bool sent = s->SendMsg (msg, remote);
|
||||
s->m_Owner->SendMessageStatusMessage (nonce, sent ? eI2CPMessageStatusGuaranteedSuccess : eI2CPMessageStatusGuaranteedFailure);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
auto s = GetSharedFromThis ();
|
||||
RequestDestination (ident,
|
||||
[s, msg, nonce](std::shared_ptr<i2p::data::LeaseSet> ls)
|
||||
{
|
||||
@@ -109,7 +116,7 @@ namespace client
|
||||
}
|
||||
else
|
||||
{
|
||||
auto outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
outboundTunnel = GetTunnelPool ()->GetNextOutboundTunnel ();
|
||||
auto leases = remote->GetNonExpiredLeases ();
|
||||
if (!leases.empty ())
|
||||
remoteLease = leases[rand () % leases.size ()];
|
||||
@@ -339,6 +346,7 @@ namespace client
|
||||
void I2CPSession::CreateSessionMessageHandler (const uint8_t * buf, size_t len)
|
||||
{
|
||||
RAND_bytes ((uint8_t *)&m_SessionID, 2);
|
||||
m_Owner.InsertSession (shared_from_this ());
|
||||
auto identity = std::make_shared<i2p::data::IdentityEx>();
|
||||
size_t offset = identity->FromBuffer (buf, len);
|
||||
if (!offset)
|
||||
@@ -453,23 +461,23 @@ namespace client
|
||||
{
|
||||
i2p::data::IdentityEx identity;
|
||||
size_t identsize = identity.FromBuffer (buf + offset, len - offset);
|
||||
if (identsize)
|
||||
{
|
||||
offset += identsize;
|
||||
uint32_t payloadLen = bufbe32toh (buf + offset);
|
||||
if (payloadLen + offset <= len)
|
||||
{
|
||||
offset += 4;
|
||||
uint32_t nonce = bufbe32toh (buf + offset + payloadLen);
|
||||
if (m_IsSendAccepted)
|
||||
SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted
|
||||
m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "I2CP: cannot send message, too big");
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "I2CP: invalid identity");
|
||||
if (identsize)
|
||||
{
|
||||
offset += identsize;
|
||||
uint32_t payloadLen = bufbe32toh (buf + offset);
|
||||
if (payloadLen + offset <= len)
|
||||
{
|
||||
offset += 4;
|
||||
uint32_t nonce = bufbe32toh (buf + offset + payloadLen);
|
||||
if (m_IsSendAccepted)
|
||||
SendMessageStatusMessage (nonce, eI2CPMessageStatusAccepted); // accepted
|
||||
m_Destination->SendMsgTo (buf + offset, payloadLen, identity.GetIdentHash (), nonce);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "I2CP: cannot send message, too big");
|
||||
}
|
||||
else
|
||||
LogPrint(eLogError, "I2CP: invalid identity");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -704,7 +712,6 @@ namespace client
|
||||
{
|
||||
LogPrint (eLogDebug, "I2CP: new connection from ", ep);
|
||||
auto session = std::make_shared<I2CPSession>(*this, socket);
|
||||
m_Sessions[session->GetSessionID ()] = session;
|
||||
session->Start ();
|
||||
}
|
||||
else
|
||||
@@ -717,6 +724,17 @@ namespace client
|
||||
Accept ();
|
||||
}
|
||||
|
||||
bool I2CPServer::InsertSession (std::shared_ptr<I2CPSession> session)
|
||||
{
|
||||
if (!session) return false;
|
||||
if (!m_Sessions.insert({session->GetSessionID (), session}).second)
|
||||
{
|
||||
LogPrint (eLogError, "I2CP: duplicate session id ", session->GetSessionID ());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void I2CPServer::RemoveSession (uint16_t sessionID)
|
||||
{
|
||||
m_Sessions.erase (sessionID);
|
||||
|
||||
5
I2CP.h
5
I2CP.h
@@ -112,6 +112,7 @@ namespace client
|
||||
void Start ();
|
||||
void Stop ();
|
||||
uint16_t GetSessionID () const { return m_SessionID; };
|
||||
std::shared_ptr<const I2CPDestination> GetDestination () const { return m_Destination; };
|
||||
|
||||
// called from I2CPDestination
|
||||
void SendI2CPMessage (uint8_t type, const uint8_t * payload, size_t len);
|
||||
@@ -173,6 +174,7 @@ namespace client
|
||||
void Stop ();
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
|
||||
bool InsertSession (std::shared_ptr<I2CPSession> session);
|
||||
void RemoveSession (uint16_t sessionID);
|
||||
|
||||
private:
|
||||
@@ -196,6 +198,9 @@ namespace client
|
||||
public:
|
||||
|
||||
const decltype(m_MessagesHandlers)& GetMessagesHandlers () const { return m_MessagesHandlers; };
|
||||
|
||||
// for HTTP
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,13 @@ namespace i2p
|
||||
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage ()
|
||||
{
|
||||
auto msg = new I2NPMessageBuffer<i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34>(); // reserved for alignment and NTCP 16 + 6 + 12
|
||||
msg->Align (12);
|
||||
return std::shared_ptr<I2NPMessage>(msg);
|
||||
}
|
||||
|
||||
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len)
|
||||
{
|
||||
return (len < I2NP_MAX_SHORT_MESSAGE_SIZE/2) ? NewI2NPShortMessage () : NewI2NPMessage ();
|
||||
@@ -102,7 +109,7 @@ namespace i2p
|
||||
{
|
||||
RAND_bytes ((uint8_t *)&msgID, 4);
|
||||
htobe32buf (buf + DELIVERY_STATUS_MSGID_OFFSET, msgID);
|
||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, I2PD_NET_ID);
|
||||
htobe64buf (buf + DELIVERY_STATUS_TIMESTAMP_OFFSET, i2p::context.GetNetID ());
|
||||
}
|
||||
m->len += DELIVERY_STATUS_SIZE;
|
||||
m->FillI2NPMessageHeader (eI2NPDeliveryStatus);
|
||||
@@ -464,7 +471,7 @@ namespace i2p
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
|
||||
{
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
auto msg = NewI2NPTunnelMessage ();
|
||||
msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
|
||||
msg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
return msg;
|
||||
@@ -472,7 +479,7 @@ namespace i2p
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
|
||||
{
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
auto msg = NewI2NPTunnelMessage ();
|
||||
htobe32buf (msg->GetPayload (), tunnelID);
|
||||
msg->len += 4; // tunnelID
|
||||
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
|
||||
@@ -482,7 +489,7 @@ namespace i2p
|
||||
|
||||
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
|
||||
{
|
||||
auto msg = NewI2NPShortMessage ();
|
||||
auto msg = NewI2NPTunnelMessage ();
|
||||
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
|
||||
return msg;
|
||||
}
|
||||
@@ -547,7 +554,6 @@ namespace i2p
|
||||
uint8_t typeID = msg[I2NP_HEADER_TYPEID_OFFSET];
|
||||
uint32_t msgID = bufbe32toh (msg + I2NP_HEADER_MSGID_OFFSET);
|
||||
LogPrint (eLogDebug, "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)
|
||||
|
||||
189
I2PControl.cpp
189
I2PControl.cpp
@@ -2,6 +2,8 @@
|
||||
#include <sstream>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/date_time/local_time/local_time.hpp>
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
|
||||
@@ -14,7 +16,6 @@
|
||||
#include "Crypto.h"
|
||||
#include "FS.h"
|
||||
#include "Log.h"
|
||||
#include "HTTP.h"
|
||||
#include "Config.h"
|
||||
#include "NetDb.h"
|
||||
#include "RouterContext.h"
|
||||
@@ -24,6 +25,7 @@
|
||||
#include "Transports.h"
|
||||
#include "version.h"
|
||||
#include "util.h"
|
||||
#include "ClientContext.h"
|
||||
#include "I2PControl.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -77,6 +79,8 @@ namespace client
|
||||
m_RouterInfoHandlers["i2p.router.net.bw.outbound.1s"] = &I2PControlService::OutboundBandwidth1S;
|
||||
m_RouterInfoHandlers["i2p.router.net.status"] = &I2PControlService::NetStatusHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.tunnels.participating"] = &I2PControlService::TunnelsParticipatingHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.tunnels.successrate"] =
|
||||
&I2PControlService::TunnelsSuccessRateHandler;
|
||||
m_RouterInfoHandlers["i2p.router.net.total.received.bytes"] = &I2PControlService::NetTotalReceivedBytes;
|
||||
m_RouterInfoHandlers["i2p.router.net.total.sent.bytes"] = &I2PControlService::NetTotalSentBytes;
|
||||
|
||||
@@ -186,67 +190,84 @@ namespace client
|
||||
size_t bytes_transferred, std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf)
|
||||
{
|
||||
if (ecode) {
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: read error: ", ecode.message ());
|
||||
return;
|
||||
}
|
||||
/* try to parse received data */
|
||||
std::stringstream json;
|
||||
std::string response;
|
||||
bool isHTTP = false;
|
||||
if (memcmp (buf->data (), "POST", 4) == 0) {
|
||||
long int remains = 0;
|
||||
isHTTP = true;
|
||||
i2p::http::HTTPReq req;
|
||||
std::size_t len = req.parse(buf->data(), bytes_transferred);
|
||||
if (len <= 0) {
|
||||
LogPrint(eLogError, "I2PControl: incomplete/malformed POST request");
|
||||
return;
|
||||
}
|
||||
/* append to json chunk of data from 1st request */
|
||||
json.write(buf->data() + len, bytes_transferred - len);
|
||||
remains = req.content_length() - len;
|
||||
/* if request has Content-Length header, fetch rest of data and store to json buffer */
|
||||
while (remains > 0) {
|
||||
len = ((long int) buf->size() < remains) ? buf->size() : remains;
|
||||
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), len));
|
||||
json.write(buf->data(), bytes_transferred);
|
||||
remains -= bytes_transferred;
|
||||
}
|
||||
} else {
|
||||
json.write(buf->data(), bytes_transferred);
|
||||
}
|
||||
LogPrint(eLogDebug, "I2PControl: json from request: ", json.str());
|
||||
}
|
||||
else
|
||||
{
|
||||
bool isHtml = !memcmp (buf->data (), "POST", 4);
|
||||
try
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss.write (buf->data (), bytes_transferred);
|
||||
if (isHtml)
|
||||
{
|
||||
std::string header;
|
||||
size_t contentLength = 0;
|
||||
while (!ss.eof () && header != "\r")
|
||||
{
|
||||
std::getline(ss, header);
|
||||
auto colon = header.find (':');
|
||||
if (colon != std::string::npos && header.substr (0, colon) == "Content-Length")
|
||||
contentLength = std::stoi (header.substr (colon + 1));
|
||||
}
|
||||
if (ss.eof ())
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: malformed request, HTTP header expected");
|
||||
return; // TODO:
|
||||
}
|
||||
std::streamoff rem = contentLength + ss.tellg () - bytes_transferred; // more bytes to read
|
||||
if (rem > 0)
|
||||
{
|
||||
bytes_transferred = boost::asio::read (*socket, boost::asio::buffer (buf->data (), rem));
|
||||
ss.write (buf->data (), bytes_transferred);
|
||||
}
|
||||
}
|
||||
std::ostringstream response;
|
||||
#if GCC47_BOOST149
|
||||
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
|
||||
BuildErrorResponse(response, 32603, "JSON requests is not supported with this version of boost");
|
||||
LogPrint (eLogError, "I2PControl: json_read is not supported due bug in boost 1.49 with gcc 4.7");
|
||||
response << "{\"id\":null,\"error\":";
|
||||
response << "{\"code\":-32603,\"message\":\"JSON requests is not supported with this version of boost\"},";
|
||||
response << "\"jsonrpc\":\"2.0\"}";
|
||||
#else
|
||||
/* now try to parse json itself */
|
||||
try {
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json (json, pt);
|
||||
boost::property_tree::ptree pt;
|
||||
boost::property_tree::read_json (ss, pt);
|
||||
|
||||
std::string id = pt.get<std::string>("id");
|
||||
std::string method = pt.get<std::string>("method");
|
||||
auto it = m_MethodHandlers.find (method);
|
||||
if (it != m_MethodHandlers.end ()) {
|
||||
std::ostringstream ss;
|
||||
ss << "{\"id\":" << id << ",\"result\":{";
|
||||
(this->*(it->second))(pt.get_child ("params"), ss);
|
||||
ss << "},\"jsonrpc\":\"2.0\"}";
|
||||
response = ss.str();
|
||||
} else {
|
||||
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
|
||||
BuildErrorResponse(response, 32601, "Method not found");
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
|
||||
BuildErrorResponse(response, 32603, ex.what());
|
||||
} catch (...) {
|
||||
LogPrint (eLogError, "I2PControl: handle request unknown exception");
|
||||
}
|
||||
std::string id = pt.get<std::string>("id");
|
||||
std::string method = pt.get<std::string>("method");
|
||||
auto it = m_MethodHandlers.find (method);
|
||||
if (it != m_MethodHandlers.end ())
|
||||
{
|
||||
response << "{\"id\":" << id << ",\"result\":{";
|
||||
(this->*(it->second))(pt.get_child ("params"), response);
|
||||
response << "},\"jsonrpc\":\"2.0\"}";
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "I2PControl: unknown method ", method);
|
||||
response << "{\"id\":null,\"error\":";
|
||||
response << "{\"code\":-32601,\"message\":\"Method not found\"},";
|
||||
response << "\"jsonrpc\":\"2.0\"}";
|
||||
}
|
||||
#endif
|
||||
SendResponse (socket, buf, response, isHTTP);
|
||||
SendResponse (socket, buf, response, isHtml);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: exception when handle request: ", ex.what ());
|
||||
std::ostringstream response;
|
||||
response << "{\"id\":null,\"error\":";
|
||||
response << "{\"code\":-32700,\"message\":\"" << ex.what () << "\"},";
|
||||
response << "\"jsonrpc\":\"2.0\"}";
|
||||
SendResponse (socket, buf, response, isHtml);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LogPrint (eLogError, "I2PControl: handle request unknown exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void I2PControlService::InsertParam (std::ostringstream& ss, const std::string& name, int value) const
|
||||
@@ -268,28 +289,27 @@ namespace client
|
||||
ss << "\"" << name << "\":" << std::fixed << std::setprecision(2) << value;
|
||||
}
|
||||
|
||||
void I2PControlService::BuildErrorResponse (std::string & content, int code, const char *message) {
|
||||
std::stringstream ss;
|
||||
ss << "{\"id\":null,\"error\":";
|
||||
ss << "{\"code\":" << -code << ",\"message\":\"" << message << "\"},";
|
||||
ss << "\"jsonrpc\":\"2.0\"}";
|
||||
content = ss.str();
|
||||
}
|
||||
|
||||
void I2PControlService::SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::string& content, bool isHTTP)
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::ostringstream& response, bool isHtml)
|
||||
{
|
||||
if (isHTTP) {
|
||||
i2p::http::HTTPRes res;
|
||||
res.code = 200;
|
||||
res.add_header("Content-Type", "application/json");
|
||||
res.add_header("Connection", "close");
|
||||
res.body = content;
|
||||
std::string tmp = res.to_string();
|
||||
content = tmp;
|
||||
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);
|
||||
}
|
||||
std::copy(content.begin(), content.end(), buf->begin());
|
||||
boost::asio::async_write (*socket, boost::asio::buffer (buf->data (), content.length()),
|
||||
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));
|
||||
@@ -316,7 +336,7 @@ namespace client
|
||||
}
|
||||
InsertParam (results, "API", api);
|
||||
results << ",";
|
||||
std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch ());
|
||||
std::string token = boost::lexical_cast<std::string>(i2p::util::GetSecondsSinceEpoch ());
|
||||
m_Tokens.insert (token);
|
||||
InsertParam (results, "Token", token);
|
||||
}
|
||||
@@ -358,7 +378,7 @@ namespace client
|
||||
|
||||
void I2PControlService::RouterInfoHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); ++it)
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
LogPrint (eLogDebug, "I2PControl: RouterInfo request: ", it->first);
|
||||
auto it1 = m_RouterInfoHandlers.find (it->first);
|
||||
@@ -384,7 +404,8 @@ namespace client
|
||||
|
||||
void I2PControlService::StatusHandler (std::ostringstream& results)
|
||||
{
|
||||
InsertParam (results, "i2p.router.status", "???"); // TODO:
|
||||
auto dest = i2p::client::context.GetSharedLocalDestination ();
|
||||
InsertParam (results, "i2p.router.status", (dest && dest->IsReady ()) ? "1" : "0");
|
||||
}
|
||||
|
||||
void I2PControlService::NetDbKnownPeersHandler (std::ostringstream& results)
|
||||
@@ -408,6 +429,12 @@ namespace client
|
||||
InsertParam (results, "i2p.router.net.tunnels.participating", transit);
|
||||
}
|
||||
|
||||
void I2PControlService::TunnelsSuccessRateHandler (std::ostringstream& results)
|
||||
{
|
||||
int rate = i2p::tunnel::tunnels.GetTunnelCreationSuccessRate ();
|
||||
InsertParam (results, "i2p.router.net.tunnels.successrate", rate);
|
||||
}
|
||||
|
||||
void I2PControlService::InboundBandwidth1S (std::ostringstream& results)
|
||||
{
|
||||
double bw = i2p::transport::transports.GetInBandwidth ();
|
||||
@@ -434,7 +461,7 @@ namespace client
|
||||
|
||||
void I2PControlService::RouterManagerHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); ++it)
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
if (it != params.begin ()) results << ",";
|
||||
LogPrint (eLogDebug, "I2PControl: RouterManager request: ", it->first);
|
||||
@@ -483,7 +510,7 @@ namespace client
|
||||
// network setting
|
||||
void I2PControlService::NetworkSettingHandler (const boost::property_tree::ptree& params, std::ostringstream& results)
|
||||
{
|
||||
for (auto it = params.begin (); it != params.end (); ++it)
|
||||
for (auto it = params.begin (); it != params.end (); it++)
|
||||
{
|
||||
if (it != params.begin ()) results << ",";
|
||||
LogPrint (eLogDebug, "I2PControl: NetworkSetting request: ", it->first);
|
||||
@@ -529,7 +556,7 @@ namespace client
|
||||
X509_gmtime_adj (X509_get_notAfter (x509), I2P_CONTROL_CERTIFICATE_VALIDITY*24*60*60); // expiration
|
||||
X509_set_pubkey (x509, pkey); // public key
|
||||
X509_NAME * name = X509_get_subject_name (x509);
|
||||
X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"RU", -1, -1, 0); // country (Russia by default)
|
||||
X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *)"A1", -1, -1, 0); // country (Anonymous proxy)
|
||||
X509_NAME_add_entry_by_txt (name, "O", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_ORGANIZATION, -1, -1, 0); // organization
|
||||
X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, (unsigned char *)I2P_CONTROL_CERTIFICATE_COMMON_NAME, -1, -1, 0); // common name
|
||||
X509_set_issuer_name (x509, name); // set issuer to ourselves
|
||||
|
||||
@@ -45,9 +45,8 @@ namespace client
|
||||
void ReadRequest (std::shared_ptr<ssl_socket> socket);
|
||||
void HandleRequestReceived (const boost::system::error_code& ecode, size_t bytes_transferred,
|
||||
std::shared_ptr<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
void BuildErrorResponse (std::string & content, int code, const char *message);
|
||||
void SendResponse (std::shared_ptr<ssl_socket> socket,
|
||||
std::shared_ptr<I2PControlBuffer> buf, std::string& response, bool isHtml);
|
||||
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<ssl_socket> socket, std::shared_ptr<I2PControlBuffer> buf);
|
||||
|
||||
@@ -82,6 +81,7 @@ namespace client
|
||||
void NetDbActivePeersHandler (std::ostringstream& results);
|
||||
void NetStatusHandler (std::ostringstream& results);
|
||||
void TunnelsParticipatingHandler (std::ostringstream& results);
|
||||
void TunnelsSuccessRateHandler (std::ostringstream& results);
|
||||
void InboundBandwidth1S (std::ostringstream& results);
|
||||
void OutboundBandwidth1S (std::ostringstream& results);
|
||||
void NetTotalReceivedBytes (std::ostringstream& results);
|
||||
@@ -120,4 +120,3 @@ namespace client
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -32,7 +32,12 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream) {}
|
||||
TCPIPPipe::TCPIPPipe(I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> upstream, std::shared_ptr<boost::asio::ip::tcp::socket> downstream) : I2PServiceHandler(owner), m_up(upstream), m_down(downstream)
|
||||
{
|
||||
boost::asio::socket_base::receive_buffer_size option(TCP_IP_PIPE_BUFFER_SIZE);
|
||||
upstream->set_option(option);
|
||||
downstream->set_option(option);
|
||||
}
|
||||
|
||||
TCPIPPipe::~TCPIPPipe()
|
||||
{
|
||||
@@ -48,7 +53,6 @@ namespace client
|
||||
void TCPIPPipe::Terminate()
|
||||
{
|
||||
if(Kill()) return;
|
||||
Done(shared_from_this());
|
||||
if (m_up) {
|
||||
if (m_up->is_open()) {
|
||||
m_up->close();
|
||||
@@ -61,6 +65,7 @@ namespace client
|
||||
}
|
||||
m_down = nullptr;
|
||||
}
|
||||
Done(shared_from_this());
|
||||
}
|
||||
|
||||
void TCPIPPipe::AsyncReceiveUpstream()
|
||||
@@ -85,11 +90,11 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::UpstreamWrite(const uint8_t * buf, size_t len)
|
||||
void TCPIPPipe::UpstreamWrite(size_t len)
|
||||
{
|
||||
if (m_up) {
|
||||
LogPrint(eLogDebug, "TCPIPPipe: upstream: ", (int) len, " bytes written");
|
||||
boost::asio::async_write(*m_up, boost::asio::buffer(buf, len),
|
||||
boost::asio::async_write(*m_up, boost::asio::buffer(m_upstream_buf, len),
|
||||
boost::asio::transfer_all(),
|
||||
std::bind(&TCPIPPipe::HandleUpstreamWrite,
|
||||
shared_from_this(),
|
||||
@@ -100,11 +105,11 @@ namespace client
|
||||
}
|
||||
}
|
||||
|
||||
void TCPIPPipe::DownstreamWrite(const uint8_t * buf, size_t len)
|
||||
void TCPIPPipe::DownstreamWrite(size_t len)
|
||||
{
|
||||
if (m_down) {
|
||||
LogPrint(eLogDebug, "TCPIPPipe: downstream: ", (int) len, " bytes written");
|
||||
boost::asio::async_write(*m_down, boost::asio::buffer(buf, len),
|
||||
boost::asio::async_write(*m_down, boost::asio::buffer(m_downstream_buf, len),
|
||||
boost::asio::transfer_all(),
|
||||
std::bind(&TCPIPPipe::HandleDownstreamWrite,
|
||||
shared_from_this(),
|
||||
@@ -126,9 +131,8 @@ namespace client
|
||||
} else {
|
||||
if (bytes_transfered > 0 ) {
|
||||
memcpy(m_upstream_buf, m_downstream_to_up_buf, bytes_transfered);
|
||||
UpstreamWrite(m_upstream_buf, bytes_transfered);
|
||||
}
|
||||
AsyncReceiveDownstream();
|
||||
UpstreamWrite(bytes_transfered);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,6 +141,8 @@ namespace client
|
||||
LogPrint(eLogError, "TCPIPPipe: downstream write error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
} else {
|
||||
AsyncReceiveUpstream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +151,8 @@ namespace client
|
||||
LogPrint(eLogError, "TCPIPPipe: upstream write error:" , ecode.message());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate();
|
||||
} else {
|
||||
AsyncReceiveDownstream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,10 +165,9 @@ namespace client
|
||||
Terminate();
|
||||
} else {
|
||||
if (bytes_transfered > 0 ) {
|
||||
memcpy(m_upstream_buf, m_upstream_to_down_buf, bytes_transfered);
|
||||
DownstreamWrite(m_upstream_buf, bytes_transfered);
|
||||
memcpy(m_downstream_buf, m_upstream_to_down_buf, bytes_transfered);
|
||||
}
|
||||
AsyncReceiveUpstream();
|
||||
DownstreamWrite(bytes_transfered);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
I2PService.h
12
I2PService.h
@@ -38,6 +38,7 @@ namespace client
|
||||
}
|
||||
|
||||
inline std::shared_ptr<ClientDestination> GetLocalDestination () { return m_LocalDestination; }
|
||||
inline std::shared_ptr<const ClientDestination> GetLocalDestination () const { 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);
|
||||
|
||||
@@ -76,7 +77,7 @@ namespace client
|
||||
std::atomic<bool> m_Dead; //To avoid cleaning up multiple times
|
||||
};
|
||||
|
||||
const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192;
|
||||
const size_t TCP_IP_PIPE_BUFFER_SIZE = 8192 * 8;
|
||||
|
||||
// bidirectional pipe for 2 tcp/ip sockets
|
||||
class TCPIPPipe: public I2PServiceHandler, public std::enable_shared_from_this<TCPIPPipe> {
|
||||
@@ -92,8 +93,8 @@ namespace client
|
||||
void HandleDownstreamReceived(const boost::system::error_code & ecode, std::size_t bytes_transferred);
|
||||
void HandleUpstreamWrite(const boost::system::error_code & ecode);
|
||||
void HandleDownstreamWrite(const boost::system::error_code & ecode);
|
||||
void UpstreamWrite(const uint8_t * buf, size_t len);
|
||||
void DownstreamWrite(const uint8_t * buf, size_t len);
|
||||
void UpstreamWrite(size_t len);
|
||||
void DownstreamWrite(size_t len);
|
||||
private:
|
||||
uint8_t m_upstream_to_down_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_to_up_buf[TCP_IP_PIPE_BUFFER_SIZE];
|
||||
uint8_t m_upstream_buf[TCP_IP_PIPE_BUFFER_SIZE], m_downstream_buf[TCP_IP_PIPE_BUFFER_SIZE];
|
||||
@@ -120,10 +121,11 @@ namespace client
|
||||
void Stop ();
|
||||
|
||||
const boost::asio::ip::tcp::acceptor& GetAcceptor () const { return m_Acceptor; };
|
||||
|
||||
|
||||
virtual const char* GetName() { return "Generic TCP/IP accepting daemon"; }
|
||||
|
||||
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);
|
||||
|
||||
459
I2PTunnel.cpp
459
I2PTunnel.cpp
@@ -9,6 +9,17 @@ namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
|
||||
/** set standard socket options */
|
||||
static void I2PTunnelSetSocketOptions(std::shared_ptr<boost::asio::ip::tcp::socket> socket)
|
||||
{
|
||||
if (socket && socket->is_open())
|
||||
{
|
||||
boost::asio::socket_base::receive_buffer_size option(I2P_TUNNEL_CONNECTION_BUFFER_SIZE);
|
||||
socket->set_option(option);
|
||||
}
|
||||
}
|
||||
|
||||
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 ()),
|
||||
@@ -34,7 +45,7 @@ namespace client
|
||||
I2PTunnelConnection::~I2PTunnelConnection ()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void I2PTunnelConnection::I2PConnect (const uint8_t * msg, size_t len)
|
||||
{
|
||||
if (m_Stream)
|
||||
@@ -47,12 +58,44 @@ namespace client
|
||||
StreamReceive ();
|
||||
Receive ();
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Connect ()
|
||||
|
||||
static boost::asio::ip::address GetLoopbackAddressFor(const i2p::data::IdentHash & addr)
|
||||
{
|
||||
if (m_Socket)
|
||||
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
|
||||
boost::asio::ip::address_v4::bytes_type bytes;
|
||||
const uint8_t * ident = addr;
|
||||
bytes[0] = 127;
|
||||
memcpy (bytes.data ()+1, ident, 3);
|
||||
boost::asio::ip::address ourIP = boost::asio::ip::address_v4 (bytes);
|
||||
return ourIP;
|
||||
}
|
||||
|
||||
static void MapToLoopback(const std::shared_ptr<boost::asio::ip::tcp::socket> & sock, const i2p::data::IdentHash & addr)
|
||||
{
|
||||
|
||||
// bind to 127.x.x.x address
|
||||
// where x.x.x are first three bytes from ident
|
||||
auto ourIP = GetLoopbackAddressFor(addr);
|
||||
sock->bind (boost::asio::ip::tcp::endpoint (ourIP, 0));
|
||||
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Connect (bool isUniqueLocal)
|
||||
{
|
||||
I2PTunnelSetSocketOptions(m_Socket);
|
||||
if (m_Socket)
|
||||
{
|
||||
#ifdef __linux__
|
||||
if (isUniqueLocal && m_RemoteEndpoint.address ().is_v4 () &&
|
||||
m_RemoteEndpoint.address ().to_v4 ().to_bytes ()[0] == 127)
|
||||
{
|
||||
m_Socket->open (boost::asio::ip::tcp::v4 ());
|
||||
auto ident = m_Stream->GetRemoteIdentity()->GetIdentHash();
|
||||
MapToLoopback(m_Socket, ident);
|
||||
}
|
||||
#endif
|
||||
m_Socket->async_connect (m_RemoteEndpoint, std::bind (&I2PTunnelConnection::HandleConnect,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
void I2PTunnelConnection::Terminate ()
|
||||
@@ -63,7 +106,10 @@ namespace client
|
||||
m_Stream->Close ();
|
||||
m_Stream.reset ();
|
||||
}
|
||||
boost::system::error_code ec;
|
||||
m_Socket->shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); // avoid RST
|
||||
m_Socket->close ();
|
||||
|
||||
Done(shared_from_this ());
|
||||
}
|
||||
|
||||
@@ -78,9 +124,11 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: read error: ", ecode.message ());
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -139,14 +187,18 @@ namespace client
|
||||
{
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "I2PTunnel: stream read error: ", ecode.message ());
|
||||
if (bytes_transferred > 0)
|
||||
Write (m_StreamBuffer, bytes_transferred); // postpone termination
|
||||
else
|
||||
else if (ecode == boost::asio::error::timed_out && m_Stream && m_Stream->IsOpen ())
|
||||
StreamReceive ();
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
else
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
Write (m_StreamBuffer, bytes_transferred);
|
||||
@@ -175,21 +227,72 @@ namespace client
|
||||
// send destination first like received from I2P
|
||||
std::string dest = m_Stream->GetRemoteIdentity ()->ToBase64 ();
|
||||
dest += "\n";
|
||||
memcpy (m_StreamBuffer, dest.c_str (), dest.size ());
|
||||
if(sizeof(m_StreamBuffer) >= dest.size()) {
|
||||
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,
|
||||
void I2PClientTunnelConnectionHTTP::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 == "\r") endOfHeader = true;
|
||||
else
|
||||
{
|
||||
if (!m_ConnectionSent && !line.compare(0, 10, "Connection"))
|
||||
{
|
||||
m_OutHeader << "Connection: close\r\n";
|
||||
m_ConnectionSent = true;
|
||||
}
|
||||
else if (!m_ProxyConnectionSent && !line.compare(0, 16, "Proxy-Connection"))
|
||||
{
|
||||
m_OutHeader << "Proxy-Connection: close\r\n";
|
||||
m_ProxyConnectionSent = true;
|
||||
}
|
||||
else
|
||||
m_OutHeader << line << "\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (endOfHeader)
|
||||
{
|
||||
if (!m_ConnectionSent) m_OutHeader << "Connection: close\r\n";
|
||||
if (!m_ProxyConnectionSent) m_OutHeader << "Proxy-Connection: close\r\n";
|
||||
m_OutHeader << "\r\n"; // end of header
|
||||
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
|
||||
m_InHeader.str ("");
|
||||
m_HeaderSent = true;
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
I2PServerTunnelConnectionHTTP::I2PServerTunnelConnectionHTTP (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), m_From (stream->GetRemoteIdentity ())
|
||||
{
|
||||
}
|
||||
|
||||
void I2PTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
|
||||
void I2PServerTunnelConnectionHTTP::Write (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_HeaderSent)
|
||||
I2PTunnelConnection::Write (buf, len);
|
||||
@@ -207,8 +310,8 @@ namespace client
|
||||
if (line == "\r") endOfHeader = true;
|
||||
else
|
||||
{
|
||||
if (line.find ("Host:") != std::string::npos)
|
||||
m_OutHeader << "Host: " << m_Host << "\r\n";
|
||||
if (m_Host.length () > 0 && line.find ("Host:") != std::string::npos)
|
||||
m_OutHeader << "Host: " << m_Host << "\r\n"; // override host
|
||||
else
|
||||
m_OutHeader << line << "\n";
|
||||
}
|
||||
@@ -228,6 +331,7 @@ namespace client
|
||||
{
|
||||
m_OutHeader << "\r\n"; // end of header
|
||||
m_OutHeader << m_InHeader.str ().substr (m_InHeader.tellg ()); // data right after header
|
||||
m_InHeader.str ("");
|
||||
m_HeaderSent = true;
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutHeader.str ().c_str (), m_OutHeader.str ().length ());
|
||||
}
|
||||
@@ -244,26 +348,24 @@ namespace client
|
||||
|
||||
void I2PTunnelConnectionIRC::Write (const uint8_t * buf, size_t len)
|
||||
{
|
||||
if (m_NeedsWebIrc) {
|
||||
m_OutPacket.str ("");
|
||||
if (m_NeedsWebIrc)
|
||||
{
|
||||
m_NeedsWebIrc = false;
|
||||
m_OutPacket.str ("");
|
||||
m_OutPacket << "WEBIRC " << this->m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " 127.0.0.1\n";
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
|
||||
m_OutPacket << "WEBIRC " << m_WebircPass << " cgiirc " << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ()) << " " << GetSocket ()->local_endpoint ().address () << std::endl;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
m_OutPacket.str ("");
|
||||
m_InPacket.clear ();
|
||||
m_InPacket.write ((const char *)buf, len);
|
||||
|
||||
while (!m_InPacket.eof () && !m_InPacket.fail ())
|
||||
{
|
||||
std::string line;
|
||||
std::getline (m_InPacket, line);
|
||||
if (line.length () == 0 && m_InPacket.eof ()) {
|
||||
if (line.length () == 0 && m_InPacket.eof ())
|
||||
m_InPacket.str ("");
|
||||
}
|
||||
auto pos = line.find ("USER");
|
||||
if (pos != std::string::npos && pos == 0)
|
||||
if (!pos) // start of line
|
||||
{
|
||||
pos = line.find (" ");
|
||||
pos++;
|
||||
@@ -273,9 +375,9 @@ namespace client
|
||||
m_OutPacket << line.substr (0, pos);
|
||||
m_OutPacket << context.GetAddressBook ().ToAddress (m_From->GetIdentHash ());
|
||||
m_OutPacket << line.substr (nextpos) << '\n';
|
||||
} else {
|
||||
}
|
||||
else
|
||||
m_OutPacket << line << '\n';
|
||||
}
|
||||
}
|
||||
I2PTunnelConnection::Write ((uint8_t *)m_OutPacket.str ().c_str (), m_OutPacket.str ().length ());
|
||||
}
|
||||
@@ -380,11 +482,11 @@ namespace client
|
||||
|
||||
I2PServerTunnel::I2PServerTunnel (const std::string& name, const std::string& address,
|
||||
int port, std::shared_ptr<ClientDestination> localDestination, int inport, bool gzip):
|
||||
I2PService (localDestination), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false)
|
||||
I2PService (localDestination), m_IsUniqueLocal(true), m_Name (name), m_Address (address), m_Port (port), m_IsAccessList (false)
|
||||
{
|
||||
m_PortDestination = localDestination->CreateStreamingDestination (inport > 0 ? inport : port, gzip);
|
||||
}
|
||||
|
||||
|
||||
void I2PServerTunnel::Start ()
|
||||
{
|
||||
m_Endpoint.port (m_Port);
|
||||
@@ -457,31 +559,31 @@ namespace client
|
||||
return;
|
||||
}
|
||||
}
|
||||
CreateI2PConnection (stream);
|
||||
// new connection
|
||||
auto conn = CreateI2PConnection (stream);
|
||||
AddHandler (conn);
|
||||
conn->Connect (m_IsUniqueLocal);
|
||||
}
|
||||
}
|
||||
|
||||
void I2PServerTunnel::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
std::shared_ptr<I2PTunnelConnection> 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 ();
|
||||
return std::make_shared<I2PTunnelConnection> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint ());
|
||||
|
||||
}
|
||||
|
||||
I2PServerTunnelHTTP::I2PServerTunnelHTTP (const std::string& name, const std::string& address,
|
||||
int port, std::shared_ptr<ClientDestination> localDestination,
|
||||
const std::string& host, int inport, bool gzip):
|
||||
I2PServerTunnel (name, address, port, localDestination, inport, gzip),
|
||||
m_Host (host.length () > 0 ? host : address)
|
||||
m_Host (host)
|
||||
{
|
||||
}
|
||||
|
||||
void I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelHTTP::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
auto conn = std::make_shared<I2PTunnelConnectionHTTP> (this, stream,
|
||||
return std::make_shared<I2PServerTunnelConnectionHTTP> (this, stream,
|
||||
std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), m_Host);
|
||||
AddHandler (conn);
|
||||
conn->Connect ();
|
||||
}
|
||||
|
||||
I2PServerTunnelIRC::I2PServerTunnelIRC (const std::string& name, const std::string& address,
|
||||
@@ -492,12 +594,283 @@ namespace client
|
||||
{
|
||||
}
|
||||
|
||||
void I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
std::shared_ptr<I2PTunnelConnection> I2PServerTunnelIRC::CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream)
|
||||
{
|
||||
auto conn = std::make_shared<I2PTunnelConnectionIRC> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), this->m_WebircPass);
|
||||
AddHandler (conn);
|
||||
conn->Connect ();
|
||||
return std::make_shared<I2PTunnelConnectionIRC> (this, stream, std::make_shared<boost::asio::ip::tcp::socket> (GetService ()), GetEndpoint (), this->m_WebircPass);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
void I2PUDPServerTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||
auto session = ObtainUDPSession(from, toPort, fromPort);
|
||||
session->IPSocket.send_to(boost::asio::buffer(buf, len), m_RemoteEndpoint);
|
||||
session->LastActivity = i2p::util::GetMillisecondsSinceEpoch();
|
||||
}
|
||||
|
||||
void I2PUDPServerTunnel::ExpireStale(const uint64_t delta) {
|
||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
||||
auto itr = m_Sessions.begin();
|
||||
while(itr != m_Sessions.end()) {
|
||||
if(now - (*itr)->LastActivity >= delta )
|
||||
itr = m_Sessions.erase(itr);
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
void I2PUDPClientTunnel::ExpireStale(const uint64_t delta) {
|
||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||
uint64_t now = i2p::util::GetMillisecondsSinceEpoch();
|
||||
std::vector<uint16_t> removePorts;
|
||||
for (const auto & s : m_Sessions) {
|
||||
if (now - s.second.second >= delta)
|
||||
removePorts.push_back(s.first);
|
||||
}
|
||||
for(auto port : removePorts) {
|
||||
m_Sessions.erase(port);
|
||||
}
|
||||
}
|
||||
|
||||
UDPSessionPtr I2PUDPServerTunnel::ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort)
|
||||
{
|
||||
auto ih = from.GetIdentHash();
|
||||
for (auto & s : m_Sessions )
|
||||
{
|
||||
if ( s->Identity == ih)
|
||||
{
|
||||
/** found existing session */
|
||||
LogPrint(eLogDebug, "UDPServer: found session ", s->IPSocket.local_endpoint(), " ", ih.ToBase32());
|
||||
return s;
|
||||
}
|
||||
}
|
||||
boost::asio::ip::address addr;
|
||||
/** create new udp session */
|
||||
if(m_IsUniqueLocal && m_LocalAddress.is_loopback())
|
||||
{
|
||||
auto ident = from.GetIdentHash();
|
||||
addr = GetLoopbackAddressFor(ident);
|
||||
}
|
||||
else
|
||||
addr = m_LocalAddress;
|
||||
boost::asio::ip::udp::endpoint ep(addr, 0);
|
||||
m_Sessions.push_back(std::make_shared<UDPSession>(ep, m_LocalDest, m_RemoteEndpoint, &ih, localPort, remotePort));
|
||||
auto & back = m_Sessions.back();
|
||||
return back;
|
||||
}
|
||||
|
||||
UDPSession::UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
|
||||
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
|
||||
boost::asio::ip::udp::endpoint endpoint, const i2p::data::IdentHash * to,
|
||||
uint16_t ourPort, uint16_t theirPort) :
|
||||
m_Destination(localDestination->GetDatagramDestination()),
|
||||
IPSocket(localDestination->GetService(), localEndpoint),
|
||||
SendEndpoint(endpoint),
|
||||
LastActivity(i2p::util::GetMillisecondsSinceEpoch()),
|
||||
LocalPort(ourPort),
|
||||
RemotePort(theirPort)
|
||||
{
|
||||
memcpy(Identity, to->data(), 32);
|
||||
Receive();
|
||||
}
|
||||
|
||||
|
||||
void UDPSession::Receive() {
|
||||
LogPrint(eLogDebug, "UDPSession: Receive");
|
||||
IPSocket.async_receive_from(boost::asio::buffer(m_Buffer, I2P_UDP_MAX_MTU),
|
||||
FromEndpoint, std::bind(&UDPSession::HandleReceived, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void UDPSession::HandleReceived(const boost::system::error_code & ecode, std::size_t len)
|
||||
{
|
||||
if(!ecode)
|
||||
{
|
||||
LogPrint(eLogDebug, "UDPSession: forward ", len, "B from ", FromEndpoint);
|
||||
LastActivity = i2p::util::GetMillisecondsSinceEpoch();
|
||||
m_Destination->SendDatagramTo(m_Buffer, len, Identity, LocalPort, RemotePort);
|
||||
Receive();
|
||||
} else {
|
||||
LogPrint(eLogError, "UDPSession: ", ecode.message());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
I2PUDPServerTunnel::I2PUDPServerTunnel(const std::string & name, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||
boost::asio::ip::address localAddress, boost::asio::ip::udp::endpoint forwardTo, uint16_t port) :
|
||||
m_IsUniqueLocal(true),
|
||||
m_Name(name),
|
||||
m_LocalAddress(localAddress),
|
||||
m_RemoteEndpoint(forwardTo)
|
||||
{
|
||||
m_LocalDest = localDestination;
|
||||
m_LocalDest->Start();
|
||||
auto dgram = m_LocalDest->CreateDatagramDestination();
|
||||
dgram->SetReceiver(std::bind(&I2PUDPServerTunnel::HandleRecvFromI2P, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
||||
}
|
||||
|
||||
I2PUDPServerTunnel::~I2PUDPServerTunnel()
|
||||
{
|
||||
auto dgram = m_LocalDest->GetDatagramDestination();
|
||||
if (dgram) dgram->ResetReceiver();
|
||||
|
||||
LogPrint(eLogInfo, "UDPServer: done");
|
||||
}
|
||||
|
||||
void I2PUDPServerTunnel::Start() {
|
||||
m_LocalDest->Start();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPServerTunnel::GetSessions()
|
||||
{
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > sessions;
|
||||
std::lock_guard<std::mutex> lock(m_SessionsMutex);
|
||||
|
||||
for ( UDPSessionPtr s : m_Sessions )
|
||||
{
|
||||
if (!s->m_Destination) continue;
|
||||
auto info = s->m_Destination->GetInfoForRemote(s->Identity);
|
||||
if(!info) continue;
|
||||
|
||||
auto sinfo = std::make_shared<DatagramSessionInfo>();
|
||||
sinfo->Name = m_Name;
|
||||
sinfo->LocalIdent = std::make_shared<i2p::data::IdentHash>(m_LocalDest->GetIdentHash().data());
|
||||
sinfo->RemoteIdent = std::make_shared<i2p::data::IdentHash>(s->Identity.data());
|
||||
sinfo->CurrentIBGW = info->IBGW;
|
||||
sinfo->CurrentOBEP = info->OBEP;
|
||||
sessions.push_back(sinfo);
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
I2PUDPClientTunnel::I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
|
||||
boost::asio::ip::udp::endpoint localEndpoint,
|
||||
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||
uint16_t remotePort) :
|
||||
m_Name(name),
|
||||
m_RemoteDest(remoteDest),
|
||||
m_LocalDest(localDestination),
|
||||
m_LocalEndpoint(localEndpoint),
|
||||
m_RemoteIdent(nullptr),
|
||||
m_ResolveThread(nullptr),
|
||||
m_LocalSocket(localDestination->GetService(), localEndpoint),
|
||||
RemotePort(remotePort),
|
||||
m_cancel_resolve(false)
|
||||
{
|
||||
auto dgram = m_LocalDest->CreateDatagramDestination();
|
||||
dgram->SetReceiver(std::bind(&I2PUDPClientTunnel::HandleRecvFromI2P, this,
|
||||
std::placeholders::_1, std::placeholders::_2,
|
||||
std::placeholders::_3, std::placeholders::_4,
|
||||
std::placeholders::_5));
|
||||
}
|
||||
|
||||
|
||||
|
||||
void I2PUDPClientTunnel::Start() {
|
||||
m_LocalDest->Start();
|
||||
if (m_ResolveThread == nullptr)
|
||||
m_ResolveThread = new std::thread(std::bind(&I2PUDPClientTunnel::TryResolving, this));
|
||||
RecvFromLocal();
|
||||
}
|
||||
|
||||
void I2PUDPClientTunnel::RecvFromLocal()
|
||||
{
|
||||
m_LocalSocket.async_receive_from(boost::asio::buffer(m_RecvBuff, I2P_UDP_MAX_MTU),
|
||||
m_RecvEndpoint, std::bind(&I2PUDPClientTunnel::HandleRecvFromLocal, this, std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void I2PUDPClientTunnel::HandleRecvFromLocal(const boost::system::error_code & ec, std::size_t transferred)
|
||||
{
|
||||
if(ec) {
|
||||
LogPrint(eLogError, "UDP Client: ", ec.message());
|
||||
return;
|
||||
}
|
||||
if(!m_RemoteIdent) {
|
||||
LogPrint(eLogWarning, "UDP Client: remote endpoint not resolved yet");
|
||||
RecvFromLocal();
|
||||
return; // drop, remote not resolved
|
||||
}
|
||||
auto remotePort = m_RecvEndpoint.port();
|
||||
auto itr = m_Sessions.find(remotePort);
|
||||
if (itr == m_Sessions.end()) {
|
||||
// track new udp convo
|
||||
m_Sessions[remotePort] = {boost::asio::ip::udp::endpoint(m_RecvEndpoint), 0};
|
||||
}
|
||||
// send off to remote i2p destination
|
||||
LogPrint(eLogDebug, "UDP Client: send ", transferred, " to ", m_RemoteIdent->ToBase32(), ":", RemotePort);
|
||||
m_LocalDest->GetDatagramDestination()->SendDatagramTo(m_RecvBuff, transferred, *m_RemoteIdent, remotePort, RemotePort);
|
||||
// mark convo as active
|
||||
m_Sessions[remotePort].second = i2p::util::GetMillisecondsSinceEpoch();
|
||||
RecvFromLocal();
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > I2PUDPClientTunnel::GetSessions()
|
||||
{
|
||||
// TODO: implement
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > infos;
|
||||
return infos;
|
||||
}
|
||||
|
||||
void I2PUDPClientTunnel::TryResolving() {
|
||||
LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
|
||||
i2p::data::IdentHash * h = new i2p::data::IdentHash;
|
||||
|
||||
while(!context.GetAddressBook().GetIdentHash(m_RemoteDest, *h) && !m_cancel_resolve)
|
||||
{
|
||||
LogPrint(eLogWarning, "UDP Tunnel: failed to lookup ", m_RemoteDest);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
if(m_cancel_resolve)
|
||||
{
|
||||
LogPrint(eLogError, "UDP Tunnel: lookup of ", m_RemoteDest, " was cancelled");
|
||||
return;
|
||||
}
|
||||
m_RemoteIdent = h;
|
||||
LogPrint(eLogInfo, "UDP Tunnel: resolved ", m_RemoteDest, " to ", m_RemoteIdent->ToBase32());
|
||||
}
|
||||
|
||||
void I2PUDPClientTunnel::HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len)
|
||||
{
|
||||
if(m_RemoteIdent && from.GetIdentHash() == *m_RemoteIdent)
|
||||
{
|
||||
auto itr = m_Sessions.find(toPort);
|
||||
// found convo ?
|
||||
if(itr != m_Sessions.end())
|
||||
{
|
||||
// found convo
|
||||
if (len > 0) {
|
||||
LogPrint(eLogDebug, "UDP Client: got ", len, "B from ", from.GetIdentHash().ToBase32());
|
||||
m_LocalSocket.send_to(boost::asio::buffer(buf, len), itr->second.first);
|
||||
// mark convo as active
|
||||
itr->second.second = i2p::util::GetMillisecondsSinceEpoch();
|
||||
}
|
||||
}
|
||||
else
|
||||
LogPrint(eLogWarning, "UDP Client: not tracking udp session using port ", (int) toPort);
|
||||
}
|
||||
else
|
||||
LogPrint(eLogWarning, "UDP Client: unwarrented traffic from ", from.GetIdentHash().ToBase32());
|
||||
|
||||
}
|
||||
|
||||
I2PUDPClientTunnel::~I2PUDPClientTunnel() {
|
||||
auto dgram = m_LocalDest->GetDatagramDestination();
|
||||
if (dgram) dgram->ResetReceiver();
|
||||
|
||||
m_Sessions.clear();
|
||||
|
||||
if(m_LocalSocket.is_open())
|
||||
m_LocalSocket.close();
|
||||
|
||||
m_cancel_resolve = true;
|
||||
|
||||
if(m_ResolveThread)
|
||||
{
|
||||
m_ResolveThread->join();
|
||||
delete m_ResolveThread;
|
||||
m_ResolveThread = nullptr;
|
||||
}
|
||||
if (m_RemoteIdent) delete m_RemoteIdent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
176
I2PTunnel.h
176
I2PTunnel.h
@@ -4,11 +4,13 @@
|
||||
#include <inttypes.h>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Identity.h"
|
||||
#include "Destination.h"
|
||||
#include "Datagram.h"
|
||||
#include "Streaming.h"
|
||||
#include "I2PService.h"
|
||||
|
||||
@@ -16,7 +18,7 @@ namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 8192;
|
||||
const size_t I2P_TUNNEL_CONNECTION_BUFFER_SIZE = 65536;
|
||||
const int I2P_TUNNEL_CONNECTION_MAX_IDLE = 3600; // in seconds
|
||||
const int I2P_TUNNEL_DESTINATION_REQUEST_TIMEOUT = 10; // in seconds
|
||||
// for HTTP tunnels
|
||||
@@ -36,7 +38,7 @@ namespace client
|
||||
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 ();
|
||||
void Connect (bool isUniqueLocal = true);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -51,6 +53,8 @@ namespace client
|
||||
void HandleStreamReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred);
|
||||
void HandleConnect (const boost::system::error_code& ecode);
|
||||
|
||||
std::shared_ptr<const boost::asio::ip::tcp::socket> GetSocket () const { return m_Socket; };
|
||||
|
||||
private:
|
||||
|
||||
uint8_t m_Buffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE], m_StreamBuffer[I2P_TUNNEL_CONNECTION_BUFFER_SIZE];
|
||||
@@ -60,11 +64,30 @@ namespace client
|
||||
bool m_IsQuiet; // don't send destination
|
||||
};
|
||||
|
||||
class I2PTunnelConnectionHTTP: public I2PTunnelConnection
|
||||
class I2PClientTunnelConnectionHTTP: public I2PTunnelConnection
|
||||
{
|
||||
public:
|
||||
|
||||
I2PClientTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<boost::asio::ip::tcp::socket> socket,
|
||||
std::shared_ptr<i2p::stream::Stream> stream):
|
||||
I2PTunnelConnection (owner, socket, stream), m_HeaderSent (false),
|
||||
m_ConnectionSent (false), m_ProxyConnectionSent (false) {};
|
||||
|
||||
protected:
|
||||
|
||||
void Write (const uint8_t * buf, size_t len);
|
||||
|
||||
private:
|
||||
|
||||
std::stringstream m_InHeader, m_OutHeader;
|
||||
bool m_HeaderSent, m_ConnectionSent, m_ProxyConnectionSent;
|
||||
};
|
||||
|
||||
class I2PServerTunnelConnectionHTTP: public I2PTunnelConnection
|
||||
{
|
||||
public:
|
||||
|
||||
I2PTunnelConnectionHTTP (I2PService * owner, std::shared_ptr<i2p::stream::Stream> stream,
|
||||
I2PServerTunnelConnectionHTTP (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);
|
||||
|
||||
@@ -128,8 +151,131 @@ namespace client
|
||||
std::string m_Name, m_Destination;
|
||||
const i2p::data::IdentHash * m_DestinationIdentHash;
|
||||
int m_DestinationPort;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
/** 2 minute timeout for udp sessions */
|
||||
const uint64_t I2P_UDP_SESSION_TIMEOUT = 1000 * 60 * 2;
|
||||
|
||||
/** max size for i2p udp */
|
||||
const size_t I2P_UDP_MAX_MTU = i2p::datagram::MAX_DATAGRAM_SIZE;
|
||||
|
||||
struct UDPSession
|
||||
{
|
||||
i2p::datagram::DatagramDestination * m_Destination;
|
||||
boost::asio::ip::udp::socket IPSocket;
|
||||
i2p::data::IdentHash Identity;
|
||||
boost::asio::ip::udp::endpoint FromEndpoint;
|
||||
boost::asio::ip::udp::endpoint SendEndpoint;
|
||||
uint64_t LastActivity;
|
||||
|
||||
uint16_t LocalPort;
|
||||
uint16_t RemotePort;
|
||||
|
||||
uint8_t m_Buffer[I2P_UDP_MAX_MTU];
|
||||
|
||||
UDPSession(boost::asio::ip::udp::endpoint localEndpoint,
|
||||
const std::shared_ptr<i2p::client::ClientDestination> & localDestination,
|
||||
boost::asio::ip::udp::endpoint remote, const i2p::data::IdentHash * ident,
|
||||
uint16_t ourPort, uint16_t theirPort);
|
||||
void HandleReceived(const boost::system::error_code & ecode, std::size_t len);
|
||||
void Receive();
|
||||
};
|
||||
|
||||
|
||||
/** read only info about a datagram session */
|
||||
struct DatagramSessionInfo
|
||||
{
|
||||
/** the name of this forward */
|
||||
std::string Name;
|
||||
/** ident hash of local destination */
|
||||
std::shared_ptr<const i2p::data::IdentHash> LocalIdent;
|
||||
/** ident hash of remote destination */
|
||||
std::shared_ptr<const i2p::data::IdentHash> RemoteIdent;
|
||||
/** ident hash of IBGW in use currently in this session or nullptr if none is set */
|
||||
std::shared_ptr<const i2p::data::IdentHash> CurrentIBGW;
|
||||
/** ident hash of OBEP in use for this session or nullptr if none is set */
|
||||
std::shared_ptr<const i2p::data::IdentHash> CurrentOBEP;
|
||||
/** i2p router's udp endpoint */
|
||||
boost::asio::ip::udp::endpoint LocalEndpoint;
|
||||
/** client's udp endpoint */
|
||||
boost::asio::ip::udp::endpoint RemoteEndpoint;
|
||||
/** how long has this converstation been idle in ms */
|
||||
uint64_t idle;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<UDPSession> UDPSessionPtr;
|
||||
|
||||
/** server side udp tunnel, many i2p inbound to 1 ip outbound */
|
||||
class I2PUDPServerTunnel
|
||||
{
|
||||
public:
|
||||
I2PUDPServerTunnel(const std::string & name,
|
||||
std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||
boost::asio::ip::address localAddress,
|
||||
boost::asio::ip::udp::endpoint forwardTo, uint16_t port);
|
||||
~I2PUDPServerTunnel();
|
||||
/** expire stale udp conversations */
|
||||
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
||||
void Start();
|
||||
const char * GetName() const { return m_Name.c_str(); }
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
|
||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
||||
|
||||
void SetUniqueLocal(bool isUniqueLocal = true) { m_IsUniqueLocal = isUniqueLocal; }
|
||||
|
||||
private:
|
||||
|
||||
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
UDPSessionPtr ObtainUDPSession(const i2p::data::IdentityEx& from, uint16_t localPort, uint16_t remotePort);
|
||||
|
||||
private:
|
||||
bool m_IsUniqueLocal;
|
||||
const std::string m_Name;
|
||||
boost::asio::ip::address m_LocalAddress;
|
||||
boost::asio::ip::udp::endpoint m_RemoteEndpoint;
|
||||
std::mutex m_SessionsMutex;
|
||||
std::vector<UDPSessionPtr> m_Sessions;
|
||||
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
|
||||
};
|
||||
|
||||
class I2PUDPClientTunnel
|
||||
{
|
||||
public:
|
||||
I2PUDPClientTunnel(const std::string & name, const std::string &remoteDest,
|
||||
boost::asio::ip::udp::endpoint localEndpoint, std::shared_ptr<i2p::client::ClientDestination> localDestination,
|
||||
uint16_t remotePort);
|
||||
~I2PUDPClientTunnel();
|
||||
void Start();
|
||||
const char * GetName() const { return m_Name.c_str(); }
|
||||
std::vector<std::shared_ptr<DatagramSessionInfo> > GetSessions();
|
||||
|
||||
bool IsLocalDestination(const i2p::data::IdentHash & destination) const { return destination == m_LocalDest->GetIdentHash(); }
|
||||
|
||||
std::shared_ptr<ClientDestination> GetLocalDestination () const { return m_LocalDest; }
|
||||
void ExpireStale(const uint64_t delta=I2P_UDP_SESSION_TIMEOUT);
|
||||
|
||||
private:
|
||||
typedef std::pair<boost::asio::ip::udp::endpoint, uint64_t> UDPConvo;
|
||||
void RecvFromLocal();
|
||||
void HandleRecvFromLocal(const boost::system::error_code & e, std::size_t transferred);
|
||||
void HandleRecvFromI2P(const i2p::data::IdentityEx& from, uint16_t fromPort, uint16_t toPort, const uint8_t * buf, size_t len);
|
||||
void TryResolving();
|
||||
const std::string m_Name;
|
||||
std::mutex m_SessionsMutex;
|
||||
std::map<uint16_t, UDPConvo > m_Sessions; // maps i2p port -> local udp convo
|
||||
const std::string m_RemoteDest;
|
||||
std::shared_ptr<i2p::client::ClientDestination> m_LocalDest;
|
||||
const boost::asio::ip::udp::endpoint m_LocalEndpoint;
|
||||
i2p::data::IdentHash * m_RemoteIdent;
|
||||
std::thread * m_ResolveThread;
|
||||
boost::asio::ip::udp::socket m_LocalSocket;
|
||||
boost::asio::ip::udp::endpoint m_RecvEndpoint;
|
||||
uint8_t m_RecvBuff[I2P_UDP_MAX_MTU];
|
||||
uint16_t RemotePort;
|
||||
bool m_cancel_resolve;
|
||||
};
|
||||
|
||||
class I2PServerTunnel: public I2PService
|
||||
{
|
||||
public:
|
||||
@@ -142,30 +288,36 @@ namespace client
|
||||
|
||||
void SetAccessList (const std::set<i2p::data::IdentHash>& accessList);
|
||||
|
||||
void SetUniqueLocal (bool isUniqueLocal) { m_IsUniqueLocal = isUniqueLocal; }
|
||||
bool IsUniqueLocal () const { return m_IsUniqueLocal; }
|
||||
|
||||
const std::string& GetAddress() const { return m_Address; }
|
||||
int GetPort () const { return m_Port; };
|
||||
uint16_t GetLocalPort () const { return m_PortDestination->GetLocalPort (); };
|
||||
const boost::asio::ip::tcp::endpoint& GetEndpoint () const { return m_Endpoint; }
|
||||
|
||||
const char* GetName() { return m_Name.c_str (); }
|
||||
|
||||
private:
|
||||
|
||||
void SetMaxConnsPerMinute(const uint32_t conns) { m_PortDestination->SetMaxConnsPerMinute(conns); }
|
||||
|
||||
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);
|
||||
virtual std::shared_ptr<I2PTunnelConnection> CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
bool m_IsUniqueLocal;
|
||||
std::string m_Name, 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;
|
||||
bool m_IsAccessList;
|
||||
};
|
||||
|
||||
class I2PServerTunnelHTTP: public I2PServerTunnel
|
||||
@@ -178,7 +330,7 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
std::shared_ptr<I2PTunnelConnection> CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
|
||||
private:
|
||||
|
||||
@@ -195,7 +347,7 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
void CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
std::shared_ptr<I2PTunnelConnection> CreateI2PConnection (std::shared_ptr<i2p::stream::Stream> stream);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
71
Identity.cpp
71
Identity.cpp
@@ -35,11 +35,12 @@ namespace data
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx ():
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type)
|
||||
IdentityEx::IdentityEx(const uint8_t * publicKey, const uint8_t * signingKey, SigningKeyType type):
|
||||
m_IsVerifierCreated (false)
|
||||
{
|
||||
memcpy (m_StandardIdentity.publicKey, publicKey, sizeof (m_StandardIdentity.publicKey));
|
||||
if (type != SIGNING_KEY_TYPE_DSA_SHA1)
|
||||
@@ -135,19 +136,19 @@ namespace data
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const uint8_t * buf, size_t len):
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
FromBuffer (buf, len);
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const IdentityEx& other):
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
IdentityEx::IdentityEx (const Identity& standard):
|
||||
m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
m_IsVerifierCreated (false), m_ExtendedLen (0), m_ExtendedBuffer (nullptr)
|
||||
{
|
||||
*this = standard;
|
||||
}
|
||||
@@ -173,6 +174,7 @@ namespace data
|
||||
m_ExtendedBuffer = nullptr;
|
||||
|
||||
m_Verifier = nullptr;
|
||||
m_IsVerifierCreated = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -187,6 +189,7 @@ namespace data
|
||||
m_ExtendedLen = 0;
|
||||
|
||||
m_Verifier = nullptr;
|
||||
m_IsVerifierCreated = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -200,7 +203,9 @@ namespace data
|
||||
}
|
||||
memcpy (&m_StandardIdentity, buf, DEFAULT_IDENTITY_SIZE);
|
||||
|
||||
delete[] m_ExtendedBuffer; m_ExtendedBuffer = nullptr;
|
||||
if(m_ExtendedBuffer) delete[] m_ExtendedBuffer;
|
||||
m_ExtendedBuffer = nullptr;
|
||||
|
||||
m_ExtendedLen = bufbe16toh (m_StandardIdentity.certificate + 1);
|
||||
if (m_ExtendedLen)
|
||||
{
|
||||
@@ -304,6 +309,7 @@ namespace data
|
||||
|
||||
void IdentityEx::CreateVerifier () const
|
||||
{
|
||||
if (m_Verifier) return; // don't create again
|
||||
auto keyType = GetSigningKeyType ();
|
||||
switch (keyType)
|
||||
{
|
||||
@@ -371,8 +377,24 @@ namespace data
|
||||
|
||||
void IdentityEx::UpdateVerifier (i2p::crypto::Verifier * verifier) const
|
||||
{
|
||||
if (!m_Verifier || !verifier)
|
||||
m_Verifier.reset (verifier);
|
||||
if (!m_Verifier)
|
||||
{
|
||||
auto created = m_IsVerifierCreated.exchange (true);
|
||||
if (!created)
|
||||
m_Verifier.reset (verifier);
|
||||
else
|
||||
{
|
||||
delete verifier;
|
||||
int count = 0;
|
||||
while (!m_Verifier && count < 500) // 5 seconds
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::milliseconds(10));
|
||||
count++;
|
||||
}
|
||||
if (!m_Verifier)
|
||||
LogPrint (eLogError, "Identity: couldn't get verifier in 5 seconds");
|
||||
}
|
||||
}
|
||||
else
|
||||
delete verifier;
|
||||
}
|
||||
@@ -380,7 +402,8 @@ namespace data
|
||||
void IdentityEx::DropVerifier () const
|
||||
{
|
||||
// TODO: potential race condition with Verify
|
||||
m_Verifier = nullptr;
|
||||
m_IsVerifierCreated = false;
|
||||
m_Verifier = nullptr;
|
||||
}
|
||||
|
||||
PrivateKeys& PrivateKeys::operator=(const Keys& keys)
|
||||
@@ -410,6 +433,7 @@ namespace data
|
||||
memcpy (m_PrivateKey, buf + ret, 256); // private key always 256
|
||||
ret += 256;
|
||||
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
|
||||
if(signingPrivateKeySize + ret > len) return 0; // overflow
|
||||
memcpy (m_SigningPrivateKey, buf + ret, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
m_Signer = nullptr;
|
||||
@@ -422,7 +446,8 @@ namespace data
|
||||
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 ();
|
||||
size_t signingPrivateKeySize = m_Public->GetSigningPrivateKeyLen ();
|
||||
if(ret + signingPrivateKeySize > len) return 0; // overflow
|
||||
memcpy (buf + ret, m_SigningPrivateKey, signingPrivateKeySize);
|
||||
ret += signingPrivateKeySize;
|
||||
return ret;
|
||||
@@ -452,16 +477,18 @@ namespace data
|
||||
|
||||
void PrivateKeys::Sign (const uint8_t * buf, int len, uint8_t * signature) const
|
||||
{
|
||||
if (m_Signer)
|
||||
m_Signer->Sign (buf, len, signature);
|
||||
if (!m_Signer)
|
||||
CreateSigner();
|
||||
m_Signer->Sign (buf, len, signature);
|
||||
}
|
||||
|
||||
void PrivateKeys::CreateSigner ()
|
||||
void PrivateKeys::CreateSigner () const
|
||||
{
|
||||
if (m_Signer) return;
|
||||
switch (m_Public->GetSigningKeyType ())
|
||||
{
|
||||
case SIGNING_KEY_TYPE_DSA_SHA1:
|
||||
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey));
|
||||
m_Signer.reset (new i2p::crypto::DSASigner (m_SigningPrivateKey, m_Public->GetStandardIdentity ().signingKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_ECDSA_SHA256_P256:
|
||||
m_Signer.reset (new i2p::crypto::ECDSAP256Signer (m_SigningPrivateKey));
|
||||
@@ -482,7 +509,7 @@ namespace data
|
||||
m_Signer.reset (new i2p::crypto::RSASHA5124096Signer (m_SigningPrivateKey));
|
||||
break;
|
||||
case SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519:
|
||||
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey));
|
||||
m_Signer.reset (new i2p::crypto::EDDSA25519Signer (m_SigningPrivateKey, m_Public->GetStandardIdentity ().certificate - i2p::crypto::EDDSA25519_PUBLIC_KEY_LENGTH));
|
||||
break;
|
||||
default:
|
||||
LogPrint (eLogError, "Identity: Signing key type ", (int)m_Public->GetSigningKeyType (), " is not supported");
|
||||
@@ -566,11 +593,25 @@ namespace data
|
||||
XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
|
||||
{
|
||||
XORMetric m;
|
||||
#if defined(__AVX__) // for AVX
|
||||
__asm__
|
||||
(
|
||||
"vmovups %1, %%ymm0 \n"
|
||||
"vmovups %2, %%ymm1 \n"
|
||||
"vxorps %%ymm0, %%ymm1, %%ymm1 \n"
|
||||
"vmovups %%ymm1, %0 \n"
|
||||
: "=m"(*m.metric)
|
||||
: "m"(*key1), "m"(*key2)
|
||||
: "memory", "%xmm0", "%xmm1" // should be replaced by %ymm0/1 once supported by compiler
|
||||
);
|
||||
#else
|
||||
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];
|
||||
#endif
|
||||
|
||||
return m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <string.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include "Base.h"
|
||||
#include "Signature.h"
|
||||
|
||||
@@ -92,6 +93,8 @@ namespace data
|
||||
CryptoKeyType GetCryptoKeyType () const;
|
||||
void DropVerifier () const; // to save memory
|
||||
|
||||
bool operator == (const IdentityEx & other) const { return GetIdentHash() == other.GetIdentHash(); }
|
||||
|
||||
private:
|
||||
|
||||
void CreateVerifier () const;
|
||||
@@ -102,6 +105,7 @@ namespace data
|
||||
Identity m_StandardIdentity;
|
||||
IdentHash m_IdentHash;
|
||||
mutable std::unique_ptr<i2p::crypto::Verifier> m_Verifier;
|
||||
mutable std::atomic_bool m_IsVerifierCreated; // make sure we don't create twice
|
||||
size_t m_ExtendedLen;
|
||||
uint8_t * m_ExtendedBuffer;
|
||||
};
|
||||
@@ -133,14 +137,14 @@ namespace data
|
||||
|
||||
private:
|
||||
|
||||
void CreateSigner ();
|
||||
void CreateSigner () const;
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<IdentityEx> m_Public;
|
||||
uint8_t m_PrivateKey[256];
|
||||
uint8_t m_SigningPrivateKey[1024]; // assume private key doesn't exceed 1024 bytes
|
||||
std::unique_ptr<i2p::crypto::Signer> m_Signer;
|
||||
mutable std::unique_ptr<i2p::crypto::Signer> m_Signer;
|
||||
};
|
||||
|
||||
// kademlia
|
||||
|
||||
21
LeaseSet.cpp
21
LeaseSet.cpp
@@ -91,7 +91,7 @@ namespace data
|
||||
if (m_StoreLeases)
|
||||
{
|
||||
auto ret = m_Leases.insert (std::make_shared<Lease>(lease));
|
||||
if (!ret.second) *(*ret.first) = lease; // update existing
|
||||
if (!ret.second) (*ret.first)->endDate = lease.endDate; // update existing
|
||||
(*ret.first)->isUpdated = true;
|
||||
// check if lease's gateway is in our netDb
|
||||
if (!netdb.FindRouter (lease.tunnelGateway))
|
||||
@@ -162,8 +162,21 @@ namespace data
|
||||
{
|
||||
return ExtractTimestamp (buf, len) > ExtractTimestamp (m_Buffer, m_BufferLen);
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeases (bool withThreshold) const
|
||||
|
||||
bool LeaseSet::ExpiresSoon(const uint64_t dlt, const uint64_t fudge) const
|
||||
{
|
||||
auto now = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if (fudge) now += rand() % fudge;
|
||||
if (now >= m_ExpirationTime) return true;
|
||||
return m_ExpirationTime - now <= dlt;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeases (bool withThreshold) const
|
||||
{
|
||||
return GetNonExpiredLeasesExcluding( [] (const Lease & l) -> bool { return false; }, withThreshold);
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<const Lease> > LeaseSet::GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold) const
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
std::vector<std::shared_ptr<const Lease> > leases;
|
||||
@@ -174,7 +187,7 @@ namespace data
|
||||
endDate += LEASE_ENDDATE_THRESHOLD;
|
||||
else
|
||||
endDate -= LEASE_ENDDATE_THRESHOLD;
|
||||
if (ts < endDate)
|
||||
if (ts < endDate && !exclude(*it))
|
||||
leases.push_back (it);
|
||||
}
|
||||
return leases;
|
||||
|
||||
13
LeaseSet.h
13
LeaseSet.h
@@ -7,6 +7,7 @@
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include "Identity.h"
|
||||
#include "Timestamp.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -24,7 +25,13 @@ namespace data
|
||||
IdentHash tunnelGateway;
|
||||
uint32_t tunnelID;
|
||||
uint64_t endDate; // 0 means invalid
|
||||
bool isUpdated; // trasient
|
||||
bool isUpdated; // trasient
|
||||
/* return true if this lease expires within t millisecond + fudge factor */
|
||||
bool ExpiresWithin( const uint64_t t, const uint64_t fudge = 1000 ) const {
|
||||
auto expire = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
if(fudge) expire += rand() % fudge;
|
||||
return endDate - expire >= t;
|
||||
}
|
||||
};
|
||||
|
||||
struct LeaseCmp
|
||||
@@ -38,6 +45,8 @@ namespace data
|
||||
};
|
||||
};
|
||||
|
||||
typedef std::function<bool(const Lease & l)> LeaseInspectFunc;
|
||||
|
||||
const size_t MAX_LS_BUFFER_SIZE = 3072;
|
||||
const size_t LEASE_SIZE = 44; // 32 + 4 + 8
|
||||
const uint8_t MAX_NUM_LEASES = 16;
|
||||
@@ -56,10 +65,12 @@ namespace data
|
||||
size_t GetBufferLen () const { return m_BufferLen; };
|
||||
bool IsValid () const { return m_IsValid; };
|
||||
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeases (bool withThreshold = true) const;
|
||||
const std::vector<std::shared_ptr<const Lease> > GetNonExpiredLeasesExcluding (LeaseInspectFunc exclude, bool withThreshold = true) const;
|
||||
bool HasExpiredLeases () const;
|
||||
bool IsExpired () const;
|
||||
bool IsEmpty () const { return m_Leases.empty (); };
|
||||
uint64_t GetExpirationTime () const { return m_ExpirationTime; };
|
||||
bool ExpiresSoon(const uint64_t dlt=1000 * 5, const uint64_t fudge = 0) const ;
|
||||
bool operator== (const LeaseSet& other) const
|
||||
{ return m_BufferLen == other.m_BufferLen && !memcmp (m_Buffer, other.m_Buffer, m_BufferLen); };
|
||||
|
||||
|
||||
116
Log.cpp
116
Log.cpp
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace i2p {
|
||||
namespace log {
|
||||
Log logger;
|
||||
static Log logger;
|
||||
/**
|
||||
* @brief Maps our loglevel to their symbolic name
|
||||
*/
|
||||
@@ -22,6 +22,22 @@ namespace log {
|
||||
"debug" // eLogDebug
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Colorize log output -- array of terminal control sequences
|
||||
* @note Using ISO 6429 (ANSI) color sequences
|
||||
*/
|
||||
#ifdef _WIN32
|
||||
static const char *LogMsgColors[] = { "", "", "", "", "" };
|
||||
#else /* UNIX */
|
||||
static const char *LogMsgColors[] = {
|
||||
[eLogError] = "\033[1;31m", /* red */
|
||||
[eLogWarning] = "\033[1;33m", /* yellow */
|
||||
[eLogInfo] = "\033[1;36m", /* cyan */
|
||||
[eLogDebug] = "\033[1;34m", /* blue */
|
||||
[eNumLogLevels] = "\033[0m", /* reset */
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* @brief Maps our log levels to syslog one
|
||||
@@ -42,13 +58,29 @@ namespace log {
|
||||
|
||||
Log::Log():
|
||||
m_Destination(eLogStdout), m_MinLevel(eLogInfo),
|
||||
m_LogStream (nullptr), m_Logfile(""), m_IsReady(false)
|
||||
m_LogStream (nullptr), m_Logfile(""), m_HasColors(true),
|
||||
m_IsRunning (false), m_Thread (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Log::~Log ()
|
||||
{
|
||||
switch (m_Destination) {
|
||||
delete m_Thread;
|
||||
}
|
||||
|
||||
void Log::Start ()
|
||||
{
|
||||
if (!m_IsRunning)
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Log::Run, this));
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Stop ()
|
||||
{
|
||||
switch (m_Destination)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
case eLogSyslog :
|
||||
closelog();
|
||||
@@ -62,7 +94,14 @@ namespace log {
|
||||
/* do nothing */
|
||||
break;
|
||||
}
|
||||
Process();
|
||||
m_IsRunning = false;
|
||||
m_Queue.WakeUp ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Log::SetLogLevel (const std::string& level) {
|
||||
@@ -90,45 +129,53 @@ namespace log {
|
||||
* Unfortunately, with current startup process with late fork() this
|
||||
* will give us nothing but pain. Maybe later. See in NetDb as example.
|
||||
*/
|
||||
void Log::Process() {
|
||||
std::unique_lock<std::mutex> l(m_OutputLock);
|
||||
void Log::Process(std::shared_ptr<LogMsg> msg)
|
||||
{
|
||||
if (!msg) return;
|
||||
std::hash<std::thread::id> hasher;
|
||||
unsigned short short_tid;
|
||||
while (1) {
|
||||
auto msg = m_Queue.GetNextWithTimeout (1);
|
||||
if (!msg)
|
||||
break;
|
||||
short_tid = (short) (hasher(msg->tid) % 1000);
|
||||
switch (m_Destination) {
|
||||
short_tid = (short) (hasher(msg->tid) % 1000);
|
||||
switch (m_Destination) {
|
||||
#ifndef _WIN32
|
||||
case eLogSyslog:
|
||||
syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
|
||||
break;
|
||||
case eLogSyslog:
|
||||
syslog(GetSyslogPrio(msg->level), "[%03u] %s", short_tid, msg->text.c_str());
|
||||
break;
|
||||
#endif
|
||||
case eLogFile:
|
||||
case eLogStream:
|
||||
if (m_LogStream)
|
||||
*m_LogStream << TimeAsString(msg->timestamp)
|
||||
<< "@" << short_tid
|
||||
<< "/" << g_LogLevelStr[msg->level]
|
||||
<< " - " << msg->text << std::endl;
|
||||
break;
|
||||
case eLogStdout:
|
||||
default:
|
||||
std::cout << TimeAsString(msg->timestamp)
|
||||
case eLogFile:
|
||||
case eLogStream:
|
||||
if (m_LogStream)
|
||||
*m_LogStream << TimeAsString(msg->timestamp)
|
||||
<< "@" << short_tid
|
||||
<< "/" << g_LogLevelStr[msg->level]
|
||||
<< " - " << msg->text << std::endl;
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
break;
|
||||
case eLogStdout:
|
||||
default:
|
||||
std::cout << TimeAsString(msg->timestamp)
|
||||
<< "@" << short_tid
|
||||
<< "/" << LogMsgColors[msg->level] << g_LogLevelStr[msg->level] << LogMsgColors[eNumLogLevels]
|
||||
<< " - " << msg->text << std::endl;
|
||||
break;
|
||||
} // switch
|
||||
}
|
||||
|
||||
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg) {
|
||||
void Log::Run ()
|
||||
{
|
||||
Reopen ();
|
||||
while (m_IsRunning)
|
||||
{
|
||||
std::shared_ptr<LogMsg> msg;
|
||||
while (msg = m_Queue.Get ())
|
||||
Process (msg);
|
||||
if (m_LogStream) m_LogStream->flush();
|
||||
if (m_IsRunning)
|
||||
m_Queue.Wait ();
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Append(std::shared_ptr<i2p::log::LogMsg> & msg)
|
||||
{
|
||||
m_Queue.Put(msg);
|
||||
if (!m_IsReady)
|
||||
return;
|
||||
Process();
|
||||
}
|
||||
|
||||
void Log::SendTo (const std::string& path)
|
||||
@@ -138,6 +185,7 @@ namespace log {
|
||||
auto os = std::make_shared<std::ofstream> (path, flags);
|
||||
if (os->is_open ())
|
||||
{
|
||||
m_HasColors = false;
|
||||
m_Logfile = path;
|
||||
m_Destination = eLogFile;
|
||||
m_LogStream = os;
|
||||
@@ -147,12 +195,14 @@ namespace log {
|
||||
}
|
||||
|
||||
void Log::SendTo (std::shared_ptr<std::ostream> os) {
|
||||
m_HasColors = false;
|
||||
m_Destination = eLogStream;
|
||||
m_LogStream = os;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
void Log::SendTo(const char *name, int facility) {
|
||||
m_HasColors = false;
|
||||
m_Destination = eLogSyslog;
|
||||
m_LogStream = nullptr;
|
||||
openlog(name, LOG_CONS | LOG_PID, facility);
|
||||
|
||||
41
Log.h
41
Log.h
@@ -16,6 +16,7 @@
|
||||
#include <sstream>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include "Queue.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -42,6 +43,7 @@ enum LogType {
|
||||
|
||||
namespace i2p {
|
||||
namespace log {
|
||||
|
||||
struct LogMsg; /* forward declaration */
|
||||
|
||||
class Log
|
||||
@@ -55,8 +57,9 @@ namespace log {
|
||||
std::time_t m_LastTimestamp;
|
||||
char m_LastDateTime[64];
|
||||
i2p::util::Queue<std::shared_ptr<LogMsg> > m_Queue;
|
||||
volatile bool m_IsReady;
|
||||
mutable std::mutex m_OutputLock;
|
||||
bool m_HasColors;
|
||||
volatile bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
|
||||
private:
|
||||
|
||||
@@ -64,10 +67,8 @@ namespace log {
|
||||
Log (const Log &);
|
||||
const Log& operator=(const Log&);
|
||||
|
||||
/**
|
||||
* @brief process stored messages in queue
|
||||
*/
|
||||
void Process ();
|
||||
void Run ();
|
||||
void Process (std::shared_ptr<LogMsg> msg);
|
||||
|
||||
/**
|
||||
* @brief Makes formatted string from unix timestamp
|
||||
@@ -85,6 +86,9 @@ namespace log {
|
||||
LogType GetLogType () { return m_Destination; };
|
||||
LogLevel GetLogLevel () { return m_MinLevel; };
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
/**
|
||||
* @brief Sets minimal allowed level for log messages
|
||||
* @param level String with wanted minimal msg level
|
||||
@@ -118,12 +122,6 @@ namespace log {
|
||||
*/
|
||||
void Append(std::shared_ptr<i2p::log::LogMsg> &);
|
||||
|
||||
/** @brief Allow log output */
|
||||
void Ready() { m_IsReady = true; }
|
||||
|
||||
/** @brief Flushes the output log stream */
|
||||
void Flush();
|
||||
|
||||
/** @brief Reopen log file */
|
||||
void Reopen();
|
||||
};
|
||||
@@ -140,27 +138,27 @@ namespace log {
|
||||
std::string text; /**< message text as single string */
|
||||
LogLevel level; /**< message level */
|
||||
std::thread::id tid; /**< id of thread that generated message */
|
||||
|
||||
|
||||
LogMsg (LogLevel lvl, std::time_t ts, const std::string & txt): timestamp(ts), text(txt), level(lvl) {};
|
||||
};
|
||||
|
||||
Log & Logger();
|
||||
} // log
|
||||
} // i2p
|
||||
}
|
||||
|
||||
/** internal usage only -- folding args array to single string */
|
||||
template<typename TValue>
|
||||
void LogPrint (std::stringstream& s, TValue arg)
|
||||
void LogPrint (std::stringstream& s, TValue&& arg) noexcept
|
||||
{
|
||||
s << arg;
|
||||
s << std::forward<TValue>(arg);
|
||||
}
|
||||
|
||||
/** internal usage only -- folding args array to single string */
|
||||
template<typename TValue, typename... TArgs>
|
||||
void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
|
||||
void LogPrint (std::stringstream& s, TValue&& arg, TArgs&&... args) noexcept
|
||||
{
|
||||
LogPrint (s, arg);
|
||||
LogPrint (s, args...);
|
||||
LogPrint (s, std::forward<TValue>(arg));
|
||||
LogPrint (s, std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,7 +167,7 @@ void LogPrint (std::stringstream& s, TValue arg, TArgs... args)
|
||||
* @param args Array of message parts
|
||||
*/
|
||||
template<typename... TArgs>
|
||||
void LogPrint (LogLevel level, TArgs... args)
|
||||
void LogPrint (LogLevel level, TArgs&&... args) noexcept
|
||||
{
|
||||
i2p::log::Log &log = i2p::log::Logger();
|
||||
if (level > log.GetLogLevel ())
|
||||
@@ -177,8 +175,9 @@ void LogPrint (LogLevel level, TArgs... args)
|
||||
|
||||
// fold message to single string
|
||||
std::stringstream ss("");
|
||||
LogPrint (ss, args ...);
|
||||
|
||||
LogPrint (ss, std::forward<TArgs>(args)...);
|
||||
|
||||
auto msg = std::make_shared<i2p::log::LogMsg>(level, std::time(nullptr), ss.str());
|
||||
msg->tid = std::this_thread::get_id();
|
||||
log.Append(msg);
|
||||
|
||||
23
Makefile
23
Makefile
@@ -4,15 +4,21 @@ ARLIB := libi2pd.a
|
||||
SHLIB_CLIENT := libi2pdclient.so
|
||||
ARLIB_CLIENT := libi2pdclient.a
|
||||
I2PD := i2pd
|
||||
GREP := fgrep
|
||||
GREP := grep
|
||||
DEPS := obj/make.dep
|
||||
|
||||
include filelist.mk
|
||||
|
||||
USE_AESNI := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
USE_AESNI := yes
|
||||
USE_AVX := yes
|
||||
USE_STATIC := no
|
||||
USE_MESHNET := no
|
||||
USE_UPNP := no
|
||||
|
||||
ifeq ($(WEBSOCKETS),1)
|
||||
NEEDED_CXXFLAGS += -DWITH_EVENTS
|
||||
DAEMON_SRC += Websocket.cpp
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
@@ -21,7 +27,7 @@ ifeq ($(UNAME),Darwin)
|
||||
else
|
||||
include Makefile.osx
|
||||
endif
|
||||
else ifeq ($(shell echo $(UNAME) | $(GREP) -c FreeBSD),1)
|
||||
else ifeq ($(shell echo $(UNAME) | $(GREP) -Ec '(Free|Open)BSD'),1)
|
||||
DAEMON_SRC += DaemonLinux.cpp
|
||||
include Makefile.bsd
|
||||
else ifeq ($(UNAME),Linux)
|
||||
@@ -89,10 +95,15 @@ strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
|
||||
strip $^
|
||||
|
||||
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
|
||||
BRANCH=$(shell git rev-parse --abbrev-ref HEAD)
|
||||
dist:
|
||||
git archive --format=tar.gz -9 --worktree-attributes \
|
||||
--prefix=i2pd_$(LATEST_TAG)/ $(LATEST_TAG) -o i2pd_$(LATEST_TAG).tar.gz
|
||||
|
||||
last-dist:
|
||||
git archive --format=tar.gz -9 --worktree-attributes \
|
||||
--prefix=i2pd_$(LATEST_TAG)/ $(BRANCH) -o ../i2pd_$(LATEST_TAG).orig.tar.gz
|
||||
|
||||
doxygen:
|
||||
doxygen -s docs/Doxyfile
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# set defaults instead redefine
|
||||
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic
|
||||
CXXFLAGS ?= -g -Wall -Wextra -Wno-unused-parameter -pedantic -Wno-misleading-indentation
|
||||
INCFLAGS ?=
|
||||
|
||||
## NOTE: The NEEDED_CXXFLAGS are here so that custom CXXFLAGS can be specified at build time
|
||||
@@ -28,6 +28,9 @@ endif
|
||||
NEEDED_CXXFLAGS += -fPIC
|
||||
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
# NOTE: on glibc you will get this warning:
|
||||
# Using 'getaddrinfo' in statically linked applications requires at runtime
|
||||
# the shared libraries from the glibc version used for linking
|
||||
LIBDIR := /usr/lib
|
||||
LDLIBS = $(LIBDIR)/libboost_system.a
|
||||
LDLIBS += $(LIBDIR)/libboost_date_time.a
|
||||
@@ -36,24 +39,35 @@ ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS += $(LIBDIR)/libssl.a
|
||||
LDLIBS += $(LIBDIR)/libcrypto.a
|
||||
LDLIBS += $(LIBDIR)/libz.a
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt
|
||||
LDLIBS += -lpthread -static-libstdc++ -static-libgcc -lrt -ldl
|
||||
USE_AESNI := no
|
||||
else
|
||||
LDLIBS = -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
# UPNP Support (miniupnpc 1.5 or 1.6)
|
||||
# UPNP Support (miniupnpc 1.5 and higher)
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -lminiupnpc
|
||||
CXXFLAGS += -DUSE_UPNP
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS += $(LIBDIR)/libminiupnpc.a
|
||||
else
|
||||
LDLIBS += -lminiupnpc
|
||||
endif
|
||||
endif
|
||||
|
||||
IS_64 := $(shell $(CXX) -dumpmachine 2>&1 | $(GREP) -c "64")
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
ifeq ($(IS_64),1)
|
||||
#check if AES-NI is supported by CPU
|
||||
ifneq ($(shell grep -c aes /proc/cpuinfo),0)
|
||||
CPU_FLAGS = -maes -DAESNI
|
||||
ifneq ($(shell $(GREP) -c aes /proc/cpuinfo),0)
|
||||
CPU_FLAGS += -maes -DAESNI
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AVX),yes)
|
||||
#check if AVX supported by CPU
|
||||
ifneq ($(shell $(GREP) -c avx /proc/cpuinfo),0)
|
||||
CPU_FLAGS += -mavx
|
||||
endif
|
||||
endif
|
||||
|
||||
@@ -37,10 +37,20 @@ ifeq ($(USE_WIN32_APP), yes)
|
||||
DAEMON_OBJS += $(patsubst %.rc,obj/%.o,$(DAEMON_RC))
|
||||
endif
|
||||
|
||||
# don't change following line to ifeq ($(USE_AESNI),yes) !!!
|
||||
ifeq ($(USE_AESNI),1)
|
||||
CPU_FLAGS = -maes -DAESNI
|
||||
CPU_FLAGS += -maes -DAESNI
|
||||
else
|
||||
CPU_FLAGS = -msse
|
||||
CPU_FLAGS += -msse
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AVX),1)
|
||||
CPU_FLAGS += -mavx
|
||||
endif
|
||||
|
||||
ifeq ($(USE_ASLR),yes)
|
||||
LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va \
|
||||
-Wl,--dynamicbase,--export-all-symbols
|
||||
endif
|
||||
|
||||
obj/%.o : %.rc
|
||||
|
||||
23
Makefile.osx
23
Makefile.osx
@@ -1,21 +1,28 @@
|
||||
CXX = clang++
|
||||
CXXFLAGS = -g -Wall -std=c++11 -DMAC_OSX
|
||||
CXXFLAGS = -Os -Wall -std=c++11 -DMAC_OSX
|
||||
#CXXFLAGS = -g -O2 -Wall -std=c++11
|
||||
INCFLAGS = -I/usr/local/include -I/usr/local/ssl/include
|
||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib -L/usr/local/ssl/lib
|
||||
INCFLAGS = -I/usr/local/include
|
||||
LDFLAGS = -Wl,-rpath,/usr/local/lib -L/usr/local/lib
|
||||
|
||||
ifeq ($(USE_STATIC),yes)
|
||||
LDLIBS = -lz /usr/local/lib/libcrypto.a /usr/local/lib/libssl.a /usr/local/lib/libboost_system.a /usr/local/lib/libboost_date_time.a /usr/local/lib/libboost_filesystem.a /usr/local/lib/libboost_program_options.a -lpthread
|
||||
else
|
||||
LDLIBS = -lz -lcrypto -lssl -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(USE_UPNP),yes)
|
||||
LDFLAGS += -ldl
|
||||
CXXFLAGS += -DUSE_UPNP
|
||||
endif
|
||||
|
||||
# OSX Notes
|
||||
# http://www.hutsby.net/2011/08/macs-with-aes-ni.html
|
||||
# Seems like all recent Mac's have AES-NI, after firmware upgrade 2.2
|
||||
# Found no good way to detect it from command line. TODO: Might be some osx sysinfo magic
|
||||
ifeq ($(USE_AESNI),yes)
|
||||
ifeq ($(USE_AESNI),1)
|
||||
CXXFLAGS += -maes -DAESNI
|
||||
else
|
||||
CXXFLAGS += -msse
|
||||
endif
|
||||
|
||||
ifeq ($(USE_AVX),1)
|
||||
CXXFLAGS += -mavx
|
||||
endif
|
||||
|
||||
# Disabled, since it will be the default make rule. I think its better
|
||||
|
||||
380
NTCPSession.cpp
380
NTCPSession.cpp
@@ -1,5 +1,6 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <future>
|
||||
|
||||
#include "I2PEndian.h"
|
||||
#include "Base.h"
|
||||
@@ -11,6 +12,9 @@
|
||||
#include "Transports.h"
|
||||
#include "NetDb.h"
|
||||
#include "NTCPSession.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
using namespace i2p::crypto;
|
||||
|
||||
@@ -21,7 +25,7 @@ namespace transport
|
||||
NTCPSession::NTCPSession (NTCPServer& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter):
|
||||
TransportSession (in_RemoteRouter, NTCP_TERMINATION_TIMEOUT),
|
||||
m_Server (server), m_Socket (m_Server.GetService ()),
|
||||
m_TerminationTimer (m_Server.GetService ()), m_IsEstablished (false), m_IsTerminated (false),
|
||||
m_IsEstablished (false), m_IsTerminated (false),
|
||||
m_ReceiveBufferOffset (0), m_NextMessage (nullptr), m_IsSending (false)
|
||||
{
|
||||
m_Establisher = new Establisher;
|
||||
@@ -32,12 +36,12 @@ namespace transport
|
||||
delete m_Establisher;
|
||||
}
|
||||
|
||||
void NTCPSession::CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key)
|
||||
void NTCPSession::CreateAESKey (uint8_t * pubKey)
|
||||
{
|
||||
uint8_t sharedKey[256];
|
||||
m_DHKeysPair->Agree (pubKey, sharedKey);
|
||||
m_DHKeysPair->Agree (pubKey, sharedKey); // time consuming operation
|
||||
|
||||
uint8_t * aesKey = key;
|
||||
i2p::crypto::AESKey aesKey;
|
||||
if (sharedKey[0] & 0x80)
|
||||
{
|
||||
aesKey[0] = 0;
|
||||
@@ -60,6 +64,9 @@ namespace transport
|
||||
}
|
||||
memcpy (aesKey, nonZero, 32);
|
||||
}
|
||||
|
||||
m_Decryption.SetKey (aesKey);
|
||||
m_Encryption.SetKey (aesKey);
|
||||
}
|
||||
|
||||
void NTCPSession::Done ()
|
||||
@@ -78,7 +85,6 @@ namespace transport
|
||||
m_Server.RemoveNTCPSession (shared_from_this ());
|
||||
m_SendQueue.clear ();
|
||||
m_NextMessage = nullptr;
|
||||
m_TerminationTimer.cancel ();
|
||||
LogPrint (eLogDebug, "NTCP: session terminated");
|
||||
}
|
||||
}
|
||||
@@ -110,22 +116,14 @@ namespace transport
|
||||
|
||||
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 ();
|
||||
}
|
||||
// receive Phase1
|
||||
boost::asio::async_read (m_Socket, boost::asio::buffer(&m_Establisher->phase1, sizeof (NTCPPhase1)), boost::asio::transfer_all (),
|
||||
std::bind(&NTCPSession::HandlePhase1Received, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase1Sent (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
@@ -169,15 +167,25 @@ namespace transport
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
SendPhase2 ();
|
||||
|
||||
// TODO: check for number of pending keys
|
||||
auto s = shared_from_this ();
|
||||
auto keyCreated = std::async (std::launch::async, [s] ()
|
||||
{
|
||||
if (!s->m_DHKeysPair)
|
||||
s->m_DHKeysPair = transports.GetNextDHKeysPair ();
|
||||
s->CreateAESKey (s->m_Establisher->phase1.pubKey);
|
||||
}).share ();
|
||||
m_Server.GetService ().post ([s, keyCreated]()
|
||||
{
|
||||
keyCreated.get ();
|
||||
s->SendPhase2 ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::SendPhase2 ()
|
||||
{
|
||||
if (!m_DHKeysPair)
|
||||
m_DHKeysPair = transports.GetNextDHKeysPair ();
|
||||
const uint8_t * y = m_DHKeysPair->GetPublicKey ();
|
||||
memcpy (m_Establisher->phase2.pubKey, y, 256);
|
||||
uint8_t xy[512];
|
||||
@@ -188,11 +196,7 @@ namespace transport
|
||||
memcpy (m_Establisher->phase2.encrypted.timestamp, &tsB, 4);
|
||||
RAND_bytes (m_Establisher->phase2.encrypted.filler, 12);
|
||||
|
||||
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);
|
||||
@@ -235,35 +239,47 @@ namespace transport
|
||||
}
|
||||
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->GetPublicKey (), 256);
|
||||
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
|
||||
uint8_t digest[32];
|
||||
SHA256 (xy, 512, digest);
|
||||
if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash");
|
||||
transports.ReuseDHKeysPair (m_DHKeysPair);
|
||||
m_DHKeysPair = nullptr;
|
||||
Terminate ();
|
||||
return ;
|
||||
}
|
||||
SendPhase3 ();
|
||||
auto s = shared_from_this ();
|
||||
// create AES key in separate thread
|
||||
auto keyCreated = std::async (std::launch::async, [s] ()
|
||||
{
|
||||
s->CreateAESKey (s->m_Establisher->phase2.pubKey);
|
||||
}).share (); // TODO: use move capture in C++ 14 instead shared_future
|
||||
// let other operations execute while a key gets created
|
||||
m_Server.GetService ().post ([s, keyCreated]()
|
||||
{
|
||||
keyCreated.get (); // we might wait if no more pending operations
|
||||
s->HandlePhase2 ();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPSession::HandlePhase2 ()
|
||||
{
|
||||
m_Decryption.SetIV (m_Establisher->phase2.pubKey + 240);
|
||||
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->GetPublicKey (), 256);
|
||||
memcpy (xy + 256, m_Establisher->phase2.pubKey, 256);
|
||||
uint8_t digest[32];
|
||||
SHA256 (xy, 512, digest);
|
||||
if (memcmp(m_Establisher->phase2.encrypted.hxy, digest, 32))
|
||||
{
|
||||
LogPrint (eLogError, "NTCP: Phase 2 process error: incorrect hash");
|
||||
transports.ReuseDHKeysPair (m_DHKeysPair);
|
||||
m_DHKeysPair = nullptr;
|
||||
Terminate ();
|
||||
return ;
|
||||
}
|
||||
SendPhase3 ();
|
||||
}
|
||||
|
||||
void NTCPSession::SendPhase3 ()
|
||||
{
|
||||
auto keys = i2p::context.GetPrivateKeys ();
|
||||
auto& keys = i2p::context.GetPrivateKeys ();
|
||||
uint8_t * buf = m_ReceiveBuffer;
|
||||
htobe16buf (buf, keys.GetPublic ()->GetFullLen ());
|
||||
buf += 2;
|
||||
@@ -330,12 +346,15 @@ namespace transport
|
||||
m_Decryption.Decrypt (m_ReceiveBuffer, bytes_transferred, m_ReceiveBuffer);
|
||||
uint8_t * buf = m_ReceiveBuffer;
|
||||
uint16_t size = bufbe16toh (buf);
|
||||
SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (buf + 2, size));
|
||||
if (m_Server.FindNTCPSession (m_RemoteIdentity->GetIdentHash ()))
|
||||
auto identity = std::make_shared<i2p::data::IdentityEx> (buf + 2, size);
|
||||
if (m_Server.FindNTCPSession (identity->GetIdentHash ()))
|
||||
{
|
||||
LogPrint (eLogInfo, "NTCP: session already exists");
|
||||
Terminate ();
|
||||
}
|
||||
auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already
|
||||
SetRemoteIdentity (existing ? existing->GetRouterIdentity () : identity);
|
||||
|
||||
size_t expectedSize = size + 2/*size*/ + 4/*timestamp*/ + m_RemoteIdentity->GetSignatureLen ();
|
||||
size_t paddingLen = expectedSize & 0x0F;
|
||||
if (paddingLen) paddingLen = (16 - paddingLen);
|
||||
@@ -409,7 +428,7 @@ namespace transport
|
||||
s.Insert (m_RemoteIdentity->GetIdentHash (), 32); // ident
|
||||
s.Insert (tsA); // tsA
|
||||
s.Insert (tsB); // tsB
|
||||
auto keys = i2p::context.GetPrivateKeys ();
|
||||
auto& keys = i2p::context.GetPrivateKeys ();
|
||||
auto signatureLen = keys.GetPublic ()->GetSignatureLen ();
|
||||
s.Sign (keys, m_ReceiveBuffer);
|
||||
size_t paddingSize = signatureLen & 0x0F; // %16
|
||||
@@ -499,11 +518,10 @@ namespace transport
|
||||
|
||||
void NTCPSession::HandleReceived (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
if (ecode) {
|
||||
if (ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
LogPrint (eLogDebug, "NTCP: Read error: ", ecode.message ());
|
||||
if (!m_NumReceivedBytes)
|
||||
m_Server.Ban (m_ConnectedFrom);
|
||||
//if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
}
|
||||
@@ -515,50 +533,68 @@ namespace transport
|
||||
|
||||
if (m_ReceiveBufferOffset >= 16)
|
||||
{
|
||||
int numReloads = 0;
|
||||
do
|
||||
{
|
||||
uint8_t * nextBlock = m_ReceiveBuffer;
|
||||
while (m_ReceiveBufferOffset >= 16)
|
||||
// process received data
|
||||
uint8_t * nextBlock = m_ReceiveBuffer;
|
||||
while (m_ReceiveBufferOffset >= 16)
|
||||
{
|
||||
if (!DecryptNextBlock (nextBlock)) // 16 bytes
|
||||
{
|
||||
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 (eLogInfo, "NTCP: Read more bytes error: ", ec.message ());
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
m_NumReceivedBytes += moreBytes;
|
||||
m_ReceiveBufferOffset += moreBytes;
|
||||
numReloads++;
|
||||
}
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
nextBlock += 16;
|
||||
m_ReceiveBufferOffset -= 16;
|
||||
}
|
||||
while (m_ReceiveBufferOffset >= 16);
|
||||
m_Handler.Flush ();
|
||||
}
|
||||
if (m_ReceiveBufferOffset > 0)
|
||||
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset);
|
||||
}
|
||||
|
||||
// read and process more is available
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_Socket.available(ec);
|
||||
if (moreBytes && !ec)
|
||||
{
|
||||
uint8_t * buf = nullptr, * moreBuf = m_ReceiveBuffer;
|
||||
if (moreBytes + m_ReceiveBufferOffset > NTCP_BUFFER_SIZE)
|
||||
{
|
||||
buf = new uint8_t[moreBytes + m_ReceiveBufferOffset + 16];
|
||||
moreBuf = buf;
|
||||
uint8_t rem = ((size_t)buf) & 0x0f;
|
||||
if (rem) moreBuf += (16 - rem); // align 16
|
||||
if (m_ReceiveBufferOffset)
|
||||
memcpy (moreBuf, m_ReceiveBuffer, m_ReceiveBufferOffset);
|
||||
}
|
||||
moreBytes = m_Socket.read_some (boost::asio::buffer (moreBuf + m_ReceiveBufferOffset, moreBytes), ec);
|
||||
if (ec)
|
||||
{
|
||||
LogPrint (eLogInfo, "NTCP: Read more bytes error: ", ec.message ());
|
||||
delete[] buf;
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
m_ReceiveBufferOffset += moreBytes;
|
||||
m_NumReceivedBytes += moreBytes;
|
||||
i2p::transport::transports.UpdateReceivedBytes (moreBytes);
|
||||
// process more data
|
||||
uint8_t * nextBlock = moreBuf;
|
||||
while (m_ReceiveBufferOffset >= 16)
|
||||
{
|
||||
if (!DecryptNextBlock (nextBlock)) // 16 bytes
|
||||
{
|
||||
delete[] buf;
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
nextBlock += 16;
|
||||
m_ReceiveBufferOffset -= 16;
|
||||
}
|
||||
if (m_ReceiveBufferOffset > 0)
|
||||
memcpy (m_ReceiveBuffer, nextBlock, m_ReceiveBufferOffset); // nextBlock points to memory inside buf
|
||||
delete[] buf;
|
||||
}
|
||||
m_Handler.Flush ();
|
||||
|
||||
ScheduleTermination (); // reset termination timer
|
||||
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
@@ -574,40 +610,46 @@ namespace transport
|
||||
if (dataSize)
|
||||
{
|
||||
// new message
|
||||
if (dataSize + 16U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
|
||||
if (dataSize + 16U + 15U > NTCP_MAX_MESSAGE_SIZE - 2) // + 6 + padding
|
||||
{
|
||||
LogPrint (eLogError, "NTCP: data size ", dataSize, " exceeds max size");
|
||||
return false;
|
||||
}
|
||||
auto msg = (dataSize + 16U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
|
||||
m_NextMessage = msg;
|
||||
memcpy (m_NextMessage->buf, buf, 16);
|
||||
m_NextMessage = (dataSize + 16U + 15U) <= I2NP_MAX_SHORT_MESSAGE_SIZE - 2 ? NewI2NPShortMessage () : NewI2NPMessage ();
|
||||
m_NextMessage->Align (16);
|
||||
m_NextMessage->offset += 2; // size field
|
||||
m_NextMessage->len = m_NextMessage->offset + dataSize;
|
||||
memcpy (m_NextMessage->GetBuffer () - 2, buf, 16);
|
||||
m_NextMessageOffset = 16;
|
||||
m_NextMessage->offset = 2; // size field
|
||||
m_NextMessage->len = dataSize + 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
// timestamp
|
||||
LogPrint (eLogDebug, "NTCP: Timestamp");
|
||||
int diff = (int)bufbe32toh (buf + 2) - (int)i2p::util::GetSecondsSinceEpoch ();
|
||||
LogPrint (eLogInfo, "NTCP: Timestamp. Time difference ", diff, " seconds");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else // message continues
|
||||
{
|
||||
m_Decryption.Decrypt (encrypted, m_NextMessage->buf + m_NextMessageOffset);
|
||||
m_Decryption.Decrypt (encrypted, m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset);
|
||||
m_NextMessageOffset += 16;
|
||||
}
|
||||
|
||||
if (m_NextMessageOffset >= m_NextMessage->len + 4) // +checksum
|
||||
if (m_NextMessageOffset >= m_NextMessage->GetLength () + 2 + 4) // +checksum
|
||||
{
|
||||
// we have a complete I2NP message
|
||||
uint8_t checksum[4];
|
||||
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->buf, m_NextMessageOffset - 4));
|
||||
if (!memcmp (m_NextMessage->buf + m_NextMessageOffset - 4, checksum, 4))
|
||||
htobe32buf (checksum, adler32 (adler32 (0, Z_NULL, 0), m_NextMessage->GetBuffer () - 2, m_NextMessageOffset - 4));
|
||||
if (!memcmp (m_NextMessage->GetBuffer () - 2 + m_NextMessageOffset - 4, checksum, 4))
|
||||
{
|
||||
if (!m_NextMessage->IsExpired ())
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
QueueIntEvent("transport.recvmsg", GetIdentHashBase64(), 1);
|
||||
#endif
|
||||
m_Handler.PutNextMessage (m_NextMessage);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogInfo, "NTCP: message expired");
|
||||
}
|
||||
@@ -645,7 +687,7 @@ namespace transport
|
||||
sendBuffer = m_TimeSyncBuffer;
|
||||
len = 4;
|
||||
htobuf16(sendBuffer, 0);
|
||||
htobe32buf (sendBuffer + 2, time (0));
|
||||
htobe32buf (sendBuffer + 2, i2p::util::GetSecondsSinceEpoch ());
|
||||
}
|
||||
int rem = (len + 6) & 0x0F; // %16
|
||||
int padding = 0;
|
||||
@@ -685,6 +727,7 @@ namespace transport
|
||||
}
|
||||
else
|
||||
{
|
||||
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
m_NumSentBytes += bytes_transferred;
|
||||
i2p::transport::transports.UpdateSentBytes (bytes_transferred);
|
||||
if (!m_SendQueue.empty())
|
||||
@@ -692,8 +735,6 @@ namespace transport
|
||||
Send (m_SendQueue);
|
||||
m_SendQueue.clear ();
|
||||
}
|
||||
else
|
||||
ScheduleTermination (); // reset termination timer
|
||||
}
|
||||
}
|
||||
|
||||
@@ -728,29 +769,11 @@ namespace transport
|
||||
else
|
||||
Send (msgs);
|
||||
}
|
||||
|
||||
void NTCPSession::ScheduleTermination ()
|
||||
{
|
||||
m_TerminationTimer.cancel ();
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(GetTerminationTimeout ()));
|
||||
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 (eLogDebug, "NTCP: No activity for ", GetTerminationTimeout (), " seconds");
|
||||
//Terminate ();
|
||||
m_Socket.close ();// invoke Terminate () from HandleReceive
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------
|
||||
NTCPServer::NTCPServer ():
|
||||
m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service),
|
||||
m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
|
||||
m_TerminationTimer (m_Service), m_NTCPAcceptor (nullptr), m_NTCPV6Acceptor (nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -769,6 +792,7 @@ namespace transport
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address: addresses)
|
||||
{
|
||||
if (!address) continue;
|
||||
if (address->transportStyle == i2p::data::RouterInfo::eTransportNTCP)
|
||||
{
|
||||
if (address->host.is_v4())
|
||||
@@ -809,24 +833,35 @@ namespace transport
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ScheduleTermination ();
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPServer::Stop ()
|
||||
{
|
||||
{
|
||||
// we have to copy it because Terminate changes m_NTCPSessions
|
||||
auto ntcpSessions = m_NTCPSessions;
|
||||
for (auto& it: ntcpSessions)
|
||||
it.second->Terminate ();
|
||||
}
|
||||
m_NTCPSessions.clear ();
|
||||
|
||||
if (m_IsRunning)
|
||||
{
|
||||
m_IsRunning = false;
|
||||
if (m_NTCPAcceptor)
|
||||
delete m_NTCPAcceptor;
|
||||
m_NTCPAcceptor = nullptr;
|
||||
if (m_NTCPV6Acceptor)
|
||||
delete m_NTCPV6Acceptor;
|
||||
m_NTCPV6Acceptor = nullptr;
|
||||
|
||||
m_TerminationTimer.cancel ();
|
||||
if (m_NTCPAcceptor)
|
||||
{
|
||||
delete m_NTCPAcceptor;
|
||||
m_NTCPAcceptor = nullptr;
|
||||
}
|
||||
if (m_NTCPV6Acceptor)
|
||||
{
|
||||
delete m_NTCPV6Acceptor;
|
||||
m_NTCPV6Acceptor = nullptr;
|
||||
}
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
@@ -861,6 +896,7 @@ namespace transport
|
||||
if (it != m_NTCPSessions.end ())
|
||||
{
|
||||
LogPrint (eLogWarning, "NTCP: session to ", ident.ToBase64 (), " already exists");
|
||||
session->Terminate();
|
||||
return false;
|
||||
}
|
||||
m_NTCPSessions.insert (std::pair<i2p::data::IdentHash, std::shared_ptr<NTCPSession> >(ident, session));
|
||||
@@ -890,18 +926,6 @@ namespace transport
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP: 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 (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
|
||||
conn = nullptr;
|
||||
}
|
||||
else
|
||||
m_BanList.erase (it);
|
||||
}
|
||||
if (conn)
|
||||
conn->ServerLogin ();
|
||||
}
|
||||
@@ -927,18 +951,6 @@ namespace transport
|
||||
if (!ec)
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP: 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 (eLogWarning, "NTCP: ", ep.address (), " is banned for ", it->second - ts, " more seconds");
|
||||
conn = nullptr;
|
||||
}
|
||||
else
|
||||
m_BanList.erase (it);
|
||||
}
|
||||
if (conn)
|
||||
conn->ServerLogin ();
|
||||
}
|
||||
@@ -958,18 +970,31 @@ namespace transport
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP: Connecting to ", address ,":", port);
|
||||
m_Service.post([=]()
|
||||
{
|
||||
{
|
||||
if (this->AddNTCPSession (conn))
|
||||
{
|
||||
auto timer = std::make_shared<boost::asio::deadline_timer>(m_Service);
|
||||
timer->expires_from_now (boost::posix_time::seconds(NTCP_CONNECT_TIMEOUT));
|
||||
timer->async_wait ([conn](const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogInfo, "NTCP: Not connected in ", NTCP_CONNECT_TIMEOUT, " seconds");
|
||||
conn->Terminate ();
|
||||
}
|
||||
});
|
||||
conn->GetSocket ().async_connect (boost::asio::ip::tcp::endpoint (address, port),
|
||||
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn));
|
||||
std::bind (&NTCPServer::HandleConnect, this, std::placeholders::_1, conn, timer));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn)
|
||||
void NTCPServer::HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer)
|
||||
{
|
||||
timer->cancel ();
|
||||
if (ecode)
|
||||
{
|
||||
LogPrint (eLogError, "NTCP: Can't connect to ", conn->GetSocket ().remote_endpoint (), ": ", ecode.message ());
|
||||
LogPrint (eLogInfo, "NTCP: Connect error ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
i2p::data::netdb.SetUnreachable (conn->GetRemoteIdentity ()->GetIdentHash (), true);
|
||||
conn->Terminate ();
|
||||
@@ -983,11 +1008,30 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void NTCPServer::Ban (const boost::asio::ip::address& addr)
|
||||
void NTCPServer::ScheduleTermination ()
|
||||
{
|
||||
uint32_t ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
m_BanList[addr] = ts + NTCP_BAN_EXPIRATION_TIMEOUT;
|
||||
LogPrint (eLogWarning, "NTCP: ", addr, " has been banned for ", NTCP_BAN_EXPIRATION_TIMEOUT, " seconds");
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(NTCP_TERMINATION_CHECK_TIMEOUT));
|
||||
m_TerminationTimer.async_wait (std::bind (&NTCPServer::HandleTerminationTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void NTCPServer::HandleTerminationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto& it: m_NTCPSessions)
|
||||
if (it.second->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
auto session = it.second;
|
||||
m_Service.post ([session]
|
||||
{
|
||||
LogPrint (eLogDebug, "NTCP: No activity for ", session->GetTerminationTimeout (), " seconds");
|
||||
session->Terminate ();
|
||||
});
|
||||
}
|
||||
ScheduleTermination ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,10 +35,11 @@ namespace transport
|
||||
};
|
||||
|
||||
const size_t NTCP_MAX_MESSAGE_SIZE = 16384;
|
||||
const size_t NTCP_BUFFER_SIZE = 4160; // fits 4 tunnel messages (4*1028)
|
||||
const size_t NTCP_BUFFER_SIZE = 1028; // fits 1 tunnel data message
|
||||
const int NTCP_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int NTCP_TERMINATION_TIMEOUT = 120; // 2 minutes
|
||||
const int NTCP_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||
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
|
||||
const int NTCP_CLOCK_SKEW = 60; // in seconds
|
||||
const int NTCP_MAX_OUTGOING_QUEUE_SIZE = 200; // how many messages we can queue up
|
||||
|
||||
@@ -53,8 +54,8 @@ namespace transport
|
||||
void Done ();
|
||||
|
||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
||||
bool IsEstablished () const { return m_IsEstablished; };
|
||||
|
||||
bool IsEstablished () const { return m_IsEstablished; };
|
||||
|
||||
void ClientLogin ();
|
||||
void ServerLogin ();
|
||||
void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs);
|
||||
@@ -66,12 +67,13 @@ namespace transport
|
||||
void SendTimeSyncMessage ();
|
||||
void SetIsEstablished (bool isEstablished) { m_IsEstablished = isEstablished; }
|
||||
|
||||
void CreateAESKey (uint8_t * pubKey, i2p::crypto::AESKey& key);
|
||||
void CreateAESKey (uint8_t * pubKey);
|
||||
|
||||
// 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 HandlePhase2 ();
|
||||
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);
|
||||
|
||||
@@ -95,16 +97,10 @@ namespace transport
|
||||
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;
|
||||
@@ -126,8 +122,6 @@ namespace transport
|
||||
|
||||
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
|
||||
@@ -146,11 +140,10 @@ namespace transport
|
||||
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);
|
||||
|
||||
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
|
||||
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
|
||||
bool IsBoundV4() const { return m_NTCPAcceptor != nullptr; };
|
||||
bool IsBoundV6() const { return m_NTCPV6Acceptor != nullptr; };
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
void Ban (const boost::asio::ip::address& addr);
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
|
||||
private:
|
||||
|
||||
@@ -158,7 +151,11 @@ namespace transport
|
||||
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);
|
||||
void HandleConnect (const boost::system::error_code& ecode, std::shared_ptr<NTCPSession> conn, std::shared_ptr<boost::asio::deadline_timer> timer);
|
||||
|
||||
// timer
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
@@ -166,9 +163,9 @@ namespace transport
|
||||
std::thread * m_Thread;
|
||||
boost::asio::io_service m_Service;
|
||||
boost::asio::io_service::work m_Work;
|
||||
boost::asio::deadline_timer m_TerminationTimer;
|
||||
boost::asio::ip::tcp::acceptor * m_NTCPAcceptor, * m_NTCPV6Acceptor;
|
||||
std::map<i2p::data::IdentHash, std::shared_ptr<NTCPSession> > m_NTCPSessions; // access from m_Thread only
|
||||
std::map<boost::asio::ip::address, uint32_t> m_BanList; // IP -> ban expiration time in seconds
|
||||
|
||||
public:
|
||||
|
||||
|
||||
261
NetDb.cpp
261
NetDb.cpp
@@ -14,6 +14,7 @@
|
||||
#include "RouterContext.h"
|
||||
#include "Garlic.h"
|
||||
#include "NetDb.h"
|
||||
#include "Config.h"
|
||||
|
||||
using namespace i2p::transport;
|
||||
|
||||
@@ -23,7 +24,7 @@ namespace data
|
||||
{
|
||||
NetDb netdb;
|
||||
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_HiddenMode(false)
|
||||
NetDb::NetDb (): m_IsRunning (false), m_Thread (nullptr), m_Reseeder (nullptr), m_Storage("netDb", "r", "routerInfo-", "dat"), m_FloodfillBootstrap(nullptr), m_HiddenMode(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -34,13 +35,15 @@ namespace data
|
||||
}
|
||||
|
||||
void NetDb::Start ()
|
||||
{
|
||||
{
|
||||
m_Storage.SetPlace(i2p::fs::GetDataDir());
|
||||
m_Storage.Init(i2p::data::GetBase64SubstitutionTable(), 64);
|
||||
InitProfilesStorage ();
|
||||
m_Families.LoadCertificates ();
|
||||
m_Families.LoadCertificates ();
|
||||
Load ();
|
||||
if (m_RouterInfos.size () < 25) // reseed if # of router less than 50
|
||||
|
||||
uint16_t threshold; i2p::config::GetOption("reseed.threshold", threshold);
|
||||
if (m_RouterInfos.size () < threshold) // reseed if # of router less than threshold
|
||||
Reseed ();
|
||||
|
||||
m_IsRunning = true;
|
||||
@@ -68,7 +71,7 @@ namespace data
|
||||
m_Requests.Stop ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NetDb::Run ()
|
||||
{
|
||||
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
|
||||
@@ -117,7 +120,6 @@ namespace data
|
||||
{
|
||||
SaveUpdated ();
|
||||
ManageLeaseSets ();
|
||||
ManageLookupResponses ();
|
||||
}
|
||||
lastSave = ts;
|
||||
}
|
||||
@@ -126,10 +128,8 @@ namespace data
|
||||
i2p::context.CleanupDestination ();
|
||||
lastDestinationCleanup = ts;
|
||||
}
|
||||
// if we're in hidden mode don't publish or explore
|
||||
// if (m_HiddenMode) continue;
|
||||
|
||||
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL) // publish
|
||||
|
||||
if (ts - lastPublish >= NETDB_PUBLISH_INTERVAL && !m_HiddenMode) // publish
|
||||
{
|
||||
Publish ();
|
||||
lastPublish = ts;
|
||||
@@ -142,13 +142,16 @@ namespace data
|
||||
LogPrint(eLogError, "NetDb: no known routers, reseed seems to be totally failed");
|
||||
break;
|
||||
}
|
||||
else // we have peers now
|
||||
m_FloodfillBootstrap = nullptr;
|
||||
if (numRouters < 2500 || ts - lastExploratory >= 90)
|
||||
{
|
||||
numRouters = 800/numRouters;
|
||||
if (numRouters < 1) numRouters = 1;
|
||||
if (numRouters > 9) numRouters = 9;
|
||||
m_Requests.ManageRequests ();
|
||||
Explore (numRouters);
|
||||
m_Requests.ManageRequests ();
|
||||
if(!m_HiddenMode)
|
||||
Explore (numRouters);
|
||||
lastExploratory = ts;
|
||||
}
|
||||
}
|
||||
@@ -186,23 +189,34 @@ namespace data
|
||||
// TODO: check if floodfill has been changed
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "NetDb: RouterInfo is older: ", ident.ToBase64());
|
||||
|
||||
updated = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
r = std::make_shared<RouterInfo> (buf, len);
|
||||
if (!r->IsUnreachable ())
|
||||
{
|
||||
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
|
||||
bool inserted = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RouterInfosMutex);
|
||||
m_RouterInfos[r->GetIdentHash ()] = r;
|
||||
inserted = m_RouterInfos.insert ({r->GetIdentHash (), r}).second;
|
||||
}
|
||||
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
|
||||
if (inserted)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
m_Floodfills.push_back (r);
|
||||
LogPrint (eLogInfo, "NetDb: RouterInfo added: ", ident.ToBase64());
|
||||
if (r->IsFloodfill () && r->IsReachable ()) // floodfill must be reachable
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FloodfillsMutex);
|
||||
m_Floodfills.push_back (r);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Duplicated RouterInfo ", ident.ToBase64());
|
||||
updated = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -296,11 +310,56 @@ namespace data
|
||||
m_Reseeder = new Reseeder ();
|
||||
m_Reseeder->LoadCertificates (); // we need certificates for SU3 verification
|
||||
}
|
||||
int reseedRetries = 0;
|
||||
while (reseedRetries < 10 && !m_Reseeder->ReseedNowSU3 ())
|
||||
reseedRetries++;
|
||||
if (reseedRetries >= 10)
|
||||
LogPrint (eLogWarning, "NetDb: failed to reseed after 10 attempts");
|
||||
|
||||
// try reseeding from floodfill first if specified
|
||||
std::string riPath;
|
||||
if(i2p::config::GetOption("reseed.floodfill", riPath)) {
|
||||
auto ri = std::make_shared<RouterInfo>(riPath);
|
||||
if (ri->IsFloodfill()) {
|
||||
const uint8_t * riData = ri->GetBuffer();
|
||||
int riLen = ri->GetBufferLen();
|
||||
if(!i2p::data::netdb.AddRouterInfo(riData, riLen)) {
|
||||
// bad router info
|
||||
LogPrint(eLogError, "NetDb: bad router info");
|
||||
return;
|
||||
}
|
||||
m_FloodfillBootstrap = ri;
|
||||
ReseedFromFloodfill(*ri);
|
||||
// don't try reseed servers if trying to boostrap from floodfill
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
m_Reseeder->Bootstrap ();
|
||||
}
|
||||
|
||||
void NetDb::ReseedFromFloodfill(const RouterInfo & ri, int numRouters, int numFloodfills)
|
||||
{
|
||||
LogPrint(eLogInfo, "NetDB: reseeding from floodfill ", ri.GetIdentHashBase64());
|
||||
std::vector<std::shared_ptr<i2p::I2NPMessage> > requests;
|
||||
|
||||
i2p::data::IdentHash ourIdent = i2p::context.GetIdentHash();
|
||||
i2p::data::IdentHash ih = ri.GetIdentHash();
|
||||
i2p::data::IdentHash randomIdent;
|
||||
|
||||
// make floodfill lookups
|
||||
while(numFloodfills > 0) {
|
||||
randomIdent.Randomize();
|
||||
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, false);
|
||||
requests.push_back(msg);
|
||||
numFloodfills --;
|
||||
}
|
||||
|
||||
// make regular router lookups
|
||||
while(numRouters > 0) {
|
||||
randomIdent.Randomize();
|
||||
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg(randomIdent, ourIdent, 0, true);
|
||||
requests.push_back(msg);
|
||||
numRouters --;
|
||||
}
|
||||
|
||||
// send them off
|
||||
i2p::transport::transports.SendMessages(ih, requests);
|
||||
}
|
||||
|
||||
bool NetDb::LoadRouterInfo (const std::string & path)
|
||||
@@ -329,6 +388,67 @@ namespace data
|
||||
for ( auto & entry : m_LeaseSets)
|
||||
v(entry.first, entry.second);
|
||||
}
|
||||
|
||||
void NetDb::VisitStoredRouterInfos(RouterInfoVisitor v)
|
||||
{
|
||||
m_Storage.Iterate([v] (const std::string & filename) {
|
||||
auto ri = std::make_shared<i2p::data::RouterInfo>(filename);
|
||||
v(ri);
|
||||
});
|
||||
}
|
||||
|
||||
void NetDb::VisitRouterInfos(RouterInfoVisitor v)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_RouterInfosMutex);
|
||||
for ( const auto & item : m_RouterInfos )
|
||||
v(item.second);
|
||||
}
|
||||
|
||||
size_t NetDb::VisitRandomRouterInfos(RouterInfoFilter filter, RouterInfoVisitor v, size_t n)
|
||||
{
|
||||
std::vector<std::shared_ptr<const RouterInfo> > found;
|
||||
const size_t max_iters_per_cyle = 3;
|
||||
size_t iters = max_iters_per_cyle;
|
||||
while(n > 0)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_RouterInfosMutex);
|
||||
uint32_t idx = rand () % m_RouterInfos.size ();
|
||||
uint32_t i = 0;
|
||||
for (const auto & it : m_RouterInfos) {
|
||||
if(i >= idx) // are we at the random start point?
|
||||
{
|
||||
// yes, check if we want this one
|
||||
if(filter(it.second))
|
||||
{
|
||||
// we have a match
|
||||
--n;
|
||||
found.push_back(it.second);
|
||||
// reset max iterations per cycle
|
||||
iters = max_iters_per_cyle;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // not there yet
|
||||
++i;
|
||||
}
|
||||
// we have enough
|
||||
if(n == 0) break;
|
||||
--iters;
|
||||
// have we tried enough this cycle ?
|
||||
if(!iters) {
|
||||
// yes let's try the next cycle
|
||||
--n;
|
||||
iters = max_iters_per_cyle;
|
||||
}
|
||||
}
|
||||
// visit the ones we found
|
||||
size_t visited = 0;
|
||||
for(const auto & ri : found ) {
|
||||
v(ri);
|
||||
++visited;
|
||||
}
|
||||
return visited;
|
||||
}
|
||||
|
||||
void NetDb::Load ()
|
||||
{
|
||||
@@ -438,6 +558,21 @@ namespace data
|
||||
m_Requests.RequestComplete (destination, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete)
|
||||
{
|
||||
|
||||
auto dest = m_Requests.CreateRequest (destination, exploritory, requestComplete); // non-exploratory
|
||||
if (!dest)
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: destination ", destination.ToBase64(), " is requested already");
|
||||
return;
|
||||
}
|
||||
LogPrint(eLogInfo, "NetDb: destination ", destination.ToBase64(), " being requested directly from ", from.ToBase64());
|
||||
// direct
|
||||
transports.SendMessage (from, dest->CreateRequestMessage (nullptr, nullptr));
|
||||
}
|
||||
|
||||
|
||||
void NetDb::HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> m)
|
||||
{
|
||||
@@ -446,7 +581,7 @@ namespace data
|
||||
IdentHash ident (buf + DATABASE_STORE_KEY_OFFSET);
|
||||
if (ident.IsZero ())
|
||||
{
|
||||
LogPrint (eLogError, "NetDb: database store with zero ident, dropped");
|
||||
LogPrint (eLogDebug, "NetDb: database store with zero ident, dropped");
|
||||
return;
|
||||
}
|
||||
uint32_t replyToken = bufbe32toh (buf + DATABASE_STORE_REPLY_TOKEN_OFFSET);
|
||||
@@ -465,14 +600,14 @@ namespace data
|
||||
if (outbound)
|
||||
outbound->SendTunnelDataMsg (buf + offset, tunnelID, deliveryStatus);
|
||||
else
|
||||
LogPrint (eLogError, "NetDb: no outbound tunnels for DatabaseStore reply found");
|
||||
LogPrint (eLogWarning, "NetDb: no outbound tunnels for DatabaseStore reply found");
|
||||
}
|
||||
offset += 32;
|
||||
}
|
||||
// we must send reply back before this check
|
||||
if (ident == i2p::context.GetIdentHash ())
|
||||
{
|
||||
LogPrint (eLogError, "NetDb: database store with own RouterInfo received, dropped");
|
||||
LogPrint (eLogDebug, "NetDb: database store with own RouterInfo received, dropped");
|
||||
return;
|
||||
}
|
||||
size_t payloadOffset = offset;
|
||||
@@ -495,8 +630,13 @@ namespace data
|
||||
}
|
||||
uint8_t uncompressed[2048];
|
||||
size_t uncompressedSize = m_Inflator.Inflate (buf + offset, size, uncompressed, 2048);
|
||||
if (uncompressedSize)
|
||||
if (uncompressedSize && uncompressedSize < 2048)
|
||||
updated = AddRouterInfo (ident, uncompressed, uncompressedSize);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogInfo, "NetDb: decompression failed ", uncompressedSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (replyToken && context.IsFloodfill () && updated)
|
||||
@@ -555,7 +695,7 @@ namespace data
|
||||
if (!dest->IsExploratory ())
|
||||
{
|
||||
// reply to our destination. Try other floodfills
|
||||
if (outbound && inbound )
|
||||
if (outbound && inbound)
|
||||
{
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
auto count = dest->GetExcludedPeers ().size ();
|
||||
@@ -599,7 +739,7 @@ namespace data
|
||||
// no more requests for detination possible. delete it
|
||||
m_Requests.RequestComplete (ident, nullptr);
|
||||
}
|
||||
else
|
||||
else if(!m_FloodfillBootstrap)
|
||||
LogPrint (eLogWarning, "NetDb: requested destination for ", key, " not found");
|
||||
|
||||
// try responses
|
||||
@@ -616,7 +756,10 @@ namespace data
|
||||
{
|
||||
// router with ident not found or too old (1 hour)
|
||||
LogPrint (eLogDebug, "NetDb: found new/outdated router. Requesting RouterInfo ...");
|
||||
RequestDestination (router);
|
||||
if(m_FloodfillBootstrap)
|
||||
RequestDestinationFrom(router, m_FloodfillBootstrap->GetIdentHash(), true);
|
||||
else
|
||||
RequestDestination (router);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "NetDb: [:|||:]");
|
||||
@@ -711,33 +854,17 @@ namespace data
|
||||
}
|
||||
|
||||
if (!replyMsg)
|
||||
{
|
||||
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
|
||||
// find or cleate response
|
||||
std::vector<IdentHash> closestFloodfills;
|
||||
bool found = false;
|
||||
if (!numExcluded)
|
||||
{
|
||||
auto it = m_LookupResponses.find (ident);
|
||||
if (it != m_LookupResponses.end ())
|
||||
{
|
||||
closestFloodfills = it->second.first;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
std::set<IdentHash> excludedRouters;
|
||||
const uint8_t * exclude_ident = excluded;
|
||||
for (int i = 0; i < numExcluded; i++)
|
||||
{
|
||||
excludedRouters.insert (exclude_ident);
|
||||
exclude_ident += 32;
|
||||
}
|
||||
closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
|
||||
if (!numExcluded) // save if no excluded
|
||||
m_LookupResponses[ident] = std::make_pair(closestFloodfills, i2p::util::GetSecondsSinceEpoch ());
|
||||
{
|
||||
std::set<IdentHash> excludedRouters;
|
||||
const uint8_t * exclude_ident = excluded;
|
||||
for (int i = 0; i < numExcluded; i++)
|
||||
{
|
||||
excludedRouters.insert (exclude_ident);
|
||||
exclude_ident += 32;
|
||||
}
|
||||
auto closestFloodfills = GetClosestFloodfills (ident, 3, excludedRouters, true);
|
||||
if (closestFloodfills.empty ())
|
||||
LogPrint (eLogWarning, "NetDb: Requested ", key, " not found, ", numExcluded, " peers excluded");
|
||||
replyMsg = CreateDatabaseSearchReply (ident, closestFloodfills);
|
||||
}
|
||||
}
|
||||
@@ -783,7 +910,6 @@ namespace data
|
||||
|
||||
uint8_t randomHash[32];
|
||||
std::vector<i2p::tunnel::TunnelMessageBlock> msgs;
|
||||
std::set<const RouterInfo *> floodfills;
|
||||
LogPrint (eLogInfo, "NetDb: exploring new ", numDestinations, " routers ...");
|
||||
for (int i = 0; i < numDestinations; i++)
|
||||
{
|
||||
@@ -795,9 +921,8 @@ namespace data
|
||||
return;
|
||||
}
|
||||
auto floodfill = GetClosestFloodfill (randomHash, dest->GetExcludedPeers ());
|
||||
if (floodfill && !floodfills.count (floodfill.get ())) // request floodfill only once
|
||||
if (floodfill)
|
||||
{
|
||||
floodfills.insert (floodfill.get ());
|
||||
if (i2p::transport::transports.IsConnected (floodfill->GetIdentHash ()))
|
||||
throughTunnels = false;
|
||||
if (throughTunnels)
|
||||
@@ -862,12 +987,12 @@ namespace data
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter () const
|
||||
std::shared_ptr<const RouterInfo> NetDb::GetRandomPeerTestRouter (bool v4only) const
|
||||
{
|
||||
return GetRandomRouter (
|
||||
[](std::shared_ptr<const RouterInfo> router)->bool
|
||||
[v4only](std::shared_ptr<const RouterInfo> router)->bool
|
||||
{
|
||||
return !router->IsHidden () && router->IsPeerTesting ();
|
||||
return !router->IsHidden () && router->IsPeerTesting () && router->IsSSU (v4only);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1045,17 +1170,5 @@ namespace data
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void NetDb::ManageLookupResponses ()
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto it = m_LookupResponses.begin (); it != m_LookupResponses.end ();)
|
||||
{
|
||||
if (ts > it->second.second + 180) // 3 minutes
|
||||
it = m_LookupResponses.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
32
NetDb.h
32
NetDb.h
@@ -35,7 +35,13 @@ namespace data
|
||||
|
||||
/** function for visiting a leaseset stored in a floodfill */
|
||||
typedef std::function<void(const IdentHash, std::shared_ptr<LeaseSet>)> LeaseSetVisitor;
|
||||
|
||||
|
||||
/** function for visiting a router info we have locally */
|
||||
typedef std::function<void(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoVisitor;
|
||||
|
||||
/** function for visiting a router info and determining if we want to use it */
|
||||
typedef std::function<bool(std::shared_ptr<const i2p::data::RouterInfo>)> RouterInfoFilter;
|
||||
|
||||
class NetDb
|
||||
{
|
||||
public:
|
||||
@@ -45,7 +51,7 @@ namespace data
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
|
||||
bool AddRouterInfo (const uint8_t * buf, int len);
|
||||
bool AddRouterInfo (const IdentHash& ident, const uint8_t * buf, int len);
|
||||
bool AddLeaseSet (const IdentHash& ident, const uint8_t * buf, int len, std::shared_ptr<i2p::tunnel::InboundTunnel> from);
|
||||
@@ -54,6 +60,7 @@ namespace data
|
||||
std::shared_ptr<RouterProfile> FindRouterProfile (const IdentHash& ident) const;
|
||||
|
||||
void RequestDestination (const IdentHash& destination, RequestedDestination::RequestComplete requestComplete = nullptr);
|
||||
void RequestDestinationFrom (const IdentHash& destination, const IdentHash & from, bool exploritory, RequestedDestination::RequestComplete requestComplete = nullptr);
|
||||
|
||||
void HandleDatabaseStoreMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
void HandleDatabaseSearchReplyMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
@@ -62,7 +69,7 @@ namespace data
|
||||
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> GetRandomPeerTestRouter (bool v4only = true) const;
|
||||
std::shared_ptr<const RouterInfo> GetRandomIntroducer () const;
|
||||
std::shared_ptr<const RouterInfo> GetClosestFloodfill (const IdentHash& destination, const std::set<IdentHash>& excluded, bool closeThanUsOnly = false) const;
|
||||
std::vector<IdentHash> GetClosestFloodfills (const IdentHash& destination, size_t num,
|
||||
@@ -86,7 +93,15 @@ namespace data
|
||||
|
||||
/** visit all lease sets we currently store */
|
||||
void VisitLeaseSets(LeaseSetVisitor v);
|
||||
|
||||
/** visit all router infos we have currently on disk, usually insanely expensive, does not access in memory RI */
|
||||
void VisitStoredRouterInfos(RouterInfoVisitor v);
|
||||
/** visit all router infos we have loaded in memory, cheaper than VisitLocalRouterInfos but locks access while visiting */
|
||||
void VisitRouterInfos(RouterInfoVisitor v);
|
||||
/** visit N random router that match using filter, then visit them with a visitor, return number of RouterInfos that were visited */
|
||||
size_t VisitRandomRouterInfos(RouterInfoFilter f, RouterInfoVisitor v, size_t n);
|
||||
|
||||
void ClearRouterInfos () { m_RouterInfos.clear (); };
|
||||
|
||||
private:
|
||||
|
||||
void Load ();
|
||||
@@ -97,13 +112,14 @@ namespace data
|
||||
void Publish ();
|
||||
void ManageLeaseSets ();
|
||||
void ManageRequests ();
|
||||
void ManageLookupResponses ();
|
||||
|
||||
void ReseedFromFloodfill(const RouterInfo & ri, int numRouters=40, int numFloodfills=20);
|
||||
|
||||
template<typename Filter>
|
||||
std::shared_ptr<const RouterInfo> GetRandomRouter (Filter filter) const;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
mutable std::mutex m_LeaseSetsMutex;
|
||||
std::map<IdentHash, std::shared_ptr<LeaseSet> > m_LeaseSets;
|
||||
mutable std::mutex m_RouterInfosMutex;
|
||||
@@ -124,7 +140,9 @@ namespace data
|
||||
friend class NetDbRequests;
|
||||
NetDbRequests m_Requests;
|
||||
|
||||
std::map<IdentHash, std::pair<std::vector<IdentHash>, uint64_t> > m_LookupResponses; // ident->(closest FFs, timestamp)
|
||||
/** router info we are bootstrapping from or nullptr if we are not currently doing that*/
|
||||
std::shared_ptr<RouterInfo> m_FloodfillBootstrap;
|
||||
|
||||
|
||||
/** true if in hidden mode */
|
||||
bool m_HiddenMode;
|
||||
|
||||
@@ -11,10 +11,15 @@ namespace data
|
||||
std::shared_ptr<I2NPMessage> RequestedDestination::CreateRequestMessage (std::shared_ptr<const RouterInfo> router,
|
||||
std::shared_ptr<const i2p::tunnel::InboundTunnel> replyTunnel)
|
||||
{
|
||||
auto msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
|
||||
std::shared_ptr<I2NPMessage> msg;
|
||||
if(replyTunnel)
|
||||
msg = i2p::CreateRouterInfoDatabaseLookupMsg (m_Destination,
|
||||
replyTunnel->GetNextIdentHash (), replyTunnel->GetNextTunnelID (), m_IsExploratory,
|
||||
&m_ExcludedPeers);
|
||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
||||
else
|
||||
msg = i2p::CreateRouterInfoDatabaseLookupMsg(m_Destination, i2p::context.GetIdentHash(), 0, m_IsExploratory, &m_ExcludedPeers);
|
||||
if(router)
|
||||
m_ExcludedPeers.insert (router->GetIdentHash ());
|
||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
return msg;
|
||||
}
|
||||
@@ -68,8 +73,7 @@ namespace data
|
||||
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
|
||||
if (!m_RequestedDestinations.insert (std::make_pair (destination, dest)).second) // not inserted
|
||||
return nullptr;
|
||||
}
|
||||
return dest;
|
||||
@@ -77,20 +81,28 @@ namespace data
|
||||
|
||||
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::shared_ptr<RequestedDestination> request;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
||||
m_RequestedDestinations.erase (it);
|
||||
auto it = m_RequestedDestinations.find (ident);
|
||||
if (it != m_RequestedDestinations.end ())
|
||||
{
|
||||
request = it->second;
|
||||
m_RequestedDestinations.erase (it);
|
||||
}
|
||||
}
|
||||
if (request)
|
||||
{
|
||||
if (r)
|
||||
request->Success (r);
|
||||
else
|
||||
request->Fail ();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<RequestedDestination> NetDbRequests::FindRequest (const IdentHash& ident) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_RequestedDestinationsMutex);
|
||||
auto it = m_RequestedDestinations.find (ident);
|
||||
if (it != m_RequestedDestinations.end ())
|
||||
return it->second;
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace data
|
||||
|
||||
private:
|
||||
|
||||
std::mutex m_RequestedDestinationsMutex;
|
||||
mutable std::mutex m_RequestedDestinationsMutex;
|
||||
std::map<IdentHash, std::shared_ptr<RequestedDestination> > m_RequestedDestinations;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,8 +12,8 @@ namespace data
|
||||
{
|
||||
i2p::fs::HashedStorage m_ProfilesStorage("peerProfiles", "p", "profile-", "txt");
|
||||
|
||||
RouterProfile::RouterProfile (const IdentHash& identHash):
|
||||
m_IdentHash (identHash), m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
|
||||
RouterProfile::RouterProfile ():
|
||||
m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
|
||||
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
|
||||
m_NumTimesTaken (0), m_NumTimesRejected (0)
|
||||
{
|
||||
@@ -29,7 +29,7 @@ namespace data
|
||||
m_LastUpdateTime = GetTime ();
|
||||
}
|
||||
|
||||
void RouterProfile::Save ()
|
||||
void RouterProfile::Save (const IdentHash& identHash)
|
||||
{
|
||||
// fill sections
|
||||
boost::property_tree::ptree participation;
|
||||
@@ -46,7 +46,7 @@ namespace data
|
||||
pt.put_child (PEER_PROFILE_SECTION_USAGE, usage);
|
||||
|
||||
// save to file
|
||||
std::string ident = m_IdentHash.ToBase64 ();
|
||||
std::string ident = identHash.ToBase64 ();
|
||||
std::string path = m_ProfilesStorage.Path(ident);
|
||||
|
||||
try {
|
||||
@@ -57,51 +57,64 @@ namespace data
|
||||
}
|
||||
}
|
||||
|
||||
void RouterProfile::Load ()
|
||||
void RouterProfile::Load (const IdentHash& identHash)
|
||||
{
|
||||
std::string ident = m_IdentHash.ToBase64 ();
|
||||
std::string ident = identHash.ToBase64 ();
|
||||
std::string path = m_ProfilesStorage.Path(ident);
|
||||
boost::property_tree::ptree pt;
|
||||
|
||||
if (!i2p::fs::Exists(path)) {
|
||||
if (!i2p::fs::Exists(path))
|
||||
{
|
||||
LogPrint(eLogWarning, "Profiling: no profile yet for ", ident);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
boost::property_tree::read_ini (path, pt);
|
||||
} catch (std::exception& ex) {
|
||||
} catch (std::exception& ex)
|
||||
{
|
||||
/* boost exception verbose enough */
|
||||
LogPrint (eLogError, "Profiling: ", ex.what ());
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
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 {
|
||||
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) {
|
||||
}
|
||||
catch (boost::property_tree::ptree_bad_path& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "Profiling: Missing section ", PEER_PROFILE_SECTION_PARTICIPATION, " in profile for ", ident);
|
||||
}
|
||||
try {
|
||||
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) {
|
||||
}
|
||||
catch (boost::property_tree::ptree_bad_path& ex)
|
||||
{
|
||||
LogPrint (eLogWarning, "Missing section ", PEER_PROFILE_SECTION_USAGE, " in profile for ", ident);
|
||||
}
|
||||
} else {
|
||||
*this = RouterProfile (m_IdentHash);
|
||||
}
|
||||
} catch (std::exception& ex) {
|
||||
}
|
||||
else
|
||||
*this = RouterProfile ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "Profiling: Can't read profile ", ident, " :", ex.what ());
|
||||
}
|
||||
}
|
||||
@@ -149,8 +162,8 @@ namespace data
|
||||
|
||||
std::shared_ptr<RouterProfile> GetRouterProfile (const IdentHash& identHash)
|
||||
{
|
||||
auto profile = std::make_shared<RouterProfile> (identHash);
|
||||
profile->Load (); // if possible
|
||||
auto profile = std::make_shared<RouterProfile> ();
|
||||
profile->Load (identHash); // if possible
|
||||
return profile;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,11 +26,11 @@ namespace data
|
||||
{
|
||||
public:
|
||||
|
||||
RouterProfile (const IdentHash& identHash);
|
||||
RouterProfile ();
|
||||
RouterProfile& operator= (const RouterProfile& ) = default;
|
||||
|
||||
void Save ();
|
||||
void Load ();
|
||||
void Save (const IdentHash& identHash);
|
||||
void Load (const IdentHash& identHash);
|
||||
|
||||
bool IsBad ();
|
||||
|
||||
@@ -48,7 +48,6 @@ namespace data
|
||||
|
||||
private:
|
||||
|
||||
IdentHash m_IdentHash;
|
||||
boost::posix_time::ptime m_LastUpdateTime;
|
||||
// participation
|
||||
uint32_t m_NumTunnelsAgreed;
|
||||
|
||||
3
Queue.h
3
Queue.h
@@ -25,7 +25,8 @@ namespace util
|
||||
m_NonEmpty.notify_one ();
|
||||
}
|
||||
|
||||
void Put (const std::vector<Element>& vec)
|
||||
template<template<typename, typename...>class Container, typename... R>
|
||||
void Put (const Container<Element, R...>& vec)
|
||||
{
|
||||
if (!vec.empty ())
|
||||
{
|
||||
|
||||
24
README.md
24
README.md
@@ -1,21 +1,28 @@
|
||||
i2pd
|
||||
====
|
||||
|
||||
i2pd is a full-featured C++ implementation of
|
||||
[I2P](https://geti2p.net/en/about/intro) client.
|
||||
[Русская версия](https://github.com/PurpleI2P/i2pd_docs_ru/blob/master/README.md)
|
||||
|
||||
I2P (Invisible Internet Project) is anonymous network which works on top of
|
||||
public Internet. Privacy and anonymity are achieved by strong encryption and
|
||||
bouncing your traffic through thousands of I2P nodes all around the world.
|
||||
i2pd (I2P Daemon) is a full-featured C++ implementation of I2P client.
|
||||
|
||||
We are building network which helps people to communicate and share information
|
||||
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.
|
||||
|
||||
* [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)
|
||||
* [Twitter](https://twitter.com/i2porignal)
|
||||
* [Specifications](https://geti2p.net/spec)
|
||||
* [Twitter](https://twitter.com/hashtag/i2pd)
|
||||
|
||||
Installing
|
||||
----------
|
||||
@@ -32,11 +39,12 @@ i2pd from source on your OS.
|
||||
* Mac OS X
|
||||
* FreeBSD
|
||||
* Android
|
||||
* iOS
|
||||
|
||||
Using i2pd
|
||||
----------
|
||||
|
||||
See [documentation](https://i2pd.readthedocs.io/en/latest/) and
|
||||
See [documentation](https://i2pd.readthedocs.io/en/latest/usage.html) and
|
||||
[example config file](https://github.com/PurpleI2P/i2pd/blob/openssl/docs/i2pd.conf).
|
||||
|
||||
Donations
|
||||
|
||||
273
Reseed.cpp
273
Reseed.cpp
@@ -3,6 +3,7 @@
|
||||
#include <sstream>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <zlib.h>
|
||||
@@ -22,25 +23,6 @@ namespace i2p
|
||||
{
|
||||
namespace data
|
||||
{
|
||||
static std::vector<std::string> httpsReseedHostList =
|
||||
{
|
||||
#ifdef MESHNET
|
||||
// meshnet i2p reseeds
|
||||
"https://reseed.i2p.rocks:8443/"
|
||||
#else
|
||||
// mainline i2p reseeds
|
||||
"https://reseed.i2p-projekt.de/", // Only HTTPS
|
||||
"https://i2p.mooo.com/netDb/",
|
||||
"https://netdb.i2p2.no/", // Only SU3 (v3) support, SNI required
|
||||
"https://us.reseed.i2p2.no:444/",
|
||||
"https://uk.reseed.i2p2.no:444/",
|
||||
"https://i2p.manas.ca:8443/",
|
||||
"https://i2p-0.manas.ca:8443/",
|
||||
"https://reseed.i2p.vzaws.com:8443/", // Only SU3 (v3) support
|
||||
"https://user.mx24.eu/", // Only HTTPS and SU3 (v3) support
|
||||
"https://download.xxlspeed.com/" // Only HTTPS and SU3 (v3) support
|
||||
#endif
|
||||
};
|
||||
|
||||
Reseeder::Reseeder()
|
||||
{
|
||||
@@ -50,24 +32,76 @@ namespace data
|
||||
{
|
||||
}
|
||||
|
||||
int Reseeder::ReseedNowSU3 ()
|
||||
/** @brief tries to bootstrap into I2P network (from local files and servers, with respect of options)
|
||||
*/
|
||||
void Reseeder::Bootstrap ()
|
||||
{
|
||||
std::string su3FileName; i2p::config::GetOption("reseed.file", su3FileName);
|
||||
std::string zipFileName; i2p::config::GetOption("reseed.zipfile", zipFileName);
|
||||
|
||||
if (su3FileName.length() > 0) // bootstrap from SU3 file or URL
|
||||
{
|
||||
int num;
|
||||
if (su3FileName.length() > 8 && su3FileName.substr(0, 8) == "https://")
|
||||
{
|
||||
num = ReseedFromSU3Url (su3FileName); // from https URL
|
||||
}
|
||||
else
|
||||
{
|
||||
num = ProcessSU3File (su3FileName.c_str ());
|
||||
}
|
||||
if (num == 0)
|
||||
LogPrint (eLogWarning, "Reseed: failed to reseed from ", su3FileName);
|
||||
}
|
||||
else if (zipFileName.length() > 0) // bootstrap from ZIP file
|
||||
{
|
||||
int num = ProcessZIPFile (zipFileName.c_str ());
|
||||
if (num == 0)
|
||||
LogPrint (eLogWarning, "Reseed: failed to reseed from ", zipFileName);
|
||||
}
|
||||
else // bootstrap from reseed servers
|
||||
{
|
||||
int num = ReseedFromServers ();
|
||||
if (num == 0)
|
||||
LogPrint (eLogWarning, "Reseed: failed to reseed from servers");
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief bootstrap from random server, retry 10 times
|
||||
* @return number of entries added to netDb
|
||||
*/
|
||||
int Reseeder::ReseedFromServers ()
|
||||
{
|
||||
std::string filename; i2p::config::GetOption("reseed.file", filename);
|
||||
if (filename.length() > 0) // reseed file is specified
|
||||
{
|
||||
auto num = ProcessSU3File (filename.c_str ());
|
||||
if (num > 0) return num; // success
|
||||
LogPrint (eLogWarning, "Can't reseed from ", filename, " . Trying from hosts");
|
||||
}
|
||||
auto ind = rand () % httpsReseedHostList.size ();
|
||||
std::string& reseedHost = httpsReseedHostList[ind];
|
||||
return ReseedFromSU3 (reseedHost);
|
||||
std::string reseedURLs; i2p::config::GetOption("reseed.urls", reseedURLs);
|
||||
std::vector<std::string> httpsReseedHostList;
|
||||
boost::split(httpsReseedHostList, reseedURLs, boost::is_any_of(","), boost::token_compress_on);
|
||||
|
||||
if (reseedURLs.length () == 0)
|
||||
{
|
||||
LogPrint (eLogWarning, "Reseed: No reseed servers specified");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int reseedRetries = 0;
|
||||
while (reseedRetries < 10)
|
||||
{
|
||||
auto ind = rand () % httpsReseedHostList.size ();
|
||||
std::string reseedUrl = httpsReseedHostList[ind] + "i2pseeds.su3";
|
||||
auto num = ReseedFromSU3Url (reseedUrl);
|
||||
if (num > 0) return num; // success
|
||||
reseedRetries++;
|
||||
}
|
||||
LogPrint (eLogWarning, "Reseed: failed to reseed from servers after 10 attempts");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Reseeder::ReseedFromSU3 (const std::string& host)
|
||||
/** @brief bootstrap from HTTPS URL with SU3 file
|
||||
* @param url
|
||||
* @return number of entries added to netDb
|
||||
*/
|
||||
int Reseeder::ReseedFromSU3Url (const std::string& url)
|
||||
{
|
||||
std::string url = host + "i2pseeds.su3";
|
||||
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", host);
|
||||
LogPrint (eLogInfo, "Reseed: Downloading SU3 from ", url);
|
||||
std::string su3 = HttpsRequest (url);
|
||||
if (su3.length () > 0)
|
||||
{
|
||||
@@ -93,10 +127,24 @@ namespace data
|
||||
}
|
||||
}
|
||||
|
||||
int Reseeder::ProcessZIPFile (const char * filename)
|
||||
{
|
||||
std::ifstream s(filename, std::ifstream::binary);
|
||||
if (s.is_open ())
|
||||
{
|
||||
s.seekg (0, std::ios::end);
|
||||
auto len = s.tellg ();
|
||||
s.seekg (0, std::ios::beg);
|
||||
return ProcessZIPStream (s, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Reseed: 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];
|
||||
@@ -145,53 +193,73 @@ namespace data
|
||||
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)
|
||||
bool verify; i2p::config::GetOption("reseed.verify", verify);
|
||||
if (verify)
|
||||
{
|
||||
//try to verify signature
|
||||
auto it = m_SigningKeys.find (signerID);
|
||||
if (it != m_SigningKeys.end ())
|
||||
{
|
||||
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
|
||||
// TODO: implement all signature types
|
||||
if (signatureType == SIGNING_KEY_TYPE_RSA_SHA512_4096)
|
||||
{
|
||||
// calculate digest
|
||||
uint8_t digest[64];
|
||||
SHA512 (tbs, tbsLen, digest);
|
||||
// encrypt signature
|
||||
BN_CTX * bnctx = BN_CTX_new ();
|
||||
BIGNUM * s = BN_new (), * n = BN_new ();
|
||||
BN_bin2bn (signature, signatureLength, s);
|
||||
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
|
||||
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
|
||||
uint8_t * enSigBuf = new uint8_t[signatureLength];
|
||||
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
|
||||
// digest is right aligned
|
||||
// we can't use RSA_verify due wrong padding in SU3
|
||||
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
|
||||
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
|
||||
delete[] enSigBuf;
|
||||
BN_free (s); BN_free (n);
|
||||
BN_CTX_free (bnctx);
|
||||
}
|
||||
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
|
||||
{
|
||||
// calculate digest
|
||||
uint8_t digest[64];
|
||||
SHA512 (tbs, tbsLen, digest);
|
||||
// encrypt signature
|
||||
BN_CTX * bnctx = BN_CTX_new ();
|
||||
BIGNUM * s = BN_new (), * n = BN_new ();
|
||||
BN_bin2bn (signature, signatureLength, s);
|
||||
BN_bin2bn (it->second, i2p::crypto::RSASHA5124096_KEY_LENGTH, n);
|
||||
BN_mod_exp (s, s, i2p::crypto::GetRSAE (), n, bnctx); // s = s^e mod n
|
||||
uint8_t * enSigBuf = new uint8_t[signatureLength];
|
||||
i2p::crypto::bn2buf (s, enSigBuf, signatureLength);
|
||||
// digest is right aligned
|
||||
// we can't use RSA_verify due wrong padding in SU3
|
||||
if (memcmp (enSigBuf + (signatureLength - 64), digest, 64))
|
||||
LogPrint (eLogWarning, "Reseed: SU3 signature verification failed");
|
||||
else
|
||||
verify = false; // verified
|
||||
delete[] enSigBuf;
|
||||
BN_free (s); BN_free (n);
|
||||
BN_CTX_free (bnctx);
|
||||
}
|
||||
|
||||
delete[] signature;
|
||||
delete[] tbs;
|
||||
s.seekg (pos, std::ios::beg);
|
||||
delete[] signature;
|
||||
delete[] tbs;
|
||||
s.seekg (pos, std::ios::beg);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Reseed: Signature type ", signatureType, " is not supported");
|
||||
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "Reseed: Certificate for ", signerID, " not loaded");
|
||||
|
||||
|
||||
if (verify) // not verified
|
||||
{
|
||||
LogPrint (eLogError, "Reseed: SU3 verification failed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// handle content
|
||||
return ProcessZIPStream (s, contentLength);
|
||||
}
|
||||
|
||||
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::ProcessZIPStream (std::istream& s, uint64_t contentLength)
|
||||
{
|
||||
int numFiles = 0;
|
||||
size_t contentPos = s.tellg ();
|
||||
while (!s.eof ())
|
||||
@@ -307,6 +375,34 @@ namespace data
|
||||
if (end - contentPos >= contentLength)
|
||||
break; // we are beyond contentLength
|
||||
}
|
||||
if (numFiles) // check if routers are not outdated
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
int numOutdated = 0;
|
||||
i2p::data::netdb.VisitRouterInfos (
|
||||
[&numOutdated, ts](std::shared_ptr<const RouterInfo> r)
|
||||
{
|
||||
if (r && ts > r->GetTimestamp () + 10*i2p::data::NETDB_MAX_EXPIRATION_TIMEOUT*1000LL) // 270 hours
|
||||
{
|
||||
LogPrint (eLogError, "Reseed: router ", r->GetIdentHash().ToBase64 (), " is outdated by ", (ts - r->GetTimestamp ())/1000LL/3600LL, " hours");
|
||||
numOutdated++;
|
||||
}
|
||||
});
|
||||
if (numOutdated > numFiles/2) // more than half
|
||||
{
|
||||
LogPrint (eLogError, "Reseed: mammoth's shit\n"
|
||||
" *_____*\n"
|
||||
" *_*****_*\n"
|
||||
" *_(O)_(O)_*\n"
|
||||
" **____V____**\n"
|
||||
" **_________**\n"
|
||||
" **_________**\n"
|
||||
" *_________*\n"
|
||||
" ***___***");
|
||||
i2p::data::netdb.ClearRouterInfos ();
|
||||
numFiles = 0;
|
||||
}
|
||||
}
|
||||
return numFiles;
|
||||
}
|
||||
|
||||
@@ -332,7 +428,7 @@ namespace data
|
||||
|
||||
void Reseeder::LoadCertificate (const std::string& filename)
|
||||
{
|
||||
SSL_CTX * ctx = SSL_CTX_new (TLSv1_method ());
|
||||
SSL_CTX * ctx = SSL_CTX_new (TLS_method ());
|
||||
int ret = SSL_CTX_use_certificate_file (ctx, filename.c_str (), SSL_FILETYPE_PEM);
|
||||
if (ret)
|
||||
{
|
||||
@@ -344,11 +440,23 @@ namespace data
|
||||
// extract issuer name
|
||||
char name[100];
|
||||
X509_NAME_oneline (X509_get_issuer_name(cert), name, 100);
|
||||
char * cn = strstr (name, "CN=");
|
||||
if (cn)
|
||||
{
|
||||
cn += 3;
|
||||
char * terminator = strchr (cn, '/');
|
||||
if (terminator) terminator[0] = 0;
|
||||
}
|
||||
// extract RSA key (we need n only, e = 65537)
|
||||
RSA * key = X509_get_pubkey (cert)->pkey.rsa;
|
||||
RSA * key = EVP_PKEY_get0_RSA (X509_get_pubkey (cert));
|
||||
const BIGNUM * n, * e, * d;
|
||||
RSA_get0_key(key, &n, &e, &d);
|
||||
PublicKey value;
|
||||
i2p::crypto::bn2buf (key->n, value, 512);
|
||||
m_SigningKeys[name] = value;
|
||||
i2p::crypto::bn2buf (n, value, 512);
|
||||
if (cn)
|
||||
m_SigningKeys[cn] = value;
|
||||
else
|
||||
LogPrint (eLogError, "Reseed: Can't find CN field in ", filename);
|
||||
}
|
||||
SSL_free (ssl);
|
||||
}
|
||||
@@ -402,14 +510,15 @@ namespace data
|
||||
s.lowest_layer().connect (*it, ecode);
|
||||
if (!ecode)
|
||||
{
|
||||
SSL_set_tlsext_host_name(s.native_handle(), url.host.c_str ());
|
||||
s.handshake (boost::asio::ssl::stream_base::client, ecode);
|
||||
if (!ecode)
|
||||
{
|
||||
LogPrint (eLogDebug, "Reseed: Connected to ", url.host, ":", url.port);
|
||||
i2p::http::HTTPReq req;
|
||||
req.uri = url.to_string();
|
||||
req.add_header("User-Agent", "Wget/1.11.4");
|
||||
req.add_header("Connection", "close");
|
||||
req.AddHeader("User-Agent", "Wget/1.11.4");
|
||||
req.AddHeader("Connection", "close");
|
||||
s.write_some (boost::asio::buffer (req.to_string()));
|
||||
// read response
|
||||
std::stringstream rs;
|
||||
|
||||
11
Reseed.h
11
Reseed.h
@@ -21,7 +21,11 @@ namespace data
|
||||
|
||||
Reseeder();
|
||||
~Reseeder();
|
||||
int ReseedNowSU3 ();
|
||||
void Bootstrap ();
|
||||
int ReseedFromServers ();
|
||||
int ReseedFromSU3Url (const std::string& url);
|
||||
int ProcessSU3File (const char * filename);
|
||||
int ProcessZIPFile (const char * filename);
|
||||
|
||||
void LoadCertificates ();
|
||||
|
||||
@@ -29,10 +33,9 @@ namespace data
|
||||
|
||||
void LoadCertificate (const std::string& filename);
|
||||
|
||||
int ReseedFromSU3 (const std::string& host);
|
||||
int ProcessSU3File (const char * filename);
|
||||
int ProcessSU3Stream (std::istream& s);
|
||||
|
||||
int ProcessZIPStream (std::istream& s, uint64_t contentLength);
|
||||
|
||||
bool FindZipDataDescriptor (std::istream& s);
|
||||
|
||||
std::string HttpsRequest (const std::string& address);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include <fstream>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Config.h"
|
||||
#include "Crypto.h"
|
||||
#include "Timestamp.h"
|
||||
@@ -18,7 +17,8 @@ namespace i2p
|
||||
|
||||
RouterContext::RouterContext ():
|
||||
m_LastUpdateTime (0), m_AcceptsTunnels (true), m_IsFloodfill (false),
|
||||
m_StartupTime (0), m_Status (eRouterStatusOK )
|
||||
m_StartupTime (0), m_Status (eRouterStatusOK), m_Error (eRouterErrorNone),
|
||||
m_NetID (I2PD_NET_ID)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@ namespace i2p
|
||||
bool ipv6; i2p::config::GetOption("ipv6", ipv6);
|
||||
bool nat; i2p::config::GetOption("nat", nat);
|
||||
std::string ifname; i2p::config::GetOption("ifname", ifname);
|
||||
std::string ifname4; i2p::config::GetOption("ifname4", ifname4);
|
||||
std::string ifname6; i2p::config::GetOption("ifname6", ifname6);
|
||||
if (ipv4)
|
||||
{
|
||||
std::string host = "127.0.0.1";
|
||||
@@ -61,6 +63,10 @@ namespace i2p
|
||||
else if (!nat && !ifname.empty())
|
||||
/* bind to interface, we have no NAT so set external address too */
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname, false).to_string(); // v4
|
||||
|
||||
if(ifname4.size())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname4, false).to_string();
|
||||
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
|
||||
routerInfo.AddNTCPAddress (host.c_str(), port);
|
||||
}
|
||||
@@ -71,12 +77,17 @@ namespace i2p
|
||||
i2p::config::GetOption("host", host);
|
||||
else if (!ifname.empty())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname, true).to_string(); // v6
|
||||
|
||||
if(ifname6.size())
|
||||
host = i2p::util::net::GetInterfaceAddress(ifname6, true).to_string();
|
||||
|
||||
routerInfo.AddSSUAddress (host.c_str(), port, routerInfo.GetIdentHash ());
|
||||
routerInfo.AddNTCPAddress (host.c_str(), port);
|
||||
}
|
||||
|
||||
routerInfo.SetCaps (i2p::data::RouterInfo::eReachable |
|
||||
i2p::data::RouterInfo::eSSUTesting | i2p::data::RouterInfo::eSSUIntroducer); // LR, BC
|
||||
routerInfo.SetProperty ("netId", std::to_string (I2PD_NET_ID));
|
||||
routerInfo.SetProperty ("netId", std::to_string (m_NetID));
|
||||
routerInfo.SetProperty ("router.version", I2P_VERSION);
|
||||
routerInfo.CreateBuffer (m_Keys);
|
||||
m_RouterInfo.SetRouterIdentity (GetIdentity ());
|
||||
@@ -95,6 +106,7 @@ namespace i2p
|
||||
if (status != m_Status)
|
||||
{
|
||||
m_Status = status;
|
||||
m_Error = eRouterErrorNone;
|
||||
switch (m_Status)
|
||||
{
|
||||
case eRouterStatusOK:
|
||||
@@ -193,7 +205,7 @@ namespace i2p
|
||||
|
||||
void RouterContext::SetBandwidth (char L) {
|
||||
uint16_t limit = 0;
|
||||
enum { low, high, extra } type = high;
|
||||
enum { low, high, extra, unlim } type = high;
|
||||
/* detect parameters */
|
||||
switch (L)
|
||||
{
|
||||
@@ -203,7 +215,7 @@ namespace i2p
|
||||
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH2 : limit = 128; type = high; break;
|
||||
case i2p::data::CAPS_FLAG_HIGH_BANDWIDTH3 : limit = 256; type = high; break;
|
||||
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH1 : limit = 2048; type = extra; break;
|
||||
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 9999; type = extra; break;
|
||||
case i2p::data::CAPS_FLAG_EXTRA_BANDWIDTH2 : limit = 9999; type = unlim; break;
|
||||
default:
|
||||
limit = 48; type = low;
|
||||
}
|
||||
@@ -214,8 +226,9 @@ namespace i2p
|
||||
switch (type)
|
||||
{
|
||||
case low : /* not set */; break;
|
||||
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break; // 'P'
|
||||
case unlim : caps |= i2p::data::RouterInfo::eExtraBandwidth; // no break here, extra + high means 'X'
|
||||
case high : caps |= i2p::data::RouterInfo::eHighBandwidth; break;
|
||||
case extra : caps |= i2p::data::RouterInfo::eExtraBandwidth; break;
|
||||
}
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
UpdateRouterInfo ();
|
||||
@@ -241,7 +254,12 @@ namespace i2p
|
||||
void RouterContext::SetUnreachable ()
|
||||
{
|
||||
// set caps
|
||||
m_RouterInfo.SetCaps (i2p::data::RouterInfo::eUnreachable | i2p::data::RouterInfo::eSSUTesting); // LU, B
|
||||
uint8_t caps = m_RouterInfo.GetCaps ();
|
||||
caps &= ~i2p::data::RouterInfo::eReachable;
|
||||
caps |= i2p::data::RouterInfo::eUnreachable;
|
||||
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
|
||||
caps &= ~i2p::data::RouterInfo::eSSUIntroducer; // can't be introducer
|
||||
m_RouterInfo.SetCaps (caps);
|
||||
// remove NTCP address
|
||||
auto& addresses = m_RouterInfo.GetAddresses ();
|
||||
for (auto it = addresses.begin (); it != addresses.end (); ++it)
|
||||
@@ -254,8 +272,9 @@ namespace i2p
|
||||
}
|
||||
}
|
||||
// delete previous introducers
|
||||
for (auto& addr : addresses)
|
||||
addr->introducers.clear ();
|
||||
for (auto& addr : addresses)
|
||||
if (addr->ssu)
|
||||
addr->ssu->introducers.clear ();
|
||||
|
||||
// update
|
||||
UpdateRouterInfo ();
|
||||
@@ -286,7 +305,8 @@ namespace i2p
|
||||
}
|
||||
// delete previous introducers
|
||||
for (auto& addr : addresses)
|
||||
addr->introducers.clear ();
|
||||
if (addr->ssu)
|
||||
addr->ssu->introducers.clear ();
|
||||
|
||||
// update
|
||||
UpdateRouterInfo ();
|
||||
@@ -355,8 +375,8 @@ namespace i2p
|
||||
if (m_IsFloodfill)
|
||||
{
|
||||
// update routers and leasesets
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumLeaseSets ()));
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, boost::lexical_cast<std::string>(i2p::data::netdb.GetNumRouters ()));
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_LEASESETS, std::to_string(i2p::data::netdb.GetNumLeaseSets ()));
|
||||
m_RouterInfo.SetProperty (i2p::data::ROUTER_INFO_PROPERTY_ROUTERS, std::to_string(i2p::data::netdb.GetNumRouters ()));
|
||||
UpdateRouterInfo ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,16 @@ namespace i2p
|
||||
{
|
||||
eRouterStatusOK = 0,
|
||||
eRouterStatusTesting = 1,
|
||||
eRouterStatusFirewalled = 2
|
||||
eRouterStatusFirewalled = 2,
|
||||
eRouterStatusError = 3
|
||||
};
|
||||
|
||||
enum RouterError
|
||||
{
|
||||
eRouterErrorNone = 0,
|
||||
eRouterErrorClockSkew = 1
|
||||
};
|
||||
|
||||
class RouterContext: public i2p::garlic::GarlicDestination
|
||||
{
|
||||
public:
|
||||
@@ -49,6 +56,10 @@ namespace i2p
|
||||
uint64_t GetBandwidthLimit () const { return m_BandwidthLimit; };
|
||||
RouterStatus GetStatus () const { return m_Status; };
|
||||
void SetStatus (RouterStatus status);
|
||||
RouterError GetError () const { return m_Error; };
|
||||
void SetError (RouterError error) { m_Status = eRouterStatusError; m_Error = error; };
|
||||
int GetNetID () const { return m_NetID; };
|
||||
void SetNetID (int netID) { m_NetID = netID; };
|
||||
|
||||
void UpdatePort (int port); // called from Daemon
|
||||
void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon
|
||||
@@ -107,6 +118,8 @@ namespace i2p
|
||||
uint64_t m_StartupTime; // in seconds since epoch
|
||||
uint32_t m_BandwidthLimit; // allowed bandwidth
|
||||
RouterStatus m_Status;
|
||||
RouterError m_Error;
|
||||
int m_NetID;
|
||||
std::mutex m_GarlicMutex;
|
||||
};
|
||||
|
||||
|
||||
208
RouterInfo.cpp
208
RouterInfo.cpp
@@ -3,12 +3,17 @@
|
||||
#include "I2PEndian.h"
|
||||
#include <fstream>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#if (BOOST_VERSION >= 105300)
|
||||
#include <boost/atomic.hpp>
|
||||
#endif
|
||||
#include "version.h"
|
||||
#include "Crypto.h"
|
||||
#include "Base.h"
|
||||
#include "Timestamp.h"
|
||||
#include "Log.h"
|
||||
#include "NetDb.h"
|
||||
#include "RouterContext.h"
|
||||
#include "RouterInfo.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -17,14 +22,14 @@ namespace data
|
||||
{
|
||||
RouterInfo::RouterInfo (): m_Buffer (nullptr)
|
||||
{
|
||||
m_Addresses = std::make_shared<Addresses>(); // create empty list
|
||||
m_Addresses = boost::make_shared<Addresses>(); // create empty list
|
||||
}
|
||||
|
||||
RouterInfo::RouterInfo (const std::string& fullPath):
|
||||
m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false),
|
||||
m_SupportedTransports (0), m_Caps (0)
|
||||
{
|
||||
m_Addresses = std::make_shared<Addresses>(); // create empty list
|
||||
m_Addresses = boost::make_shared<Addresses>(); // create empty list
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
ReadFromFile ();
|
||||
}
|
||||
@@ -32,7 +37,7 @@ namespace data
|
||||
RouterInfo::RouterInfo (const uint8_t * buf, int len):
|
||||
m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), m_Caps (0)
|
||||
{
|
||||
m_Addresses = std::make_shared<Addresses>(); // create empty list
|
||||
m_Addresses = boost::make_shared<Addresses>(); // create empty list
|
||||
m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE];
|
||||
memcpy (m_Buffer, buf, len);
|
||||
m_BufferLen = len;
|
||||
@@ -125,14 +130,6 @@ namespace data
|
||||
m_IsUnreachable = true;
|
||||
return;
|
||||
}
|
||||
std::stringstream str (std::string ((char *)m_Buffer + identityLen, m_BufferLen - identityLen));
|
||||
ReadFromStream (str);
|
||||
if (!str)
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: malformed message");
|
||||
m_IsUnreachable = true;
|
||||
return;
|
||||
}
|
||||
if (verifySignature)
|
||||
{
|
||||
// verify signature
|
||||
@@ -141,9 +138,20 @@ namespace data
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: signature verification failed");
|
||||
m_IsUnreachable = true;
|
||||
return;
|
||||
}
|
||||
m_RouterIdentity->DropVerifier ();
|
||||
}
|
||||
// parse RI
|
||||
std::stringstream str;
|
||||
str.write ((const char *)m_Buffer + identityLen, m_BufferLen - identityLen);
|
||||
ReadFromStream (str);
|
||||
if (!str)
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: malformed message");
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RouterInfo::ReadFromStream (std::istream& s)
|
||||
@@ -151,7 +159,7 @@ namespace data
|
||||
s.read ((char *)&m_Timestamp, sizeof (m_Timestamp));
|
||||
m_Timestamp = be64toh (m_Timestamp);
|
||||
// read addresses
|
||||
auto addresses = std::make_shared<Addresses>();
|
||||
auto addresses = boost::make_shared<Addresses>();
|
||||
uint8_t numAddresses;
|
||||
s.read ((char *)&numAddresses, sizeof (numAddresses)); if (!s) return;
|
||||
bool introducers = false;
|
||||
@@ -159,61 +167,75 @@ namespace data
|
||||
{
|
||||
uint8_t supportedTransports = 0;
|
||||
bool isValidAddress = true;
|
||||
Address address;
|
||||
s.read ((char *)&address.cost, sizeof (address.cost));
|
||||
s.read ((char *)&address.date, sizeof (address.date));
|
||||
auto address = std::make_shared<Address>();
|
||||
s.read ((char *)&address->cost, sizeof (address->cost));
|
||||
s.read ((char *)&address->date, sizeof (address->date));
|
||||
char transportStyle[5];
|
||||
ReadString (transportStyle, s);
|
||||
ReadString (transportStyle, 5, s);
|
||||
if (!strcmp (transportStyle, "NTCP"))
|
||||
address.transportStyle = eTransportNTCP;
|
||||
address->transportStyle = eTransportNTCP;
|
||||
else if (!strcmp (transportStyle, "SSU"))
|
||||
address.transportStyle = eTransportSSU;
|
||||
{
|
||||
address->transportStyle = eTransportSSU;
|
||||
address->ssu.reset (new SSUExt ());
|
||||
address->ssu->mtu = 0;
|
||||
}
|
||||
else
|
||||
address.transportStyle = eTransportUnknown;
|
||||
address.port = 0;
|
||||
address.mtu = 0;
|
||||
address->transportStyle = eTransportUnknown;
|
||||
address->port = 0;
|
||||
uint16_t size, r = 0;
|
||||
s.read ((char *)&size, sizeof (size)); if (!s) return;
|
||||
size = be16toh (size);
|
||||
while (r < size)
|
||||
{
|
||||
char key[500], value[500];
|
||||
r += ReadString (key, s);
|
||||
char key[255], value[255];
|
||||
r += ReadString (key, 255, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // =
|
||||
r += ReadString (value, s);
|
||||
r += ReadString (value, 255, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // ;
|
||||
if (!s) return;
|
||||
if (!strcmp (key, "host"))
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
address.host = boost::asio::ip::address::from_string (value, ecode);
|
||||
address->host = boost::asio::ip::address::from_string (value, ecode);
|
||||
if (ecode)
|
||||
{
|
||||
if (address.transportStyle == eTransportNTCP)
|
||||
if (address->transportStyle == eTransportNTCP)
|
||||
{
|
||||
supportedTransports |= eNTCPV4; // TODO:
|
||||
address.addressString = value;
|
||||
address->addressString = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
supportedTransports |= eSSUV4; // TODO:
|
||||
address.addressString = value;
|
||||
address->addressString = value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add supported protocol
|
||||
if (address.host.is_v4 ())
|
||||
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
|
||||
if (address->host.is_v4 ())
|
||||
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV4 : eSSUV4;
|
||||
else
|
||||
supportedTransports |= (address.transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
|
||||
supportedTransports |= (address->transportStyle == eTransportNTCP) ? eNTCPV6 : eSSUV6;
|
||||
}
|
||||
}
|
||||
else if (!strcmp (key, "port"))
|
||||
address.port = boost::lexical_cast<int>(value);
|
||||
address->port = boost::lexical_cast<int>(value);
|
||||
else if (!strcmp (key, "mtu"))
|
||||
address.mtu = boost::lexical_cast<int>(value);
|
||||
{
|
||||
if (address->ssu)
|
||||
address->ssu->mtu = boost::lexical_cast<int>(value);
|
||||
else
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'mtu' for NTCP");
|
||||
}
|
||||
else if (!strcmp (key, "key"))
|
||||
Base64ToByteStream (value, strlen (value), address.key, 32);
|
||||
{
|
||||
if (address->ssu)
|
||||
Base64ToByteStream (value, strlen (value), address->ssu->key, 32);
|
||||
else
|
||||
LogPrint (eLogWarning, "RouterInfo: Unexpected field 'key' for NTCP");
|
||||
}
|
||||
else if (!strcmp (key, "caps"))
|
||||
ExtractCaps (value);
|
||||
else if (key[0] == 'i')
|
||||
@@ -223,9 +245,14 @@ namespace data
|
||||
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 (index > 9)
|
||||
{
|
||||
LogPrint (eLogError, "RouterInfo: Unexpected introducer's index ", index, " skipped");
|
||||
if (s) continue; else return;
|
||||
}
|
||||
if (index >= address->ssu->introducers.size ())
|
||||
address->ssu->introducers.resize (index + 1);
|
||||
Introducer& introducer = address->ssu->introducers.at (index);
|
||||
if (!strcmp (key, "ihost"))
|
||||
{
|
||||
boost::system::error_code ecode;
|
||||
@@ -242,11 +269,15 @@ namespace data
|
||||
}
|
||||
if (isValidAddress)
|
||||
{
|
||||
addresses->push_back(std::make_shared<Address>(address));
|
||||
addresses->push_back(address);
|
||||
m_SupportedTransports |= supportedTransports;
|
||||
}
|
||||
}
|
||||
m_Addresses = addresses;
|
||||
#if (BOOST_VERSION >= 105300)
|
||||
boost::atomic_store (&m_Addresses, addresses);
|
||||
#else
|
||||
m_Addresses = addresses; // race condition
|
||||
#endif
|
||||
// read peers
|
||||
uint8_t numPeers;
|
||||
s.read ((char *)&numPeers, sizeof (numPeers)); if (!s) return;
|
||||
@@ -257,26 +288,21 @@ namespace data
|
||||
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);
|
||||
char key[255], value[255];
|
||||
r += ReadString (key, 255, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // =
|
||||
r += ReadString (value, s);
|
||||
r += ReadString (value, 255, s);
|
||||
s.seekg (1, std::ios_base::cur); r++; // ;
|
||||
if (!s) return;
|
||||
m_Properties[key] = value;
|
||||
|
||||
// extract caps
|
||||
if (!strcmp (key, "caps"))
|
||||
ExtractCaps (value);
|
||||
// check netId
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != I2PD_NET_ID)
|
||||
else if (!strcmp (key, ROUTER_INFO_PROPERTY_NETID) && atoi (value) != i2p::context.GetNetID ())
|
||||
{
|
||||
LogPrint (eLogError, "Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
|
||||
LogPrint (eLogError, "RouterInfo: Unexpected ", ROUTER_INFO_PROPERTY_NETID, "=", value);
|
||||
m_IsUnreachable = true;
|
||||
}
|
||||
// family
|
||||
@@ -348,19 +374,23 @@ namespace data
|
||||
void RouterInfo::UpdateCapsProperty ()
|
||||
{
|
||||
std::string caps;
|
||||
if (m_Caps & eFloodfill) {
|
||||
if (m_Caps & eFloodfill)
|
||||
{
|
||||
if (m_Caps & eExtraBandwidth) caps += (m_Caps & eHighBandwidth) ?
|
||||
CAPS_FLAG_EXTRA_BANDWIDTH2 : // 'X'
|
||||
CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
|
||||
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
|
||||
caps += CAPS_FLAG_FLOODFILL; // floodfill
|
||||
caps += (m_Caps & eExtraBandwidth)
|
||||
? CAPS_FLAG_EXTRA_BANDWIDTH1 // 'P'
|
||||
: CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
|
||||
} else {
|
||||
if (m_Caps & eExtraBandwidth) {
|
||||
caps += CAPS_FLAG_EXTRA_BANDWIDTH1; // 'P'
|
||||
} else if (m_Caps & eHighBandwidth) {
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_Caps & eExtraBandwidth)
|
||||
{
|
||||
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_EXTRA_BANDWIDTH2 /* 'X' */ : CAPS_FLAG_EXTRA_BANDWIDTH1; /*'P' */
|
||||
caps += CAPS_FLAG_HIGH_BANDWIDTH3; // 'O'
|
||||
} else {
|
||||
caps += CAPS_FLAG_LOW_BANDWIDTH2; // 'L'
|
||||
}
|
||||
else
|
||||
caps += (m_Caps & eHighBandwidth) ? CAPS_FLAG_HIGH_BANDWIDTH3 /* 'O' */: CAPS_FLAG_LOW_BANDWIDTH2 /* 'L' */; // bandwidth
|
||||
}
|
||||
if (m_Caps & eHidden) caps += CAPS_FLAG_HIDDEN; // hidden
|
||||
if (m_Caps & eReachable) caps += CAPS_FLAG_REACHABLE; // reachable
|
||||
@@ -407,10 +437,10 @@ namespace data
|
||||
if (address.transportStyle == eTransportSSU)
|
||||
{
|
||||
// write introducers if any
|
||||
if (address.introducers.size () > 0)
|
||||
if (address.ssu->introducers.size () > 0)
|
||||
{
|
||||
int i = 0;
|
||||
for (const auto& introducer: address.introducers)
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("ihost" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
@@ -419,7 +449,7 @@ namespace data
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (const auto& introducer: address.introducers)
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("ikey" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
@@ -431,7 +461,7 @@ namespace data
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (const auto& introducer: address.introducers)
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("iport" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
@@ -440,7 +470,7 @@ namespace data
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
for (const auto& introducer: address.introducers)
|
||||
for (const auto& introducer: address.ssu->introducers)
|
||||
{
|
||||
WriteString ("itag" + boost::lexical_cast<std::string>(i), properties);
|
||||
properties << '=';
|
||||
@@ -453,16 +483,16 @@ namespace data
|
||||
WriteString ("key", properties);
|
||||
properties << '=';
|
||||
char value[64];
|
||||
size_t l = ByteStreamToBase64 (address.key, 32, value, 64);
|
||||
size_t l = ByteStreamToBase64 (address.ssu->key, 32, value, 64);
|
||||
value[l] = 0;
|
||||
WriteString (value, properties);
|
||||
properties << ';';
|
||||
// write mtu
|
||||
if (address.mtu)
|
||||
if (address.ssu->mtu)
|
||||
{
|
||||
WriteString ("mtu", properties);
|
||||
properties << '=';
|
||||
WriteString (boost::lexical_cast<std::string>(address.mtu), properties);
|
||||
WriteString (boost::lexical_cast<std::string>(address.ssu->mtu), properties);
|
||||
properties << ';';
|
||||
}
|
||||
}
|
||||
@@ -545,16 +575,26 @@ namespace data
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t RouterInfo::ReadString (char * str, std::istream& s)
|
||||
size_t RouterInfo::ReadString (char * str, size_t len, std::istream& s) const
|
||||
{
|
||||
uint8_t len;
|
||||
s.read ((char *)&len, 1);
|
||||
s.read (str, len);
|
||||
str[len] = 0;
|
||||
return len+1;
|
||||
uint8_t l;
|
||||
s.read ((char *)&l, 1);
|
||||
if (l < len)
|
||||
{
|
||||
s.read (str, l);
|
||||
if (!s) l = 0; // failed, return empty string
|
||||
str[l] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "RouterInfo: string length ", (int)l, " exceeds buffer size ", len);
|
||||
s.seekg (l, std::ios::cur); // skip
|
||||
str[0] = 0;
|
||||
}
|
||||
return l+1;
|
||||
}
|
||||
|
||||
void RouterInfo::WriteString (const std::string& str, std::ostream& s)
|
||||
void RouterInfo::WriteString (const std::string& str, std::ostream& s) const
|
||||
{
|
||||
uint8_t len = str.size ();
|
||||
s.write ((char *)&len, 1);
|
||||
@@ -569,7 +609,6 @@ namespace data
|
||||
addr->transportStyle = eTransportNTCP;
|
||||
addr->cost = 2;
|
||||
addr->date = 0;
|
||||
addr->mtu = 0;
|
||||
for (const auto& it: *m_Addresses) // don't insert same address twice
|
||||
if (*it == *addr) return;
|
||||
m_SupportedTransports |= addr->host.is_v6 () ? eNTCPV6 : eNTCPV4;
|
||||
@@ -584,8 +623,9 @@ namespace data
|
||||
addr->transportStyle = eTransportSSU;
|
||||
addr->cost = 10; // NTCP should have priority over SSU
|
||||
addr->date = 0;
|
||||
addr->mtu = mtu;
|
||||
memcpy (addr->key, key, 32);
|
||||
addr->ssu.reset (new SSUExt ());
|
||||
addr->ssu->mtu = mtu;
|
||||
memcpy (addr->ssu->key, key, 32);
|
||||
for (const auto& it: *m_Addresses) // don't insert same address twice
|
||||
if (*it == *addr) return;
|
||||
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
|
||||
@@ -601,9 +641,9 @@ namespace data
|
||||
{
|
||||
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
|
||||
{
|
||||
for (auto& intro: addr->introducers)
|
||||
for (auto& intro: addr->ssu->introducers)
|
||||
if (intro.iTag == introducer.iTag) return false; // already presented
|
||||
addr->introducers.push_back (introducer);
|
||||
addr->ssu->introducers.push_back (introducer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -616,10 +656,10 @@ namespace data
|
||||
{
|
||||
if (addr->transportStyle == eTransportSSU && addr->host.is_v4 ())
|
||||
{
|
||||
for (auto it = addr->introducers.begin (); it != addr->introducers.end (); ++it)
|
||||
for (auto it = addr->ssu->introducers.begin (); it != addr->ssu->introducers.end (); ++it)
|
||||
if ( boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e)
|
||||
{
|
||||
addr->introducers.erase (it);
|
||||
addr->ssu->introducers.erase (it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -752,7 +792,11 @@ namespace data
|
||||
|
||||
std::shared_ptr<const RouterInfo::Address> RouterInfo::GetAddress (TransportStyle s, bool v4only, bool v6only) const
|
||||
{
|
||||
#if (BOOST_VERSION >= 105300)
|
||||
auto addresses = boost::atomic_load (&m_Addresses);
|
||||
#else
|
||||
auto addresses = m_Addresses;
|
||||
#endif
|
||||
for (const auto& address : *addresses)
|
||||
{
|
||||
if (address->transportStyle == s)
|
||||
|
||||
22
RouterInfo.h
22
RouterInfo.h
@@ -8,6 +8,7 @@
|
||||
#include <list>
|
||||
#include <iostream>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "Identity.h"
|
||||
#include "Profiling.h"
|
||||
|
||||
@@ -78,17 +79,22 @@ namespace data
|
||||
uint32_t iTag;
|
||||
};
|
||||
|
||||
struct SSUExt
|
||||
{
|
||||
int mtu;
|
||||
IntroKey key; // intro key for SSU
|
||||
std::vector<Introducer> introducers;
|
||||
};
|
||||
|
||||
struct Address
|
||||
{
|
||||
TransportStyle transportStyle;
|
||||
boost::asio::ip::address host;
|
||||
std::string addressString;
|
||||
int port, mtu;
|
||||
int port;
|
||||
uint64_t date;
|
||||
uint8_t cost;
|
||||
// SSU only
|
||||
IntroKey key; // intro key for SSU
|
||||
std::vector<Introducer> introducers;
|
||||
std::unique_ptr<SSUExt> ssu; // not null for SSU
|
||||
|
||||
bool IsCompatible (const boost::asio::ip::address& other) const
|
||||
{
|
||||
@@ -167,7 +173,7 @@ namespace data
|
||||
bool SaveToFile (const std::string& fullPath);
|
||||
|
||||
std::shared_ptr<RouterProfile> GetProfile () const;
|
||||
void SaveProfile () { if (m_Profile) m_Profile->Save (); };
|
||||
void SaveProfile () { if (m_Profile) m_Profile->Save (GetIdentHash ()); };
|
||||
|
||||
void Update (const uint8_t * buf, int len);
|
||||
void DeleteBuffer () { delete[] m_Buffer; m_Buffer = nullptr; };
|
||||
@@ -188,8 +194,8 @@ namespace data
|
||||
void ReadFromStream (std::istream& s);
|
||||
void ReadFromBuffer (bool verifySignature);
|
||||
void WriteToStream (std::ostream& s) const;
|
||||
static size_t ReadString (char* str, std::istream& s);
|
||||
static void WriteString (const std::string& str, std::ostream& s);
|
||||
size_t ReadString (char* str, size_t len, std::istream& s) const;
|
||||
void WriteString (const std::string& str, std::ostream& s) const;
|
||||
void ExtractCaps (const char * value);
|
||||
std::shared_ptr<const Address> GetAddress (TransportStyle s, bool v4only, bool v6only = false) const;
|
||||
void UpdateCapsProperty ();
|
||||
@@ -201,7 +207,7 @@ namespace data
|
||||
uint8_t * m_Buffer;
|
||||
size_t m_BufferLen;
|
||||
uint64_t m_Timestamp;
|
||||
std::shared_ptr<Addresses> m_Addresses;
|
||||
boost::shared_ptr<Addresses> m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9
|
||||
std::map<std::string, std::string> m_Properties;
|
||||
bool m_IsUpdated, m_IsUnreachable;
|
||||
uint8_t m_SupportedTransports, m_Caps;
|
||||
|
||||
390
SAM.cpp
390
SAM.cpp
@@ -3,21 +3,21 @@
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include "Base.h"
|
||||
#include "Identity.h"
|
||||
#include "Log.h"
|
||||
#include "Destination.h"
|
||||
#include "ClientContext.h"
|
||||
#include "util.h"
|
||||
#include "SAM.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace client
|
||||
{
|
||||
SAMSocket::SAMSocket (SAMBridge& owner):
|
||||
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_BufferOffset (0), m_SocketType (eSAMSocketTypeUnknown), m_IsSilent (false),
|
||||
m_Stream (nullptr), m_Session (nullptr)
|
||||
{
|
||||
}
|
||||
@@ -25,21 +25,21 @@ namespace client
|
||||
SAMSocket::~SAMSocket ()
|
||||
{
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::CloseStream ()
|
||||
{
|
||||
if (m_Stream)
|
||||
{
|
||||
{
|
||||
m_Stream->Close ();
|
||||
m_Stream.reset ();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::Terminate ()
|
||||
{
|
||||
CloseStream ();
|
||||
|
||||
|
||||
switch (m_SocketType)
|
||||
{
|
||||
case eSAMSocketTypeSession:
|
||||
@@ -47,14 +47,14 @@ namespace client
|
||||
break;
|
||||
case eSAMSocketTypeStream:
|
||||
{
|
||||
if (m_Session)
|
||||
if (m_Session)
|
||||
m_Session->DelSocket (shared_from_this ());
|
||||
break;
|
||||
}
|
||||
case eSAMSocketTypeAcceptor:
|
||||
{
|
||||
if (m_Session)
|
||||
{
|
||||
{
|
||||
m_Session->DelSocket (shared_from_this ());
|
||||
if (m_Session->localDestination)
|
||||
m_Session->localDestination->StopAcceptingStreams ();
|
||||
@@ -71,8 +71,8 @@ namespace client
|
||||
|
||||
void SAMSocket::ReceiveHandshake ()
|
||||
{
|
||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer, SAM_SOCKET_BUFFER_SIZE),
|
||||
std::bind(&SAMSocket::HandleHandshakeReceived, shared_from_this (),
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace client
|
||||
Terminate ();
|
||||
}
|
||||
else
|
||||
{
|
||||
{
|
||||
m_Buffer[bytes_transferred] = 0;
|
||||
char * eol = (char *)memchr (m_Buffer, '\n', bytes_transferred);
|
||||
if (eol)
|
||||
@@ -94,8 +94,8 @@ namespace client
|
||||
char * separator = strchr (m_Buffer, ' ');
|
||||
if (separator)
|
||||
{
|
||||
separator = strchr (separator + 1, ' ');
|
||||
if (separator)
|
||||
separator = strchr (separator + 1, ' ');
|
||||
if (separator)
|
||||
*separator = 0;
|
||||
}
|
||||
|
||||
@@ -108,22 +108,22 @@ namespace client
|
||||
separator++;
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (separator, params);
|
||||
auto it = params.find (SAM_PARAM_MAX);
|
||||
//auto it = params.find (SAM_PARAM_MAX);
|
||||
// TODO: check MIN as well
|
||||
if (it != params.end ())
|
||||
version = it->second;
|
||||
//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
|
||||
#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::bind(&SAMSocket::HandleHandshakeReplySent, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
}
|
||||
else
|
||||
SendMessageReply (SAM_HANDSHAKE_I2P_ERROR, strlen (SAM_HANDSHAKE_I2P_ERROR), true);
|
||||
}
|
||||
@@ -145,25 +145,25 @@ namespace client
|
||||
}
|
||||
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));
|
||||
}
|
||||
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)
|
||||
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::bind(&SAMSocket::HandleMessageReplySent, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, close));
|
||||
else
|
||||
{
|
||||
if (close)
|
||||
Terminate ();
|
||||
else
|
||||
Receive ();
|
||||
}
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleMessageReplySent (const boost::system::error_code& ecode, std::size_t bytes_transferred, bool close)
|
||||
@@ -179,8 +179,8 @@ namespace client
|
||||
if (close)
|
||||
Terminate ();
|
||||
else
|
||||
Receive ();
|
||||
}
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleMessage (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
@@ -205,8 +205,8 @@ namespace client
|
||||
char * separator = strchr (m_Buffer, ' ');
|
||||
if (separator)
|
||||
{
|
||||
separator = strchr (separator + 1, ' ');
|
||||
if (separator)
|
||||
separator = strchr (separator + 1, ' ');
|
||||
if (separator)
|
||||
*separator = 0;
|
||||
else
|
||||
separator = eol;
|
||||
@@ -236,12 +236,12 @@ namespace client
|
||||
*separator = ' ';
|
||||
*eol = '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
// since it's SAM v1 reply is not expected
|
||||
Receive ();
|
||||
}
|
||||
else
|
||||
{
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SAM: unexpected message ", m_Buffer);
|
||||
Terminate ();
|
||||
}
|
||||
@@ -252,8 +252,9 @@ namespace client
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
{
|
||||
LogPrint (eLogWarning, "SAM: incomplete message ", bytes_transferred);
|
||||
m_BufferOffset = bytes_transferred;
|
||||
// try to receive remaining message
|
||||
@@ -267,10 +268,10 @@ namespace client
|
||||
LogPrint (eLogDebug, "SAM: session create: ", buf);
|
||||
std::map<std::string, std::string> params;
|
||||
ExtractParams (buf, params);
|
||||
std::string& style = params[SAM_PARAM_STYLE];
|
||||
std::string& style = params[SAM_PARAM_STYLE];
|
||||
std::string& id = params[SAM_PARAM_ID];
|
||||
std::string& destination = params[SAM_PARAM_DESTINATION];
|
||||
m_ID = id;
|
||||
m_ID = id;
|
||||
if (m_Owner.FindSession (id))
|
||||
{
|
||||
// session exists
|
||||
@@ -278,15 +279,39 @@ namespace client
|
||||
return;
|
||||
}
|
||||
|
||||
// create destination
|
||||
m_Session = m_Owner.CreateSession (id, destination == SAM_VALUE_TRANSIENT ? "" : destination, ¶ms);
|
||||
std::shared_ptr<boost::asio::ip::udp::endpoint> forward = nullptr;
|
||||
if (style == SAM_VALUE_DATAGRAM && params.find(SAM_VALUE_HOST) != params.end() && params.find(SAM_VALUE_PORT) != params.end())
|
||||
{
|
||||
// udp forward selected
|
||||
boost::system::error_code e;
|
||||
// TODO: support hostnames in udp forward
|
||||
auto addr = boost::asio::ip::address::from_string(params[SAM_VALUE_HOST], e);
|
||||
if (e)
|
||||
{
|
||||
// not an ip address
|
||||
SendI2PError("Invalid IP Address in HOST");
|
||||
return;
|
||||
}
|
||||
|
||||
auto port = std::stoi(params[SAM_VALUE_PORT]);
|
||||
if (port == -1)
|
||||
{
|
||||
SendI2PError("Invalid port");
|
||||
return;
|
||||
}
|
||||
forward = std::make_shared<boost::asio::ip::udp::endpoint>(addr, port);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
m_Session->UDPEndpoint = forward;
|
||||
auto dest = m_Session->localDestination->CreateDatagramDestination ();
|
||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
||||
dest->SetReceiver (std::bind (&SAMSocket::HandleI2PDatagramReceive, shared_from_this (),
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5));
|
||||
}
|
||||
|
||||
@@ -296,7 +321,7 @@ namespace client
|
||||
{
|
||||
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));
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -314,7 +339,7 @@ namespace client
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +352,7 @@ namespace client
|
||||
priv[l1] = 0;
|
||||
#ifdef _MSC_VER
|
||||
size_t l2 = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
||||
#else
|
||||
#else
|
||||
size_t l2 = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_CREATE_REPLY_OK, priv);
|
||||
#endif
|
||||
SendMessageReply (m_Buffer, l2, false);
|
||||
@@ -341,7 +366,7 @@ namespace client
|
||||
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;
|
||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
||||
m_ID = id;
|
||||
m_Session = m_Owner.FindSession (id);
|
||||
if (m_Session)
|
||||
@@ -365,7 +390,7 @@ namespace client
|
||||
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);
|
||||
SendMessageReply (SAM_STREAM_STATUS_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
||||
}
|
||||
|
||||
void SAMSocket::Connect (std::shared_ptr<const i2p::data::LeaseSet> remote)
|
||||
@@ -374,7 +399,7 @@ namespace client
|
||||
m_Session->AddSocket (shared_from_this ());
|
||||
m_Stream = m_Session->localDestination->CreateStream (remote);
|
||||
m_Stream->Send ((uint8_t *)m_Buffer, 0); // connect
|
||||
I2PReceive ();
|
||||
I2PReceive ();
|
||||
SendMessageReply (SAM_STREAM_STATUS_OK, strlen(SAM_STREAM_STATUS_OK), false);
|
||||
}
|
||||
|
||||
@@ -396,21 +421,17 @@ namespace client
|
||||
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;
|
||||
if (silent == SAM_VALUE_TRUE) m_IsSilent = true;
|
||||
m_ID = id;
|
||||
m_Session = m_Owner.FindSession (id);
|
||||
if (m_Session)
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeAcceptor;
|
||||
m_Session->AddSocket (shared_from_this ());
|
||||
if (!m_Session->localDestination->IsAcceptingStreams ())
|
||||
{
|
||||
m_SocketType = eSAMSocketTypeAcceptor;
|
||||
m_Session->AddSocket (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);
|
||||
}
|
||||
m_Session->localDestination->AcceptOnce (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_INVALID_ID, strlen(SAM_STREAM_STATUS_INVALID_ID), true);
|
||||
}
|
||||
@@ -420,11 +441,11 @@ namespace client
|
||||
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;
|
||||
size_t size = std::stoi(params[SAM_PARAM_SIZE]), offset = data - buf;
|
||||
if (offset + size <= len)
|
||||
{
|
||||
{
|
||||
if (m_Session)
|
||||
{
|
||||
{
|
||||
auto d = m_Session->localDestination->GetDatagramDestination ();
|
||||
if (d)
|
||||
{
|
||||
@@ -437,24 +458,24 @@ namespace client
|
||||
}
|
||||
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,
|
||||
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,
|
||||
#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);
|
||||
@@ -468,60 +489,71 @@ namespace client
|
||||
std::string& name = params[SAM_PARAM_NAME];
|
||||
std::shared_ptr<const i2p::data::IdentityEx> identity;
|
||||
i2p::data::IdentHash ident;
|
||||
auto dest = m_Session == nullptr ? context.GetSharedLocalDestination() : m_Session->localDestination;
|
||||
if (name == "ME")
|
||||
SendNamingLookupReply (m_Session->localDestination->GetIdentity ());
|
||||
SendNamingLookupReply (dest->GetIdentity ());
|
||||
else if ((identity = context.GetAddressBook ().GetAddress (name)) != nullptr)
|
||||
SendNamingLookupReply (identity);
|
||||
else if (m_Session && m_Session->localDestination &&
|
||||
context.GetAddressBook ().GetIdentHash (name, ident))
|
||||
else if (context.GetAddressBook ().GetIdentHash (name, ident))
|
||||
{
|
||||
auto leaseSet = m_Session->localDestination->FindLeaseSet (ident);
|
||||
auto leaseSet = dest->FindLeaseSet (ident);
|
||||
if (leaseSet)
|
||||
SendNamingLookupReply (leaseSet->GetIdentity ());
|
||||
else
|
||||
m_Session->localDestination->RequestDestination (ident,
|
||||
dest->RequestDestination (ident,
|
||||
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
|
||||
shared_from_this (), std::placeholders::_1, ident));
|
||||
}
|
||||
else
|
||||
shared_from_this (), std::placeholders::_1, ident));
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "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
|
||||
#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)
|
||||
void SAMSocket::SendI2PError(const std::string & msg)
|
||||
{
|
||||
LogPrint (eLogError, "SAM: i2p error ", msg);
|
||||
#ifdef _MSC_VER
|
||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
|
||||
#else
|
||||
size_t len = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_SESSION_STATUS_I2P_ERROR, msg.c_str());
|
||||
#endif
|
||||
SendMessageReply (m_Buffer, len, true);
|
||||
}
|
||||
|
||||
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 (eLogError, "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,
|
||||
size_t len = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY_INVALID_KEY,
|
||||
context.GetAddressBook ().ToAddress (ident).c_str());
|
||||
#else
|
||||
#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 (std::shared_ptr<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 = 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);
|
||||
@@ -529,7 +561,7 @@ namespace client
|
||||
|
||||
void SAMSocket::ExtractParams (char * buf, std::map<std::string, std::string>& params)
|
||||
{
|
||||
char * separator;
|
||||
char * separator;
|
||||
do
|
||||
{
|
||||
separator = strchr (buf, ' ');
|
||||
@@ -540,11 +572,11 @@ namespace client
|
||||
*value = 0;
|
||||
value++;
|
||||
params[buf] = value;
|
||||
}
|
||||
}
|
||||
buf = separator + 1;
|
||||
}
|
||||
while (separator);
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::Receive ()
|
||||
{
|
||||
@@ -554,7 +586,7 @@ namespace client
|
||||
Terminate ();
|
||||
return;
|
||||
}
|
||||
m_Socket.async_read_some (boost::asio::buffer(m_Buffer + m_BufferOffset, SAM_SOCKET_BUFFER_SIZE - m_BufferOffset),
|
||||
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));
|
||||
}
|
||||
@@ -570,7 +602,7 @@ namespace client
|
||||
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)
|
||||
@@ -578,20 +610,38 @@ namespace client
|
||||
if (!ecode)
|
||||
s->Receive ();
|
||||
else
|
||||
s->Terminate ();
|
||||
s->m_Owner.GetService ().post ([s] { 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);
|
||||
}
|
||||
{
|
||||
if (m_Stream->GetStatus () == i2p::stream::eStreamStatusNew ||
|
||||
m_Stream->GetStatus () == i2p::stream::eStreamStatusOpen) // regular
|
||||
{
|
||||
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);
|
||||
}
|
||||
else // closed by peer
|
||||
{
|
||||
// get remaning data
|
||||
auto len = m_Stream->ReadSome (m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
|
||||
if (len > 0) // still some data
|
||||
{
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, len),
|
||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
else // no more data
|
||||
Terminate ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMSocket::HandleI2PReceive (const boost::system::error_code& ecode, std::size_t bytes_transferred)
|
||||
{
|
||||
@@ -599,7 +649,21 @@ namespace client
|
||||
{
|
||||
LogPrint (eLogError, "SAM: stream read error: ", ecode.message ());
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
Terminate ();
|
||||
{
|
||||
if (bytes_transferred > 0)
|
||||
boost::asio::async_write (m_Socket, boost::asio::buffer (m_StreamBuffer, bytes_transferred),
|
||||
std::bind (&SAMSocket::HandleWriteI2PData, shared_from_this (), std::placeholders::_1)); // postpone termination
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Owner.GetService ().post ([s] { s->Terminate (); });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto s = shared_from_this ();
|
||||
m_Owner.GetService ().post ([s] { s->Terminate (); });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -625,12 +689,20 @@ namespace client
|
||||
if (stream)
|
||||
{
|
||||
LogPrint (eLogDebug, "SAM: incoming I2P connection for session ", m_ID);
|
||||
m_SocketType = eSAMSocketTypeStream;
|
||||
m_Stream = stream;
|
||||
context.GetAddressBook ().InsertAddress (stream->GetRemoteIdentity ());
|
||||
auto session = m_Owner.FindSession (m_ID);
|
||||
if (session)
|
||||
session->localDestination->StopAcceptingStreams ();
|
||||
m_SocketType = eSAMSocketTypeStream;
|
||||
if (session)
|
||||
{
|
||||
// find more pending acceptors
|
||||
for (auto it: session->ListSockets ())
|
||||
if (it->m_SocketType == eSAMSocketTypeAcceptor)
|
||||
{
|
||||
session->localDestination->AcceptOnce (std::bind (&SAMSocket::HandleI2PAccept, it, std::placeholders::_1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!m_IsSilent)
|
||||
{
|
||||
// get remote peer address
|
||||
@@ -638,44 +710,66 @@ namespace client
|
||||
const size_t ident_len = ident_ptr->GetFullLen();
|
||||
uint8_t* ident = new uint8_t[ident_len];
|
||||
|
||||
// send remote peer address as base64
|
||||
// send remote peer address as base64
|
||||
const size_t l = ident_ptr->ToBuffer (ident, ident_len);
|
||||
const size_t l1 = i2p::data::ByteStreamToBase64 (ident, l, (char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE);
|
||||
delete[] ident;
|
||||
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 (eLogWarning, "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));
|
||||
auto ep = m_Session->UDPEndpoint;
|
||||
if (ep)
|
||||
{
|
||||
// udp forward enabled
|
||||
size_t bsz = base64.size();
|
||||
size_t sz = bsz + 1 + len;
|
||||
// build datagram body
|
||||
uint8_t * data = new uint8_t[sz];
|
||||
// Destination
|
||||
memcpy(data, base64.c_str(), bsz);
|
||||
// linefeed
|
||||
data[bsz] = '\n';
|
||||
// Payload
|
||||
memcpy(data+bsz+1, buf, len);
|
||||
// send to remote endpoint
|
||||
m_Owner.SendTo(data, sz, ep);
|
||||
delete [] data;
|
||||
}
|
||||
else
|
||||
LogPrint (eLogWarning, "SAM: received datagram size ", len," exceeds buffer");
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
size_t l = sprintf_s ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)len);
|
||||
#else
|
||||
size_t l = snprintf ((char *)m_StreamBuffer, SAM_SOCKET_BUFFER_SIZE, SAM_DATAGRAM_RECEIVED, base64.c_str (), (long unsigned int)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)
|
||||
localDestination (dest),
|
||||
UDPEndpoint(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SAMSession::~SAMSession ()
|
||||
{
|
||||
CloseStreams();
|
||||
@@ -705,7 +799,7 @@ namespace client
|
||||
{
|
||||
if (m_IsRunning)
|
||||
Stop ();
|
||||
}
|
||||
}
|
||||
|
||||
void SAMBridge::Start ()
|
||||
{
|
||||
@@ -724,26 +818,26 @@ namespace client
|
||||
m_Sessions.clear ();
|
||||
m_Service.stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
{
|
||||
m_Thread->join ();
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMBridge::Run ()
|
||||
{
|
||||
void SAMBridge::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
{
|
||||
m_Service.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SAM: runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SAMBridge::Accept ()
|
||||
@@ -760,7 +854,7 @@ namespace client
|
||||
boost::system::error_code ec;
|
||||
auto ep = socket->GetSocket ().remote_endpoint (ec);
|
||||
if (!ec)
|
||||
{
|
||||
{
|
||||
LogPrint (eLogDebug, "SAM: new connection from ", ep);
|
||||
socket->ReceiveHandshake ();
|
||||
}
|
||||
@@ -774,10 +868,10 @@ namespace client
|
||||
Accept ();
|
||||
}
|
||||
|
||||
std::shared_ptr<SAMSession> SAMBridge::CreateSession (const std::string& id, const std::string& destination,
|
||||
std::shared_ptr<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;
|
||||
std::shared_ptr<ClientDestination> localDestination = nullptr;
|
||||
if (destination != "")
|
||||
{
|
||||
i2p::data::PrivateKeys keys;
|
||||
@@ -792,10 +886,10 @@ namespace client
|
||||
{
|
||||
auto it = params->find (SAM_PARAM_SIGNATURE_TYPE);
|
||||
if (it != params->end ())
|
||||
// TODO: extract string values
|
||||
signatureType = boost::lexical_cast<int> (it->second);
|
||||
// TODO: extract string values
|
||||
signatureType = std::stoi(it->second);
|
||||
}
|
||||
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
|
||||
localDestination = i2p::client::context.CreateNewLocalDestination (true, signatureType, params);
|
||||
}
|
||||
if (localDestination)
|
||||
{
|
||||
@@ -816,13 +910,13 @@ namespace client
|
||||
std::unique_lock<std::mutex> l(m_SessionsMutex);
|
||||
auto it = m_Sessions.find (id);
|
||||
if (it != m_Sessions.end ())
|
||||
{
|
||||
{
|
||||
session = it->second;
|
||||
m_Sessions.erase (it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (session)
|
||||
{
|
||||
{
|
||||
session->localDestination->StopAcceptingStreams ();
|
||||
session->CloseStreams ();
|
||||
}
|
||||
@@ -837,12 +931,20 @@ namespace client
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SAMBridge::SendTo(const uint8_t * buf, size_t len, std::shared_ptr<boost::asio::ip::udp::endpoint> remote)
|
||||
{
|
||||
if(remote)
|
||||
{
|
||||
m_DatagramSocket.send_to(boost::asio::buffer(buf, len), *remote);
|
||||
}
|
||||
}
|
||||
|
||||
void SAMBridge::ReceiveDatagram ()
|
||||
{
|
||||
m_DatagramSocket.async_receive_from (
|
||||
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
|
||||
boost::asio::buffer (m_DatagramReceiveBuffer, i2p::datagram::MAX_DATAGRAM_SIZE),
|
||||
m_SenderEndpoint,
|
||||
std::bind (&SAMBridge::HandleReceivedDatagram, this, std::placeholders::_1, std::placeholders::_2));
|
||||
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)
|
||||
@@ -852,7 +954,7 @@ namespace client
|
||||
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);
|
||||
size_t payloadLen = bytes_transferred - ((uint8_t *)eol - m_DatagramReceiveBuffer);
|
||||
LogPrint (eLogDebug, "SAM: datagram received ", m_DatagramReceiveBuffer," size=", payloadLen);
|
||||
char * sessionID = strchr ((char *)m_DatagramReceiveBuffer, ' ');
|
||||
if (sessionID)
|
||||
@@ -864,12 +966,12 @@ namespace client
|
||||
*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 (eLogError, "SAM: Session ", sessionID, " not found");
|
||||
}
|
||||
|
||||
63
SAM.h
63
SAM.h
@@ -20,45 +20,48 @@ 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 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_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_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_SESSION_STATUS_I2P_ERROR[] = "SESSION STATUS RESULT=I2P_ERROR MESSAGE=%s\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_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[] = "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_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_DESTINATION[] = "DESTINATION";
|
||||
const char SAM_PARAM_NAME[] = "NAME";
|
||||
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
|
||||
const char SAM_PARAM_SIGNATURE_TYPE[] = "SIGNATURE_TYPE";
|
||||
const char SAM_PARAM_SIZE[] = "SIZE";
|
||||
const char SAM_VALUE_TRANSIENT[] = "TRANSIENT";
|
||||
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";
|
||||
const char SAM_VALUE_RAW[] = "RAW";
|
||||
const char SAM_VALUE_TRUE[] = "true";
|
||||
const char SAM_VALUE_FALSE[] = "false";
|
||||
const char SAM_VALUE_HOST[] = "HOST";
|
||||
const char SAM_VALUE_PORT[] = "PORT";
|
||||
|
||||
enum SAMSocketType
|
||||
{
|
||||
@@ -76,8 +79,8 @@ namespace client
|
||||
public:
|
||||
|
||||
SAMSocket (SAMBridge& owner);
|
||||
~SAMSocket ();
|
||||
void CloseStream (); // TODO: implement it better
|
||||
~SAMSocket ();
|
||||
void CloseStream (); // TODO: implement it better
|
||||
|
||||
boost::asio::ip::tcp::socket& GetSocket () { return m_Socket; };
|
||||
void ReceiveHandshake ();
|
||||
@@ -86,16 +89,16 @@ namespace client
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
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 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 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);
|
||||
@@ -106,7 +109,8 @@ namespace client
|
||||
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 SendI2PError(const std::string & msg);
|
||||
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);
|
||||
@@ -129,12 +133,13 @@ namespace client
|
||||
bool m_IsSilent;
|
||||
std::shared_ptr<i2p::stream::Stream> m_Stream;
|
||||
std::shared_ptr<SAMSession> m_Session;
|
||||
};
|
||||
};
|
||||
|
||||
struct SAMSession
|
||||
{
|
||||
std::shared_ptr<ClientDestination> localDestination;
|
||||
std::list<std::shared_ptr<SAMSocket> > m_Sockets;
|
||||
std::shared_ptr<boost::asio::ip::udp::endpoint> UDPEndpoint;
|
||||
std::mutex m_SocketsMutex;
|
||||
|
||||
/** safely add a socket to this session */
|
||||
@@ -158,8 +163,8 @@ namespace client
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
SAMSession (std::shared_ptr<ClientDestination> dest);
|
||||
|
||||
SAMSession (std::shared_ptr<ClientDestination> dest);
|
||||
~SAMSession ();
|
||||
|
||||
void CloseStreams ();
|
||||
@@ -174,13 +179,16 @@ namespace client
|
||||
|
||||
void Start ();
|
||||
void Stop ();
|
||||
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
std::shared_ptr<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);
|
||||
std::shared_ptr<SAMSession> FindSession (const std::string& id) const;
|
||||
|
||||
/** send raw data to remote endpoint from our UDP Socket */
|
||||
void SendTo(const uint8_t * buf, size_t len, std::shared_ptr<boost::asio::ip::udp::endpoint> remote);
|
||||
|
||||
private:
|
||||
|
||||
void Run ();
|
||||
@@ -194,7 +202,7 @@ namespace client
|
||||
private:
|
||||
|
||||
bool m_IsRunning;
|
||||
std::thread * m_Thread;
|
||||
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;
|
||||
@@ -207,9 +215,8 @@ namespace client
|
||||
|
||||
// for HTTP
|
||||
const decltype(m_Sessions)& GetSessions () const { return m_Sessions; };
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "I2PEndian.h"
|
||||
#include "I2PTunnel.h"
|
||||
#include "I2PService.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -638,7 +639,7 @@ namespace proxy
|
||||
{
|
||||
LogPrint(eLogInfo, "SOCKS: forwarding to upstream");
|
||||
EnterState(UPSTREAM_RESOLVE);
|
||||
boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress,boost::lexical_cast<std::string>(m_UpstreamProxyPort) );
|
||||
boost::asio::ip::tcp::resolver::query q(m_UpstreamProxyAddress, std::to_string(m_UpstreamProxyPort));
|
||||
m_proxy_resolver.async_resolve(q, std::bind(&SOCKSHandler::HandleUpstreamResolved, shared_from_this(),
|
||||
std::placeholders::_1, std::placeholders::_2));
|
||||
}
|
||||
|
||||
231
SSU.cpp
231
SSU.cpp
@@ -13,57 +13,69 @@ namespace transport
|
||||
|
||||
SSUServer::SSUServer (const boost::asio::ip::address & addr, int port):
|
||||
m_OnlyV6(true), m_IsRunning(false),
|
||||
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
|
||||
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
|
||||
m_EndpointV6 (addr, port),
|
||||
m_Socket (m_ReceiversService, m_Endpoint), m_SocketV6 (m_ReceiversService),
|
||||
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service)
|
||||
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
|
||||
m_ReceiversThreadV6 (nullptr), m_Work (m_Service), m_WorkV6 (m_ServiceV6),
|
||||
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
|
||||
m_EndpointV6 (addr, port), m_Socket (m_ReceiversService, m_Endpoint),
|
||||
m_SocketV6 (m_ReceiversServiceV6), m_IntroducersUpdateTimer (m_Service),
|
||||
m_PeerTestsCleanupTimer (m_Service), m_TerminationTimer (m_Service),
|
||||
m_TerminationTimerV6 (m_ServiceV6)
|
||||
{
|
||||
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);
|
||||
OpenSocketV6 ();
|
||||
}
|
||||
|
||||
SSUServer::SSUServer (int port):
|
||||
m_OnlyV6(false), m_IsRunning(false),
|
||||
m_Thread (nullptr), m_ThreadV6 (nullptr), m_ReceiversThread (nullptr),
|
||||
m_Work (m_Service), m_WorkV6 (m_ServiceV6), m_ReceiversWork (m_ReceiversService),
|
||||
m_ReceiversThreadV6 (nullptr), m_Work (m_Service), m_WorkV6 (m_ServiceV6),
|
||||
m_ReceiversWork (m_ReceiversService), m_ReceiversWorkV6 (m_ReceiversServiceV6),
|
||||
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 (m_ReceiversService), m_SocketV6 (m_ReceiversServiceV6),
|
||||
m_IntroducersUpdateTimer (m_Service), m_PeerTestsCleanupTimer (m_Service),
|
||||
m_TerminationTimer (m_Service), m_TerminationTimerV6 (m_ServiceV6)
|
||||
{
|
||||
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (65535));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (65535));
|
||||
OpenSocket ();
|
||||
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);
|
||||
}
|
||||
OpenSocketV6 ();
|
||||
}
|
||||
|
||||
SSUServer::~SSUServer ()
|
||||
{
|
||||
}
|
||||
|
||||
void SSUServer::OpenSocket ()
|
||||
{
|
||||
m_Socket.open (boost::asio::ip::udp::v4());
|
||||
m_Socket.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
|
||||
m_Socket.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
|
||||
m_Socket.bind (m_Endpoint);
|
||||
}
|
||||
|
||||
void SSUServer::OpenSocketV6 ()
|
||||
{
|
||||
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 (SSU_SOCKET_RECEIVE_BUFFER_SIZE));
|
||||
m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE));
|
||||
m_SocketV6.bind (m_EndpointV6);
|
||||
}
|
||||
|
||||
void SSUServer::Start ()
|
||||
{
|
||||
m_IsRunning = true;
|
||||
m_ReceiversThread = new std::thread (std::bind (&SSUServer::RunReceivers, this));
|
||||
if (!m_OnlyV6)
|
||||
{
|
||||
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));
|
||||
ScheduleTermination ();
|
||||
}
|
||||
if (context.SupportsV6 ())
|
||||
{
|
||||
m_ReceiversThreadV6 = new std::thread (std::bind (&SSUServer::RunReceiversV6, this));
|
||||
m_ThreadV6 = new std::thread (std::bind (&SSUServer::RunV6, this));
|
||||
m_ReceiversService.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
m_ReceiversServiceV6.post (std::bind (&SSUServer::ReceiveV6, this));
|
||||
ScheduleTerminationV6 ();
|
||||
}
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
ScheduleIntroducersUpdateTimer (); // wait for 30 seconds and decide if we need introducers
|
||||
@@ -73,11 +85,14 @@ namespace transport
|
||||
{
|
||||
DeleteAllSessions ();
|
||||
m_IsRunning = false;
|
||||
m_TerminationTimer.cancel ();
|
||||
m_TerminationTimerV6.cancel ();
|
||||
m_Service.stop ();
|
||||
m_Socket.close ();
|
||||
m_ServiceV6.stop ();
|
||||
m_SocketV6.close ();
|
||||
m_ReceiversService.stop ();
|
||||
m_ReceiversServiceV6.stop ();
|
||||
if (m_ReceiversThread)
|
||||
{
|
||||
m_ReceiversThread->join ();
|
||||
@@ -90,6 +105,12 @@ namespace transport
|
||||
delete m_Thread;
|
||||
m_Thread = nullptr;
|
||||
}
|
||||
if (m_ReceiversThreadV6)
|
||||
{
|
||||
m_ReceiversThreadV6->join ();
|
||||
delete m_ReceiversThreadV6;
|
||||
m_ReceiversThreadV6 = nullptr;
|
||||
}
|
||||
if (m_ThreadV6)
|
||||
{
|
||||
m_ThreadV6->join ();
|
||||
@@ -143,16 +164,41 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::AddRelay (uint32_t tag, const boost::asio::ip::udp::endpoint& relay)
|
||||
void SSUServer::RunReceiversV6 ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_ReceiversServiceV6.run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receivers runtime exception: ", ex.what ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay)
|
||||
{
|
||||
m_Relays[tag] = relay;
|
||||
}
|
||||
|
||||
void SSUServer::RemoveRelay (uint32_t tag)
|
||||
{
|
||||
m_Relays.erase (tag);
|
||||
}
|
||||
|
||||
std::shared_ptr<SSUSession> SSUServer::FindRelaySession (uint32_t tag)
|
||||
{
|
||||
auto it = m_Relays.find (tag);
|
||||
if (it != m_Relays.end ())
|
||||
return FindSession (it->second);
|
||||
{
|
||||
if (it->second->GetState () == eSessionStateEstablished)
|
||||
return it->second;
|
||||
else
|
||||
m_Relays.erase (it);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -188,21 +234,40 @@ namespace transport
|
||||
|
||||
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();
|
||||
}
|
||||
if (!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, 0, ec);
|
||||
if (!ec)
|
||||
{
|
||||
packets.push_back (packet);
|
||||
moreBytes = m_Socket.available(ec);
|
||||
if (ec) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: receive_from error: ", ec.message ());
|
||||
delete packet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_Service.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_Sessions));
|
||||
Receive ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
|
||||
delete packet;
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: receive error: ", ecode.message ());
|
||||
m_Socket.close ();
|
||||
OpenSocket ();
|
||||
Receive ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,22 +279,42 @@ namespace transport
|
||||
std::vector<SSUPacket *> packets;
|
||||
packets.push_back (packet);
|
||||
|
||||
size_t moreBytes = m_SocketV6.available ();
|
||||
while (moreBytes && packets.size () < 25)
|
||||
boost::system::error_code ec;
|
||||
size_t moreBytes = m_SocketV6.available (ec);
|
||||
if (!ec)
|
||||
{
|
||||
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();
|
||||
while (moreBytes && packets.size () < 25)
|
||||
{
|
||||
packet = new SSUPacket ();
|
||||
packet->len = m_SocketV6.receive_from (boost::asio::buffer (packet->buf, SSU_MTU_V6), packet->from, 0, ec);
|
||||
if (!ec)
|
||||
{
|
||||
packets.push_back (packet);
|
||||
moreBytes = m_SocketV6.available(ec);
|
||||
if (ec) break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive_from error: ", ec.message ());
|
||||
delete packet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
m_ServiceV6.post (std::bind (&SSUServer::HandleReceivedPackets, this, packets, &m_SessionsV6));
|
||||
ReceiveV6 ();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
|
||||
delete packet;
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: v6 receive error: ", ecode.message ());
|
||||
m_SocketV6.close ();
|
||||
OpenSocketV6 ();
|
||||
ReceiveV6 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,9 +377,9 @@ namespace transport
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest)
|
||||
void SSUServer::CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest, bool v4only)
|
||||
{
|
||||
auto address = router->GetSSUAddress (!context.SupportsV6 ());
|
||||
auto address = router->GetSSUAddress (v4only || !context.SupportsV6 ());
|
||||
if (address)
|
||||
CreateSession (router, address->host, address->port, peerTest);
|
||||
else
|
||||
@@ -357,7 +442,7 @@ namespace transport
|
||||
return;
|
||||
}
|
||||
// create new session
|
||||
int numIntroducers = address->introducers.size ();
|
||||
int numIntroducers = address->ssu->introducers.size ();
|
||||
if (numIntroducers > 0)
|
||||
{
|
||||
std::shared_ptr<SSUSession> introducerSession;
|
||||
@@ -365,7 +450,7 @@ namespace transport
|
||||
// we might have a session to introducer already
|
||||
for (int i = 0; i < numIntroducers; i++)
|
||||
{
|
||||
auto intr = &(address->introducers[i]);
|
||||
auto intr = &(address->ssu->introducers[i]);
|
||||
boost::asio::ip::udp::endpoint ep (intr->iHost, intr->iPort);
|
||||
if (ep.address ().is_v4 ()) // ipv4 only
|
||||
{
|
||||
@@ -640,6 +725,58 @@ namespace transport
|
||||
SchedulePeerTestsCleanupTimer ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleTermination ()
|
||||
{
|
||||
m_TerminationTimer.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT));
|
||||
m_TerminationTimer.async_wait (std::bind (&SSUServer::HandleTerminationTimer,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandleTerminationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto& it: m_Sessions)
|
||||
if (it.second->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
auto session = it.second;
|
||||
m_Service.post ([session]
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
|
||||
session->Failed ();
|
||||
});
|
||||
}
|
||||
ScheduleTermination ();
|
||||
}
|
||||
}
|
||||
|
||||
void SSUServer::ScheduleTerminationV6 ()
|
||||
{
|
||||
m_TerminationTimerV6.expires_from_now (boost::posix_time::seconds(SSU_TERMINATION_CHECK_TIMEOUT));
|
||||
m_TerminationTimerV6.async_wait (std::bind (&SSUServer::HandleTerminationTimerV6,
|
||||
this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUServer::HandleTerminationTimerV6 (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
for (auto& it: m_SessionsV6)
|
||||
if (it.second->IsTerminationTimeoutExpired (ts))
|
||||
{
|
||||
auto session = it.second;
|
||||
m_ServiceV6.post ([session]
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: no activity with ", session->GetRemoteEndpoint (), " for ", session->GetTerminationTimeout (), " seconds");
|
||||
session->Failed ();
|
||||
});
|
||||
}
|
||||
ScheduleTerminationV6 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
32
SSU.h
32
SSU.h
@@ -23,11 +23,14 @@ 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 int SSU_TERMINATION_CHECK_TIMEOUT = 30; // 30 seconds
|
||||
const size_t SSU_MAX_NUM_INTRODUCERS = 3;
|
||||
const size_t SSU_SOCKET_RECEIVE_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||
const size_t SSU_SOCKET_SEND_BUFFER_SIZE = 0x1FFFF; // 128K
|
||||
|
||||
struct SSUPacket
|
||||
{
|
||||
i2p::crypto::AESAlignedBuffer<1500> buf;
|
||||
i2p::crypto::AESAlignedBuffer<SSU_MTU_V6 + 18> buf; // max MTU + iv + size
|
||||
boost::asio::ip::udp::endpoint from;
|
||||
size_t len;
|
||||
};
|
||||
@@ -41,7 +44,7 @@ namespace transport
|
||||
~SSUServer ();
|
||||
void Start ();
|
||||
void Stop ();
|
||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false);
|
||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest = false, bool v4only = false);
|
||||
void CreateSession (std::shared_ptr<const i2p::data::RouterInfo> router,
|
||||
const boost::asio::ip::address& addr, int port, bool peerTest = false);
|
||||
void CreateDirectSession (std::shared_ptr<const i2p::data::RouterInfo> router, boost::asio::ip::udp::endpoint remoteEndpoint, bool peerTest);
|
||||
@@ -56,7 +59,8 @@ namespace transport
|
||||
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);
|
||||
void AddRelay (uint32_t tag, std::shared_ptr<SSUSession> relay);
|
||||
void RemoveRelay (uint32_t tag);
|
||||
std::shared_ptr<SSUSession> FindRelaySession (uint32_t tag);
|
||||
|
||||
void NewPeerTest (uint32_t nonce, PeerTestParticipant role, std::shared_ptr<SSUSession> session = nullptr);
|
||||
@@ -67,9 +71,12 @@ namespace transport
|
||||
|
||||
private:
|
||||
|
||||
void OpenSocket ();
|
||||
void OpenSocketV6 ();
|
||||
void Run ();
|
||||
void RunV6 ();
|
||||
void RunReceivers ();
|
||||
void RunReceiversV6 ();
|
||||
void Receive ();
|
||||
void ReceiveV6 ();
|
||||
void HandleReceivedFrom (const boost::system::error_code& ecode, std::size_t bytes_transferred, SSUPacket * packet);
|
||||
@@ -90,6 +97,12 @@ namespace transport
|
||||
void SchedulePeerTestsCleanupTimer ();
|
||||
void HandlePeerTestsCleanupTimer (const boost::system::error_code& ecode);
|
||||
|
||||
// timer
|
||||
void ScheduleTermination ();
|
||||
void HandleTerminationTimer (const boost::system::error_code& ecode);
|
||||
void ScheduleTerminationV6 ();
|
||||
void HandleTerminationTimerV6 (const boost::system::error_code& ecode);
|
||||
|
||||
private:
|
||||
|
||||
struct PeerTest
|
||||
@@ -101,17 +114,18 @@ namespace transport
|
||||
|
||||
bool m_OnlyV6;
|
||||
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;
|
||||
std::thread * m_Thread, * m_ThreadV6, * m_ReceiversThread, * m_ReceiversThreadV6;
|
||||
boost::asio::io_service m_Service, m_ServiceV6, m_ReceiversService, m_ReceiversServiceV6;
|
||||
boost::asio::io_service::work m_Work, m_WorkV6, m_ReceiversWork, m_ReceiversWorkV6;
|
||||
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;
|
||||
boost::asio::deadline_timer m_IntroducersUpdateTimer, m_PeerTestsCleanupTimer,
|
||||
m_TerminationTimer, m_TerminationTimerV6;
|
||||
std::list<boost::asio::ip::udp::endpoint> m_Introducers; // introducers we are connected to
|
||||
std::map<boost::asio::ip::udp::endpoint, std::shared_ptr<SSUSession> > m_Sessions, m_SessionsV6;
|
||||
std::map<uint32_t, boost::asio::ip::udp::endpoint> m_Relays; // we are introducer
|
||||
std::map<uint32_t, std::shared_ptr<SSUSession> > 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; };
|
||||
|
||||
30
SSUData.cpp
30
SSUData.cpp
@@ -5,6 +5,9 @@
|
||||
#include "NetDb.h"
|
||||
#include "SSU.h"
|
||||
#include "SSUData.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -49,25 +52,25 @@ namespace transport
|
||||
|
||||
void SSUData::AdjustPacketSize (std::shared_ptr<const i2p::data::RouterInfo> remoteRouter)
|
||||
{
|
||||
if (remoteRouter) return;
|
||||
if (!remoteRouter) return;
|
||||
auto ssuAddress = remoteRouter->GetSSUAddress ();
|
||||
if (ssuAddress && ssuAddress->mtu)
|
||||
if (ssuAddress && ssuAddress->ssu->mtu)
|
||||
{
|
||||
if (m_Session.IsV6 ())
|
||||
m_PacketSize = ssuAddress->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
m_PacketSize = ssuAddress->ssu->mtu - IPV6_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
else
|
||||
m_PacketSize = ssuAddress->mtu - IPV4_HEADER_SIZE - UDP_HEADER_SIZE;
|
||||
m_PacketSize = ssuAddress->ssu->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 (eLogDebug, "SSU: MTU=", ssuAddress->mtu, " packet size=", m_PacketSize);
|
||||
LogPrint (eLogDebug, "SSU: MTU=", ssuAddress->ssu->mtu, " packet size=", m_PacketSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->mtu);
|
||||
LogPrint (eLogWarning, "SSU: Unexpected MTU ", ssuAddress->ssu->mtu);
|
||||
m_PacketSize = m_MaxPacketSize;
|
||||
}
|
||||
}
|
||||
@@ -151,8 +154,7 @@ namespace transport
|
||||
{
|
||||
uint32_t msgID = bufbe32toh (buf); // message ID
|
||||
buf += 4;
|
||||
uint8_t frag[4];
|
||||
frag[0] = 0;
|
||||
uint8_t frag[4] = {0};
|
||||
memcpy (frag + 1, buf, 3);
|
||||
buf += 3;
|
||||
uint32_t fragmentInfo = bufbe32toh (frag); // fragment info
|
||||
@@ -234,8 +236,13 @@ namespace transport
|
||||
{
|
||||
m_ReceivedMessages.insert (msgID);
|
||||
m_LastMessageReceivedTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
if (!msg->IsExpired ())
|
||||
if (!msg->IsExpired ())
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
QueueIntEvent("transport.recvmsg", m_Session.GetIdentHashBase64(), 1);
|
||||
#endif
|
||||
m_Handler.PutNextMessage (msg);
|
||||
}
|
||||
else
|
||||
LogPrint (eLogDebug, "SSU: message expired");
|
||||
}
|
||||
@@ -363,7 +370,7 @@ namespace transport
|
||||
|
||||
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 buf[48 + 18] = {0}; // 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++;
|
||||
@@ -385,7 +392,7 @@ namespace transport
|
||||
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
|
||||
return;
|
||||
}
|
||||
uint8_t buf[64 + 18];
|
||||
uint8_t buf[64 + 18] = {0};
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
|
||||
payload++;
|
||||
@@ -455,6 +462,7 @@ namespace transport
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (m_SentMessages.empty ()) return; // nothing to resend
|
||||
if (numResent < MAX_OUTGOING_WINDOW_SIZE)
|
||||
ScheduleResend ();
|
||||
else
|
||||
|
||||
@@ -21,13 +21,13 @@ namespace transport
|
||||
#ifdef MESHNET
|
||||
const size_t SSU_MTU_V6 = 1286;
|
||||
#else
|
||||
const size_t SSU_MTU_V6 = 1472;
|
||||
const size_t SSU_MTU_V6 = 1488;
|
||||
#endif
|
||||
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 size_t SSU_V6_MAX_PACKET_SIZE = SSU_MTU_V6 - IPV6_HEADER_SIZE - UDP_HEADER_SIZE; // 1440
|
||||
const int RESEND_INTERVAL = 3; // in seconds
|
||||
const int MAX_NUM_RESENDS = 5;
|
||||
const int DECAY_INTERVAL = 20; // in seconds
|
||||
|
||||
144
SSUSession.cpp
144
SSUSession.cpp
@@ -4,6 +4,7 @@
|
||||
#include "Timestamp.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Transports.h"
|
||||
#include "NetDb.h"
|
||||
#include "SSU.h"
|
||||
#include "SSUSession.h"
|
||||
|
||||
@@ -14,22 +15,22 @@ namespace transport
|
||||
SSUSession::SSUSession (SSUServer& server, boost::asio::ip::udp::endpoint& remoteEndpoint,
|
||||
std::shared_ptr<const i2p::data::RouterInfo> router, bool peerTest ):
|
||||
TransportSession (router, SSU_TERMINATION_TIMEOUT),
|
||||
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_Timer (GetService ()),
|
||||
m_Server (server), m_RemoteEndpoint (remoteEndpoint), m_ConnectTimer (GetService ()),
|
||||
m_IsPeerTest (peerTest),m_State (eSessionStateUnknown), m_IsSessionKey (false),
|
||||
m_RelayTag (0),m_Data (*this), m_IsDataReceived (false)
|
||||
m_RelayTag (0), m_SentRelayTag (0), m_Data (*this), m_IsDataReceived (false)
|
||||
{
|
||||
if (router)
|
||||
{
|
||||
// we are client
|
||||
auto address = router->GetSSUAddress (false);
|
||||
if (address) m_IntroKey = address->key;
|
||||
if (address) m_IntroKey = address->ssu->key;
|
||||
m_Data.AdjustPacketSize (router); // mtu
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are server
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
|
||||
if (address) m_IntroKey = address->key;
|
||||
if (address) m_IntroKey = address->ssu->key;
|
||||
}
|
||||
m_CreationTime = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
@@ -97,7 +98,7 @@ namespace transport
|
||||
{
|
||||
if (!len) return; // ignore zero-length packets
|
||||
if (m_State == eSessionStateEstablished)
|
||||
ScheduleTermination ();
|
||||
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
|
||||
if (m_IsSessionKey && Validate (buf, len, m_MacKey)) // try session key first
|
||||
DecryptSessionKey (buf, len);
|
||||
@@ -115,8 +116,8 @@ namespace transport
|
||||
LogPrint (eLogInfo, "SSU is not supported");
|
||||
return;
|
||||
}
|
||||
if (Validate (buf, len, address->key))
|
||||
Decrypt (buf, len, address->key);
|
||||
if (Validate (buf, len, address->ssu->key))
|
||||
Decrypt (buf, len, address->ssu->key);
|
||||
else
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: MAC verification failed ", len, " bytes from ", senderEndpoint);
|
||||
@@ -229,7 +230,7 @@ namespace transport
|
||||
}
|
||||
|
||||
LogPrint (eLogDebug, "SSU message: session created");
|
||||
m_Timer.cancel (); // connect timer
|
||||
m_ConnectTimer.cancel (); // connect timer
|
||||
SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, signed on time
|
||||
auto headerSize = GetSSUHeaderSize (buf);
|
||||
if (headerSize >= len)
|
||||
@@ -272,6 +273,16 @@ namespace transport
|
||||
s.Insert (payload, 8); // relayTag and signed on time
|
||||
m_RelayTag = bufbe32toh (payload);
|
||||
payload += 4; // relayTag
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting)
|
||||
{
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
uint32_t signedOnTime = bufbe32toh(payload);
|
||||
if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW)
|
||||
{
|
||||
LogPrint (eLogError, "SSU: clock skew detected ", (int)ts - signedOnTime, ". Check your clock");
|
||||
i2p::context.SetError (eRouterErrorClockSkew);
|
||||
}
|
||||
}
|
||||
payload += 4; // signed on time
|
||||
// decrypt signature
|
||||
size_t signatureLen = m_RemoteIdentity->GetSignatureLen ();
|
||||
@@ -307,9 +318,19 @@ namespace transport
|
||||
payload++; // identity fragment info
|
||||
uint16_t identitySize = bufbe16toh (payload);
|
||||
payload += 2; // size of identity fragment
|
||||
SetRemoteIdentity (std::make_shared<i2p::data::IdentityEx> (payload, identitySize));
|
||||
auto identity = std::make_shared<i2p::data::IdentityEx> (payload, identitySize);
|
||||
auto existing = i2p::data::netdb.FindRouter (identity->GetIdentHash ()); // check if exists already
|
||||
SetRemoteIdentity (existing ? existing->GetRouterIdentity () : identity);
|
||||
m_Data.UpdatePacketSize (m_RemoteIdentity->GetIdentHash ());
|
||||
payload += identitySize; // identity
|
||||
auto ts = i2p::util::GetSecondsSinceEpoch ();
|
||||
uint32_t signedOnTime = bufbe32toh(payload);
|
||||
if (signedOnTime < ts - SSU_CLOCK_SKEW || signedOnTime > ts + SSU_CLOCK_SKEW)
|
||||
{
|
||||
LogPrint (eLogError, "SSU message 'confirmed' time difference ", (int)ts - signedOnTime, " exceeds clock skew");
|
||||
Failed ();
|
||||
return;
|
||||
}
|
||||
if (m_SignedData)
|
||||
m_SignedData->Insert (payload, 4); // insert Alice's signed on time
|
||||
payload += 4; // signed-on time
|
||||
@@ -332,7 +353,7 @@ namespace transport
|
||||
|
||||
void SSUSession::SendSessionRequest ()
|
||||
{
|
||||
uint8_t buf[320 + 18]; // 304 bytes for ipv4, 320 for ipv6
|
||||
uint8_t buf[320 + 18] = {0}; // 304 bytes for ipv4, 320 for ipv6
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
uint8_t flag = 0;
|
||||
// fill extended options, 3 bytes extended options don't change message size
|
||||
@@ -374,7 +395,7 @@ namespace transport
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t buf[96 + 18];
|
||||
uint8_t buf[96 + 18] = {0};
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
htobe32buf (payload, introducer.iTag);
|
||||
payload += 4;
|
||||
@@ -384,7 +405,7 @@ namespace transport
|
||||
payload += 2;
|
||||
*payload = 0; // challenge
|
||||
payload++;
|
||||
memcpy (payload, (const uint8_t *)address->key, 32);
|
||||
memcpy (payload, (const uint8_t *)address->ssu->key, 32);
|
||||
payload += 32;
|
||||
htobe32buf (payload, nonce); // nonce
|
||||
|
||||
@@ -409,7 +430,7 @@ namespace transport
|
||||
SignedData s; // x,y, remote IP, remote port, our IP, our port, relayTag, signed on time
|
||||
s.Insert (x, 256); // x
|
||||
|
||||
uint8_t buf[384 + 18];
|
||||
uint8_t buf[384 + 18] = {0};
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
memcpy (payload, m_DHKeysPair->GetPublicKey (), 256);
|
||||
s.Insert (payload, 256); // y
|
||||
@@ -440,14 +461,12 @@ namespace transport
|
||||
else
|
||||
s.Insert (address->host.to_v6 ().to_bytes ().data (), 16); // our IP V6
|
||||
s.Insert<uint16_t> (htobe16 (address->port)); // our port
|
||||
uint32_t relayTag = 0;
|
||||
if (sendRelayTag && i2p::context.GetRouterInfo ().IsIntroducer () && !IsV6 ())
|
||||
{
|
||||
RAND_bytes((uint8_t *)&relayTag, 4);
|
||||
if (!relayTag) relayTag = 1;
|
||||
m_Server.AddRelay (relayTag, m_RemoteEndpoint);
|
||||
RAND_bytes((uint8_t *)&m_SentRelayTag, 4);
|
||||
if (!m_SentRelayTag) m_SentRelayTag = 1;
|
||||
}
|
||||
htobe32buf (payload, relayTag);
|
||||
htobe32buf (payload, m_SentRelayTag);
|
||||
payload += 4; // relay tag
|
||||
htobe32buf (payload, i2p::util::GetSecondsSinceEpoch ()); // signed on time
|
||||
payload += 4;
|
||||
@@ -457,14 +476,18 @@ namespace transport
|
||||
m_SignedData = std::unique_ptr<SignedData>(new SignedData (s));
|
||||
s.Insert (payload - 4, 4); // BOB's signed on time
|
||||
s.Sign (i2p::context.GetPrivateKeys (), payload); // DSA signature
|
||||
// TODO: fill padding with random data
|
||||
|
||||
uint8_t iv[16];
|
||||
RAND_bytes (iv, 16); // random iv
|
||||
// encrypt signature and padding with newly created session key
|
||||
size_t signatureLen = i2p::context.GetIdentity ()->GetSignatureLen ();
|
||||
size_t paddingSize = signatureLen & 0x0F; // %16
|
||||
if (paddingSize > 0) signatureLen += (16 - paddingSize);
|
||||
if (paddingSize > 0)
|
||||
{
|
||||
// fill random padding
|
||||
RAND_bytes(payload + signatureLen, (16 - paddingSize));
|
||||
signatureLen += (16 - paddingSize);
|
||||
}
|
||||
m_SessionKeyEncryption.SetIV (iv);
|
||||
m_SessionKeyEncryption.Encrypt (payload, signatureLen, payload);
|
||||
payload += signatureLen;
|
||||
@@ -477,7 +500,7 @@ namespace transport
|
||||
|
||||
void SSUSession::SendSessionConfirmed (const uint8_t * y, const uint8_t * ourAddress, size_t ourAddressLen)
|
||||
{
|
||||
uint8_t buf[512 + 18];
|
||||
uint8_t buf[512 + 18] = {0};
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = 1; // 1 fragment
|
||||
payload++; // info
|
||||
@@ -492,9 +515,8 @@ namespace transport
|
||||
auto signatureLen = i2p::context.GetIdentity ()->GetSignatureLen ();
|
||||
size_t paddingSize = ((payload - buf) + signatureLen)%16;
|
||||
if (paddingSize > 0) paddingSize = 16 - paddingSize;
|
||||
// TODO: fill padding
|
||||
RAND_bytes(payload, paddingSize); // fill padding with random
|
||||
payload += paddingSize; // padding size
|
||||
|
||||
// signature
|
||||
SignedData s; // x,y, our IP, our port, remote IP, remote port, relayTag, our signed on time
|
||||
s.Insert (m_DHKeysPair->GetPublicKey (), 256); // x
|
||||
@@ -543,14 +565,14 @@ namespace transport
|
||||
void SSUSession::SendRelayResponse (uint32_t nonce, const boost::asio::ip::udp::endpoint& from,
|
||||
const uint8_t * introKey, const boost::asio::ip::udp::endpoint& to)
|
||||
{
|
||||
uint8_t buf[80 + 18]; // 64 Alice's ipv4 and 80 Alice's ipv6
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
// Charlie's address always v4
|
||||
if (!to.address ().is_v4 ())
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: Charlie's IP must be v4");
|
||||
return;
|
||||
}
|
||||
uint8_t buf[80 + 18] = {0}; // 64 Alice's ipv4 and 80 Alice's ipv6
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = 4;
|
||||
payload++; // size
|
||||
htobe32buf (payload, to.address ().to_v4 ().to_ulong ()); // Charlie's IP
|
||||
@@ -603,7 +625,7 @@ namespace transport
|
||||
LogPrint (eLogWarning, "SSU: Alice's IP must be v4");
|
||||
return;
|
||||
}
|
||||
uint8_t buf[48 + 18];
|
||||
uint8_t buf[48 + 18] = {0};
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = 4;
|
||||
payload++; // size
|
||||
@@ -804,9 +826,9 @@ namespace transport
|
||||
|
||||
void SSUSession::ScheduleConnectTimer ()
|
||||
{
|
||||
m_Timer.cancel ();
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
|
||||
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
|
||||
m_ConnectTimer.cancel ();
|
||||
m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
|
||||
m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
@@ -826,8 +848,8 @@ namespace transport
|
||||
if (m_State == eSessionStateUnknown)
|
||||
{
|
||||
// set connect timer
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
|
||||
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
|
||||
m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
|
||||
m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
uint32_t nonce;
|
||||
@@ -840,8 +862,8 @@ namespace transport
|
||||
{
|
||||
m_State = eSessionStateIntroduced;
|
||||
// set connect timer
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
|
||||
m_Timer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
|
||||
m_ConnectTimer.expires_from_now (boost::posix_time::seconds(SSU_CONNECT_TIMEOUT));
|
||||
m_ConnectTimer.async_wait (std::bind (&SSUSession::HandleConnectTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
@@ -851,7 +873,9 @@ namespace transport
|
||||
SendSesionDestroyed ();
|
||||
transports.PeerDisconnected (shared_from_this ());
|
||||
m_Data.Stop ();
|
||||
m_Timer.cancel ();
|
||||
m_ConnectTimer.cancel ();
|
||||
if (m_SentRelayTag)
|
||||
m_Server.RemoveRelay (m_SentRelayTag); // relay tag is not valid anymore
|
||||
}
|
||||
|
||||
void SSUSession::Done ()
|
||||
@@ -868,7 +892,9 @@ namespace transport
|
||||
transports.PeerConnected (shared_from_this ());
|
||||
if (m_IsPeerTest)
|
||||
SendPeerTest ();
|
||||
ScheduleTermination ();
|
||||
if (m_SentRelayTag)
|
||||
m_Server.AddRelay (m_SentRelayTag, shared_from_this ());
|
||||
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
|
||||
void SSUSession::Failed ()
|
||||
@@ -880,24 +906,6 @@ namespace transport
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::ScheduleTermination ()
|
||||
{
|
||||
m_Timer.cancel ();
|
||||
m_Timer.expires_from_now (boost::posix_time::seconds(GetTerminationTimeout ()));
|
||||
m_Timer.async_wait (std::bind (&SSUSession::HandleTerminationTimer,
|
||||
shared_from_this (), std::placeholders::_1));
|
||||
}
|
||||
|
||||
void SSUSession::HandleTerminationTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
LogPrint (eLogWarning, "SSU: no activity with ", m_RemoteEndpoint, " for ", GetTerminationTimeout (), " seconds");
|
||||
Failed ();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SSUSession::SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs)
|
||||
{
|
||||
GetService ().post (std::bind (&SSUSession::PostI2NPMessages, shared_from_this (), msgs));
|
||||
@@ -944,7 +952,7 @@ namespace transport
|
||||
// existing test
|
||||
case ePeerTestParticipantAlice1:
|
||||
{
|
||||
if (m_State == eSessionStateEstablished)
|
||||
if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice");
|
||||
if (i2p::context.GetStatus () == eRouterStatusTesting) // still not OK
|
||||
@@ -953,6 +961,8 @@ namespace transport
|
||||
else
|
||||
{
|
||||
LogPrint (eLogDebug, "SSU: first peer test from Charlie. We are Alice");
|
||||
if (m_State == eSessionStateEstablished)
|
||||
LogPrint (eLogWarning, "SSU: first peer test from Charlie through established session. We are Alice");
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
m_Server.UpdatePeerTest (nonce, ePeerTestParticipantAlice2);
|
||||
SendPeerTest (nonce, senderEndpoint.address (), senderEndpoint.port (), introKey, true, false); // to Charlie
|
||||
@@ -961,7 +971,7 @@ namespace transport
|
||||
}
|
||||
case ePeerTestParticipantAlice2:
|
||||
{
|
||||
if (m_State == eSessionStateEstablished)
|
||||
if (m_Server.GetPeerTestSession (nonce) == shared_from_this ()) // Alice-Bob
|
||||
LogPrint (eLogDebug, "SSU: peer test from Bob. We are Alice");
|
||||
else
|
||||
{
|
||||
@@ -1009,7 +1019,7 @@ namespace transport
|
||||
else // v6
|
||||
{
|
||||
boost::asio::ip::address_v6::bytes_type bytes;
|
||||
memcpy (bytes.data (), address, 6);
|
||||
memcpy (bytes.data (), address, 16);
|
||||
addr = boost::asio::ip::address_v6 (bytes);
|
||||
}
|
||||
SendPeerTest (nonce, addr, be16toh (port), introKey); // to Alice with her address received from Bob
|
||||
@@ -1036,7 +1046,7 @@ namespace transport
|
||||
// toAddress is true for Alice<->Chalie communications only
|
||||
// sendAddress is false if message comes from Alice
|
||||
{
|
||||
uint8_t buf[80 + 18];
|
||||
uint8_t buf[80 + 18] = {0};
|
||||
uint8_t iv[16];
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
htobe32buf (payload, nonce);
|
||||
@@ -1051,7 +1061,7 @@ namespace transport
|
||||
}
|
||||
else if (address.is_v6 ())
|
||||
{
|
||||
*payload = 6;
|
||||
*payload = 16;
|
||||
memcpy (payload + 1, address.to_v6 ().to_bytes ().data (), 16); // our IP V6
|
||||
}
|
||||
else
|
||||
@@ -1071,7 +1081,7 @@ namespace transport
|
||||
// send our intro key to address instead it's own
|
||||
auto addr = i2p::context.GetRouterInfo ().GetSSUAddress ();
|
||||
if (addr)
|
||||
memcpy (payload, addr->key, 32); // intro key
|
||||
memcpy (payload, addr->ssu->key, 32); // intro key
|
||||
else
|
||||
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
|
||||
}
|
||||
@@ -1092,14 +1102,14 @@ namespace transport
|
||||
// encrypt message with session key
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_PEER_TEST, buf, 80);
|
||||
Send (buf, 80);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SSUSession::SendPeerTest ()
|
||||
{
|
||||
// we are Alice
|
||||
LogPrint (eLogDebug, "SSU: sending peer test");
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (false);
|
||||
auto address = i2p::context.GetRouterInfo ().GetSSUAddress (i2p::context.SupportsV4 ());
|
||||
if (!address)
|
||||
{
|
||||
LogPrint (eLogInfo, "SSU is not supported. Can't send peer test");
|
||||
@@ -1109,15 +1119,15 @@ namespace transport
|
||||
RAND_bytes ((uint8_t *)&nonce, 4);
|
||||
if (!nonce) nonce = 1;
|
||||
m_IsPeerTest = false;
|
||||
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1);
|
||||
SendPeerTest (nonce, boost::asio::ip::address(), 0, address->key, false, false); // address and port always zero for Alice
|
||||
m_Server.NewPeerTest (nonce, ePeerTestParticipantAlice1, shared_from_this ());
|
||||
SendPeerTest (nonce, boost::asio::ip::address(), 0, address->ssu->key, false, false); // address and port always zero for Alice
|
||||
}
|
||||
|
||||
void SSUSession::SendKeepAlive ()
|
||||
{
|
||||
if (m_State == eSessionStateEstablished)
|
||||
{
|
||||
uint8_t buf[48 + 18];
|
||||
uint8_t buf[48 + 18] = {0};
|
||||
uint8_t * payload = buf + sizeof (SSUHeader);
|
||||
*payload = 0; // flags
|
||||
payload++;
|
||||
@@ -1126,7 +1136,7 @@ namespace transport
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, 48);
|
||||
Send (buf, 48);
|
||||
LogPrint (eLogDebug, "SSU: keep-alive sent");
|
||||
ScheduleTermination ();
|
||||
m_LastActivityTimestamp = i2p::util::GetSecondsSinceEpoch ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1134,7 +1144,7 @@ namespace transport
|
||||
{
|
||||
if (m_IsSessionKey)
|
||||
{
|
||||
uint8_t buf[48 + 18];
|
||||
uint8_t buf[48 + 18] = {0};
|
||||
// encrypt message with session key
|
||||
FillHeaderAndEncrypt (PAYLOAD_TYPE_SESSION_DESTROYED, buf, 48);
|
||||
try
|
||||
@@ -1151,7 +1161,7 @@ namespace transport
|
||||
|
||||
void SSUSession::Send (uint8_t type, const uint8_t * payload, size_t len)
|
||||
{
|
||||
uint8_t buf[SSU_MTU_V4 + 18];
|
||||
uint8_t buf[SSU_MTU_V4 + 18] = {0};
|
||||
size_t msgSize = len + sizeof (SSUHeader);
|
||||
size_t paddingSize = msgSize & 0x0F; // %16
|
||||
if (paddingSize > 0) msgSize += (16 - paddingSize);
|
||||
|
||||
11
SSUSession.h
11
SSUSession.h
@@ -27,6 +27,7 @@ namespace transport
|
||||
|
||||
const int SSU_CONNECT_TIMEOUT = 5; // 5 seconds
|
||||
const int SSU_TERMINATION_TIMEOUT = 330; // 5.5 minutes
|
||||
const int SSU_CLOCK_SKEW = 60; // in seconds
|
||||
|
||||
// payload types (4 bits)
|
||||
const uint8_t PAYLOAD_TYPE_SESSION_REQUEST = 0;
|
||||
@@ -77,6 +78,7 @@ namespace transport
|
||||
void WaitForIntroduction ();
|
||||
void Close ();
|
||||
void Done ();
|
||||
void Failed ();
|
||||
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);
|
||||
@@ -114,7 +116,6 @@ namespace transport
|
||||
void ProcessRelayResponse (const uint8_t * buf, size_t len);
|
||||
void ProcessRelayIntro (const 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);
|
||||
@@ -131,19 +132,17 @@ namespace transport
|
||||
void DecryptSessionKey (uint8_t * buf, size_t len);
|
||||
bool Validate (uint8_t * buf, size_t len, const i2p::crypto::MACKey& macKey);
|
||||
|
||||
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;
|
||||
boost::asio::deadline_timer m_ConnectTimer;
|
||||
bool m_IsPeerTest;
|
||||
SessionState m_State;
|
||||
bool m_IsSessionKey;
|
||||
uint32_t m_RelayTag;
|
||||
uint32_t m_RelayTag; // received from peer
|
||||
uint32_t m_SentRelayTag; // sent by us
|
||||
i2p::crypto::CBCEncryption m_SessionKeyEncryption;
|
||||
i2p::crypto::CBCDecryption m_SessionKeyDecryption;
|
||||
i2p::crypto::AESKey m_SessionKey;
|
||||
|
||||
@@ -467,17 +467,27 @@ namespace crypto
|
||||
return GetEd25519 ()->Verify (m_PublicKey, digest, signature);
|
||||
}
|
||||
|
||||
EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey)
|
||||
EDDSA25519Signer::EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
|
||||
{
|
||||
// expand key
|
||||
SHA512 (signingPrivateKey, EDDSA25519_PRIVATE_KEY_LENGTH, m_ExpandedPrivateKey);
|
||||
m_ExpandedPrivateKey[0] &= 0xF8; // drop last 3 bits
|
||||
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x1F; // drop first 3 bits
|
||||
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0x3F; // drop first 2 bits
|
||||
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] |= 0x40; // set second bit
|
||||
|
||||
// generate and encode public key
|
||||
BN_CTX * ctx = BN_CTX_new ();
|
||||
auto publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx);
|
||||
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx);
|
||||
|
||||
if (signingPublicKey && memcmp (m_PublicKeyEncoded, signingPublicKey, EDDSA25519_PUBLIC_KEY_LENGTH))
|
||||
{
|
||||
// keys don't match, it means older key with 0x1F
|
||||
LogPrint (eLogWarning, "Older EdDSA key detected");
|
||||
m_ExpandedPrivateKey[EDDSA25519_PRIVATE_KEY_LENGTH - 1] &= 0xDF; // drop third bit
|
||||
publicKey = GetEd25519 ()->GeneratePublicKey (m_ExpandedPrivateKey, ctx);
|
||||
GetEd25519 ()->EncodePublicKey (publicKey, m_PublicKeyEncoded, ctx);
|
||||
}
|
||||
BN_CTX_free (ctx);
|
||||
}
|
||||
|
||||
|
||||
64
Signature.h
64
Signature.h
@@ -43,7 +43,7 @@ namespace crypto
|
||||
DSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey = CreateDSA ();
|
||||
m_PublicKey->pub_key = BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL);
|
||||
DSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, DSA_PUBLIC_KEY_LENGTH, NULL), NULL);
|
||||
}
|
||||
|
||||
~DSAVerifier ()
|
||||
@@ -58,8 +58,7 @@ namespace crypto
|
||||
SHA1 (buf, len, digest);
|
||||
// signature
|
||||
DSA_SIG * sig = DSA_SIG_new();
|
||||
sig->r = BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL);
|
||||
sig->s = BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL);
|
||||
DSA_SIG_set0 (sig, BN_bin2bn (signature, DSA_SIGNATURE_LENGTH/2, NULL), BN_bin2bn (signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2, NULL));
|
||||
// DSA verification
|
||||
int ret = DSA_do_verify (digest, 20, sig, m_PublicKey);
|
||||
DSA_SIG_free(sig);
|
||||
@@ -78,10 +77,11 @@ namespace crypto
|
||||
{
|
||||
public:
|
||||
|
||||
DSASigner (const uint8_t * signingPrivateKey)
|
||||
DSASigner (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey)
|
||||
// openssl 1.1 always requires DSA public key even for signing
|
||||
{
|
||||
m_PrivateKey = CreateDSA ();
|
||||
m_PrivateKey->priv_key = BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL);
|
||||
DSA_set0_key (m_PrivateKey, BN_bin2bn (signingPublicKey, DSA_PUBLIC_KEY_LENGTH, NULL), BN_bin2bn (signingPrivateKey, DSA_PRIVATE_KEY_LENGTH, NULL));
|
||||
}
|
||||
|
||||
~DSASigner ()
|
||||
@@ -94,8 +94,10 @@ namespace crypto
|
||||
uint8_t digest[20];
|
||||
SHA1 (buf, len, digest);
|
||||
DSA_SIG * sig = DSA_do_sign (digest, 20, m_PrivateKey);
|
||||
bn2buf (sig->r, signature, DSA_SIGNATURE_LENGTH/2);
|
||||
bn2buf (sig->s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
|
||||
const BIGNUM * r, * s;
|
||||
DSA_SIG_get0 (sig, &r, &s);
|
||||
bn2buf (r, signature, DSA_SIGNATURE_LENGTH/2);
|
||||
bn2buf (s, signature + DSA_SIGNATURE_LENGTH/2, DSA_SIGNATURE_LENGTH/2);
|
||||
DSA_SIG_free(sig);
|
||||
}
|
||||
|
||||
@@ -108,10 +110,11 @@ namespace crypto
|
||||
{
|
||||
DSA * dsa = CreateDSA ();
|
||||
DSA_generate_key (dsa);
|
||||
bn2buf (dsa->priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
||||
bn2buf (dsa->pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
||||
DSA_free (dsa);
|
||||
|
||||
const BIGNUM * pub_key, * priv_key;
|
||||
DSA_get0_key(dsa, &pub_key, &priv_key);
|
||||
bn2buf (priv_key, signingPrivateKey, DSA_PRIVATE_KEY_LENGTH);
|
||||
bn2buf (pub_key, signingPublicKey, DSA_PUBLIC_KEY_LENGTH);
|
||||
DSA_free (dsa);
|
||||
}
|
||||
|
||||
struct SHA256Hash
|
||||
@@ -152,9 +155,10 @@ namespace crypto
|
||||
ECDSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey = EC_KEY_new_by_curve_name (curve);
|
||||
EC_KEY_set_public_key_affine_coordinates (m_PublicKey,
|
||||
BN_bin2bn (signingKey, keyLen/2, NULL),
|
||||
BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL));
|
||||
BIGNUM * x = BN_bin2bn (signingKey, keyLen/2, NULL);
|
||||
BIGNUM * y = BN_bin2bn (signingKey + keyLen/2, keyLen/2, NULL);
|
||||
EC_KEY_set_public_key_affine_coordinates (m_PublicKey, x, y);
|
||||
BN_free (x); BN_free (y);
|
||||
}
|
||||
|
||||
~ECDSAVerifier ()
|
||||
@@ -167,8 +171,9 @@ namespace crypto
|
||||
uint8_t digest[Hash::hashLen];
|
||||
Hash::CalculateHash (buf, len, digest);
|
||||
ECDSA_SIG * sig = ECDSA_SIG_new();
|
||||
sig->r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
|
||||
sig->s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
|
||||
auto r = BN_bin2bn (signature, GetSignatureLen ()/2, NULL);
|
||||
auto s = BN_bin2bn (signature + GetSignatureLen ()/2, GetSignatureLen ()/2, NULL);
|
||||
ECDSA_SIG_set0(sig, r, s);
|
||||
// ECDSA verification
|
||||
int ret = ECDSA_do_verify (digest, Hash::hashLen, sig, m_PublicKey);
|
||||
ECDSA_SIG_free(sig);
|
||||
@@ -205,9 +210,11 @@ namespace crypto
|
||||
uint8_t digest[Hash::hashLen];
|
||||
Hash::CalculateHash (buf, len, digest);
|
||||
ECDSA_SIG * sig = ECDSA_do_sign (digest, Hash::hashLen, m_PrivateKey);
|
||||
const BIGNUM * r, * s;
|
||||
ECDSA_SIG_get0 (sig, &r, &s);
|
||||
// signatureLen = keyLen
|
||||
bn2buf (sig->r, signature, keyLen/2);
|
||||
bn2buf (sig->s, signature + keyLen/2, keyLen/2);
|
||||
bn2buf (r, signature, keyLen/2);
|
||||
bn2buf (s, signature + keyLen/2, keyLen/2);
|
||||
ECDSA_SIG_free(sig);
|
||||
}
|
||||
|
||||
@@ -269,9 +276,7 @@ namespace crypto
|
||||
RSAVerifier (const uint8_t * signingKey)
|
||||
{
|
||||
m_PublicKey = RSA_new ();
|
||||
memset (m_PublicKey, 0, sizeof (RSA));
|
||||
m_PublicKey->e = BN_dup (GetRSAE ());
|
||||
m_PublicKey->n = BN_bin2bn (signingKey, keyLen, NULL);
|
||||
RSA_set0_key (m_PublicKey, BN_bin2bn (signingKey, keyLen, NULL) /* n */ , BN_dup (GetRSAE ()) /* d */, NULL);
|
||||
}
|
||||
|
||||
~RSAVerifier ()
|
||||
@@ -303,10 +308,8 @@ namespace crypto
|
||||
RSASigner (const uint8_t * signingPrivateKey)
|
||||
{
|
||||
m_PrivateKey = RSA_new ();
|
||||
memset (m_PrivateKey, 0, sizeof (RSA));
|
||||
m_PrivateKey->e = BN_dup (GetRSAE ());
|
||||
m_PrivateKey->n = BN_bin2bn (signingPrivateKey, keyLen, NULL);
|
||||
m_PrivateKey->d = BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL);
|
||||
RSA_set0_key (m_PrivateKey, BN_bin2bn (signingPrivateKey, keyLen, NULL), /* n */
|
||||
BN_dup (GetRSAE ()) /* e */, BN_bin2bn (signingPrivateKey + keyLen, keyLen, NULL) /* d */);
|
||||
}
|
||||
|
||||
~RSASigner ()
|
||||
@@ -332,9 +335,11 @@ namespace crypto
|
||||
RSA * rsa = RSA_new ();
|
||||
BIGNUM * e = BN_dup (GetRSAE ()); // make it non-const
|
||||
RSA_generate_key_ex (rsa, publicKeyLen*8, e, NULL);
|
||||
bn2buf (rsa->n, signingPrivateKey, publicKeyLen);
|
||||
bn2buf (rsa->d, signingPrivateKey + publicKeyLen, publicKeyLen);
|
||||
bn2buf (rsa->n, signingPublicKey, publicKeyLen);
|
||||
const BIGNUM * n, * d, * e1;
|
||||
RSA_get0_key (rsa, &n, &e1, &d);
|
||||
bn2buf (n, signingPrivateKey, publicKeyLen);
|
||||
bn2buf (d, signingPrivateKey + publicKeyLen, publicKeyLen);
|
||||
bn2buf (n, signingPublicKey, publicKeyLen);
|
||||
BN_free (e); // this e is not assigned to rsa->e
|
||||
RSA_free (rsa);
|
||||
}
|
||||
@@ -419,7 +424,8 @@ namespace crypto
|
||||
{
|
||||
public:
|
||||
|
||||
EDDSA25519Signer (const uint8_t * signingPrivateKey);
|
||||
EDDSA25519Signer (const uint8_t * signingPrivateKey, const uint8_t * signingPublicKey = nullptr);
|
||||
// we pass signingPublicKey to check if it matches private key
|
||||
void Sign (const uint8_t * buf, int len, uint8_t * signature) const;
|
||||
const uint8_t * GetPublicKey () const { return m_PublicKeyEncoded; };
|
||||
|
||||
|
||||
671
Streaming.cpp
671
Streaming.cpp
File diff suppressed because it is too large
Load Diff
78
Streaming.h
78
Streaming.h
@@ -18,6 +18,7 @@
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Garlic.h"
|
||||
#include "Tunnel.h"
|
||||
#include "util.h" // MemoryPool
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -51,6 +52,23 @@ namespace stream
|
||||
const int INITIAL_RTO = 9000; // in milliseconds
|
||||
const size_t MAX_PENDING_INCOMING_BACKLOG = 128;
|
||||
const int PENDING_INCOMING_TIMEOUT = 10; // in seconds
|
||||
const int MAX_RECEIVE_TIMEOUT = 30; // in seconds
|
||||
|
||||
/** i2cp option for limiting inbound stremaing connections */
|
||||
const char I2CP_PARAM_STREAMING_MAX_CONNS_PER_MIN[] = "maxconns";
|
||||
/** default maximum connections attempts per minute per destination */
|
||||
const uint32_t DEFAULT_MAX_CONNS_PER_MIN = 600;
|
||||
|
||||
/**
|
||||
* max banned destinations per local destination
|
||||
* TODO: make configurable
|
||||
*/
|
||||
const uint16_t MAX_BANNED_CONNS = 9999;
|
||||
/**
|
||||
* length of a ban in ms
|
||||
* TODO: make configurable
|
||||
*/
|
||||
const uint64_t DEFAULT_BAN_INTERVAL = 60 * 60 * 1000;
|
||||
|
||||
struct Packet
|
||||
{
|
||||
@@ -134,16 +152,20 @@ namespace stream
|
||||
size_t GetSendBufferSize () const { return m_SendBuffer.rdbuf ()->in_avail (); };
|
||||
int GetWindowSize () const { return m_WindowSize; };
|
||||
int GetRTT () const { return m_RTT; };
|
||||
|
||||
|
||||
/** don't call me */
|
||||
void Terminate ();
|
||||
|
||||
private:
|
||||
|
||||
void Terminate ();
|
||||
|
||||
void CleanUp ();
|
||||
|
||||
void SendBuffer ();
|
||||
void SendQuickAck ();
|
||||
void SendClose ();
|
||||
bool SendPacket (Packet * packet);
|
||||
void SendPackets (const std::vector<Packet *>& packets);
|
||||
void SendUpdatedLeaseSet ();
|
||||
|
||||
void SavePacket (Packet * packet);
|
||||
void ProcessPacket (Packet * packet);
|
||||
@@ -153,7 +175,7 @@ namespace stream
|
||||
void UpdateCurrentRemoteLease (bool expired = false);
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler);
|
||||
void HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout);
|
||||
|
||||
void ScheduleResend ();
|
||||
void HandleResendTimer (const boost::system::error_code& ecode);
|
||||
@@ -204,18 +226,34 @@ namespace stream
|
||||
void SetAcceptor (const Acceptor& acceptor);
|
||||
void ResetAcceptor ();
|
||||
bool IsAcceptorSet () const { return m_Acceptor != nullptr; };
|
||||
void AcceptOnce (const Acceptor& acceptor);
|
||||
|
||||
std::shared_ptr<i2p::client::ClientDestination> GetOwner () const { return m_Owner; };
|
||||
void SetOwner (std::shared_ptr<i2p::client::ClientDestination> owner) { m_Owner = owner; };
|
||||
uint16_t GetLocalPort () const { return m_LocalPort; };
|
||||
|
||||
void HandleDataMessagePayload (const uint8_t * buf, size_t len);
|
||||
std::shared_ptr<I2NPMessage> CreateDataMessage (const uint8_t * payload, size_t len, uint16_t toPort);
|
||||
|
||||
/** set max connections per minute per destination */
|
||||
void SetMaxConnsPerMinute(const uint32_t conns);
|
||||
|
||||
Packet * NewPacket () { return m_PacketsPool.Acquire (); };
|
||||
void DeletePacket (Packet * p) { if (p) m_PacketsPool.Release (p); };
|
||||
|
||||
private:
|
||||
|
||||
void HandleNextPacket (Packet * packet);
|
||||
std::shared_ptr<Stream> CreateNewIncomingStream ();
|
||||
void HandlePendingIncomingTimer (const boost::system::error_code& ecode);
|
||||
|
||||
/** handle cleaning up connection tracking for ratelimits */
|
||||
void HandleConnTrack(const boost::system::error_code& ecode);
|
||||
|
||||
bool DropNewStream(const i2p::data::IdentHash & ident);
|
||||
|
||||
void ScheduleConnTrack();
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<i2p::client::ClientDestination> m_Owner;
|
||||
@@ -224,9 +262,21 @@ namespace stream
|
||||
std::mutex m_StreamsMutex;
|
||||
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
|
||||
Acceptor m_Acceptor;
|
||||
uint32_t m_LastIncomingReceiveStreamID;
|
||||
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
|
||||
boost::asio::deadline_timer m_PendingIncomingTimer;
|
||||
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
|
||||
|
||||
std::mutex m_ConnsMutex;
|
||||
/** how many connections per minute did each identity have */
|
||||
std::map<i2p::data::IdentHash, uint32_t> m_Conns;
|
||||
boost::asio::deadline_timer m_ConnTrackTimer;
|
||||
uint32_t m_ConnsPerMinute;
|
||||
/** banned identities */
|
||||
std::vector<i2p::data::IdentHash> m_Banned;
|
||||
uint64_t m_LastBanClear;
|
||||
|
||||
i2p::util::MemoryPool<Packet> m_PacketsPool;
|
||||
|
||||
public:
|
||||
|
||||
@@ -246,18 +296,19 @@ namespace stream
|
||||
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);
|
||||
s->HandleReceiveTimer (boost::asio::error::make_error_code (boost::asio::error::operation_aborted), buffer, handler, 0);
|
||||
else
|
||||
{
|
||||
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(timeout));
|
||||
int t = (timeout > MAX_RECEIVE_TIMEOUT) ? MAX_RECEIVE_TIMEOUT : timeout;
|
||||
s->m_ReceiveTimer.expires_from_now (boost::posix_time::seconds(t));
|
||||
s->m_ReceiveTimer.async_wait ([=](const boost::system::error_code& ecode)
|
||||
{ s->HandleReceiveTimer (ecode, buffer, handler); });
|
||||
{ s->HandleReceiveTimer (ecode, buffer, handler, timeout - t); });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template<typename Buffer, typename ReceiveHandler>
|
||||
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler)
|
||||
void Stream::HandleReceiveTimer (const boost::system::error_code& ecode, const Buffer& buffer, ReceiveHandler handler, int remainingTimeout)
|
||||
{
|
||||
size_t received = ConcatenatePackets (boost::asio::buffer_cast<uint8_t *>(buffer), boost::asio::buffer_size(buffer));
|
||||
if (received > 0)
|
||||
@@ -271,8 +322,17 @@ namespace stream
|
||||
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);
|
||||
if (remainingTimeout <= 0)
|
||||
handler (boost::asio::error::make_error_code (boost::asio::error::timed_out), received);
|
||||
else
|
||||
{
|
||||
// itermediate iterrupt
|
||||
SendUpdatedLeaseSet (); // send our leaseset if applicable
|
||||
AsyncReceive (buffer, handler, remainingTimeout);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
135
Tag.h
135
Tag.h
@@ -1,3 +1,6 @@
|
||||
#ifndef TAG_H__
|
||||
#define TAG_H__
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013-2016, The PurpleI2P Project
|
||||
*
|
||||
@@ -6,84 +9,86 @@
|
||||
* See full license text in LICENSE file at top of project tree
|
||||
*/
|
||||
|
||||
#ifndef TAG_H__
|
||||
#define TAG_H__
|
||||
|
||||
#include <string.h> /* memcpy */
|
||||
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <string.h>
|
||||
#include <openssl/rand.h>
|
||||
#include "Base.h"
|
||||
|
||||
namespace i2p {
|
||||
namespace data {
|
||||
template<int sz>
|
||||
class Tag
|
||||
|
||||
template<size_t sz>
|
||||
class Tag
|
||||
{
|
||||
BOOST_STATIC_ASSERT_MSG(sz % 8 == 0, "Tag size must be multiple of 8 bytes");
|
||||
|
||||
public:
|
||||
|
||||
Tag () = default;
|
||||
Tag (const uint8_t * buf) { memcpy (m_Buf, buf, sz); }
|
||||
|
||||
bool operator== (const Tag& other) const { return !memcmp (m_Buf, other.m_Buf, sz); }
|
||||
bool operator< (const Tag& other) const { return memcmp (m_Buf, other.m_Buf, sz) < 0; }
|
||||
|
||||
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 uint8_t * data() const { return m_Buf; }
|
||||
const uint64_t * GetLL () const { return ll; }
|
||||
|
||||
bool IsZero () const
|
||||
{
|
||||
public:
|
||||
for (size_t i = 0; i < sz/8; ++i)
|
||||
if (ll[i]) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
void Fill(uint8_t c)
|
||||
{
|
||||
memset(m_Buf, c, sz);
|
||||
}
|
||||
|
||||
Tag<sz>& operator= (const Tag<sz>& ) = default;
|
||||
#ifndef _WIN32
|
||||
Tag<sz>& operator= (Tag<sz>&& ) = default;
|
||||
#endif
|
||||
void Randomize()
|
||||
{
|
||||
RAND_bytes(m_Buf, sz);
|
||||
}
|
||||
|
||||
std::string ToBase64 () const
|
||||
{
|
||||
char str[sz*2];
|
||||
size_t l = i2p::data::ByteStreamToBase64 (m_Buf, sz, str, sz*2);
|
||||
return std::string (str, str + l);
|
||||
}
|
||||
|
||||
uint8_t * operator()() { return m_Buf; };
|
||||
const uint8_t * operator()() const { return m_Buf; };
|
||||
std::string ToBase32 () const
|
||||
{
|
||||
char str[sz*2];
|
||||
size_t l = i2p::data::ByteStreamToBase32 (m_Buf, sz, str, sz*2);
|
||||
return std::string (str, str + l);
|
||||
}
|
||||
|
||||
operator uint8_t * () { return m_Buf; };
|
||||
operator const uint8_t * () const { return m_Buf; };
|
||||
void FromBase32 (const std::string& s)
|
||||
{
|
||||
i2p::data::Base32ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
||||
}
|
||||
|
||||
const uint64_t * GetLL () const { return ll; };
|
||||
void FromBase64 (const std::string& s)
|
||||
{
|
||||
i2p::data::Base64ToByteStream (s.c_str (), s.length (), m_Buf, sz);
|
||||
}
|
||||
|
||||
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; };
|
||||
private:
|
||||
|
||||
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];
|
||||
};
|
||||
union // 8 bytes aligned
|
||||
{
|
||||
uint8_t m_Buf[sz];
|
||||
uint64_t ll[sz/8];
|
||||
};
|
||||
};
|
||||
|
||||
} // data
|
||||
} // i2p
|
||||
|
||||
|
||||
66
Timestamp.cpp
Normal file
66
Timestamp.cpp
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <boost/asio.hpp>
|
||||
#include "Log.h"
|
||||
#include "I2PEndian.h"
|
||||
#include "Timestamp.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#ifndef _WIN64
|
||||
#define _USE_32BIT_TIME_T
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
static int64_t g_TimeOffset = 0; // in seconds
|
||||
|
||||
void SyncTimeWithNTP (const std::string& address)
|
||||
{
|
||||
boost::asio::io_service service;
|
||||
boost::asio::ip::udp::resolver::query query (boost::asio::ip::udp::v4 (), address, "ntp");
|
||||
boost::system::error_code ec;
|
||||
auto it = boost::asio::ip::udp::resolver (service).resolve (query, ec);
|
||||
if (!ec && it != boost::asio::ip::udp::resolver::iterator())
|
||||
{
|
||||
auto ep = (*it).endpoint (); // take first one
|
||||
boost::asio::ip::udp::socket socket (service);
|
||||
socket.open (boost::asio::ip::udp::v4 (), ec);
|
||||
if (!ec)
|
||||
{
|
||||
uint8_t buf[48];// 48 bytes NTP request/response
|
||||
memset (buf, 0, 48);
|
||||
htobe32buf (buf, (3 << 27) | (3 << 24)); // RFC 4330
|
||||
size_t len = 0;
|
||||
try
|
||||
{
|
||||
socket.send_to (boost::asio::buffer (buf, 48), ep);
|
||||
int i = 0;
|
||||
while (!socket.available() && i < 10) // 10 seconds max
|
||||
{
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1));
|
||||
i++;
|
||||
}
|
||||
if (socket.available ())
|
||||
len = socket.receive_from (boost::asio::buffer (buf, 48), ep);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
LogPrint (eLogError, "NTP error: ", e.what ());
|
||||
}
|
||||
if (len >= 8)
|
||||
{
|
||||
auto ourTs = GetSecondsSinceEpoch ();
|
||||
uint32_t ts = bufbe32toh (buf + 32);
|
||||
if (ts > 2208988800U) ts -= 2208988800U; // 1/1/1970 from 1/1/1900
|
||||
g_TimeOffset = ts - ourTs;
|
||||
LogPrint (eLogInfo, address, " time offset from system time is ", g_TimeOffset, " seconds");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,8 @@ namespace tunnel
|
||||
TransitTunnel (receiveTunnelID, nextIdent, nextTunnelID, layerKey, ivKey),
|
||||
m_Endpoint (false) {}; // transit endpoint is always outbound
|
||||
|
||||
void Cleanup () { m_Endpoint.Cleanup (); }
|
||||
|
||||
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
|
||||
size_t GetNumTransmittedBytes () const { return m_Endpoint.GetNumReceivedBytes (); }
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "Crypto.h"
|
||||
#include "RouterInfo.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Timestamp.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
@@ -54,7 +55,8 @@ namespace transport
|
||||
public:
|
||||
|
||||
TransportSession (std::shared_ptr<const i2p::data::RouterInfo> router, int terminationTimeout):
|
||||
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout)
|
||||
m_DHKeysPair (nullptr), m_NumSentBytes (0), m_NumReceivedBytes (0), m_IsOutgoing (router), m_TerminationTimeout (terminationTimeout),
|
||||
m_LastActivityTimestamp (i2p::util::GetSecondsSinceEpoch ())
|
||||
{
|
||||
if (router)
|
||||
m_RemoteIdentity = router->GetRouterIdentity ();
|
||||
@@ -62,6 +64,8 @@ namespace transport
|
||||
|
||||
virtual ~TransportSession () {};
|
||||
virtual void Done () = 0;
|
||||
|
||||
std::string GetIdentHashBase64() const { return m_RemoteIdentity ? m_RemoteIdentity->GetIdentHash().ToBase64() : ""; }
|
||||
|
||||
std::shared_ptr<const i2p::data::IdentityEx> GetRemoteIdentity () { return m_RemoteIdentity; };
|
||||
void SetRemoteIdentity (std::shared_ptr<const i2p::data::IdentityEx> ident) { m_RemoteIdentity = ident; };
|
||||
@@ -72,6 +76,8 @@ namespace transport
|
||||
|
||||
int GetTerminationTimeout () const { return m_TerminationTimeout; };
|
||||
void SetTerminationTimeout (int terminationTimeout) { m_TerminationTimeout = terminationTimeout; };
|
||||
bool IsTerminationTimeoutExpired (uint64_t ts) const
|
||||
{ return ts >= m_LastActivityTimestamp + GetTerminationTimeout (); };
|
||||
|
||||
virtual void SendI2NPMessages (const std::vector<std::shared_ptr<I2NPMessage> >& msgs) = 0;
|
||||
|
||||
@@ -82,6 +88,7 @@ namespace transport
|
||||
size_t m_NumSentBytes, m_NumReceivedBytes;
|
||||
bool m_IsOutgoing;
|
||||
int m_TerminationTimeout;
|
||||
uint64_t m_LastActivityTimestamp;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
252
Transports.cpp
252
Transports.cpp
@@ -4,6 +4,11 @@
|
||||
#include "I2NPProtocol.h"
|
||||
#include "NetDb.h"
|
||||
#include "Transports.h"
|
||||
#include "Config.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#include "util.h"
|
||||
#endif
|
||||
|
||||
using namespace i2p::data;
|
||||
|
||||
@@ -43,11 +48,22 @@ namespace transport
|
||||
{
|
||||
while (m_IsRunning)
|
||||
{
|
||||
int num;
|
||||
while ((num = m_QueueSize - m_Queue.size ()) > 0)
|
||||
int num, total = 0;
|
||||
while ((num = m_QueueSize - (int)m_Queue.size ()) > 0 && total < 20)
|
||||
{
|
||||
CreateDHKeysPairs (num);
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Acquired.wait (l); // wait for element gets aquired
|
||||
total += num;
|
||||
}
|
||||
if (total >= 20)
|
||||
{
|
||||
LogPrint (eLogWarning, "Transports: ", total, " DH keys generated at the time");
|
||||
std::this_thread::sleep_for (std::chrono::seconds(1)); // take a break
|
||||
}
|
||||
else
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Acquired.wait (l); // wait for element gets aquired
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +71,6 @@ namespace transport
|
||||
{
|
||||
if (num > 0)
|
||||
{
|
||||
i2p::crypto::DHKeys dh;
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
auto pair = std::make_shared<i2p::crypto::DHKeys> ();
|
||||
@@ -86,14 +101,16 @@ namespace transport
|
||||
|
||||
void DHKeysPairSupplier::Return (std::shared_ptr<i2p::crypto::DHKeys> pair)
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_AcquiredMutex);
|
||||
m_Queue.push (pair);
|
||||
std::unique_lock<std::mutex>l(m_AcquiredMutex);
|
||||
if ((int)m_Queue.size () < 2*m_QueueSize)
|
||||
m_Queue.push (pair);
|
||||
}
|
||||
|
||||
Transports transports;
|
||||
|
||||
Transports::Transports ():
|
||||
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Work (m_Service), m_PeerCleanupTimer (m_Service),
|
||||
m_IsOnline (true), m_IsRunning (false), m_Thread (nullptr), m_Service (nullptr),
|
||||
m_Work (nullptr), m_PeerCleanupTimer (nullptr), m_PeerTestTimer (nullptr),
|
||||
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)
|
||||
@@ -103,10 +120,25 @@ namespace transport
|
||||
Transports::~Transports ()
|
||||
{
|
||||
Stop ();
|
||||
if (m_Service)
|
||||
{
|
||||
delete m_PeerCleanupTimer; m_PeerCleanupTimer = nullptr;
|
||||
delete m_PeerTestTimer; m_PeerTestTimer = nullptr;
|
||||
delete m_Work; m_Work = nullptr;
|
||||
delete m_Service; m_Service = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Transports::Start (bool enableNTCP, bool enableSSU)
|
||||
{
|
||||
if (!m_Service)
|
||||
{
|
||||
m_Service = new boost::asio::io_service ();
|
||||
m_Work = new boost::asio::io_service::work (*m_Service);
|
||||
m_PeerCleanupTimer = new boost::asio::deadline_timer (*m_Service);
|
||||
m_PeerTestTimer = new boost::asio::deadline_timer (*m_Service);
|
||||
}
|
||||
|
||||
m_DHKeysPairSupplier.Start ();
|
||||
m_IsRunning = true;
|
||||
m_Thread = new std::thread (std::bind (&Transports::Run, this));
|
||||
@@ -114,6 +146,7 @@ namespace transport
|
||||
auto& addresses = context.GetRouterInfo ().GetAddresses ();
|
||||
for (const auto& address : addresses)
|
||||
{
|
||||
if (!address) continue;
|
||||
if (m_NTCPServer == nullptr && enableNTCP)
|
||||
{
|
||||
m_NTCPServer = new NTCPServer ();
|
||||
@@ -150,13 +183,16 @@ namespace transport
|
||||
LogPrint (eLogError, "Transports: 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));
|
||||
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));
|
||||
m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
|
||||
m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
void Transports::Stop ()
|
||||
{
|
||||
m_PeerCleanupTimer.cancel ();
|
||||
if (m_PeerCleanupTimer) m_PeerCleanupTimer->cancel ();
|
||||
if (m_PeerTestTimer) m_PeerTestTimer->cancel ();
|
||||
m_Peers.clear ();
|
||||
if (m_SSUServer)
|
||||
{
|
||||
@@ -173,7 +209,7 @@ namespace transport
|
||||
|
||||
m_DHKeysPairSupplier.Stop ();
|
||||
m_IsRunning = false;
|
||||
m_Service.stop ();
|
||||
if (m_Service) m_Service->stop ();
|
||||
if (m_Thread)
|
||||
{
|
||||
m_Thread->join ();
|
||||
@@ -184,11 +220,11 @@ namespace transport
|
||||
|
||||
void Transports::Run ()
|
||||
{
|
||||
while (m_IsRunning)
|
||||
while (m_IsRunning && m_Service)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
m_Service->run ();
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
@@ -228,7 +264,10 @@ namespace transport
|
||||
|
||||
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));
|
||||
#ifdef WITH_EVENTS
|
||||
QueueIntEvent("transport.send", ident.ToBase64(), msgs.size());
|
||||
#endif
|
||||
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)
|
||||
@@ -237,9 +276,11 @@ namespace transport
|
||||
{
|
||||
// we send it to ourself
|
||||
for (auto& it: msgs)
|
||||
i2p::HandleI2NPMessage (it);
|
||||
m_LoopbackHandler.PutNextMessage (it);
|
||||
m_LoopbackHandler.Flush ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) return;
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it == m_Peers.end ())
|
||||
{
|
||||
@@ -344,7 +385,7 @@ namespace transport
|
||||
}
|
||||
}
|
||||
}
|
||||
LogPrint (eLogError, "Transports: No NTCP or SSU addresses available");
|
||||
LogPrint (eLogInfo, "Transports: No NTCP or SSU addresses available");
|
||||
peer.Done ();
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (ident);
|
||||
@@ -361,7 +402,7 @@ namespace transport
|
||||
|
||||
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));
|
||||
m_Service->post (std::bind (&Transports::HandleRequestComplete, this, r, ident));
|
||||
}
|
||||
|
||||
void Transports::HandleRequestComplete (std::shared_ptr<const i2p::data::RouterInfo> r, i2p::data::IdentHash ident)
|
||||
@@ -377,7 +418,7 @@ namespace transport
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint (eLogError, "Transports: RouterInfo not found, Failed to send messages");
|
||||
LogPrint (eLogWarning, "Transports: RouterInfo not found, Failed to send messages");
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
m_Peers.erase (it);
|
||||
}
|
||||
@@ -386,7 +427,7 @@ namespace transport
|
||||
|
||||
void Transports::NTCPResolve (const std::string& addr, const i2p::data::IdentHash& ident)
|
||||
{
|
||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
|
||||
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));
|
||||
@@ -429,7 +470,7 @@ namespace transport
|
||||
|
||||
void Transports::SSUResolve (const std::string& addr, const i2p::data::IdentHash& ident)
|
||||
{
|
||||
auto resolver = std::make_shared<boost::asio::ip::tcp::resolver>(m_Service);
|
||||
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::HandleSSUResolve, this,
|
||||
std::placeholders::_1, std::placeholders::_2, ident, resolver));
|
||||
@@ -472,7 +513,7 @@ namespace transport
|
||||
void Transports::CloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
||||
{
|
||||
if (!router) return;
|
||||
m_Service.post (std::bind (&Transports::PostCloseSession, this, router));
|
||||
m_Service->post (std::bind (&Transports::PostCloseSession, this, router));
|
||||
}
|
||||
|
||||
void Transports::PostCloseSession (std::shared_ptr<const i2p::data::RouterInfo> router)
|
||||
@@ -493,17 +534,23 @@ namespace transport
|
||||
|
||||
void Transports::DetectExternalIP ()
|
||||
{
|
||||
if (RoutesRestricted())
|
||||
{
|
||||
LogPrint(eLogInfo, "Transports: restricted routes enabled, not detecting ip");
|
||||
i2p::context.SetStatus (eRouterStatusOK);
|
||||
return;
|
||||
}
|
||||
if (m_SSUServer)
|
||||
{
|
||||
#ifndef MESHNET
|
||||
i2p::context.SetStatus (eRouterStatusTesting);
|
||||
#endif
|
||||
|
||||
bool nat; i2p::config::GetOption("nat", nat);
|
||||
bool isv4 = i2p::context.SupportsV4 ();
|
||||
if (nat && isv4)
|
||||
i2p::context.SetStatus (eRouterStatusTesting);
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
|
||||
if (router && router->IsSSU (!context.SupportsV6 ()))
|
||||
m_SSUServer->CreateSession (router, true); // peer test
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (isv4); // v4 only if v4
|
||||
if (router)
|
||||
m_SSUServer->CreateSession (router, true, isv4); // peer test
|
||||
else
|
||||
{
|
||||
// if not peer test capable routers found pick any
|
||||
@@ -519,22 +566,25 @@ namespace transport
|
||||
|
||||
void Transports::PeerTest ()
|
||||
{
|
||||
if (RoutesRestricted() || !i2p::context.SupportsV4 ()) return;
|
||||
if (m_SSUServer)
|
||||
{
|
||||
{
|
||||
bool statusChanged = false;
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter ();
|
||||
if (router && router->IsSSU (!context.SupportsV6 ()))
|
||||
auto router = i2p::data::netdb.GetRandomPeerTestRouter (true); // v4 only
|
||||
if (router)
|
||||
{
|
||||
if (!statusChanged)
|
||||
{
|
||||
statusChanged = true;
|
||||
i2p::context.SetStatus (eRouterStatusTesting); // first time only
|
||||
}
|
||||
m_SSUServer->CreateSession (router, true); // peer test
|
||||
m_SSUServer->CreateSession (router, true, true); // peer test v4
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!statusChanged)
|
||||
LogPrint (eLogWarning, "Can't find routers for peer test");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,7 +600,7 @@ namespace transport
|
||||
|
||||
void Transports::PeerConnected (std::shared_ptr<TransportSession> session)
|
||||
{
|
||||
m_Service.post([session, this]()
|
||||
m_Service->post([session, this]()
|
||||
{
|
||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
||||
if (!remoteIdentity) return;
|
||||
@@ -558,6 +608,9 @@ namespace transport
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "false"}});
|
||||
#endif
|
||||
bool sendDatabaseStore = true;
|
||||
if (it->second.delayedMessages.size () > 0)
|
||||
{
|
||||
@@ -577,20 +630,32 @@ namespace transport
|
||||
}
|
||||
else // incoming connection
|
||||
{
|
||||
if(RoutesRestricted() && ! IsRestrictedPeer(ident)) {
|
||||
// not trusted
|
||||
LogPrint(eLogWarning, "Transports: closing untrusted inbound connection from ", ident.ToBase64());
|
||||
session->Done();
|
||||
return;
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type" , "transport.connected"}, {"ident", ident.ToBase64()}, {"inbound", "true"}});
|
||||
#endif
|
||||
session->SendI2NPMessages ({ CreateDatabaseStoreMsg () }); // send DatabaseStore
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
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]()
|
||||
{
|
||||
m_Service->post([session, this]()
|
||||
{
|
||||
auto remoteIdentity = session->GetRemoteIdentity ();
|
||||
if (!remoteIdentity) return;
|
||||
auto ident = remoteIdentity->GetIdentHash ();
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type" , "transport.disconnected"}, {"ident", ident.ToBase64()}});
|
||||
#endif
|
||||
auto it = m_Peers.find (ident);
|
||||
if (it != m_Peers.end ())
|
||||
{
|
||||
@@ -630,7 +695,7 @@ namespace transport
|
||||
if (profile)
|
||||
{
|
||||
profile->TunnelNonReplied();
|
||||
profile->Save();
|
||||
profile->Save(it->first);
|
||||
}
|
||||
std::unique_lock<std::mutex> l(m_PeersMutex);
|
||||
it = m_Peers.erase (it);
|
||||
@@ -641,11 +706,21 @@ namespace transport
|
||||
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));
|
||||
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::HandlePeerTestTimer (const boost::system::error_code& ecode)
|
||||
{
|
||||
if (ecode != boost::asio::error::operation_aborted)
|
||||
{
|
||||
PeerTest ();
|
||||
m_PeerTestTimer->expires_from_now (boost::posix_time::minutes(PEER_TEST_INTERVAL));
|
||||
m_PeerTestTimer->async_wait (std::bind (&Transports::HandlePeerTestTimer, this, std::placeholders::_1));
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRandomPeer () const
|
||||
{
|
||||
if (m_Peers.empty ()) return nullptr;
|
||||
@@ -654,30 +729,79 @@ namespace transport
|
||||
std::advance (it, rand () % m_Peers.size ());
|
||||
return it != m_Peers.end () ? it->second.router : nullptr;
|
||||
}
|
||||
void Transports::RestrictRoutes(std::vector<std::string> families)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||
m_TrustedFamilies.clear();
|
||||
for ( const auto& fam : families )
|
||||
m_TrustedFamilies.push_back(fam);
|
||||
}
|
||||
void Transports::RestrictRoutesToFamilies(std::set<std::string> families)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||
m_TrustedFamilies.clear();
|
||||
for ( const auto& fam : families )
|
||||
m_TrustedFamilies.push_back(fam);
|
||||
}
|
||||
|
||||
bool Transports::RoutesRestricted() const {
|
||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||
return m_TrustedFamilies.size() > 0;
|
||||
}
|
||||
void Transports::RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_TrustedRoutersMutex);
|
||||
m_TrustedRouters.clear();
|
||||
for (const auto & ri : routers )
|
||||
m_TrustedRouters.push_back(ri);
|
||||
}
|
||||
|
||||
bool Transports::RoutesRestricted() const {
|
||||
std::unique_lock<std::mutex> famlock(m_FamilyMutex);
|
||||
std::unique_lock<std::mutex> routerslock(m_TrustedRoutersMutex);
|
||||
return m_TrustedFamilies.size() > 0 || m_TrustedRouters.size() > 0;
|
||||
}
|
||||
|
||||
/** XXX: if routes are not restricted this dies */
|
||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const {
|
||||
std::string fam;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_FamilyMutex);
|
||||
// TODO: random family (?)
|
||||
fam = m_TrustedFamilies[0];
|
||||
}
|
||||
boost::to_lower(fam);
|
||||
return i2p::data::netdb.GetRandomRouterInFamily(fam);
|
||||
}
|
||||
/** XXX: if routes are not restricted this dies */
|
||||
std::shared_ptr<const i2p::data::RouterInfo> Transports::GetRestrictedPeer() const
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> l(m_FamilyMutex);
|
||||
std::string fam;
|
||||
auto sz = m_TrustedFamilies.size();
|
||||
if(sz > 1)
|
||||
{
|
||||
auto it = m_TrustedFamilies.begin ();
|
||||
std::advance(it, rand() % sz);
|
||||
fam = *it;
|
||||
boost::to_lower(fam);
|
||||
}
|
||||
else if (sz == 1)
|
||||
{
|
||||
fam = m_TrustedFamilies[0];
|
||||
}
|
||||
if (fam.size())
|
||||
return i2p::data::netdb.GetRandomRouterInFamily(fam);
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
|
||||
auto sz = m_TrustedRouters.size();
|
||||
if (sz)
|
||||
{
|
||||
if(sz == 1)
|
||||
return i2p::data::netdb.FindRouter(m_TrustedRouters[0]);
|
||||
auto it = m_TrustedRouters.begin();
|
||||
std::advance(it, rand() % sz);
|
||||
return i2p::data::netdb.FindRouter(*it);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Transports::IsRestrictedPeer(const i2p::data::IdentHash & ih) const
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_TrustedRoutersMutex);
|
||||
for (const auto & r : m_TrustedRouters )
|
||||
if ( r == ih ) return true;
|
||||
}
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_FamilyMutex);
|
||||
auto ri = i2p::data::netdb.FindRouter(ih);
|
||||
for (const auto & fam : m_TrustedFamilies)
|
||||
if(ri->IsFamily(fam)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
30
Transports.h
30
Transports.h
@@ -66,6 +66,7 @@ namespace transport
|
||||
};
|
||||
|
||||
const size_t SESSION_CREATION_TIMEOUT = 10; // in seconds
|
||||
const int PEER_TEST_INTERVAL = 71; // in minutes
|
||||
const int MAX_NUM_DELAYED_MESSAGES = 50;
|
||||
class Transports
|
||||
{
|
||||
@@ -83,7 +84,7 @@ namespace transport
|
||||
bool IsOnline() const { return m_IsOnline; };
|
||||
void SetOnline (bool online) { m_IsOnline = online; };
|
||||
|
||||
boost::asio::io_service& GetService () { return m_Service; };
|
||||
boost::asio::io_service& GetService () { return *m_Service; };
|
||||
std::shared_ptr<i2p::crypto::DHKeys> GetNextDHKeysPair ();
|
||||
void ReuseDHKeysPair (std::shared_ptr<i2p::crypto::DHKeys> pair);
|
||||
|
||||
@@ -110,7 +111,11 @@ namespace transport
|
||||
/** do we want to use restricted routes? */
|
||||
bool RoutesRestricted() const;
|
||||
/** restrict routes to use only these router families for first hops */
|
||||
void RestrictRoutes(std::vector<std::string> families);
|
||||
void RestrictRoutesToFamilies(std::set<std::string> families);
|
||||
/** restrict routes to use only these routers for first hops */
|
||||
void RestrictRoutesToRouters(std::set<i2p::data::IdentHash> routers);
|
||||
|
||||
bool IsRestrictedPeer(const i2p::data::IdentHash & ident) const;
|
||||
|
||||
void PeerTest ();
|
||||
|
||||
@@ -123,7 +128,8 @@ namespace transport
|
||||
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 HandlePeerTestTimer (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);
|
||||
@@ -138,9 +144,9 @@ namespace transport
|
||||
|
||||
bool m_IsOnline, 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;
|
||||
boost::asio::io_service * m_Service;
|
||||
boost::asio::io_service::work * m_Work;
|
||||
boost::asio::deadline_timer * m_PeerCleanupTimer, * m_PeerTestTimer;
|
||||
|
||||
NTCPServer * m_NTCPServer;
|
||||
SSUServer * m_SSUServer;
|
||||
@@ -154,9 +160,15 @@ namespace transport
|
||||
uint64_t m_LastInBandwidthUpdateBytes, m_LastOutBandwidthUpdateBytes;
|
||||
uint64_t m_LastBandwidthUpdateTime;
|
||||
|
||||
/** which router families to trust for first hops */
|
||||
std::vector<std::string> m_TrustedFamilies;
|
||||
mutable std::mutex m_FamilyMutex;
|
||||
/** which router families to trust for first hops */
|
||||
std::vector<std::string> m_TrustedFamilies;
|
||||
mutable std::mutex m_FamilyMutex;
|
||||
|
||||
/** which routers for first hop to trust */
|
||||
std::vector<i2p::data::IdentHash> m_TrustedRouters;
|
||||
mutable std::mutex m_TrustedRoutersMutex;
|
||||
|
||||
i2p::I2NPMessagesHandler m_LoopbackHandler;
|
||||
|
||||
public:
|
||||
|
||||
|
||||
73
Tunnel.cpp
73
Tunnel.cpp
@@ -11,15 +11,19 @@
|
||||
#include "Transports.h"
|
||||
#include "NetDb.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
|
||||
{
|
||||
Tunnel::Tunnel (std::shared_ptr<const TunnelConfig> config):
|
||||
TunnelBase (config->GetTunnelID (), config->GetNextTunnelID (), config->GetNextIdentHash ()),
|
||||
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false)
|
||||
m_Config (config), m_Pool (nullptr), m_State (eTunnelStatePending), m_IsRecreated (false),
|
||||
m_Latency (0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -29,12 +33,14 @@ namespace tunnel
|
||||
|
||||
void Tunnel::Build (uint32_t replyMsgID, std::shared_ptr<OutboundTunnel> outboundTunnel)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
std::string peers = i2p::context.GetIdentity()->GetIdentHash().ToBase64();
|
||||
#endif
|
||||
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);
|
||||
@@ -55,8 +61,14 @@ namespace tunnel
|
||||
hop->CreateBuildRequestRecord (records + idx*TUNNEL_BUILD_RECORD_SIZE, msgID);
|
||||
hop->recordIndex = idx;
|
||||
i++;
|
||||
#ifdef WITH_EVENTS
|
||||
peers += ":" + hop->ident->GetIdentHash().ToBase64();
|
||||
#endif
|
||||
hop = hop->next;
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.build", this, peers);
|
||||
#endif
|
||||
// fill up fake records with random data
|
||||
for (int i = numHops; i < numRecords; i++)
|
||||
{
|
||||
@@ -150,6 +162,12 @@ namespace tunnel
|
||||
return established;
|
||||
}
|
||||
|
||||
bool Tunnel::LatencyFitsRange(uint64_t lower, uint64_t upper) const
|
||||
{
|
||||
auto latency = GetMeanLatency();
|
||||
return latency >= lower && latency <= upper;
|
||||
}
|
||||
|
||||
void Tunnel::EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out)
|
||||
{
|
||||
const uint8_t * inPayload = in->GetPayload () + 4;
|
||||
@@ -182,12 +200,22 @@ namespace tunnel
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Tunnel::SetState(TunnelState state)
|
||||
{
|
||||
m_State = state;
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.state", this, state);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Tunnel::PrintHops (std::stringstream& s) const
|
||||
{
|
||||
for (auto& it: m_Hops)
|
||||
// hops are in inverted order, we must print in direct order
|
||||
for (auto it = m_Hops.rbegin (); it != m_Hops.rend (); it++)
|
||||
{
|
||||
s << " ⇒ ";
|
||||
s << i2p::data::GetIdentHashAbbreviation (it->ident->GetIdentHash ());
|
||||
s << i2p::data::GetIdentHashAbbreviation ((*it)->ident->GetIdentHash ());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,6 +587,7 @@ namespace tunnel
|
||||
for (auto it = pendingTunnels.begin (); it != pendingTunnels.end ();)
|
||||
{
|
||||
auto tunnel = it->second;
|
||||
auto pool = tunnel->GetTunnelPool();
|
||||
switch (tunnel->GetState ())
|
||||
{
|
||||
case eTunnelStatePending:
|
||||
@@ -581,6 +610,11 @@ namespace tunnel
|
||||
hop = hop->next;
|
||||
}
|
||||
}
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
|
||||
#endif
|
||||
// for i2lua
|
||||
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultTimeout);
|
||||
// delete
|
||||
it = pendingTunnels.erase (it);
|
||||
m_NumFailedTunnelCreations++;
|
||||
@@ -590,6 +624,12 @@ namespace tunnel
|
||||
break;
|
||||
case eTunnelStateBuildFailed:
|
||||
LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted");
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnel.state", tunnel.get(), eTunnelStateBuildFailed);
|
||||
#endif
|
||||
// for i2lua
|
||||
if(pool) pool->OnTunnelBuildResult(tunnel, eBuildResultRejected);
|
||||
|
||||
it = pendingTunnels.erase (it);
|
||||
m_NumFailedTunnelCreations++;
|
||||
break;
|
||||
@@ -640,11 +680,13 @@ namespace tunnel
|
||||
}
|
||||
}
|
||||
|
||||
if (m_OutboundTunnels.size () < 5)
|
||||
if (m_OutboundTunnels.size () < 3)
|
||||
{
|
||||
// trying to create one more oubound tunnel
|
||||
auto inboundTunnel = GetNextInboundTunnel ();
|
||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
||||
auto router = i2p::transport::transports.RoutesRestricted() ?
|
||||
i2p::transport::transports.GetRestrictedPeer() :
|
||||
i2p::data::netdb.GetRandomRouter ();
|
||||
if (!inboundTunnel || !router) return;
|
||||
LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel");
|
||||
CreateTunnel<OutboundTunnel> (
|
||||
@@ -684,6 +726,8 @@ namespace tunnel
|
||||
|
||||
if (ts + TUNNEL_EXPIRATION_THRESHOLD > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT)
|
||||
tunnel->SetState (eTunnelStateExpiring);
|
||||
else // we don't need to cleanup expiring tunnels
|
||||
tunnel->Cleanup ();
|
||||
}
|
||||
it++;
|
||||
}
|
||||
@@ -697,16 +741,18 @@ namespace tunnel
|
||||
CreateZeroHopsOutboundTunnel ();
|
||||
if (!m_ExploratoryPool)
|
||||
{
|
||||
m_ExploratoryPool = CreateTunnelPool (2, 2, 5, 5); // 2-hop exploratory, 5 tunnels
|
||||
m_ExploratoryPool = CreateTunnelPool (2, 2, 3, 3); // 2-hop exploratory, 3 tunnels
|
||||
m_ExploratoryPool->SetLocalDestination (i2p::context.GetSharedDestination ());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 5)
|
||||
if (m_OutboundTunnels.empty () || m_InboundTunnels.size () < 3)
|
||||
{
|
||||
// trying to create one more inbound tunnel
|
||||
auto router = i2p::data::netdb.GetRandomRouter ();
|
||||
auto router = i2p::transport::transports.RoutesRestricted() ?
|
||||
i2p::transport::transports.GetRestrictedPeer() :
|
||||
i2p::data::netdb.GetRandomRouter ();
|
||||
if (!router) {
|
||||
LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel");
|
||||
return;
|
||||
@@ -731,7 +777,10 @@ namespace tunnel
|
||||
it = m_TransitTunnels.erase (it);
|
||||
}
|
||||
else
|
||||
{
|
||||
tunnel->Cleanup ();
|
||||
it++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -771,7 +820,7 @@ namespace tunnel
|
||||
|
||||
std::shared_ptr<InboundTunnel> Tunnels::CreateInboundTunnel (std::shared_ptr<TunnelConfig> config, std::shared_ptr<OutboundTunnel> outboundTunnel)
|
||||
{
|
||||
if (config)
|
||||
if (config)
|
||||
return CreateTunnel<InboundTunnel>(config, outboundTunnel);
|
||||
else
|
||||
return CreateZeroHopsInboundTunnel ();
|
||||
|
||||
62
Tunnel.h
62
Tunnel.h
@@ -19,11 +19,49 @@
|
||||
#include "TunnelGateway.h"
|
||||
#include "TunnelBase.h"
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Event.h"
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
{
|
||||
|
||||
template<typename TunnelT>
|
||||
static void EmitTunnelEvent(const std::string & ev, const TunnelT & t)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}});
|
||||
#else
|
||||
(void) ev;
|
||||
(void) t;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename TunnelT, typename T>
|
||||
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const T & val)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", std::to_string(val)}, {"inbound", std::to_string(t->IsInbound())}});
|
||||
#else
|
||||
(void) ev;
|
||||
(void) t;
|
||||
(void) val;
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename TunnelT>
|
||||
static void EmitTunnelEvent(const std::string & ev, TunnelT * t, const std::string & val)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitEvent({{"type", ev}, {"tid", std::to_string(t->GetTunnelID())}, {"value", val}, {"inbound", std::to_string(t->IsInbound())}});
|
||||
#else
|
||||
(void) ev;
|
||||
(void) t;
|
||||
(void) val;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@@ -62,12 +100,13 @@ namespace tunnel
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetPeers () const;
|
||||
std::vector<std::shared_ptr<const i2p::data::IdentityEx> > GetInvertedPeers () const;
|
||||
TunnelState GetState () const { return m_State; };
|
||||
void SetState (TunnelState state) { m_State = state; };
|
||||
void SetState (TunnelState 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; };
|
||||
|
||||
virtual bool IsInbound() const = 0;
|
||||
|
||||
std::shared_ptr<TunnelPool> GetTunnelPool () const { return m_Pool; };
|
||||
void SetTunnelPool (std::shared_ptr<TunnelPool> pool) { m_Pool = pool; };
|
||||
|
||||
@@ -79,6 +118,14 @@ namespace tunnel
|
||||
void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg);
|
||||
void EncryptTunnelMsg (std::shared_ptr<const I2NPMessage> in, std::shared_ptr<I2NPMessage> out);
|
||||
|
||||
/** @brief add latency sample */
|
||||
void AddLatencySample(const uint64_t ms) { m_Latency = (m_Latency + ms) >> 1; }
|
||||
/** @brief get this tunnel's estimated latency */
|
||||
uint64_t GetMeanLatency() const { return m_Latency; }
|
||||
/** @breif return true if this tunnel's latency fits in range [lowerbound, upperbound] */
|
||||
bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const;
|
||||
|
||||
bool LatencyIsKnown() const { return m_Latency > 0; }
|
||||
protected:
|
||||
|
||||
void PrintHops (std::stringstream& s) const;
|
||||
@@ -90,6 +137,7 @@ namespace tunnel
|
||||
std::shared_ptr<TunnelPool> m_Pool; // pool, tunnel belongs to, or null
|
||||
TunnelState m_State;
|
||||
bool m_IsRecreated;
|
||||
uint64_t m_Latency; // in milliseconds
|
||||
};
|
||||
|
||||
class OutboundTunnel: public Tunnel
|
||||
@@ -107,6 +155,8 @@ namespace tunnel
|
||||
|
||||
// implements TunnelBase
|
||||
void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg);
|
||||
|
||||
bool IsInbound() const { return false; }
|
||||
|
||||
private:
|
||||
|
||||
@@ -123,7 +173,11 @@ namespace tunnel
|
||||
void HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg);
|
||||
virtual size_t GetNumReceivedBytes () const { return m_Endpoint.GetNumReceivedBytes (); };
|
||||
void Print (std::stringstream& s) const;
|
||||
|
||||
bool IsInbound() const { return true; }
|
||||
|
||||
// override TunnelBase
|
||||
void Cleanup () { m_Endpoint.Cleanup (); };
|
||||
|
||||
private:
|
||||
|
||||
TunnelEndpoint m_Endpoint;
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace tunnel
|
||||
m_TunnelID (tunnelID), m_NextTunnelID (nextTunnelID), m_NextIdent (nextIdent),
|
||||
m_CreationTime (i2p::util::GetSecondsSinceEpoch ()) {};
|
||||
virtual ~TunnelBase () {};
|
||||
virtual void Cleanup () {};
|
||||
|
||||
virtual void HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg) = 0;
|
||||
virtual void SendTunnelDataMsg (std::shared_ptr<i2p::I2NPMessage> msg) = 0;
|
||||
@@ -48,7 +49,7 @@ namespace tunnel
|
||||
|
||||
uint32_t GetCreationTime () const { return m_CreationTime; };
|
||||
void SetCreationTime (uint32_t t) { m_CreationTime = t; };
|
||||
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_TunnelID, m_NextTunnelID;
|
||||
|
||||
@@ -101,8 +101,7 @@ namespace tunnel
|
||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_REQUEST_TIME_OFFSET, i2p::util::GetHoursSinceEpoch ());
|
||||
htobe32buf (clearText + BUILD_REQUEST_RECORD_SEND_MSG_ID_OFFSET, replyMsgID);
|
||||
RAND_bytes (clearText + BUILD_REQUEST_RECORD_PADDING_OFFSET, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE - BUILD_REQUEST_RECORD_PADDING_OFFSET);
|
||||
i2p::crypto::ElGamalEncryption elGamalEncryption (ident->GetEncryptionPublicKey ());
|
||||
elGamalEncryption.Encrypt (clearText, BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
|
||||
i2p::crypto::ElGamalEncrypt (ident->GetEncryptionPublicKey (), clearText, record + BUILD_REQUEST_RECORD_ENCRYPTED_OFFSET);
|
||||
memcpy (record + BUILD_REQUEST_RECORD_TO_PEER_OFFSET, (const uint8_t *)ident->GetIdentHash (), 16);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "I2NPProtocol.h"
|
||||
#include "Transports.h"
|
||||
#include "RouterContext.h"
|
||||
#include "Timestamp.h"
|
||||
#include "TunnelEndpoint.h"
|
||||
|
||||
namespace i2p
|
||||
@@ -115,9 +116,10 @@ namespace tunnel
|
||||
if (!isFollowOnFragment) // create new incomlete message
|
||||
{
|
||||
m.nextFragmentNum = 1;
|
||||
m.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
|
||||
if (ret.second)
|
||||
HandleOutOfSequenceFragment (msgID, ret.first->second);
|
||||
HandleOutOfSequenceFragments (msgID, ret.first->second);
|
||||
else
|
||||
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
|
||||
}
|
||||
@@ -168,7 +170,7 @@ namespace tunnel
|
||||
else
|
||||
{
|
||||
msg.nextFragmentNum++;
|
||||
HandleOutOfSequenceFragment (msgID, msg);
|
||||
HandleOutOfSequenceFragments (msgID, msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -192,40 +194,48 @@ namespace tunnel
|
||||
|
||||
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}));
|
||||
if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {isLastFragment, data, i2p::util::GetMillisecondsSinceEpoch () }}).second)
|
||||
LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID);
|
||||
}
|
||||
|
||||
void TunnelEndpoint::HandleOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
|
||||
void TunnelEndpoint::HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg)
|
||||
{
|
||||
auto it = m_OutOfSequenceFragments.find (msgID);
|
||||
if (it != m_OutOfSequenceFragments.end ())
|
||||
while (ConcatNextOutOfSequenceFragment (msgID, msg))
|
||||
{
|
||||
if (it->second.fragmentNum == msg.nextFragmentNum)
|
||||
if (!msg.nextFragmentNum) // message complete
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Out-of-sequence fragment ", (int)it->second.fragmentNum, " of message ", msgID, " found");
|
||||
size_t size = it->second.data->GetLength ();
|
||||
if (msg.data->len + size > msg.data->maxLen)
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
|
||||
auto newMsg = NewI2NPMessage ();
|
||||
*newMsg = *(msg.data);
|
||||
msg.data = newMsg;
|
||||
}
|
||||
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
|
||||
LogPrint (eLogError, "Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
|
||||
if (it->second.isLastFragment)
|
||||
{
|
||||
// message complete
|
||||
HandleNextMessage (msg);
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
}
|
||||
else
|
||||
msg.nextFragmentNum++;
|
||||
m_OutOfSequenceFragments.erase (it);
|
||||
}
|
||||
HandleNextMessage (msg);
|
||||
m_IncompleteMessages.erase (msgID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
|
||||
{
|
||||
auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum});
|
||||
if (it != m_OutOfSequenceFragments.end ())
|
||||
{
|
||||
LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found");
|
||||
size_t size = it->second.data->GetLength ();
|
||||
if (msg.data->len + size > msg.data->maxLen)
|
||||
{
|
||||
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
|
||||
auto newMsg = NewI2NPMessage ();
|
||||
*newMsg = *(msg.data);
|
||||
msg.data = newMsg;
|
||||
}
|
||||
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
|
||||
LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
|
||||
if (it->second.isLastFragment)
|
||||
// message complete
|
||||
msg.nextFragmentNum = 0;
|
||||
else
|
||||
msg.nextFragmentNum++;
|
||||
m_OutOfSequenceFragments.erase (it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TunnelEndpoint::HandleNextMessage (const TunnelMessageBlock& msg)
|
||||
@@ -262,6 +272,27 @@ namespace tunnel
|
||||
default:
|
||||
LogPrint (eLogError, "TunnelMessage: Unknown delivery type ", (int)msg.deliveryType);
|
||||
};
|
||||
}
|
||||
|
||||
void TunnelEndpoint::Cleanup ()
|
||||
{
|
||||
auto ts = i2p::util::GetMillisecondsSinceEpoch ();
|
||||
// out-of-sequence fragments
|
||||
for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();)
|
||||
{
|
||||
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
|
||||
it = m_OutOfSequenceFragments.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
// incomplete messages
|
||||
for (auto it = m_IncompleteMessages.begin (); it != m_IncompleteMessages.end ();)
|
||||
{
|
||||
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
|
||||
it = m_IncompleteMessages.erase (it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,14 +15,15 @@ namespace tunnel
|
||||
{
|
||||
struct TunnelMessageBlockEx: public TunnelMessageBlock
|
||||
{
|
||||
uint64_t receiveTime; // milliseconds since epoch
|
||||
uint8_t nextFragmentNum;
|
||||
};
|
||||
|
||||
struct Fragment
|
||||
{
|
||||
uint8_t fragmentNum;
|
||||
bool isLastFragment;
|
||||
std::shared_ptr<I2NPMessage> data;
|
||||
uint64_t receiveTime; // milliseconds since epoch
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -30,7 +31,8 @@ namespace tunnel
|
||||
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
|
||||
~TunnelEndpoint ();
|
||||
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
|
||||
|
||||
void Cleanup ();
|
||||
|
||||
void HandleDecryptedTunnelDataMsg (std::shared_ptr<I2NPMessage> msg);
|
||||
|
||||
private:
|
||||
@@ -39,12 +41,13 @@ namespace tunnel
|
||||
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);
|
||||
|
||||
bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added
|
||||
void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg);
|
||||
|
||||
private:
|
||||
|
||||
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
|
||||
std::map<uint32_t, Fragment> m_OutOfSequenceFragments;
|
||||
std::map<std::pair<uint32_t, uint8_t>, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment
|
||||
bool m_IsInbound;
|
||||
size_t m_NumReceivedBytes;
|
||||
};
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
TunnelGatewayBuffer::TunnelGatewayBuffer (uint32_t tunnelID): m_TunnelID (tunnelID),
|
||||
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0)
|
||||
TunnelGatewayBuffer::TunnelGatewayBuffer ():
|
||||
m_CurrentTunnelDataMsg (nullptr), m_RemainingSize (0)
|
||||
{
|
||||
RAND_bytes (m_NonZeroRandomBuffer, TUNNEL_DATA_MAX_PAYLOAD_SIZE);
|
||||
for (size_t i = 0; i < TUNNEL_DATA_MAX_PAYLOAD_SIZE; i++)
|
||||
@@ -20,6 +20,7 @@ namespace tunnel
|
||||
|
||||
TunnelGatewayBuffer::~TunnelGatewayBuffer ()
|
||||
{
|
||||
ClearTunnelDataMsgs ();
|
||||
}
|
||||
|
||||
void TunnelGatewayBuffer::PutI2NPMsg (const TunnelMessageBlock& block)
|
||||
@@ -48,7 +49,7 @@ namespace tunnel
|
||||
di[0] = block.deliveryType << 5; // set delivery type
|
||||
|
||||
// create fragments
|
||||
std::shared_ptr<I2NPMessage> msg = block.data;
|
||||
const std::shared_ptr<I2NPMessage> & msg = block.data;
|
||||
size_t fullMsgLen = diLen + msg->GetLength () + 2; // delivery instructions + payload + 2 bytes length
|
||||
if (fullMsgLen <= m_RemainingSize)
|
||||
{
|
||||
@@ -115,9 +116,13 @@ namespace tunnel
|
||||
m_CurrentTunnelDataMsg->len += s+7;
|
||||
if (isLastFragment)
|
||||
{
|
||||
m_RemainingSize -= s+7;
|
||||
if (!m_RemainingSize)
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
if(m_RemainingSize < (s+7)) {
|
||||
LogPrint (eLogError, "TunnelGateway: remaining size overflow: ", m_RemainingSize, " < ", s+7);
|
||||
} else {
|
||||
m_RemainingSize -= s+7;
|
||||
if (m_RemainingSize == 0)
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
}
|
||||
}
|
||||
else
|
||||
CompleteCurrentTunnelDataMessage ();
|
||||
@@ -138,10 +143,12 @@ namespace tunnel
|
||||
void TunnelGatewayBuffer::ClearTunnelDataMsgs ()
|
||||
{
|
||||
m_TunnelDataMsgs.clear ();
|
||||
m_CurrentTunnelDataMsg = nullptr;
|
||||
}
|
||||
|
||||
void TunnelGatewayBuffer::CreateCurrentTunnelDataMessage ()
|
||||
{
|
||||
m_CurrentTunnelDataMsg = nullptr;
|
||||
m_CurrentTunnelDataMsg = NewI2NPShortMessage ();
|
||||
m_CurrentTunnelDataMsg->Align (12);
|
||||
// we reserve space for padding
|
||||
@@ -158,7 +165,6 @@ namespace tunnel
|
||||
|
||||
m_CurrentTunnelDataMsg->offset = m_CurrentTunnelDataMsg->len - TUNNEL_DATA_MSG_SIZE - I2NP_HEADER_SIZE;
|
||||
uint8_t * buf = m_CurrentTunnelDataMsg->GetPayload ();
|
||||
htobe32buf (buf, m_TunnelID);
|
||||
RAND_bytes (buf + 4, 16); // original IV
|
||||
memcpy (payload + size, buf + 4, 16); // copy IV for checksum
|
||||
uint8_t hash[32];
|
||||
@@ -196,15 +202,19 @@ namespace tunnel
|
||||
void TunnelGateway::SendBuffer ()
|
||||
{
|
||||
m_Buffer.CompleteCurrentTunnelDataMessage ();
|
||||
auto tunnelMsgs = m_Buffer.GetTunnelDataMsgs ();
|
||||
for (auto& tunnelMsg : tunnelMsgs)
|
||||
std::vector<std::shared_ptr<I2NPMessage> > newTunnelMsgs;
|
||||
const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs ();
|
||||
for (auto& tunnelMsg : tunnelDataMsgs)
|
||||
{
|
||||
m_Tunnel->EncryptTunnelMsg (tunnelMsg, tunnelMsg);
|
||||
tunnelMsg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
auto newMsg = CreateEmptyTunnelDataMsg ();
|
||||
m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg);
|
||||
htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ());
|
||||
newMsg->FillI2NPMessageHeader (eI2NPTunnelData);
|
||||
newTunnelMsgs.push_back (newMsg);
|
||||
m_NumSentBytes += TUNNEL_DATA_MSG_SIZE;
|
||||
}
|
||||
i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), tunnelMsgs);
|
||||
m_Buffer.ClearTunnelDataMsgs ();
|
||||
i2p::transport::transports.SendMessages (m_Tunnel->GetNextIdentHash (), newTunnelMsgs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,10 +14,10 @@ namespace tunnel
|
||||
class TunnelGatewayBuffer
|
||||
{
|
||||
public:
|
||||
TunnelGatewayBuffer (uint32_t tunnelID);
|
||||
TunnelGatewayBuffer ();
|
||||
~TunnelGatewayBuffer ();
|
||||
void PutI2NPMsg (const TunnelMessageBlock& block);
|
||||
const std::vector<std::shared_ptr<I2NPMessage> >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
|
||||
const std::vector<std::shared_ptr<const I2NPMessage> >& GetTunnelDataMsgs () const { return m_TunnelDataMsgs; };
|
||||
void ClearTunnelDataMsgs ();
|
||||
void CompleteCurrentTunnelDataMessage ();
|
||||
|
||||
@@ -27,8 +27,7 @@ namespace tunnel
|
||||
|
||||
private:
|
||||
|
||||
uint32_t m_TunnelID;
|
||||
std::vector<std::shared_ptr<I2NPMessage> > m_TunnelDataMsgs;
|
||||
std::vector<std::shared_ptr<const I2NPMessage> > m_TunnelDataMsgs;
|
||||
std::shared_ptr<I2NPMessage> m_CurrentTunnelDataMsg;
|
||||
size_t m_RemainingSize;
|
||||
uint8_t m_NonZeroRandomBuffer[TUNNEL_DATA_MAX_PAYLOAD_SIZE];
|
||||
@@ -39,7 +38,7 @@ namespace tunnel
|
||||
public:
|
||||
|
||||
TunnelGateway (TunnelBase * tunnel):
|
||||
m_Tunnel (tunnel), m_Buffer (tunnel->GetNextTunnelID ()), m_NumSentBytes (0) {};
|
||||
m_Tunnel (tunnel), m_NumSentBytes (0) {};
|
||||
void SendTunnelDataMsg (const TunnelMessageBlock& block);
|
||||
void PutTunnelDataMsg (const TunnelMessageBlock& block);
|
||||
void SendBuffer ();
|
||||
|
||||
145
TunnelPool.cpp
145
TunnelPool.cpp
@@ -7,15 +7,22 @@
|
||||
#include "Garlic.h"
|
||||
#include "Transports.h"
|
||||
#include "Log.h"
|
||||
#include "Tunnel.h"
|
||||
#include "TunnelPool.h"
|
||||
#include "Destination.h"
|
||||
#ifdef WITH_EVENTS
|
||||
#include "Event.h"
|
||||
#endif
|
||||
|
||||
namespace i2p
|
||||
{
|
||||
namespace tunnel
|
||||
{
|
||||
|
||||
TunnelPool::TunnelPool (int numInboundHops, int numOutboundHops, int numInboundTunnels, int numOutboundTunnels):
|
||||
m_NumInboundHops (numInboundHops), m_NumOutboundHops (numOutboundHops),
|
||||
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true)
|
||||
m_NumInboundTunnels (numInboundTunnels), m_NumOutboundTunnels (numOutboundTunnels), m_IsActive (true),
|
||||
m_CustomPeerSelector(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -66,17 +73,25 @@ namespace tunnel
|
||||
{
|
||||
if (!m_IsActive) return;
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnels.created", createdTunnel);
|
||||
#endif
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
m_InboundTunnels.insert (createdTunnel);
|
||||
}
|
||||
if (m_LocalDestination)
|
||||
m_LocalDestination->SetLeaseSetUpdated ();
|
||||
|
||||
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
|
||||
}
|
||||
|
||||
void TunnelPool::TunnelExpired (std::shared_ptr<InboundTunnel> expiredTunnel)
|
||||
{
|
||||
if (expiredTunnel)
|
||||
{
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnels.expired", expiredTunnel);
|
||||
#endif
|
||||
expiredTunnel->SetTunnelPool (nullptr);
|
||||
for (auto& it: m_Tests)
|
||||
if (it.second.second == expiredTunnel) it.second.second = nullptr;
|
||||
@@ -90,9 +105,14 @@ namespace tunnel
|
||||
{
|
||||
if (!m_IsActive) return;
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnels.created", createdTunnel);
|
||||
#endif
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
m_OutboundTunnels.insert (createdTunnel);
|
||||
}
|
||||
OnTunnelBuildResult(createdTunnel, eBuildResultOkay);
|
||||
|
||||
//CreatePairedInboundTunnel (createdTunnel);
|
||||
}
|
||||
|
||||
@@ -100,6 +120,9 @@ namespace tunnel
|
||||
{
|
||||
if (expiredTunnel)
|
||||
{
|
||||
#ifdef WITH_EVENTS
|
||||
EmitTunnelEvent("tunnels.expired", expiredTunnel);
|
||||
#endif
|
||||
expiredTunnel->SetTunnelPool (nullptr);
|
||||
for (auto& it: m_Tests)
|
||||
if (it.second.first == expiredTunnel) it.second.first = nullptr;
|
||||
@@ -128,13 +151,13 @@ namespace tunnel
|
||||
|
||||
std::shared_ptr<OutboundTunnel> TunnelPool::GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
return GetNextTunnel (m_OutboundTunnels, excluded);
|
||||
}
|
||||
|
||||
std::shared_ptr<InboundTunnel> TunnelPool::GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded) const
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
return GetNextTunnel (m_InboundTunnels, excluded);
|
||||
}
|
||||
|
||||
@@ -148,11 +171,27 @@ namespace tunnel
|
||||
{
|
||||
if (it->IsEstablished () && it != excluded)
|
||||
{
|
||||
if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) {
|
||||
i ++;
|
||||
continue;
|
||||
}
|
||||
tunnel = it;
|
||||
i++;
|
||||
}
|
||||
if (i > ind && tunnel) break;
|
||||
}
|
||||
if(HasLatencyRequirement() && !tunnel) {
|
||||
ind = rand () % (tunnels.size ()/2 + 1), i = 0;
|
||||
for (const auto& it: tunnels)
|
||||
{
|
||||
if (it->IsEstablished () && it != excluded)
|
||||
{
|
||||
tunnel = it;
|
||||
i++;
|
||||
}
|
||||
if (i > ind && tunnel) break;
|
||||
}
|
||||
}
|
||||
if (!tunnel && excluded && excluded->IsEstablished ()) tunnel = excluded;
|
||||
return tunnel;
|
||||
}
|
||||
@@ -180,15 +219,6 @@ namespace tunnel
|
||||
void TunnelPool::CreateTunnels ()
|
||||
{
|
||||
int num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (const auto& it : m_InboundTunnels)
|
||||
if (it->IsEstablished ()) num++;
|
||||
}
|
||||
for (int i = num; i < m_NumInboundTunnels; i++)
|
||||
CreateInboundTunnel ();
|
||||
|
||||
num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_OutboundTunnelsMutex);
|
||||
for (const auto& it : m_OutboundTunnels)
|
||||
@@ -196,6 +226,18 @@ namespace tunnel
|
||||
}
|
||||
for (int i = num; i < m_NumOutboundTunnels; i++)
|
||||
CreateOutboundTunnel ();
|
||||
|
||||
num = 0;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(m_InboundTunnelsMutex);
|
||||
for (const auto& it : m_InboundTunnels)
|
||||
if (it->IsEstablished ()) num++;
|
||||
}
|
||||
for (int i = num; i < m_NumInboundTunnels; i++)
|
||||
CreateInboundTunnel ();
|
||||
|
||||
if (num < m_NumInboundTunnels && m_NumInboundHops <= 0 && m_LocalDestination) // zero hops IB
|
||||
m_LocalDestination->SetLeaseSetUpdated (); // update LeaseSet immediately
|
||||
}
|
||||
|
||||
void TunnelPool::TestTunnels ()
|
||||
@@ -303,7 +345,12 @@ namespace tunnel
|
||||
test.first->SetState (eTunnelStateEstablished);
|
||||
if (test.second->GetState () == eTunnelStateTestFailed)
|
||||
test.second->SetState (eTunnelStateEstablished);
|
||||
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", i2p::util::GetMillisecondsSinceEpoch () - timestamp, " milliseconds");
|
||||
uint64_t dlt = i2p::util::GetMillisecondsSinceEpoch () - timestamp;
|
||||
LogPrint (eLogDebug, "Tunnels: test of ", msgID, " successful. ", dlt, " milliseconds");
|
||||
// update latency
|
||||
uint64_t latency = dlt / 2;
|
||||
test.first->AddLatencySample(latency);
|
||||
test.second->AddLatencySample(latency);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -327,9 +374,18 @@ namespace tunnel
|
||||
|
||||
bool TunnelPool::SelectPeers (std::vector<std::shared_ptr<const i2p::data::IdentityEx> >& peers, bool isInbound)
|
||||
{
|
||||
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
|
||||
int numHops = isInbound ? m_NumInboundHops : m_NumOutboundHops;
|
||||
if (numHops <= 0) return true; // peers is empty
|
||||
// peers is empty
|
||||
if (numHops <= 0) return true;
|
||||
// custom peer selector in use ?
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
|
||||
if (m_CustomPeerSelector)
|
||||
return m_CustomPeerSelector->SelectPeers(peers, numHops, isInbound);
|
||||
}
|
||||
// explicit peers in use
|
||||
if (m_ExplicitPeers) return SelectExplicitPeers (peers, isInbound);
|
||||
|
||||
auto prevHop = i2p::context.GetSharedRouterInfo ();
|
||||
if(i2p::transport::transports.RoutesRestricted())
|
||||
{
|
||||
@@ -477,6 +533,61 @@ namespace tunnel
|
||||
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
|
||||
auto tunnel = tunnels.CreateInboundTunnel (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel);
|
||||
tunnel->SetTunnelPool (shared_from_this ());
|
||||
}
|
||||
}
|
||||
|
||||
void TunnelPool::SetCustomPeerSelector(TunnelPeerSelector selector)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
|
||||
m_CustomPeerSelector = selector;
|
||||
}
|
||||
|
||||
void TunnelPool::UnsetCustomPeerSelector()
|
||||
{
|
||||
SetCustomPeerSelector(nullptr);
|
||||
}
|
||||
|
||||
bool TunnelPool::HasCustomPeerSelector()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_CustomPeerSelectorMutex);
|
||||
return m_CustomPeerSelector != nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<InboundTunnel> TunnelPool::GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude) const
|
||||
{
|
||||
std::shared_ptr<InboundTunnel> tun = nullptr;
|
||||
std::unique_lock<std::mutex> lock(m_InboundTunnelsMutex);
|
||||
uint64_t min = 1000000;
|
||||
for (const auto & itr : m_InboundTunnels) {
|
||||
if(!itr->LatencyIsKnown()) continue;
|
||||
auto l = itr->GetMeanLatency();
|
||||
if (l >= min) continue;
|
||||
tun = itr;
|
||||
if(tun == exclude) continue;
|
||||
min = l;
|
||||
}
|
||||
return tun;
|
||||
}
|
||||
|
||||
std::shared_ptr<OutboundTunnel> TunnelPool::GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude) const
|
||||
{
|
||||
std::shared_ptr<OutboundTunnel> tun = nullptr;
|
||||
std::unique_lock<std::mutex> lock(m_OutboundTunnelsMutex);
|
||||
uint64_t min = 1000000;
|
||||
for (const auto & itr : m_OutboundTunnels) {
|
||||
if(!itr->LatencyIsKnown()) continue;
|
||||
auto l = itr->GetMeanLatency();
|
||||
if (l >= min) continue;
|
||||
tun = itr;
|
||||
if(tun == exclude) continue;
|
||||
min = l;
|
||||
}
|
||||
return tun;
|
||||
}
|
||||
|
||||
void TunnelPool::OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result)
|
||||
{
|
||||
auto peers = tunnel->GetPeers();
|
||||
if(m_CustomPeerSelector) m_CustomPeerSelector->OnBuildResult(peers, tunnel->IsInbound(), result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
45
TunnelPool.h
45
TunnelPool.h
@@ -23,6 +23,25 @@ namespace tunnel
|
||||
class InboundTunnel;
|
||||
class OutboundTunnel;
|
||||
|
||||
|
||||
enum TunnelBuildResult {
|
||||
eBuildResultOkay, // tunnel was built okay
|
||||
eBuildResultRejected, // tunnel build was explicitly rejected
|
||||
eBuildResultTimeout // tunnel build timed out
|
||||
};
|
||||
|
||||
/** interface for custom tunnel peer selection algorithm */
|
||||
struct ITunnelPeerSelector
|
||||
{
|
||||
typedef std::shared_ptr<const i2p::data::IdentityEx> Peer;
|
||||
typedef std::vector<Peer> TunnelPath;
|
||||
|
||||
virtual bool SelectPeers(TunnelPath & peers, int hops, bool isInbound) = 0;
|
||||
virtual bool OnBuildResult(TunnelPath & peers, bool isInbound, TunnelBuildResult result) = 0;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<ITunnelPeerSelector> TunnelPeerSelector;
|
||||
|
||||
class TunnelPool: public std::enable_shared_from_this<TunnelPool> // per local destination
|
||||
{
|
||||
public:
|
||||
@@ -45,7 +64,6 @@ namespace tunnel
|
||||
std::shared_ptr<OutboundTunnel> GetNextOutboundTunnel (std::shared_ptr<OutboundTunnel> excluded = nullptr) const;
|
||||
std::shared_ptr<InboundTunnel> GetNextInboundTunnel (std::shared_ptr<InboundTunnel> excluded = nullptr) const;
|
||||
std::shared_ptr<OutboundTunnel> GetNewOutboundTunnel (std::shared_ptr<OutboundTunnel> old) const;
|
||||
|
||||
void TestTunnels ();
|
||||
void ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg);
|
||||
void ProcessDeliveryStatus (std::shared_ptr<I2NPMessage> msg);
|
||||
@@ -56,9 +74,25 @@ namespace tunnel
|
||||
|
||||
int GetNumInboundTunnels () const { return m_NumInboundTunnels; };
|
||||
int GetNumOutboundTunnels () const { return m_NumOutboundTunnels; };
|
||||
|
||||
private:
|
||||
|
||||
void SetCustomPeerSelector(TunnelPeerSelector selector);
|
||||
void UnsetCustomPeerSelector();
|
||||
bool HasCustomPeerSelector();
|
||||
|
||||
/** @brief make this tunnel pool yield tunnels that fit latency range [min, max] */
|
||||
void RequireLatency(uint64_t min, uint64_t max) { m_MinLatency = min; m_MaxLatency = max; }
|
||||
|
||||
/** @brief return true if this tunnel pool has a latency requirement */
|
||||
bool HasLatencyRequirement() const { return m_MinLatency > 0 && m_MaxLatency > 0; }
|
||||
|
||||
/** @brief get the lowest latency tunnel in this tunnel pool regardless of latency requirements */
|
||||
std::shared_ptr<InboundTunnel> GetLowestLatencyInboundTunnel(std::shared_ptr<InboundTunnel> exclude=nullptr) const;
|
||||
std::shared_ptr<OutboundTunnel> GetLowestLatencyOutboundTunnel(std::shared_ptr<OutboundTunnel> exclude=nullptr) const;
|
||||
|
||||
void OnTunnelBuildResult(std::shared_ptr<Tunnel> tunnel, TunnelBuildResult result);
|
||||
|
||||
private:
|
||||
|
||||
void CreateInboundTunnel ();
|
||||
void CreateOutboundTunnel ();
|
||||
void CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel);
|
||||
@@ -80,7 +114,12 @@ namespace tunnel
|
||||
mutable std::mutex m_TestsMutex;
|
||||
std::map<uint32_t, std::pair<std::shared_ptr<OutboundTunnel>, std::shared_ptr<InboundTunnel> > > m_Tests;
|
||||
bool m_IsActive;
|
||||
std::mutex m_CustomPeerSelectorMutex;
|
||||
TunnelPeerSelector m_CustomPeerSelector;
|
||||
|
||||
uint64_t m_MinLatency=0; // if > 0 this tunnel pool will try building tunnels with minimum latency by ms
|
||||
uint64_t m_MaxLatency=0; // if > 0 this tunnel pool will try building tunnels with maximum latency by ms
|
||||
|
||||
public:
|
||||
|
||||
// for HTTP only
|
||||
|
||||
3
UPnP.cpp
3
UPnP.cpp
@@ -66,10 +66,13 @@ namespace transport
|
||||
try
|
||||
{
|
||||
m_Service.run ();
|
||||
// Discover failed
|
||||
break; // terminate the thread
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogPrint (eLogError, "UPnP: runtime exception: ", ex.what ());
|
||||
PortMapping ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user